Communauté  • Livre d'or
Chats noirs.jpg Actualités, astuces, interview... Venez lire la gazette de ce printemps de Vikidia ! DessinInterview.png

Utilisateur:Samamothée/common.js

Aller à la navigation Aller à la recherche

Note : après avoir enregistré tes préférences, tu devras forcer le rechargement complet du cache de ton navigateur pour voir les changements. Mozilla Firefox / Safari : maintiens la touche Majuscule (Shift) en cliquant sur le bouton Actualiser (Reload) ou presse Maj-Ctrl-R (Maj-Cmd-R sur Apple Mac) ; Internet Explorer / Opera : maintiens la touche Ctrl en cliquant sur le bouton Actualiser ou presse Ctrl-F5.

/*
* This script adds a tab which allows a mass patrolling on the current page. Because it was pointless to mark as patrolled 10 intermediate versions when we can read the difference between the first and the last
*/

var totalRevisions;
var treatedRevisions = 0;

if (mw.config.get('wgNamespaceNumber') >= 0) {
  mw.loader.using( [ 'mediawiki.util', 'mediawiki.api', 'oojs-ui' ], function() {
    mw.util.addPortletLink( 'p-cactions', 'javascript:markAllAsPatrolled()', 'Smart patrol', 'ca-smartpatrol' );
    //mw.util.addPortletLink( 'p-views', 'javascript:markAllAsPatrolled()', 'Smart patrol', 'ca-smartpatrol2', 'Tout marquer comme relue', '', 'ca-history' );
  } );
}

function markAllAsPatrolled() {
  new OO.ui.confirm('Êtes-vous sur de vouloir marquer comme relue toutes les modifications ?').then( function( response ) {
    if ( response === true ) {
        getRevisionsList();
    }
  } );
}

function getRevisionsList() {
  var api = new mw.Api();
  api.get( {
	'action': 'query',
	'format': 'json',
	'prop': 'revisions',
	'titles': mw.config.get( 'wgPageName' ),
	'formatversion': '2',
	'rvprop': 'ids|user',
	'rvlimit': 'max'
  } ).then( function ( data ) {
    var revisions = data.query.pages[ 0 ].revisions;
    //var user = revisions[ 0 ].user;
    totalRevisions = revisions.length;
    revisions.forEach( function ( revision ) {
      treatedRevisions++;
      //if ( user === revision.user ) {
        markRevAsPatrolled( revision.revid );
      //}
    } );

  } ).fail( function ( error ) {
      mw.notification.notify( 'Something went wrong: ' + error, { title: 'Smart Patrol', type: 'error' } );
  } );
}

function markRevAsPatrolled( revid ) {
  var api = new mw.Api();
  api.postWithToken( 'patrol', {
	'action': 'patrol',
	'revid': revid

  } ).then( function ( info ) {
    console.log( 'Success for ' + revid, { title: 'Smart Patrol', type: 'info' } );
    if ( totalRevisions == treatedRevisions ) { window.location.reload(); }

  } ).fail( function ( error ) {
    console.log( 'Something went wrong: ' + error, { title: 'Smart Patrol', type: 'error' } );
  } );
}


/* if (window.location.href === "https://fr.vikidia.org/wiki/Sp%C3%A9cial:Modifications_r%C3%A9centes") {
window.location.href="https://fr.vikidia.org/wiki/Sp%C3%A9cial:Page_au_hasard"
}*/
/* Résumé Deluxe.
 *
 * Ajoute des commentaires de modification prédéfinis
 *
 * Auteur : Dake
 * Contributions : Pabix, Tieno, Ltrlg, Rififi
 * 
 * Dépendances :
 *  — les habituelles implicites 'mediawiki' & 'jquery' ;
 *  — 'user' (chargement du common.js).
 * 
 * {{Projet:JavaScript/Script|ResumeDeluxe}}
 * Licence CC-BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0/deed.fr) Source: MediaWiki:Gadget-ResumeDeluxe.js (http://fr.wikipedia.org/wiki/MediaWiki:Gadget-ResumeDeluxe.js)
 */
