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

Jump to navigation Jump to search
No edit summary
No edit summary
Line 2: Line 2:
( function () {
( function () {
'use strict';
'use strict';
console.log('[PatchFormatter] script loaded');


var L = '{' + '{';
var L = '{' + '{';
Line 18: Line 20:
// ---- API ------------------------------------------------------------
// ---- API ------------------------------------------------------------
function getWikiData( templateName ) {
function getWikiData( templateName ) {
console.log('[PatchFormatter] getWikiData called:', templateName);
var api = new mw.Api();
var api = new mw.Api();
var text = L + '#invoke:MonsterDomosed/patchformatter|' + templateName + R;
var text = L + '#invoke:MonsterDomosed/patchformatter|' + templateName + R;
return api.get( {
return api.get( {
action: 'expandtemplates',
action: 'expandtemplates',
Line 27: Line 32:
formatversion: 2
formatversion: 2
} ).then( function ( data ) {
} ).then( function ( data ) {
console.log('[PatchFormatter] API response:', templateName, data);
var wikitext = ( data.expandtemplates && data.expandtemplates.wikitext ) || '';
var wikitext = ( data.expandtemplates && data.expandtemplates.wikitext ) || '';
return wikitext.split( ',' )
 
.map( function ( item ) { return item.trim().replace( /^'+|'+$/g, '' ); } )
var result = wikitext.split( ',' )
.filter( function ( item ) { return item.length > 0; } );
.map( function ( item ) {
return item.trim().replace( /^'+|'+$/g, '' );
} )
.filter( function ( item ) {
return item.length > 0;
} );
 
console.log('[PatchFormatter] parsed list:', templateName, result.length);
 
return result;
} );
} );
}
}
Line 36: Line 53:
// ---- PARSING --------------------------------------------------------
// ---- PARSING --------------------------------------------------------
function parseEntries( rawText ) {
function parseEntries( rawText ) {
console.log('[PatchFormatter] parseEntries input size:', rawText.length);
var entries = [];
var entries = [];
var current = null;
var current = null;
rawText.split( '\n' ).forEach( function ( line ) {
rawText.split( '\n' ).forEach( function ( line ) {
var stripped = line.trim();
var stripped = line.trim();
if ( stripped.indexOf( '-' ) === 0 ) {
if ( stripped.indexOf( '-' ) === 0 ) {
if ( current !== null ) { entries.push( current ); }
if ( current !== null ) {
entries.push( current );
}
current = stripped.slice( 1 ).trim();
current = stripped.slice( 1 ).trim();
} else if ( current !== null ) {
} else if ( current !== null ) {
Line 47: Line 70:
}
}
} );
} );
if ( current !== null ) { entries.push( current ); }


return entries
if ( current !== null ) {
.map( function ( e ) { return e.replace( /\s+/g, ' ' ).trim(); } )
entries.push( current );
.filter( function ( e ) { return e.length > 0; } );
}
 
var cleaned = entries
.map( function ( e ) {
return e.replace( /\s+/g, ' ' ).trim();
} )
.filter( function ( e ) {
return e.length > 0;
} );
 
console.log('[PatchFormatter] parsed entries:', cleaned.length);
 
return cleaned;
}
}


// ---- CLASSIFY -------------------------------------------------------
function classify( entry, heroes, items, gameModes ) {
function classify( entry, heroes, items, gameModes ) {
var idx = entry.indexOf( ':' );
var idx = entry.indexOf( ':' );
if ( idx !== -1 ) {
if ( idx !== -1 ) {
var prefix = entry.slice( 0, idx ).trim();
var prefix = entry.slice( 0, idx ).trim();
var rest = entry.slice( idx + 1 ).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 ( gameModes.indexOf( prefix ) !== -1 ) {
if ( items.indexOf( prefix ) !== -1 ) { return [ 'item', prefix, rest ]; }
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 ];
return [ 'general', null, entry ];
}
}


// ---- ICON / LINK WRAPPING ------------------------------------------
// ---- FORMAT BODY ----------------------------------------------------
function escapeRegExp( s ) {
function escapeRegExp( s ) {
return s.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
return s.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
Line 81: Line 125:


function wrap( terms, template ) {
function wrap( terms, template ) {
terms.slice().sort( function ( a, b ) { return b.length - a.length; } )
terms.slice().sort( function ( a, b ) {
.forEach( function ( term ) {
return b.length - a.length;
var pattern = new RegExp( '(?<!\\w)' + escapeRegExp( term ) + '(?!\\w)', 'g' );
} ).forEach( function ( term ) {
text = text.replace( pattern, function () {
 
return protect( template.replace( '%s', function () { return term; } ) );
var pattern = new RegExp( '(?<!\\w)' + escapeRegExp( term ) + '(?!\\w)', 'g' );
} );
 
text = text.replace( pattern, function () {
return protect( template.replace( '%s', function () {
return term;
} ) );
} );
} );
} );
}
}


Line 95: Line 144:


WIKI_LINKS.forEach( function ( pair ) {
WIKI_LINKS.forEach( function ( pair ) {
text = text.replace( pair[ 0 ], function () { return protect( pair[ 1 ] ); } );
text = text.replace( pair[ 0 ], function () {
return protect( pair[ 1 ] );
} );
} );
} );


Line 101: Line 152:
text = text.split( '\u0000' + i + '\u0000' ).join( protectedTokens[ i ] );
text = text.split( '\u0000' + i + '\u0000' ).join( protectedTokens[ i ] );
}
}
return text;
return text;
}
}


// ---- OUTPUT BUILDING ------------------------------------------------
// ---- OUTPUT ---------------------------------------------------------
function buildOutput( entries, heroes, abilities, items, gameModes ) {
function buildOutput( entries, heroes, abilities, items, gameModes ) {
console.log('[PatchFormatter] buildOutput start');
var general = [];
var general = [];
var modes = {}, modeOrder = [];
var modes = {}, modeOrder = [];
Line 113: Line 167:
entries.forEach( function ( entry ) {
entries.forEach( function ( entry ) {
var c = classify( entry, heroes, items, gameModes );
var c = classify( entry, heroes, items, gameModes );
var kind = c[ 0 ], name = c[ 1 ];
 
var kind = c[ 0 ];
var name = c[ 1 ];
var body = formatBody( c[ 2 ], heroes, abilities, items );
var body = formatBody( c[ 2 ], heroes, abilities, items );


Line 119: Line 175:
general.push( body );
general.push( body );
} else if ( kind === 'mode' ) {
} else if ( kind === 'mode' ) {
if ( !modes[ name ] ) { modes[ name ] = []; modeOrder.push( name ); }
if ( !modes[ name ] ) {
modes[ name ] = [];
modeOrder.push( name );
}
modes[ name ].push( body );
modes[ name ].push( body );
} else if ( kind === 'item' ) {
} else if ( kind === 'item' ) {
if ( !itemSections[ name ] ) { itemSections[ name ] = []; itemOrder.push( name ); }
if ( !itemSections[ name ] ) {
itemSections[ name ] = [];
itemOrder.push( name );
}
itemSections[ name ].push( body );
itemSections[ name ].push( body );
} else if ( kind === 'hero' ) {
} else if ( kind === 'hero' ) {
if ( !heroSections[ name ] ) { heroSections[ name ] = []; heroOrder.push( name ); }
if ( !heroSections[ name ] ) {
heroSections[ name ] = [];
heroOrder.push( name );
}
heroSections[ name ].push( body );
heroSections[ name ].push( body );
}
}
Line 132: Line 197:
var out = [];
var out = [];


general.forEach( function ( entry ) { out.push( '* ' + entry ); } );
general.forEach( function ( entry ) {
 
out.push( '* ' + entry );
modeOrder.forEach( function ( mode ) {
out.push( '', '== [[' + mode + ']] ==' );
modes[ mode ].forEach( function ( body ) { out.push( '* ' + body ); } );
} );
} );


if ( itemOrder.length ) {
console.log('[PatchFormatter] sections ready');
out.push( '', '== Items ==' );
itemOrder.forEach( function ( item ) {
out.push( '=== ' + L + 'ItemIcon|' + item + R + ' ===' );
itemSections[ item ].forEach( function ( body ) {
out.push( CHANGE_PREFIX + body );
out.push( '' );
} );
} );
}
 
if ( heroOrder.length ) {
out.push( '', '== Heroes ==', '' );
heroOrder.forEach( function ( hero ) {
out.push( '=== ' + L + 'HeroIcon|' + hero + R + ' ===' );
heroSections[ hero ].forEach( function ( body ) {
out.push( CHANGE_PREFIX + body );
out.push( '' );
} );
} );
}


return out.join( '\n' ).trim() + '\n';
return out.join( '\n' ).trim() + '\n';
Line 166: Line 208:
// ---- UI -------------------------------------------------------------
// ---- UI -------------------------------------------------------------
function init() {
function init() {
console.log('[PatchFormatter] init started');
var app = document.getElementById( 'patchformatter' );
var app = document.getElementById( 'patchformatter' );
if ( !app ) { return; }


var inputWidget = new OO.ui.MultilineTextInputWidget( {
if ( !app ) {
rows: 14,
console.error('[PatchFormatter] #patchformatter not found');
placeholder: 'Raw patch notes',
return;
autofocus: true
}
} );
 
console.log('[PatchFormatter] container found');
 
console.log('[PatchFormatter] OO check:', OO, OO && OO.ui);
 
try {
var inputWidget = new OO.ui.MultilineTextInputWidget( {
rows: 14,
placeholder: 'Raw patch notes',
autofocus: true
} );
 
var outputWidget = new OO.ui.MultilineTextInputWidget( {
rows: 20,
readOnly: true
} );
 
var formatButton = new OO.ui.ButtonWidget( {
label: 'Format',
flags: [ 'primary', 'progressive' ]
} );


var outputWidget = new OO.ui.MultilineTextInputWidget( {
var copyButton = new OO.ui.ButtonWidget( {
rows: 20,
label: 'Copy output',
readOnly: true
flags: [ 'progressive' ]
} );
} );


var formatButton = new OO.ui.ButtonWidget( {
var statusLabel = new OO.ui.LabelWidget( {
label: 'Format',
label: ''
flags: [ 'primary', 'progressive' ]
} );
} );


var copyButton = new OO.ui.ButtonWidget( {
console.log('[PatchFormatter] widgets created');
label: 'Copy output',
flags: [ 'progressive' ]
} );


var statusLabel = new OO.ui.LabelWidget( {
var buttonRow = new OO.ui.HorizontalLayout( {
label: ''
items: [ formatButton, copyButton, statusLabel ]
} );
} );


var buttonRow = new OO.ui.HorizontalLayout( {
var fieldset = new OO.ui.FieldsetLayout();
items: [ formatButton, copyButton, statusLabel ]
fieldset.$element.append( buttonRow.$element );
} );
var inputField = new OO.ui.FieldLayout( inputWidget, {
label: 'Raw patch notes',
align: 'top'
} );


var outputField = new OO.ui.FieldLayout( outputWidget, {
app.appendChild( fieldset.$element[ 0 ] );
label: 'Formatted wikitext',
align: 'top'
} );


// Fieldset groups everything in the required order.
console.log('[PatchFormatter] UI mounted');
var fieldset = new OO.ui.FieldsetLayout();
fieldset.addItems( [
inputField,
new OO.ui.FieldLayout( buttonRow, { align: 'top' } ),
outputField
] );


// Main wrapper panel.
formatButton.on( 'click', function () {
var panel = new OO.ui.PanelLayout( {
console.log('[PatchFormatter] format clicked');
expanded: false,
padded: true,
framed: true
} );
panel.$element.append( fieldset.$element );


app.appendChild( panel.$element[ 0 ] );
formatButton.setDisabled( true );
statusLabel.setLabel( 'Loading wiki data...' );


var listsPromise = null;
Promise.all( [
function loadLists() {
if ( !listsPromise ) {
listsPromise = Promise.all( [
getWikiData( 'get_released_heroes_list' ),
getWikiData( 'get_released_heroes_list' ),
getWikiData( 'get_all_released_item_names' ),
getWikiData( 'get_all_released_item_names' ),
getWikiData( 'get_all_released_abilities_list' )
getWikiData( 'get_all_released_abilities_list' )
] );
] ).then( function ( lists ) {
}
 
return listsPromise;
console.log('[PatchFormatter] lists loaded');
}
 
var heroes = lists[ 0 ];
var items = lists[ 1 ];
var abilities = lists[ 2 ];
 
var entries = parseEntries( inputWidget.getValue() );
 
var result = buildOutput( entries, heroes, abilities, items, GAME_MODE_SECTIONS );
 
outputWidget.setValue( result );
 
statusLabel.setLabel( 'Done' );


formatButton.on( 'click', function () {
} ).catch( function ( err ) {
formatButton.setDisabled( true );
console.error('[PatchFormatter] ERROR:', err);
statusLabel.setLabel( 'Loading wiki data...' );
statusLabel.setLabel( 'Error: ' + err );
loadLists().then( function ( lists ) {
} ).then( function () {
var heroes = lists[ 0 ], items = lists[ 1 ], abilities = lists[ 2 ];
formatButton.setDisabled( false );
var entries = parseEntries( inputWidget.getValue() );
} );
var result = buildOutput( entries, heroes, abilities, items, GAME_MODE_SECTIONS );
outputWidget.setValue( result );
statusLabel.setLabel(
'Processed ' + entries.length + ' entries (' +
heroes.length + ' heroes, ' + abilities.length + ' abilities, ' +
items.length + ' items).'
);
} ).catch( function ( err ) {
statusLabel.setLabel( 'Error: ' + err );
} ).then( function () {
formatButton.setDisabled( false );
} );
} );
} );


copyButton.on( 'click', function () {
copyButton.on( 'click', function () {
if ( navigator.clipboard ) {
console.log('[PatchFormatter] copy clicked');
navigator.clipboard.writeText( outputWidget.getValue() ).then( function () {
 
statusLabel.setLabel( 'Copied.' );
navigator.clipboard.writeText(
outputWidget.getValue()
).then( function () {
statusLabel.setLabel( 'Copied' );
} );
} );
} else {
} );
outputWidget.$input.select();
 
document.execCommand( 'copy' );
} catch ( e ) {
statusLabel.setLabel( 'Copied.' );
console.error('[PatchFormatter] INIT CRASH:', e);
}
}
} );
}
}


mw.loader.using([
mw.loader.using( [
'oojs-ui-core',
'oojs-ui-core',
'oojs-ui-widgets',
'oojs-ui-widgets',
'oojs-ui.styles',
'oojs-ui.styles',
'oojs-ui.styles.icons-editing-core',
'mediawiki.api',
'mediawiki.api',
'mediawiki.util'
'mediawiki.util'
]).then( function () {
] ).then( function () {
$( init );
console.log('[PatchFormatter] loader ready');
} );
$( init );
}() );
} );
 
}() );