Utilisateur:DreZhsh/AbuseFilterEditTools.js

Une page de Wikipédia, l'encyclopédie libre.
Note : après avoir enregistré la page, vous devrez forcer le rechargement complet du cache de votre navigateur pour voir les changements.

Mozilla / Firefox / Konqueror / Safari : maintenez la touche Majuscule (Shift) en cliquant sur le bouton Actualiser (Reload) ou pressez Maj-Ctrl-R (Cmd-R sur Apple Mac) ;

Chrome / Internet Explorer / Opera : maintenez la touche Ctrl en cliquant sur le bouton Actualiser ou pressez Ctrl-F5.
/* eslint-disable no-jquery/no-global-selector */
/* jshint esversion: 9 */
$(
	/**
	 * @name AbuseFilterEditTools
	 * @author DreZhsh <https://fr.wikipedia.org/wiki/Utilisateur:DreZhsh>
	 *
	 * Améiore l'interface d'édition des filtres
	 * en ajoutant différents outils :
	 * - Transformation des variables dépréciées par leurs alternatives
	 * - Mise en forme du code (séparation des opérateurs, mots-clés... par des espaces)
	 * - Amélioration de « Vérifier la syntaxe »
	 * - Diviser une chaîne => outil pour corriger les mauvaises séparations
	 * - Test de `ccnorm()`
	 *
	 * {{Catégorisation JS}}
	 */
	function () {
		if ( mw.config.get( 'wgCanonicalSpecialPageName' ) === 'AbuseFilter' && ( /\/(test|[0-9]+|tools)/ ).test( mw.config.get( 'wgPageName' ) ) ) {
			mw.loader.using( [ 'mediawiki.api', 'ext.abuseFilter.edit', 'oojs-ui-core' ], function () {
				mw.user.getRights().then( ( rights ) => {
					const buttonDeprecate = new OO.ui.ButtonWidget( {
							label: 'Corriger le code'
						} ),
						progressWidget = new OO.ui.ProgressBarWidget( { id: 'progressBarRqAf' } ),
						formContent = makeConcatString(),
						ccnormForm = makeCcnormTest();
					/**
					 * getInputContent
					 *
					 * Extrait le contenu de
					 * Ace Editor
					 *
					 * @return {string} Le contenu de Ace Editor
					 */
					function getInputContent() {
						const useAce = $( '#wpFilterRules' ).css( 'display' ) === 'none';
						if ( useAce ) {
							const globalArray = [];
							$( document.getElementsByClassName( 'ace_line' ) ).each( function () {
								const content = $( this ).text()
									.replace( /^\u0020{4}/, '\t' )
									.replace( /\u0020*$/, '' );
								globalArray.push( content );
							} );
							return globalArray.join( '\n' );
						} else {
							return document.getElementById( 'wpFilterRules' ).value;

						}
					}

					/**
					 * beautifyCode
					 *
					 * @return {string} Le contenu de Ace Editor
					 * mais avec les éléments de la syntaxe séparés par
					 * des espaces
					 */
					function beautifyCode() {
						const globalArray = [];
						$( '.ace_line' ).each( function () {
							const textParse = String( $( this ).text() );
							const inputContent = String( $( this ).html() )
								.replace(
									/<span class="ace_keyword ace_operator">!<\/span>/,
									'!'
								)
								.replace(
								// eslint-disable-next-line max-len
									/(<span class="ace_support ace_function">)([a-z_]*)<\/span>[\s]*<span class="ace_paren ace_lparen">\((<\/span>)/gi,
									'$1$2($3'
								)
								.replace(
								// eslint-disable-next-line max-len
									/<\/span><span class="ace_constant ace_language ace_escape">(\\[\s\S]+?)<\/span>(?:<span class="ace_string">)?/g,
									'$1'
								)
								// HTML
								.replace( /&amp;/g, '&' )
								.replace( /&gt;/g, '>' )
								.replace( /&lt;/g, '<' )
								// pas d'espace devant une ,
								.replace( /\u0020?(<\/span>),\u0020{1,2}/g, '$1,' )
								.replace( /<\/span>[\s]*,([\s]*)<span/g, ',$1</span><span' )
								.replace( /,(<\/span>)?<span/g, ',\u0020$1<span' );
							if ( Boolean( inputContent ) === false ) {
								return;
							}
							// Division de la chaîne
							const array = inputContent.split( /<\/span>/ );
							array.forEach( ( text, i ) => {
								array[ i ] = text.replace( /<span class="[^"]*">/, '' ).replace( /(^\u0020*|\u0020*$)/g, '' );
							} );
							let string = array.join( ' ' );
							string = string.replace( /[\s]+;$/g, ';' );
							if (
								( /^\u0020{2,4}/ ).test( textParse ) ||
								( /^\t/ ).test( textParse )
							) {
								const tabMatch = textParse.match( /^(\u0020{2,4}|\t)*/ ),
									tab = tabMatch[ 0 ].replace( /(\u0020{2,4}|\t)/g, '\t' ).replace( /(\t+)\u0020/, '$1' );
								string = string
									.replace( /^(\u0020{2,4}|\t)*/g, tab );
							}
							string = string.replace( /\u0020*$/, '' );
							globalArray.push( string );
						} );
						return globalArray.join( '\n' )
						// set( 'name', value ) || set_var( 'name', value ) => name := value
							.replace(
								// eslint-disable-next-line max-len
								/set(?:_var)?[\s]*\([\s]*["']([\S\s]*?)['"][\s]*,[\s]*(['"][\S\s]*?(?<!\\)['"])[\s]*\)/i,
								'$1 := $2'
							)
							.replace( /^(\t+)\u0020/gm, '$1' );
					}

					/**
					 * makeConcatString
					 *
					 * Crée le formulaire "concat string"
					 *
					 * @return {Object} Les éléments utilisés dans le reste du code
					 */
					function makeConcatString() {
						const input1 = new OO.ui.MultilineTextInputWidget( {
								autosize: true,
								placeholder: 'test|example|br|img'
							} ),
							input2 = new OO.ui.TextInputWidget( {
								placeholder: '50',
								type: 'number'
							} ),
							submit = new OO.ui.ButtonInputWidget( {
								label: 'Diviser',
								flags: [ 'primary', 'progressive' ]
							} ),
							fieldset = new OO.ui.FieldsetLayout( {
								label: 'Diviser un chaîne de caractères'
							} );
						const inputJoinString = new OO.ui.MultilineTextInputWidget( {
								autosize: true,
								placeholder: '\'test|example|br|im\' + \'g|span|b|center|i\''
							} ),
							inputSubstr = new OO.ui.TextInputWidget( {
								placeholder: 'img|',
								type: 'text'
							} ),
							submitJoin = new OO.ui.ButtonInputWidget( {
								label: 'Envoyer',
								flags: [ 'primary', 'progressive' ]
							} ),
							fieldsetJoin = new OO.ui.FieldsetLayout( {
								label: 'Modifier la division de chaînes de caractères',
								help: new OO.ui.HtmlSnippet( `Modifier la division entre deux chaînes (obtenues avec le champ ci-dessus).
Le contenu du champ "Sous-chaîne" doit être intégré dans "Chaîne". "Sous-chaîne" sera la dernière instance de la première chaîne.
Exemple : <dl>
<dt>Chaîne</dt>
<dd><code>'test|example|br|im' + 'g|span|dl|center|dd'</code></dd>
<dt>Sous-chaîne</dt>
<dd><code>img|</code></dd>
<dt>Résultat</dt>
<dd><pre>
'test|example|br|img|' + 
'span|dl|center|dd'
</pre></dd>
</dl>
` )
							} );
						fieldset.addItems( [
							new OO.ui.FieldLayout( input1, {
								label: 'Chaîne à diviser :',
								align: 'top',
								help: `Une chaîne unique 
(les caractères « + » seront considérés comme faisant partie de la chaîne).
`
							} ),
							new OO.ui.FieldLayout( input2, {
								label: 'Nombre de caractères par ligne :',
								align: 'top',
								help: 'Le maximum de caractères dans chaque chaîne.'
							} ),
							new OO.ui.FieldLayout( submit )
						] );
						fieldsetJoin.addItems( [
							new OO.ui.FieldLayout( inputJoinString, {
								label: 'Chaîne :',
								align: 'top'
							} ),
							new OO.ui.FieldLayout( inputSubstr, {
								label: 'Sous-chaîne :',
								align: 'top'
							} ),
							new OO.ui.FieldLayout( submitJoin )
						] );
						const form = new OO.ui.PanelLayout( {
							expanded: false,
							framed: true,
							padded: true,
							$content: $( '<div>' ).append( fieldset.$element ).append( fieldsetJoin.$element )
						} );
						return {
							form: form,
							submit: submit,
							input1: input1,
							input2: input2,
							inputString: inputJoinString,
							inputSubstr: inputSubstr,
							submit2: submitJoin
						};
					}

					/**
					 * makeCcnormTest
					 *
					 * Crée le formulaire "ccnorm test"
					 *
					 * @return {Object} Les éléments utilisés dans le reste du code
					 */
					function makeCcnormTest() {
						const input1 = new OO.ui.MultilineTextInputWidget( {
								autosize: true,
								placeholder: 'aäâã'
							} ),
							submit = new OO.ui.ButtonInputWidget( {
								label: 'Tester',
								flags: [ 'primary', 'progressive' ]
							} ),
							fieldset = new OO.ui.FieldsetLayout( {
								label: 'Tester une chaîne avec ccnorm',
								help: new OO.ui.HtmlSnippet( `Un message d'erreur sera affiché si la chaîne contient une lettre
ou un chiffre non supporté par la fonction « ccnorm ». Entrer <code>FORCELOAD</code> forcera le chargement
de la liste <code>equivset</code>.` )
							} );
						fieldset.addItems( [
							new OO.ui.FieldLayout( input1, {
								label: 'Chaîne à tester :',
								align: 'top'
							} ),
							new OO.ui.FieldLayout( submit )
						] );
						const form = new OO.ui.PanelLayout( {
							expanded: false,
							framed: true,
							padded: true,
							$content: fieldset.$element
						} );
						return { form: form, submit: submit, input1: input1 };
					}
					/**
					 * getEquivset
					 *
					 * Récupère la liste d'`equivset` avec l'API Fetch
					 * et la transforme pour utilisation ultérieure
					 *
					 * @async
					 */
					async function getEquivset() {
						const response = await fetch( 'https://raw.githubusercontent.com/wikimedia/Equivset/master/dist/equivset.json' ),
							data = await response.json();
						let arrayTo = [],
							arrayFrom = [];
						// eslint-disable-next-line no-underscore-dangle
						delete data._readme;
						const arrayKeys = Object.keys( data );
						for ( const key of arrayKeys ) {
							arrayFrom.push( key );
							arrayTo.push( data[ key ] );
						}
						arrayTo = arrayTo.filter(
							( item, index ) => arrayTo.indexOf( item ) === index
						);
						arrayFrom = arrayFrom.filter(
							( item, index ) => arrayFrom.indexOf( item ) === index
						);
						let availableCaracters = arrayTo.concat( arrayFrom );
						availableCaracters = availableCaracters.sort().join( '' );
						mw.storage.set( 'ccnormAvailable', availableCaracters );
						mw.cookie.set( 'ccnormUpdate', '1', 604800 );
					}
					/**
					 * ccnormProcess
					 *
					 * Fonction utilisé dans le gestionnaire d'évènements du bouton de ccnormTest
					 * pour pouvoir la réexécuter si la liste de ccnorm est obsolète ou indisponible
					 */
					function ccnormProcess() {
						if ( ( /^\s*$/ ).test( ccnormForm.input1.getValue() ) === true ) {
							$( '.aftoolsCcnorm, .aftoolsCcnormEval' ).hide();
							$( '#aftoolsCcnormCheck, #aftoolsCcnormEval, #aftoolsCcnorm' ).empty();
							ccnormForm.input1.setValidityFlag( false );
							return;
						}
						if ( ccnormForm.input1.getValue() === 'FORCELOAD' ) {
							$( '.aftoolsCcnorm, .aftoolsCcnormEval' ).hide();
							$( '#aftoolsCcnormCheck, #aftoolsCcnormEval, #aftoolsCcnorm' ).empty();
							getEquivset().then( () => {
								mw.notify( 'Liste equivset mise à jour.',
									{ autoHide: true, autoHideSeconds: 'long', title: 'Succès', type: 'success' } );
							} );
							return;
						}
						if ( mw.cookie.get( 'ccnormUpdate' ) === null ) {
							getEquivset().then( ccnormProcess );
							return;
						}
						const regexCcnorm = new RegExp( `[^${mw.storage.get( 'ccnormAvailable' )}\\s]`, 'g' ),
							stringToTest = ccnormForm.input1
								.getValue()
								.replace( /(?<!\\)\\(?!['"\\])/g, '\\\\' )
								.replace( /^['"]|(?<!\\)['"]$/g, '' )
								.replace( /([^\\])?'/g, '$1\\\'' ),
							arrayMatch = stringToTest.match( regexCcnorm ),
							regex2 = /(?:\p{L}|\p{N}|\p{M})|\p{C}/ug;
						new mw.Api().get( {
							action: 'abusefilterevalexpression',
							format: 'json',
							expression: `ccnorm('${stringToTest}')`,
							formatversion: '2'
						} ).then( function ( response ) {
							$( '#aftoolsCcnormCheck, #aftoolsCcnormEval, #aftoolsCcnorm' ).empty();
							if (
								regexCcnorm.test( stringToTest ) === true &&
							( regex2 ).test( arrayMatch.join( '' ) ) === true
							) {
								const errorUnsupported = new OO.ui.MessageWidget( {
									type: 'error',
									inline: true,
									label: new OO.ui.HtmlSnippet(
										'La chaîne contient des caractères non supportés : ces caractères ne seront pas normalisés.'
									)
								} );
								$( '#aftoolsCcnormCheck' ).append( errorUnsupported.$element );
								$( '.aftoolsCcnorm, .aftoolsCcnormEval' ).show();
								$( '#aftoolsCcnorm' ).text( arrayMatch.join().match( regex2 ).join( ' ' ) );
								$( '#aftoolsCcnormEval' ).text( response.abusefilterevalexpression.result );
							} else {
								const success = new OO.ui.MessageWidget( {
									type: 'success',
									inline: true,
									label: new OO.ui.HtmlSnippet( 'Test effectué avec succès.' )
								} );
								$( '#aftoolsCcnormCheck' ).append( success.$element );
								$( '.aftoolsCcnorm' ).hide();
								$( '.aftoolsCcnormEval' ).show();
								$( '#aftoolsCcnormEval' ).text( response.abusefilterevalexpression.result );
							}
						} );
					}
					// Initalisation
					if ( rights.indexOf( 'abusefilter-modify' ) !== 1 ) {
						$( '#mw-abusefilter-switcheditor' ).parent().before( progressWidget.$element );
						$( progressWidget.$element ).after( '<br>' );
						progressWidget.toggle( false );
						progressWidget.setProgress( 0 );
						$( '#mw-abusefilter-syntaxcheck' ).after( buttonDeprecate.$element );
						$( '.mw-abusefilter-javascript-tools' ).first().after(
							$( '<span>' )
								.attr( 'id', 'abusefilter-checksyntaxresultfield' )
						);
						if ( ( /\/(tools)/ ).test( mw.config.get( 'wgPageName' ) ) ) {
							$( '.printfooter' ).before( formContent.form.$element );
							$( formContent.submit.$element ).append( $( '<pre>' ).attr( 'id', 'aftoolsConcat' ) );
							$( formContent.submit.$element ).append( '<br>' );
							$( '#aftoolsConcat' ).hide();
							$( formContent.submit2.$element ).append( $( '<pre>' ).attr( 'id', 'aftoolsJoin' ) );
							$( formContent.submit2.$element ).append( '<br>' );
							$( '#aftoolsJoin' ).hide();
							$( '.printfooter' ).before( ccnormForm.form.$element );
							$( ccnormForm.submit.$element )
								.append( '<br>' )
								.append( '<br>' )
								.append(
									$( '<div>' )
										.append(
											$( '<div>' )
												.attr( 'id', 'aftoolsCcnormCheck' )
										)
										.append(
											$( '<h5>' )
												.attr( 'class', 'aftoolsCcnorm' )
												.text( 'Caractères non supportés' )
										)
										.append(
											$( '<pre>' )
												.attr( { class: 'aftoolsCcnorm', id: 'aftoolsCcnorm' } )
										)
										.append(
											$( '<h5>' )
												.attr( 'class', 'aftoolsCcnormEval' )
												.html( 'Résultat de l\'exécution de <code>ccnorm()</code>' )
										).append(
											$( '<pre>' )
												.attr( { class: 'aftoolsCcnormEval', id: 'aftoolsCcnormEval' } )
										)
								);
							$( '.aftoolsCcnorm, .aftoolsCcnormEval' ).hide();
						}
					} else {
						mw.log.warn( 'You must be AbuseFilter editor to use this script' );
					}

					// Corriger le code
					buttonDeprecate.on( 'click', () => {
						const useAce = $( document.getElementById( 'wpFilterRules' ) ).css( 'display' ) === 'none';
						if ( $( '.ace_text-layer' ).children().find( '.ace_deprecated' ).length !== 0 ) {
							const articleVar = {
									articleid: 'page_id',
									namespace: 'page_namespace',
									text: 'page_title',
									prefixedtext: 'page_prefixedtitle',
									restrictionsEdit: 'page_restrictions_edit',
									restrictionsMove: 'page_restrictions_move',
									restrictionsUpload: 'page_restrictions_upload',
									restrictionsCreate: 'page_restrictions_create',
									recentContributors: 'page_recent_contributors',
									firstContributor: 'page_first_contributor'
								},
								moved = {
									toArticleid: 'moved_to_id',
									toText: 'moved_to_title',
									toPrefixedtext: 'moved_to_prefixedtitle',
									fromArticleid: 'moved_from_id',
									fromText: 'moved_from_title',
									fromPrefixedtext: 'moved_from_prefixedtitle'
								},
								extensions = {
									boardArticleid: 'board_id',
									boardText: 'board_title',
									boardPrefixedtext: 'board_prefixedtitle',
									articleViews: 'page_views'
								};
							// article_
							const abusefilterNewText = getInputContent()
								.replace( /article_articleid/ig, articleVar.articleid )
								.replace( /article_namespace/ig, articleVar.namespace )
								.replace( /article_text/ig, articleVar.text )
								.replace( /article_prefixedtext/ig, articleVar.prefixedtext )
								.replace(
									/article_restrictions_edit/ig,
									articleVar.restrictionsEdit
								)
								.replace(
									/article_restrictions_move/ig,
									articleVar.restrictionsMove
								)
								.replace(
									/article_restrictions_upload/ig,
									articleVar.restrictionsUpload
								)
								.replace(
									/article_restrictions_create/ig,
									articleVar.restrictionsCreate
								)
								.replace(
									/article_recent_contributors/ig,
									articleVar.recentContributors
								)
								.replace(
									/article_first_contributor/ig,
									articleVar.firstContributor
								)
							// moved_
								.replace( /moved_to_articleid/ig, moved.toArticleid )
								.replace( /moved_to_text/ig, moved.toText )
								.replace( /moved_to_prefixedtext/ig, moved.toPrefixedtext )
								.replace( /moved_from_articleid/ig, moved.fromArticleid )
								.replace( /moved_from_text/ig, moved.fromText )
								.replace( /moved_from_prefixedtext/ig, moved.fromPrefixedtext )
							// extensions
								.replace( /board_articleid/ig, extensions.boardArticleid )
								.replace( /board_text/ig, extensions.boardText )
								.replace( /board_prefixedtext/ig, extensions.boardPrefixedtext )
								.replace( /article_views/ig, extensions.articleViews );
							// Application des modifications
							if ( useAce === true ) {
								ace.edit( 'wpAceFilterEditor' ).session.setValue( abusefilterNewText );
							} else {
								document.getElementById( 'wpFilterRules' ).value = abusefilterNewText;
							}
						} else {
							if ( useAce ) {
								ace.edit( 'wpAceFilterEditor' ).session.setValue( getInputContent() );
								ace.edit( 'wpAceFilterEditor' ).session.setValue( beautifyCode() );
							}
						}
					} );

					// ccnorm
					ccnormForm.submit.on( 'click', ccnormProcess );

					// Réunir des chaînes
					formContent.submit2.on( 'click', function () {
						if ( formContent.inputString.getValue() === '' && formContent.inputSubstr.getValue() === '' ) {
							formContent.inputString.setValidityFlag( false );
							formContent.inputSubstr.setValidityFlag( false );
							$( '#aftoolsJoin' ).hide();
							return;
						} else if ( formContent.inputString.getValue() === '' ) {
							formContent.inputString.setValidityFlag( false );
							$( '#aftoolsJoin' ).hide();
							return;
						} else if ( formContent.inputSubstr.getValue() === '' ) {
							formContent.inputSubstr.setValidityFlag( false );
							$( '#aftoolsJoin' ).hide();
							return;
						}
						const stringToSplit = formContent.inputString
								.getValue()
								.replace( /^["']|(?<!\\)["']$/g, '' )
								.replace( /(?<!\\)["'][\s]*\+[\s]*["']/g, '' ),
							subString = formContent.inputSubstr
								.getValue()
								.replace( /^["']|(?<!\\)["']$/g, '' ),
							arraySubstr = stringToSplit.split( subString );
						arraySubstr[ 0 ] += subString;
						const newString = arraySubstr.join( '\' + \n\'' );
						$( '#aftoolsJoin' ).show();
						$( '#aftoolsJoin' ).text( newString.replace( /^|$/g, '\'' ) );
					} );

					// Diviser un chaîne
					formContent.submit.on( 'click', function () {
						if ( ( /[0-9]+/ ).test( formContent.input2.getValue() ) !== true && formContent.input1.getValue() === '' ) {
							formContent.input2.setValidityFlag( false );
							formContent.input1.setValidityFlag( false );
							$( '#aftoolsConcat' ).hide();
							return;
						} else if ( ( /[0-9]+/ ).test( formContent.input2.getValue() ) !== true ) {
							formContent.input2.setValidityFlag( false );
							$( '#aftoolsConcat' ).hide();
							return;
						} else if ( formContent.input1.getValue() === '' ) {
							formContent.input1.setValidityFlag( false );
							$( '#aftoolsConcat' ).hide();
							return;
						}
						let index,
							stringToSlice = formContent.input1
								.getValue()
								.replace( /(^["']|["']$)/g, '' )
								.replace( /(?<!\\)'/g, '\\\'' ),
							stringConcat = '';
						const number = Number( formContent.input2.getValue() ),
							numberMatch = Math.floor( stringToSlice.length / number ),
							string = [];
						for ( index = 0; index < numberMatch; index++ ) {
							string.push( stringToSlice.slice( 0, number ) );
							stringToSlice = stringToSlice.slice( number );
						}
						string.push( stringToSlice );
						for ( const text of string ) {
							stringConcat += `'${text}' +\n`;
						}
						stringConcat = stringConcat.replace( / [+]\n?$/, '' );
						$( '#aftoolsConcat' ).show();
						$( '#aftoolsConcat' ).text( stringConcat );
					} );

					// Vérifier la syntaxe
					$( '#mw-abusefilter-syntaxcheck' ).off();
					$( '#mw-abusefilter-syntaxcheck' ).on( 'click', function () {
						const text = $( '#wpFilterRules' ).val(),
							useAce = $( '#wpFilterRules' ).css( 'display' ) === 'none',
							filterEditor = ace.edit( 'wpAceFilterEditor' );
						if ( progressWidget.isPending() === false ) {
							progressWidget.pushPending();
							progressWidget.setProgress( false );
						} else {
							return;
						}
						if ( progressWidget.isVisible() === false ) {
							progressWidget.toggle();
						}
						new mw.Api().post( {
							action: 'abusefilterchecksyntax',
							format: 'json',
							filter: text,
							formatversion: '2'
						} ).then( ( result ) => {
							const data = result.abusefilterchecksyntax,
								// eslint-disable-next-line max-len
								regexDeprecated = /(?<![A-Za-z_])(article_(articleid|namespace|text|prefixedtext|restrictions_(edit|move|upload|create)|recent_contributors|first_contributor)|moved_(to|from)_(articleid|text|prefixedtext)|board_(articleid|text|prefixedtext)|article_views)/g,
								// eslint-disable-next-line max-len
								performanceVariable = /(?<![A-Za-z_])((edit_diff|added_lines|new)_pst|(added|removed)_links|page_(recent_contributors|first_contributor)|new_(html|text)|(old|new)_wikitext)/g,
								// eslint-disable-next-line max-len
								fileVariable = /(?<![A-Za-z_])(file_(width|size|sha1|mime|mediatype|height|bits_per_channel))/g,
								// eslint-disable-next-line max-len
								uploadAction = /(?<![A-Za-z_])action[\s]*===?[\s]*['"](upload|stashupload)['"]/g,
								// eslint-disable-next-line max-len
								moveVariable = /(?<![A-Za-z_])(moved_(from|to)_(age|first_contributor|id|namespace|prefixedtitle|recent_contributors|restrictions_create|restrictions_edit|restrictions_move|restrictions_upload|title))/g,
								moveAction = /(?<![A-Za-z_])action[\s]*===?[\s]*['"]move['"]/g,
								// eslint-disable-next-line max-len
								editVariable = /(?<![A-Za-z_])((old|new)_(wikitext|size)|edit_diff(_pst)?|edit_delta|(added|removed)_lines(_pst)?|(all|old|added|removed)_links|new_(pst|html|text))/g,
								editAction = /(?<![A-Za-z_])action[\s]*===?[\s]*['"]edit['"]/g;
							$( '#abusefilter-checksyntaxresultfield' ).empty();
							if ( data.status === 'ok' ) {
								const messageOk = new OO.ui.MessageWidget( {
									type: 'success',
									inline: true,
									label: mw.msg( 'abusefilter-edit-syntaxok' )
								} );
								$( '#abusefilter-checksyntaxresultfield' ).append( messageOk.$element );
							} else if ( data.status === 'error' ) {
								const messageError = new OO.ui.MessageWidget( {
									type: 'error',
									inline: true,
									label: mw.message( 'abusefilter-edit-syntaxerr' ).params( [ data.message ] ).text()
								} );
								$( '#abusefilter-checksyntaxresultfield' ).append( messageError.$element );
								if ( useAce === true ) {
									const position = filterEditor
										.session
										.getDocument()
										.indexToPosition( data.character );
									filterEditor.focus();
									filterEditor.navigateTo( position.row, position.column );
									filterEditor.scrollToRow( position.row );
								} else {
									$( document.getElementById( 'wpFilterRules' ) ).trigger( 'focus' ).textSelection( 'setSelection', {
										start: data.character
									} );
								}
							}
							// Variables obsolètes
							if ( regexDeprecated.test( text ) ) {
								const deprectatedArray = text.match( regexDeprecated );
								const messageDeprecated = new OO.ui.MessageWidget( {
									type: 'warning',
									inline: true,
									label: `Problème potentiel identifié : Ce code contient des variables obsolètes (${deprectatedArray.join( ', ' )}).`
								} );
								$( '#abusefilter-checksyntaxresultfield' ).append( messageDeprecated.$element );
							}
							// Performances
							if (
								( /\/\*[\s]?aftools-disable-perfs[\s]?\*\//ig )
									.test( text ) === false &&
							performanceVariable.test( text )
							) {
								let isPst, isLinks, isPreferLines, isGeneral;
								const pstVar = [],
									pstAlt = [],
									linksVar = [],
									preferLinesVar = [],
									generalVar = [];
								const performanceArray = text.match( performanceVariable );
								for ( const variable of performanceArray ) {
									if ( ( /_pst$/i ).test( variable ) === true ) {
										isPst = true;
										pstVar.push( variable );
										pstAlt.push( variable.replace( /_pst$/i, '' ) );
									} else if (
										( /_links$/i ).test( variable ) === true
									) {
										isLinks = true;
										linksVar.push( variable );
									} else if (
										( /(?:_wikitext|_text|_html)$/i )
											.test( variable )
									) {
										isPreferLines = true;
										preferLinesVar.push( variable );
									} else {
										isGeneral = true;
										generalVar.push( variable );
									}
								}
								let stringMsg = 'Suggestion d\'amélioration : ';
								if ( isPst === true ) {
									stringMsg += `Ce filtre contient des variables <code>_pst</code> (${pstVar.join( ', ' )}) : 
remplacez-les par leurs alternatives (${pstAlt.join( ', ' )}) dans la mesure du possible ou vérifiez les alternatives 
avant les variables <code>_pst</code>. `;
								}
								if ( isLinks === true ) {
									stringMsg += `Ce filtre contient des variables <code>_links</code> (${linksVar.join( ', ' )}) : 
ne les utilisez que si vous avez besoin d'une grande précision ou que l'utilisation d'<code>added_lines</code> ou 
<code>removed_lines</code> pourrait conduire à des dysfonctionnements. `;
								}
								if ( isPreferLines === true ) {
									stringMsg += `Ce filtre contient des variables potentiellement lourdes (${preferLinesVar.join( ', ' )}) : 
utilisez <code>added_lines</code> ou <code>removed_lines</code> si possible. `;
								}
								if ( isGeneral === true ) {
									stringMsg += `Ce filtre contient des variables nécessitant une requête à la base de données 
(${generalVar.join( ', ' )}) : formulez les conditions de la façon la plus stricte possible pour éviter des requêtes superflues. `;
								}
								stringMsg += `
Lire <a href="https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:AbuseFilter/Rules_format#Performance">ceci</a> pour plus de détails.
Ajoutez <code>/* aftools-disable-perfs */</code> au début du code après avoir effectué les corrections.`;
								const messagePerformance = new OO.ui.MessageWidget( {
									type: 'notice',
									inline: true,
									label: new OO.ui.HtmlSnippet( stringMsg )
								} );
								$( '#abusefilter-checksyntaxresultfield' ).append( messagePerformance.$element );
							}
							// Variables restreintes
							if (
								uploadAction.test( text ) === false &&
							fileVariable.test( text ) === true
							) {
								const fileVariableArray = text.match( fileVariable );
								const messageRestrictedVariable = new OO.ui.MessageWidget( {
									type: 'warning',
									inline: true,
									label: new OO.ui.HtmlSnippet( `
Problème potentiel identifié : Ce code contient des variables à l'utilisation restreinte (${fileVariableArray.join( ', ' )}),
ces variables ne peuvent être utilisées qu'avec les actions <code>upload</code> et <code>stashupload</code>.
Pour résoudre ce problème, ajoutez <code>action === 'upload'</code> ou <code>action === 'stashupload'</code> ou <code>action === 'stashupload' | action === 'upload'</code>
au code conditionnel.` )
								} );
								$( '#abusefilter-checksyntaxresultfield' ).append( messageRestrictedVariable.$element );
							}
							if (
								moveAction.test( text ) === false &&
							moveVariable.test( text ) === true
							) {
								const moveVariableArray = text.match( moveVariable );
								const messageRestrictedVariable = new OO.ui.MessageWidget( {
									type: 'warning',
									inline: true,
									label: new OO.ui.HtmlSnippet( `
Problème potentiel identifié : Ce code contient des variables à l'utilisation restreinte (${moveVariableArray.join( ', ' )}),
ces variables ne peuvent être utilisées qu'avec l'action <code>move</code>.
Pour résoudre ce problème, ajoutez <code>action === 'move'</code> au code conditionnel.` )
								} );
								$( '#abusefilter-checksyntaxresultfield' ).append( messageRestrictedVariable.$element );
							}
							if (
								editAction.test( text ) === false &&
							editVariable.test( text ) === true
							) {
								const editVariableArray = text.match( editVariable );
								const messageRestrictedVariable = new OO.ui.MessageWidget( {
									type: 'warning',
									inline: true,
									label: new OO.ui.HtmlSnippet( `
Problème potentiel identifié : Ce code contient des variables à l'utilisation restreinte (${editVariableArray.join( ', ' )}),
ces variables ne peuvent être utilisées qu'avec l'action <code>edit</code>.
Pour résoudre ce problème, ajoutez <code>action === 'edit'</code> au code conditionnel.` )
								} );
								$( '#abusefilter-checksyntaxresultfield' ).append( messageRestrictedVariable.$element );
							}
							progressWidget.toggle();
							progressWidget.popPending();
							progressWidget.setProgress( false );
						}, () => {
							progressWidget.toggle();
							progressWidget.popPending();
							progressWidget.setProgress( false );
						} );
					} );
				} );
			} );
		}
	} );