User:Inductiveload/save load actions.js

/** * Simple replace-on-load-or-save framework * * Configurations: *    enable:      disable the entire gadget on a page *    debug:       enable some ill-defined debug logging *    namespaces:  enable the gadget only on the given namespaces *    saveHooks:   list of callback functions to run, in order in Save or Preview. *                  One param is given: the main edit box. *    loadHooks:   list of callbacks to run when loaded. *    quickReplacements: list of regex/replacement pairs to apply, in order *                on save. *    quickReplacements_global: true if all quick replacements should be *                 made global regexes. Default: true. *    autoRefs:   enable automatic refs insertion. Default: true * * Configure it like this, from your main JS: * * mw.hook( 'save_load_actions.config' ).add( function ( cfg ) { * *  var repls = [ *     [ /^c,, ?(.*)$/m, '$1' ], *   ]; * *   cfg.enabled = true; *   cfg.namespaces = [ 'Page', '', 'Author', 'User' ]; *   cfg.saveHooks = [ yourFunction ]; *   cfg.quickReplacements = repls; * } ); * */

// uncomment when running locally - gadgets get an auto-wrapped closure ( function ( $, mw ) {

'use strict';

const gadgetName = 'save_load_actions';

const SLAState = { config: { enabled: true, debug: false, namespaces: [ 'Page' ], saveHooks: [], loadHooks: [], quickReplacements: [], autoRefs: true }	};

function slaLog( text ) { if ( SLAState.debug ) { // eslint-disable-next-line no-console console.log( text ); }	}

function updateTextarea( editbox, content ) { // deal with CodeMirror? editbox.value = content; }

/*	 * Iterate the user's regexes and apply them in order, globally to the editbox */	function doQuickReplacements( editbox ) { let content = editbox.value; const qReps = SLAState.config.quickReplacements;

for ( let i = 0; i < qReps.length; i++ ) {

let flags = qReps[ i ][ 0 ].flags; // all quick replacements are global flags = 'g' + flags.replace( 'g', '' ); const re = new RegExp( qReps[ i ][ 0 ].source, flags ); content = content.replace( re, qReps[ i ][ 1 ] ); }		updateTextarea( editbox, content ); }

function getPageNum { return parseInt( mw.config.get( 'wgTitle' ).split( '/' ).slice( -1 )[ 0 ] ); }

/*	 * Move things that look like autorefs to the right place */	function doAutoRefs( editbox ) {

// first split up the text a pick out ref bodies let text = editbox.value;

const refBodyRe = /(?:^|\n)\* *(r[0-9]+)(?:=?(\w*)) *((?:.|\n)*?)(?=\n\n|\n\*|\n*$)/g;

// Iterate matchs for footnote bodies, replacing as we go

// Collect refs we found - we'll delete the bodies at the end // because we're going to badly mess with the indexes // We don't wish to just delete all of them, in case there's a body // and no usage - then deleting it would just delete the text, not move it		const foundNums = [];

// Git gud // eslint-disable-next-line no-restricted-properties const matches = text.matchAll( refBodyRe );

for ( const match of matches ) { const num = match[ 1 ]; const name = match[ 2 ]; const body = match[ 3 ];

// may need an autoname const autoName = name || ( 'p' + getPageNum + '_' + num );

// this is a follow-ref, place it at the top if ( num === 'r0' ) { const refMarkup = ' ';

// prepend to whole text field text = refMarkup + '\n' + text;

foundNums.push( num ); } else { // a normal reference

const reStr = '<\\s*' + num + '\\s*\\/?\\s*>';

const usageMatches = text.match( new RegExp( reStr, 'g' ) );

if ( usageMatches ) { if ( usageMatches.length === 1 ) { let refMarkup = ''; if ( name ) { // named refMarkup += ' '; const ref2Markup = ' ';

// first instance with the content text = text.replace( new RegExp( reStr, '' ), ref1Markup ); // And the rest with the named ref only text = text.replace( new RegExp( reStr, 'g' ), ref2Markup );

foundNums.push( num ); }				}			}		}

// Finally, strip out the old bodies of the ones we found text = text.replace( refBodyRe, ( wholeMatch, num ) => {			if ( foundNums.indexOf( num ) !== -1 ) {				return '';			}			return wholeMatch;		} );

updateTextarea( editbox, text ); }

/*	 * Run user functions and replacements */	function slaActionOnSave {

slaLog( 'On save' );

const editbox = document.getElementById( 'wpTextbox1' );

doQuickReplacements( editbox );

if ( SLAState.config.autoRefs ) { doAutoRefs( editbox ); }

for ( let i = 0; i < SLAState.config.saveHooks.length; i++ ) { SLAState.config.saveHooks[ i ]( editbox ); }	}

/*	 * Run setup, apply any load functions */	function slaActionOnLoad {

const editbox = document.getElementById( 'wpTextbox1' );

if ( editbox ) {

for ( let i = 0; i < SLAState.config.loadHooks.length; i++ ) { SLAState.config.loadHooks[ i ]( editbox ); }		}

// Install the on-save hook // eslint-disable-next-line no-jquery/no-global-selector $( '.editButtons' ).on( 'click', slaActionOnSave ); }

function slaActionSetup {

mw.hook( gadgetName + '.config' ).fire( SLAState.config );

if ( !SLAState.config.enabled || SLAState.config.namespaces.indexOf( mw.config.get( 'wgCanonicalNamespace' ) ) === -1 ) { return; }

if ( [ 'edit', 'submit' ].indexOf( mw.config.get( 'wgAction' ) ) !== -1 ) { mw.loader.using( 'ext.proofreadpage.page', function {			// mimic code in the extension, there is a conditionnal deps on ext.wikiEditor.				if ( mw.user.options.get( 'usebetatoolbar' ) && mw.loader.getModuleNames.indexOf( 'ext.wikiEditor' ) !== -1 ) {					const loadDeps = [ 'ext.wikiEditor' ];					if ( mw.user.options.get( 'codemirror-syntax-highlight' ) === 1 ) {						loadDeps.push( 'ext.CodeMirror.lib' );					}					mw.loader.using( loadDeps, function { slaActionOnLoad; } );				} else {					slaActionOnLoad;				}			} ); }	}	$( slaActionSetup );

// eslint-disable-next-line no-undef }( jQuery, mediaWiki ) );