mw.loader.using( 'user', function () {
	var
		
		/* Vérifier la présence d’un titre de section.
		 * Cela permet de n’ajouter un séparateur ' , ' entre deux résumés que si
		 * nécessaire.
		 * Note : on ne vérifie que le fait que le motif est en fin de chaîne, pas
		 * en début.
		 */
		sansPointVirgule = /\*\/\s*$/,
	
		/* La liste des liens qui seront affichés.
		 * Les éléments du tableau sont d’une des deux formes suivantes :
		 *   — [ 'lien' , 'resume' ]
		 *   — 'texte'
		 * où 'lien' représente l’intitulé du lien, 'resume' le résumé d’édition
		 * inséré par le lien et 'texte' les deux à la fois.
		 */
		listeLiens = [
			'orthographe',
			'catégorisation',
			[ 'portail', 'ajout de portail(s)' ],
			[ 'interwiki', 'rajout d\'interwikis' ],
			['image','rajout image'],
			'mise en forme',
			[ 'redirection',"création d\'une redirection"],
			'relecture',
			'réponse',
			'maintenance',
			[ 'homonymie', 'création d\'une page d\'homonymie ' ],
			[ 'bandeau', 'ajout de bandeau(x)' ],
			'infobox',
			'tableau',
			'références',
			[ 'MàJ', 'mise à jour' ],
			[ 'SI' , 'Supression immédiate' ],
			["Info","Rajout d\'info"],
			'retouche de la modification précédente',
			],
	
		/* Liens affichés par défaut, même format que listeLiens. */
		liensParDefaut = [],
	
		/* Objet permettant de désactiver des liens (parmi la liste par défaut
		 * ci-avant), sous la forme { 'lien1': false, 'lien2': false, ... } où
		 * 'lien1', 'lien2'... est l’intitulé du lien à ne pas afficher.
		 */
		liensAffiches = window.ResumeDeluxe_affiches || {},
	
		/* Objet jQuery contenant l’<input> ou le <textarea> constituant le résumé. */
		$resume,
	
		/* Objet jQuery contenant la liste de liens. */
		$cont;
	
	/* Fonction renvoyant l’intitulé d’un lien à partir de sa représentation sous
	 * forme de tableau ou de chaîne.
	 */
	function texteDuLien( definition ) {
		if ( $.isArray( definition ) ) {
			return definition[0];
		} else {
			// Soyons sûr d’avoir une chaîne de caractères
			return definition.toString();
		}
	}
	
	/* Fonction renvoyant le texte ajouté par un lien à partir de sa représentation
	 * sous forme de tableau ou de chaîne.
	 */
	function resumeAInserer( definition ) {
		if ( $.isArray( definition ) ) {
			return definition[1];
		} else {
			// Soyons sûr d’avoir une chaîne de caractères.
			return definition.toString();
		}
	}
	
	/* Procédure appliquant les préférences de l’utilisateur :
	 *   — si l’utilisateur a défini window.ResumeDeluxe_liens, celui-ci est utilisé
	 *     à la place de la liste par défaut liensParDefaut ;
	 *   — sinon si l’utilisateur a défini window.ResumeDeluxe_affiches, les règles
	 *     de désactivation sont appliquées ;
	 *   — sinon liensAffiches est un objet vide, donc la liste par défaut est
	 *     utilisée.
	 */
	function appliquerConfiguration() {
		var i, cle;
		
		if ( $.isArray( window.ResumeDeluxe_liens ) ) {
			listeLiens = window.ResumeDeluxe_liens;
		} else {
			for ( i = 0; i < liensParDefaut.length; i++ ) {
				cle = texteDuLien( liensParDefaut[i] );
				if ( liensAffiches[cle] !== false ) {
					listeLiens.push( liensParDefaut[i] );
				}
			}
		}
	}
	
	/* Procedure effectuant l’ajout d’une chaîne au résumé d’édition, avec un
	 * séparateur si besoin
	 */
	function ajouterAuResume( chaine ) {
		var resumeActuel = $resume.val();
		if ( resumeActuel === '' ) {
			$resume.val( chaine );
		} else if ( sansPointVirgule.test( resumeActuel ) ) {
			$resume.val( resumeActuel + chaine );
		} else {
			$resume.val( resumeActuel + ', ' + chaine );
		}
	
		// L’ÉditeurVisuel ne se base plus sur le contenu mais sur l’évènement
		$resume.change();
	
		// Conserver le focus sur le lien est inutile à l’utilisateur
		// Plaçons-le sur le champ de résumé, s’il veut compléter à la main
		$resume.focus();
	}
	
	/*  Fonction renvoyant un lien à partir de sa définition. */
	function $lien( definition ) {
		var resume = resumeAInserer( definition );
		return $( '<a>' )
			.text( texteDuLien( definition ) )
			.attr( {
				href: '#',
				title: 'Ajouter «\xA0' + resume + '\xA0» au résumé de modification'
			} )
			.click( function () {
				ajouterAuResume( resume );
				return false;
			} );
	}
	
	/* Procédure construisant la liste de liens. */
	function contruireListe() {
		var i, $ul;
	
		$ul = $( '<ul>' )
			.addClass( 'liste-horizontale' )
			.css( {
				display: 'inline',
				margin: 0
			} );
	
		for( i = 0; i < listeLiens.length; i++ ) {
			$ul.append( $( '<li>' ).append( $lien( listeLiens[i] ) ) );
		}
	
		$cont = $( '<div>' ).attr( 'id', 'ResumeDeluxe' )
			.text( 'Messages prédéfinis\xA0: ' )
			.append( $ul );
	}
	
	/* Procédure initialisant $resume et insérant la liste de liens dans le cas de
	 * l’éditeur de wikicode, quand les nœuds nécessaires sont en place.
	 */
	function demarrer_wikicode() {
		$( document ).ready( function () {
			$( '#wpSummaryLabel' ).before( $cont );
			$resume = $( '#wpSummary' );
		} );
	}
	
	/* Procédure initialisant $resume et insérant la liste de liens dans le cas de
	 * l’ÉditeurVisuel, quand les nœuds nécessaires sont en place.
	 */
	function demarrer_EditeurVisuel() {
		mw.hook( 've.saveDialog.stateChanged' ).add( function () {
			/*
			 * Traitement : si le dialogue a été recréé, on y remet ce qu’il faut.
			 * TODO Vérifier si cette vérification est encore nécessaire.
			 */
			if ( $( '#ResumeDeluxe' ).length < 1 ) {
				ve.init.target.saveDialog.$editSummaryLabel.after( $cont );
				$resume = ve.init.target.saveDialog.editSummaryInput.$input;
			}
		} );
	}
	
	/* Procédure qui détermine quel éditeur peut être utilisé et démarre le gadget 
	 * selon le résultat.
	 */
	function demarrer() {
		switch ( mw.config.get( 'wgAction' ) ) {
			case 'edit':
			case 'submit':
				// Désactivé pour la création de sections
				if ( ! /(\?|&)section=new(&|$)/.test( document.location.search ) ) {
					demarrer_wikicode();
				}
				// L’EV peut démarrer de ces modes sans rechargement
				/* falls through */
			case 'view':
				demarrer_EditeurVisuel();
				break;
		}
	}
	
	/***** Démarrage du tout *****/
	
	// Lecture des préférences
	appliquerConfiguration();
	// Construction de la liste des liens
	contruireListe();
	// Insère la liste dans l’arbre des nœuds et cherche le nœud contenant le résumé
	demarrer();
});
/**
 * Outils pour réverter
 * Fournit des liens dans les pages de diff pour révoquer facilement une modification et avertir son auteur.
 * 
 * License : CC-BY-SA-4.0, https://creativecommons.org/licenses/by-sa/4.0
 * Source : dernière version : https://fr.vikidia.org/w/index.php?title=MediaWiki:Gadget-RevertDiff.js&diff=1659871&oldid=1659838 ; origine : https://fr.wikipedia.org/w/index.php?title=MediaWiki:Gadget-RevertDiff.js&diff=109676406&oldid=109657328
 * Auteurs : dernière version : https://fr.vikidia.org/w/index.php?title=MediaWiki:Gadget-RevertDiff.js&action=history ; origine : Lorian (en-wp), Chphe (fr-wp), https://fr.wikipedia.org/w/index.php?title=MediaWiki:Gadget-RevertDiff.js&action=history avant la version 109676406 de Dr. Brains
 <nowiki>
 **/
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'mediawiki.notify'], function () {
	if (typeof window.RevertDiff === 'undefined' && location.href.match(/diff=/)) { //We're not already launched AND we're on a diff
		window.RevertDiff = true;//We're alive !

		//Some params.
		var RevertDiffParams = {
			/**
			 * Defines the available warns, in the template Averto on the french Vikidia (https://fr.vikidia.org/wiki/Template:Averto).
			 * The type is the template's parameter type, the title is the title of the section, the MaxLevel is the maximum level ("niveau=") available for that type.
			 * The Page is the template's Page. Automatically put, it is set to true if available for all levels or set to the max level where it is allowed.
			 * UserTalkPage tells that we need to return in parameter Page, if we're on a talk page, the page owner's username.
			 */
			AvailableWarns: {
				Global: {Type: "global", Title: "Ta modification a été annulée", MaxLevel: 5, Page: 1},
				Copyvio: {Type: "copyvio", Title: "Violation de copyright", MaxLevel: 2, Page: 1},
				Encyclo: {Type: "encyclo", Title: "Vikidia est une encyclopédie", MaxLevel: 3},
				Polite: {Type: "politesse", Title: "Politesse", MaxLevel: 3, UserTalkPage: true},
				Preview: {Type: "prévisualisation", Title: "Merci de [[Aide:Prévisualisation|prévisualiser]]", MaxLevel: 1, Page: true},
				Deleted: {Type: "SI", Title: "Article supprimé", MaxLevel: 1, Page: true},
				Spam: {Type: "spam", Title: "Spam", MaxLevel: 2, Page: 1},
				ShockVandalism: {Type: "vandalisme choquant", Title: "[[Vikidia:Vandalisme|Vandalisme]] choquant", MaxLevel: 3, Page: 2},
				Wp: {Type: "wp", Title: "Copie de wp", UserTalkPage: true},
				
				
			},

			BlockFrom: 2, //From which level (NON included) the warn template alerts the user that he will be blocked. Will require block permissions to apply it.

			GroupBlock: "sysop", //Who can block ? (put the minimum group, if there is more, you may need to edit userIs to allow arrays)
			GroupNoLimitMsg: "sysop", //Who doesn't have any limit in posting messages ?

			MaxMsgNonInGroup: 2, //How many msgs a user not in GroupNoLimitMsg can post

			MsgPosted: 0, //How many messages were posted. Logically we start at 0.

			/**
			 * The predevined warns available. Name will be displayed in the button, Type represents the index in AvailableWarns, Level the level of the warn used.
			 * If you wish to add a predefined warn which is uses a level superior than the block level, plese check whether the user can block (userIs(GroupBlock)) before generating the buttons.
			 */
			PredefinedWarns: [
				{Name: "Averto 0", Type: "Global", Level: 0},
				{Name: "Averto 1", Type: "Global", Level: 1},
				{Name: "Averto 2", Type: "Global", Level: 2},
				{Name: "Copyvio 0", Type: "Copyvio", Level: 0},
				{Name: "Politesse 0", Type: "Polite", Level: 0},
				{Name: "Spam 0", Type: "Spam", Level: 1},
				{Name: "Spam 1", Type: "Spam", Level: 2},
				{Name: "Aerto-wp", Type: "Copie de wp", Level: 2},
			],

			Texts: {
				//Restore
				NoRestore: "Tu ne peux pas restaurer.",
				Restore: "Restaurer",
				RestoreConfirm: 'Restaurer les modifications de $0 ?',
				RestoreGGBtn: "C'est fait !",
				RestoreGGNotif: "L'ancienne modification a été restaurée avec succès !",
				RestoreNoChange: "Aucun changement effectué.",
				RestorePutReason: "Raison",
				RestoreReasonPrompt: 'Indique ici la raison :',
				RestoreSummary: 'Restauration (retour à la version de [[Special:Contributions/$0|$0]]).',
				Restoring: "Restauration...",

				//Patrol
				Patrolling: "Marquage de la révision comme relue…",

				//Warn/Msg
				BlockReminder: "Pense à bloquer l'utilisateur ;-)",
				MaxWarnTrig: "Tu as déjà déposé le nombre maximum de messages autorisés.",
				MsgAlreadyPost: "Attention, tu as déjà déposé $0 message(s) à $1. En déposer un autre ?",
				MsgGG: "Message déposé !",
				SendingMsg: "Envoi du message...",
				Warn: "Déposer un avertissement",
				WarnConfirm: "Déposer un message à l'utilisateur $0 ?",
				WarnPredefined: "Déposer un avertissement prédéfini : ",
				WarnSubmit: "Avertir",
				Welcome: "Bienvenuter",

				//Misc
				ItsYou: "C'est toi !",
				OuchError: "Aïe aïe aïe… ",

				//Errors
				ErrorInternal: "erreur interne.",
				ErrorEditFail: "échec de la modification.",

				//EditFail
				EditFailErrCode: "Code de l'erreur : ",
				'EditFail-abusefilter-disallowed': "Un filtre anti-abus a empêché la modification.",
				'EditFail-abusefilter-warning': "Un filtre anti-abus demande à ce que tu confirmes ton action en la répétant.",
				'EditFail-default': "La page n'est peut-être pas modifiable pour toi.",
				'EditFail-protectedpage': "La page est protégée.",
				'EditFail-undofailure': "Conflit d'édition."
			},

			//To put a welcome message. Title is section's, MsgUser/MsgIp msg for respectively a registred user and an IP.
			Welcome: {
				Title: "Bienvenue !",
				MsgUser: '{{subst:Bienvenue|' + mw.config.get('wgUserName') + '}} ~~~~',
				MsgIP: '{{subst:Bienvenue IP|' + mw.config.get('wgUserName') + '}} ~~~~' 
			}
		};

		//Return a text to be shown, with the id and the vals (values to replace the $.. in txt). Vals are given in the order it appears in the text ($0, $1...)
		function RDtxt(id, vals) {
			var Txt = RevertDiffParams.Texts[id];
			if (Txt) {
				if (Array.isArray(vals))
					for (var i = 0, l = vals.length; i < l; i++)
						Txt = Txt.split("$" + i.toString()).join(vals[i]); //replaceAll isn't currently supported enough
				return Txt;
			}
		}

		//Is the user sysop, bureaucrat... uses wgUserGroups to konw it.
		function userIs(group) {
			return (mw.config.get("wgUserGroups").indexOf(group) !== -1);
		}

		//Simple function so as not to repeat a mw.notify().
		function Notify(text, type) {
			mw.notify(text, {title: "RevertDiff", type: (type === undefined ? "info" : type)});
		}
		function NotifyFail(text) {//Adds a txt before the actual error message.
			mw.notify(RDtxt('OuchError') + text, {title: "RevertDiff", type: "error"});
		}

		//Will update the max level available, depending on the type and if the user can block.
		function updateLevelList() {
			var TypeSelected = $("#sel-RD-type").val(), //Which Type of warn is selected
				$LevelNum = $("#num-RD-level")[0];

			if (TypeSelected.length > 0) {//We selected a warn
				var WarnMaxLvl = RevertDiffParams.AvailableWarns[TypeSelected].MaxLevel, //First what's the max lvl of the warn ?
						MaxLevel = userIs(RevertDiffParams.GroupBlock) ? WarnMaxLvl : (Math.min(WarnMaxLvl, RevertDiffParams.BlockFrom)); //Then we lower the max lvl to a non blocking lvl if the user can't block.
				$LevelNum.max = MaxLevel;
			}

			$LevelNum.disabled = TypeSelected.length === 0; //Enable the field only if we've selected a type of warning.
		}

		//Function that edits the page with title title using params, and call successCaalback on success (passing response in arg).
		//If there is a fail, call editFailed with the code for parameter.
		//Do not mess this with mw.Api.edit, as this last returns the current revisions to make edits on it, here we just send parameters.
		//And moreover Vikidia does not support mw.Api.edit (and not even mw.Api.newSection).
		function editPage(title, params, successCallback) {
			var Api = new mw.Api(),
				Settings = {
					action: "edit",
					format: "json",
					title: title
				};
			Object.assign(Settings, params);
			Api.postWithToken('csrf', Settings).then(function (response) {//Let's go !
				//We're sure of a success only if this is like this. Sometimes it fails (for example with AbuseFilter) but it doesn't send an "error" object.
				if (response.edit.result === "Success") {
					successCallback(response);
				} else {
					editFailed(response.edit.code);//If no success then fail.
				}
			}).fail(function (code) {
				editFailed(code);//Same here
			});
		}

		//Restores, but ask and check a given reason.
		function restoreReason(oldId, user1) {
			var Reason = prompt(RDtxt("RestoreReasonPrompt"));
			if (Reason) //putting a summary is mandatory (if you wanted to put a custom one)
				restore(oldId, user1, Reason); // We then just call the main function.
		}

		//Restores an older edit (oldId, made by user1) overriding all edits above, perhaps using a customReason.
		function restore(oldId, user1, customReason) {
			//If customReason is defined, then the user already "confirmed" by putting a summary.
			if (customReason === undefined && !confirm(RDtxt('RestoreConfirm', [user1])))
				return;

			Notify(RDtxt('Restoring'), "info");//So the users doesn't feel like waiting for nothing

			var Summary = (customReason ? customReason + ' - ' : "") + RDtxt('RestoreSummary', [user1]); //We always put the RestoreSummary but we may add a customReason.

			editPage(mw.config.get('wgPageName'), {
				undo: oldId, //Undo until oldId
				undoafter: mw.config.get('wgCurRevisionId'), //We need to tell mw from which edit to which edit we want to override
				summary: Summary
			}, function (response) {
				if (response.edit.nochange !== undefined)//Not very useful, just to be more precise.
					Notify(RDtxt('RestoreNoChange'), "info");
				else
					Notify(RDtxt('RestoreGGNotif'), 'success');

				$("#span-RD-restore").text(RDtxt('RestoreGGBtn')); //No need of it anymore

				var PatrolLinkElt = $("#mw-diff-ntitle4 > .patrollink > a"); //Can we patrol = is the button-link "Mark as patrolled" present ?
				if (PatrolLinkElt.length === 1) {
					Notify(RDtxt('Patrolling'));
					PatrolLinkElt[0].click();//The link already shows an animation and a notif when clicked.
				}
			});
		}

		//Warns user, with a warn of type type, at a level of level, and has been triggered by the element triggerer.
		function warnUser(user, type, level, triggerer) {
			var WarnObj = RevertDiffParams.AvailableWarns[type];//First let's fetch our warn.

			if (!WarnObj) {//Uh oh
				NotifyFail(RDtxt("ErrorInternal"));
				return;
			}
			var Msg = '{{subst:Averto|type=';//The template starts by this, always. There are a type and a "niveau" parameter.
			Msg += WarnObj.Type +
					'|niveau=' +
					level; //The mandatory is down

			if (WarnObj.Page === true || WarnObj.Page <= level) {//Do we want a Page argument ?
				Msg += '|page=' + mw.config.get('wgPageName');
			} else if ((WarnObj.UserTalkPage === true || WarnObj.UserTalkPage <= level) && //or a Page arguments that refers to a user talk page ?
					mw.config.get("wgNamespaceNumber") === 3) {//But are we on a User talk: namespace ? 
				Msg += '|page=' + mw.config.get('wgRelevantUserName');//Using the name of the user who owns the talk page.
			}

			Msg += '}} ~~~~'; //We always finish by this.

			//If we need to block, we remind the user by adding an additionnal sucess msg.
			postMessage(Msg, WarnObj.Title, user, (level > RevertDiffParams.BlockFrom ? RDtxt('BlockReminder') : ''), triggerer);
		}

		//Welcomes user, using a message for IPs if the user isIP, and has been triggered by the elt $triggerer.
		function welcomeUser(user, isIP, triggerer) {
			var Welcome = RevertDiffParams.Welcome;
			postMessage((isIP ? Welcome.MsgIP : Welcome.MsgUser), RevertDiffParams.Welcome.Title, user, null, triggerer);
		}

		//Protection against accidental usages of posting system. A non sysop user cannot post more than MaxMsgNonInGroup message(s).
		function confirmPost(user) {
			var UserCanPost = userIs(RevertDiffParams.GroupNoLimitMsg) || (RevertDiffParams.MsgPosted < RevertDiffParams.MaxMsgNonInGroup);

			if (UserCanPost) {
				var ConfirmMsg = "";
				if (RevertDiffParams.MsgPosted > 0) {//Already posted a message = confirmation's mandatory
					ConfirmMsg = RDtxt("MsgAlreadyPost", [RevertDiffParams.MsgPosted, user]);
				} else { // Otherwise: basic confirmation message
					ConfirmMsg = RDtxt('WarnConfirm', [user]);
				}

				return confirm(ConfirmMsg); //Asks for confirmation
			}
			alert(RDtxt('MaxWarnTrig'));
			return false; //No way: the user can't post anymore
		}

		//Posts to user a message, that contains msg and has for title title. Will add additionnalSuccessMsg to the success notif if it's set.
		//Triggered by elt $triggerer. 
		function postMessage(msg, title, user, additionnalSuccessMsg, triggerer) {
			if (!confirmPost(user))//Can the user post ?
				return;

			Notify(RDtxt('SendingMsg'), "info"); //Wait little user, be patient !

			editPage("User talk:" + user, {
				section: "new",
				sectiontitle: title,
				text: msg
			}, function () {
				switch (triggerer.nodeName) {//The action we do on the elt depends of what is it
					case "BUTTON": //We disable and change the txt
						triggerer.disabled = true;
						triggerer.textContent = RDtxt('MsgGG');
						break;

					case "FORM": //We reset it
						triggerer.reset();
						break;
				}

				Notify(RDtxt('MsgGG') + (additionnalSuccessMsg ? ' ' + additionnalSuccessMsg : ''), 'success');
				RevertDiffParams.MsgPosted++;//To remind the user he already posted one !
			});
		}

		//Function to tell that an edit failed and gives a clue about why.
		function editFailed(code) {
			var Msg = "";
			switch (code) {
				case "abusefilter-disallowed":
				case "abusefilter-warning":
				case "undofailure":
				case "protectedpage":
					Msg = RDtxt("EditFail-" + code);
					break;

				default:
					Msg = RDtxt("EditFail-default") + (code ? (" " + RDtxt("EditFailErrCode") + code) : "");//If the code is defined then let's put it anyway.
			}

			NotifyFail(RDtxt("ErrorEditFail") + " " + Msg);
		}

		$(document).ready(function () { //Aight, let's init
			// Get username of submitter
			var User1TD = $('td.diff-otitle');
			var User2TD = $('td.diff-ntitle');

			if (!User2TD.length) {//Whoops
				NotifyFail(RDtxt("ErrorInternal"));
				return;
			} else if (!User1TD.length) {//"Fake" diffs (only one version)
				return;
			}

			// Fetching the oldid
			var OldId = mw.util.getParamValue("oldid", User1TD.find('span.mw-diff-edit a').attr('href')); //This is the link to edit the old version that we use ("(edit)" is displayed on the UI).

			var User1A = User1TD.find('a.mw-userlink'), //This time the link to the user page...
				User2A = User2TD.find('a.mw-userlink'),
				User1Name = User1A.text(), // Finnally the text
				User2Name = User2A.text(),
				RestoreHTML, MessagesHTML;//And let's init.

			//Can we edit the curr page ? This variable may do false-positive (that will be catched anyway by a nice failure notification) but no false negative.
			if (mw.config.get("wgIsProbablyEditable")) {
				RestoreHTML = '(<span id=span-RD-restore><button id=btn-RD-restore>' + RDtxt('Restore') + '</button>'//The span will permit the override of the button when we will finish the restoration.
						+ '-'
						+ '<button id=btn-RD-restore-sum>' + RDtxt('RestorePutReason') + '</button></span>)';//We can choose a custom reason or not.
			} else {
				RestoreHTML = "(" + RDtxt("NoRestore") + ")";//If we can't it's a little easier
			}

			//We're not going to warn ourselves !
			if (mw.config.get("wgUserName") !== User2Name) {
				var WarnsHTML = '(' + RDtxt('Warn') + ' : ';//First warning the user

				//Showing a form to select any kind of warn we want that is usable in the template.
				//We've a "no selection" option to prevent miss-clicks (amongst with the "required" option on the select).
				WarnsHTML += '<form id=form-RD-warn><select id=sel-RD-type name=sel-RD-type required><option value="">---</option>';//Forms

				for (var Warn in RevertDiffParams.AvailableWarns)
					WarnsHTML += '<option value="' + Warn + '">' + RevertDiffParams.AvailableWarns[Warn].Type + '</option>';//Each of types available

				WarnsHTML += '</select> <input id=num-RD-level name=num-RD-level min=0 value=0 step=1 type=number disabled required /> ' + //the level selector (num)
						'<input type=submit id=sub-RD-warn value="' + RDtxt("WarnSubmit") + '" /></form>) ';//The submit btn

				WarnsHTML += '(' + RDtxt("WarnPredefined");//Now the predefined warns : 

				for (var id = 0, l = RevertDiffParams.PredefinedWarns.length; id < l; id++) {
					if (id !== 0)
						WarnsHTML += '-';//If we aren't at the end of the list add a separator.
					WarnsHTML += "<button id=btn-RD-predef-warn-" + id + ">" + RevertDiffParams.PredefinedWarns[id].Name + '</button>';//A nice btn.
				}
				WarnsHTML += ')';//Finished with warns.

				//Welcoming the user.
				var WelcomeTxt = '(<button id=btn-RD-welcome>' + RDtxt('Welcome') + '</button>)';

				MessagesHTML = WarnsHTML + " " + WelcomeTxt;//Now we have our html for the messages.
			} else {
				MessagesHTML = '(' + RDtxt('ItsYou') + ')';//Not gonna warn myself.
			}

			$("#contentSub").append("<div id=div-RD-main>" + RestoreHTML + " " + MessagesHTML + "</div>"); //Let's add ourself just below the title.

			//Now let's bind some events.
			if (mw.config.get("wgIsProbablyEditable")) //There is no btn if we can't edit.
				$("button[id^=btn-RD-restore]").on("click", function (e) {
					// If the id is btn-RD-restore-sum, we want to set a custom summarry.
					var Func = (e.target.id === "btn-RD-restore-sum" ? restoreReason : restore);//Which fn to use ?
					Func(OldId, User1Name);
				});

			if (mw.config.get("wgUserName") !== User2Name) { //Same but it is because we don't warn ourselves.
				$("button[id^=btn-RD-predef-warn-]").on("click", function (e) { //Predefined warns
					var PredefinedWarn = RevertDiffParams.PredefinedWarns[e.target.id.slice(19, 21)];
					warnUser(User2Name, PredefinedWarn.Type, PredefinedWarn.Level, e.target);
				});

				$("#btn-RD-welcome").on("click", function (e) { //Welcome
					var IsIP = User2A.hasClass("mw-anonuserlink"); //Is the link for an IP ?
					welcomeUser(User2Name, IsIP, e.target);
				});

				$("#sel-RD-type").on("change", updateLevelList); //Change warn type = update the max lvl available

				$("#form-RD-warn").on("submit", function (e) {
					warnUser(User2Name, $("#sel-RD-type").val(), $("#num-RD-level").val(), e.target);
					e.returnValue = false;//We don't reload the page here !
					e.preventDefault();
				});
			}

		});
	}//End of testing if we can use RevertDiff
});//End of closure
//</nowiki>