User:Nw520/AutoReplace.js
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)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
// <nowiki>
/**
* AutoReplace
*
* Maintainer: [[m:User:nw520]]
*/
/* eslint-disable es/no-object-entries */
/* eslint-disable es/no-object-fromentries */
/* eslint-disable no-restricted-properties */
/* eslint-disable no-restricted-syntax */
$.ready.then( function () {
let dialog = null;
let windowManager = null;
/** @type {Array<Object>} */
const replacementRules = window.nw520_config?.autoReplace_replacements ?? [];
function main() {
if ( mw.config.get( 'wgPageContentModel' ) !== 'wikitext' || new URL( window.location.href ).searchParams.get( 'undo' ) !== null || replacementRules === null ) {
return;
}
const [ autoReplacements, manualReplacements ] = replacementRules.reduce( ( [ accAuto, accManual ], val ) => {
if ( val.manual !== undefined ) {
accManual.push( val );
} else {
accAuto.push( val );
}
return [ accAuto, accManual ];
}, [ [], [] ] );
// Execute auto rules
const urlSearchParams = new URLSearchParams( window.location.search );
if ( mw.config.get( 'wgAction' ) === 'edit' && ![ 'off', 'no' ].includes( urlSearchParams.get( 'autoreplace' )?.toLowerCase() ) ) {
performReplacements( autoReplacements, true );
}
// Add action for manual rules
if ( manualReplacements.length > 0 ) {
addManualAction( manualReplacements );
}
}
async function addManualAction( manualReplacements ) {
await $.when( mw.loader.using( 'ext.wikiEditor' ), $.ready );
const groups = manualReplacements.reduce( ( acc, rule ) => {
if ( acc[ rule.manual ] === undefined ) {
acc[ rule.manual ] = [];
}
acc[ rule.manual ].push( rule );
return acc;
}, {} );
// eslint-disable-next-line no-jquery/no-global-selector
$( '#wpTextbox1' ).wikiEditor( 'addToToolbar', {
section: 'main',
group: 'format',
tools: {
AutoReplace: {
label: 'AutoReplace',
type: 'button',
icon: '//upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Noun_Project_replace_icon_581723.svg/20px-Noun_Project_replace_icon_581723.svg.png',
action: {
type: 'callback',
execute: async function ( context ) {
const filteredGroups = Object.fromEntries( Object.entries( groups ).filter( ( [ groupName, groupRules ] ) => {
const groupApplies = performReplacements( groups[ groupName ], false, true );
return groupApplies;
} ) );
if ( Object.keys( filteredGroups ).length === 0 ) {
mw.notify( 'Es gibt keine anwendbaren Ersetzungsregeln.' );
return;
}
const selectedGroup = await manualWindow( filteredGroups );
performReplacements( groups[ selectedGroup ], false );
}
}
}
}
} );
}
/**
* @param {string} text
* @param {string} group
*/
function appendSummary( text, group ) {
let summaryValue = document.forms.editform.wpSummary.value;
const groups = {};
// Parse
let section = null;
const sectionParts = summaryValue.match( /^\/\* (?<section>[^*]+) \*\/(?: *)(?<summaryValue>.*)$/ );
if ( sectionParts !== null ) {
section = sectionParts.groups.section;
summaryValue = sectionParts.groups.summaryValue;
}
for ( let groupSegment of summaryValue.split( '; ' ).filter( ( segment ) => segment !== '' ) ) {
let groupName;
const groupParts = groupSegment.match( /^(?<group>.+?): (?<text>.+?)$/ );
if ( groupParts === null ) {
groupName = groupSegment;
groupSegment = '';
} else {
groupName = groupParts.groups.group;
groupSegment = groupParts.groups.text;
}
if ( groups[ groupName ] === undefined ) {
groups[ groupName ] = [];
}
groups[ groupName ] = [ ...groups[ groupName ], ...groupSegment.split( ', ' ).filter( ( segment ) => segment !== '' ) ];
}
// Append
if ( groups[ group ?? text ] === undefined ) {
groups[ group ?? text ] = [];
}
if ( group !== null && !groups[ group ].includes( text ) ) {
groups[ group ].push( text );
}
// Stringify
const newSummary = `${section === null ? '' : `/* ${section} */ `}${Object.entries( groups ).map( ( [ groupName, groupValues ] ) => {
const valueDelimiter = groupName === '' ? '; ' : ', ';
if ( groupValues.length === 0 ) {
return `${groupName}`;
} else {
return `${groupName}: ${groupValues.join( valueDelimiter )}`;
}
} ).join( '; ' )}`;
document.forms.editform.wpSummary.value = newSummary;
}
function manualWindow( groups ) {
return new Promise( ( resolve ) => {
mw.loader.using( [ 'oojs-ui' ] ).then( () => {
const AutoReplaceWindow = function ( config ) {
AutoReplaceWindow.super.call( this, config );
this.groups = groups;
};
OO.inheritClass( AutoReplaceWindow, OO.ui.ProcessDialog );
AutoReplaceWindow.static.name = 'autoreplace';
AutoReplaceWindow.static.title = 'AutoReplace';
AutoReplaceWindow.static.actions = [
{ action: 'close', label: 'Close', flags: [ 'safe', 'close' ] }
];
AutoReplaceWindow.prototype.getBodyHeight = function () {
return 250;
};
AutoReplaceWindow.prototype.initialize = function () {
AutoReplaceWindow.super.prototype.initialize.apply( this, arguments );
this.content = new OO.ui.PanelLayout( { padded: true } );
const div = document.createElement( 'div' );
for ( const groupName of Object.keys( this.groups ) ) {
const button = new OO.ui.ButtonWidget( {
label: groupName
} );
button.$element[ 0 ].addEventListener( 'click', ( e ) => {
e.preventDefault();
resolve( groupName );
this.close();
} );
div.insertAdjacentElement( 'beforeend', button.$element[ 0 ] );
}
this.content.$element.append( div );
this.$body.append( this.content.$element );
};
/**
* @param {string} action
* @return {OO.ui.Process}
*/
AutoReplaceWindow.prototype.getActionProcess = function ( action ) {
return AutoReplaceWindow.super.prototype.getActionProcess.call( this, action ).next( () => {
const state = this.close( { action: action } );
return state.closed;
}, this );
};
// Create a window manager. Specify the name of the factory with the ‘factory’ config.
if ( windowManager === null ) {
windowManager = new OO.ui.WindowManager();
$( document.body ).append( windowManager.$element );
}
dialog = new AutoReplaceWindow( groups );
windowManager.addWindows( [ dialog ] );
windowManager.openWindow( dialog );
} );
} );
}
function performReplacements( replacements, autoReplace = false, test = false ) {
// eslint-disable-next-line no-jquery/no-global-selector
const $textbox = $( '#wpTextbox1' );
let madeChanges = false;
let lockSave = false;
// Apply replacements
for ( const replaceRule of replacements ) {
const maxIterations = replaceRule?.iterative === true ? Number.MAX_SAFE_INTEGER : ( replaceRule?.iterative ?? 1 );
let changesMade = false;
let justMadeChanges = false;
let iterations = 0;
let newText = $textbox.val();
do {
if ( iterations >= maxIterations ) {
console.warn( '[AutoReplace] Rule reached iteration limit: ', replaceRule );
break;
}
const newerText = newText.replace( replaceRule.find, replaceRule.replace );
justMadeChanges = newText !== newerText;
newText = newerText;
changesMade = changesMade || justMadeChanges;
iterations++;
} while ( justMadeChanges && !test && maxIterations !== 1 );
if ( changesMade ) {
if ( test ) {
return true;
}
$textbox.val( newText );
madeChanges = true;
lockSave = lockSave || ( replaceRule.trivial !== undefined ? !replaceRule.trivial : true );
// Append to summary
if ( replaceRule.comment !== undefined ) {
const group = typeof replaceRule.comment === 'string' ? null : replaceRule.comment[ 0 ];
const comment = typeof replaceRule.comment === 'string' ? replaceRule.comment : replaceRule.comment[ 1 ];
appendSummary( comment, group );
}
}
}
if ( lockSave ) {
// Lock save-button
document.getElementById( 'wpSave' ).addEventListener( 'click', function ( e ) {
if ( !e.ctrlKey ) {
e.preventDefault();
mw.notify( 'Es wurden automatische Änderungen durchgeführt. Bitte diese erst überprüfen.' );
}
} );
}
if ( madeChanges && ( window.nw520_config?.autoReplace_autoDiff === true || window.nw520_config?.autoReplace_autoDiff === 'auto' && autoReplace ) ) {
document.getElementById( 'wpDiff' ).click();
}
return madeChanges;
}
window.AutoReplace = {
performReplacements: performReplacements
};
main();
} );
// </nowiki>