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) ;

Firefox (sur GNU/Linux) / Chrome / Internet Explorer / Opera : maintenez la touche Ctrl en cliquant sur le bouton Actualiser ou pressez Ctrl-F5.
var AllContribs = new Array(); //tableau contenant toutes les contributions
var Users; //tableau contenant les users
var FirstUsersRequest = true; 
var FollowState = false; //l'état des liens users (vers la page ou vers le javascript)

//si on est dans la sous-page user/AdvancedContrib, alors on lance le bouzin	
if (mw.config.get('wgTitle') == mw.config.get('wgUserName') + "/AdvancedContribs" && mw.config.get('wgAction') == "view" && mw.config.get('wgUserGroups').indexOf("sysop") != -1) jQuery(startAdvancedContrib);

//rajouter l'onglet suivi des users
if(mw.config.get('wgUserGroups').indexOf("sysop") != -1) jQuery(addFollowTab);
function addFollowTab()
{
	var list = document.getElementById('p-cactions');

	if(!list) return;
	list = list.childNodes[3].childNodes[1];

	var elt = document.createElement('li');
	list.appendChild(elt);
	elt.innerHTML = "<a id='caa_userFollow' href='javascript:toggleFollowAnchor()'>Suivi des users</a>";
}

//change l'état d'un utilisateur (suivi ou non)
function toggleUser(User)
{
	var userPos = Users.indexOf(User);
	var Summeray;
	
	if(userPos == -1)
	{
		Users[Users.length] = User;
		setAnchorState(User, true);
		Summeray = "Rajoute " + User;
	}
	else
	{
		Users.splice(userPos,1);
		setAnchorState(User, false);
		Summeray = "Supprime " + User;
	}	
	
	ajaxSetUsersPage(Summeray);
}

//change la page user/AdvancedContrib selon le tableau Users
function ajaxSetUsersPage(Summeray)
{
	var req=new XMLHttpRequest();
	
	req.onreadystatechange = function()
	{
		if(req.readyState == 4)
		{
			if(req.status==200)
			{
				var div = document.getElementById('Hiddendiv');
				div.innerHTML = req.responseText.split("<form")[1].split("</form>")[0]; //on attrape le contenu du form pour avoir le token
				
				var userStr = Users.join('\n');
				var reqSubmit=new XMLHttpRequest();
				
				var wpStarttime = document.getElementsByName('wpStarttime')[0].value;
				var wpEdittime = document.getElementsByName('wpEdittime')[0].value;
				var wpEditToken = document.getElementsByName('wpEditToken')[0].value;
				var wpWatchthis = document.getElementsByName('wpWatchthis')[0].value;
				
				var post = "wpTextbox1=" + encodeURIComponent(userStr) + "&wpSummary=" + encodeURIComponent(Summeray);
				post = post + "&wpStarttime=" + encodeURIComponent(wpStarttime);
				post = post + "&wpEdittime=" + encodeURIComponent(wpEdittime);
				post = post + "&wpEditToken=" + encodeURIComponent(wpEditToken);
				post = post + "&wpWatchthis=" + encodeURIComponent(wpWatchthis);
				
				reqSubmit.open("POST","/w/index.php?title=Utilisateur:" + mw.config.get('wgUserName')  + "/AdvancedContribs&action=submit");
				reqSubmit.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
				reqSubmit.send(post);
			}
		}
	}
	
	req.open("GET","/w/index.php?title=Utilisateur:" + mw.config.get('wgUserName')  + "/AdvancedContribs&action=edit");
	req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	req.send();
}

//change l'etat des anchors associé a user (state: true pour mettre suivi, false pour mettre normal)
function setAnchorState(User, state)
{
	var anchors=document.getElementById('bodyContent').getElementsByTagName('a');
	var i;
	var userAnchor;
	
	for(i=0;i!=anchors.length;i++)
	{
		userAnchor = getUserFromTitle(anchors[i].title);
			
		if(User == userAnchor)
		{
			if(state) anchors[i].style.background='#b0feb6';
			else anchors[i].style.background='#febeb5';
		}
	}
}

//renvoi l'username en fonction du title, "" si ca ne correspond pas à un lien
function getUserFromTitle(Title)
{
	var hrefs = Title.split(":");
	
	if(hrefs.length==2)
		if(hrefs[0]=='Utilisateur')
			if(hrefs[1].indexOf('/') == -1)
				return hrefs[1];
			
	hrefs = Title.split("/");
	
	if(hrefs.length==2)
		if(hrefs[0]=='Special:Contributions')
			if(hrefs[1].split(".").length==4)
				return hrefs[1];
			
	return "";
}

//function qui cherches les anchor vers les pages users et qui change le href (soit vers la js, soit vers la page user)
function toggleFollowAnchor()
{
	var anchors=document.getElementById('bodyContent').getElementsByTagName('a');
	var i;
	var user;
	
	if(FirstUsersRequest == true)
	{
		FirstUsersRequest = false;
		//le div pour mettre le code HTML de l'edit pour choper le token
		var newDiv = document.createElement('div');
		newDiv.style.display = 'none';
		newDiv.id = 'Hiddendiv';
		
		document.getElementById('bodyContent').appendChild(newDiv);
	
		getUsers(toggleFollowAnchor); //on s'auro apelle en handler
		return;
	}

	if(FollowState == false)
	{
		for(i=0;i!=anchors.length;i++)
		{
			user = getUserFromTitle(anchors[i].title);
			
			if(user!="")
			{
				anchors[i].hrefSave = anchors[i].href;
				anchors[i].href = 'javascript:toggleUser("' + user + '")';
				if(Users.indexOf(user)!=-1 ) anchors[i].style.background='#b0feb6';
				else anchors[i].style.background='#febeb5';
			}
		}
		document.getElementById('caa_userFollow').innerHTML='Liens users normaux';
		
		FollowState = true;
	}
	else
	{
		for(i=0;i!=anchors.length;i++)
		{
			user = getUserFromTitle(anchors[i].title);
			
			if(user!="")
			{
				anchors[i].href = anchors[i].hrefSave;
				anchors[i].style.background='';
			}
		}
		document.getElementById('caa_userFollow').innerHTML='Suivi des users';
		
		FollowState = false;
	}

}


//function qui lance le bouzin
function startAdvancedContrib()
{
	var div=document.getElementById('bodyContent');
	div.innerHTML= "<div id='contribContent'>&nbsp;</div>";
	
	getUsers(getData);
}


//fonction qui charge les users, handle est ensuite appellé (getData pour les contributions, toggleFollowAnchor pour les anchors)
function getUsers(handle)
{
	var req=new XMLHttpRequest();
	var date = new Date().getTime();

	req.handle=handle;
	req.onreadystatechange = function()
	{
		if(req.readyState == 4)
		{
			if(req.status==200)
			{
				var UsersStr = req.responseText.split('\r').join('');
				var i;
				
				Users = UsersStr.split('\n');
				for(i=Users.length-1; i>=0; i--)
					if(Users[i]=="") Users.splice(i,1);
					
				req.handle();
			}
		}
	}
	
	req.open("GET","/w/index.php?title=User:" + mw.config.get('wgUserName')  + "/AdvancedContribs&action=raw&dummy=" + date);
	req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	req.send(null);
}

//lance les requetes qui vont chercher les contributions des utilisateurs
function getData()
{
	var i;

	for(i=0;i!=Users.length;i++)
	{
		var req=new XMLHttpRequest();
		
		req.onreadystatechange = function()
		{
			if(req.readyState == 4)
			{
				if(req.status==200)
				{
					AllContribs[AllContribs.length] = getContribs(req.responseXML);
					if(AllContribs.length==Users.length) writeData();
				}
			}
		}
		
		req.open("GET","/w/query.php?what=usercontribs&titles=User:" + Users[i] + "&uclimit=10&uccomments&format=xml");
		req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		req.send(null);
	}
}

//cherche dans le tableau 	AllContribs la contribution la plus récente
function getMostRecentContribPosition()
{
	var res = 0;
	var i;
	
	for(i=0;i<AllContribs.length;i++)
		if(AllContribs[i].length!=0) break;
		
	if(i==AllContribs.length) return -1;
	
	res=i;
	
	for(i=i+1;i<AllContribs.length;i++)
	{
		if(AllContribs[i].length!=0)
		{
			if(AllContribs[i][0].date.getTime() > AllContribs[res][0].date.getTime())
				res = i;
		}
	}
	
	return res;
}

//écrit toutes les contributions
function writeData()
{
	var div=document.getElementById('contribContent');
	var table = document.createElement('table');
	var RecentContrib = getMostRecentContribPosition();
	var date = AllContribs[RecentContrib][0].date;
	
	table.style.fontSize='100%';
	div.appendChild(table);
	table.cellPadding=0;
	table.cellSpacing=0;
	
	insertDateRow(table, date);
	while(true)
	{
		RecentContrib = getMostRecentContribPosition();
		if(RecentContrib==-1) break;
		if(date.getFullYear() != AllContribs[RecentContrib][0].date.getFullYear() || 
				date.getMonth() != AllContribs[RecentContrib][0].date.getMonth() || 
				date.getDate() != AllContribs[RecentContrib][0].date.getDate())
		{
			date = AllContribs[RecentContrib][0].date;
			insertDateRow(table, date);
		}
		insertLineContrib(AllContribs[RecentContrib].shift(),Users[RecentContrib], table);
	}
}

//rajoute une ligne avec la date
function insertDateRow(table, date)
{
	var row =table.insertRow(-1);
	var cell = row.insertCell(-1);

	cell.colSpan=7;
	cell.style.paddingTop= '6px';
	cell.style.borderBottom = '1px solid blue';
	cell.innerHTML = "<b>" + stringDate(date) + "</b>";
}


//rajoute une ligen html dans le tableau
function insertLineContrib(Contrib, Name, table)
{
	var row, cell;
	var pageName = htmlLink(Contrib.page);
	
	if(Contrib.top) pageName = pageName + "&nbsp;<b>(l)</b>";
	
	row=table.insertRow(-1);
	row.style.whiteSpace='nowrap';
	
	insertCellText(row, stringHour(Contrib.date));
	insertCellHTML(row, htmlHistLink(Contrib.page));
	insertCellHTML(row, htmlDiffLink(Contrib.page));
	insertCellHTML(row, htmlDeleteLink(Contrib.page, Contrib.revid));
	insertCellHTML(row, htmluserLink(Name));
	insertCellHTML(row, pageName);
	insertCellText(row, Contrib.comment);
}

//insere une cellule formatée avec de l'html dedans
function insertCellHTML(row, innerHTML)
{
	var cell=insertCell(row);
	cell.innerHTML = innerHTML;
}

//insere une cellule formatée avec du texte dedans
function insertCellText(row, Text)
{
	var cell=insertCell(row);
	cell.textContent = Text;
}

//crée une cellule formatée
function insertCell(row)
{
	var cell=row.insertCell(-1);
	cell.style.paddingRight='3px';
	return cell;
}

//renvoi le nom du mois
function getMonthName(m)
{
	switch(m)
	{
		case 0: {return "janvier";}
		case 1: {return "février";}
		case 2: {return "mars";}
		case 3: {return "avril";} 
		case 4: {return "mai";}
		case 5: {return "juin";}
		case 6: {return "juillet";} 
		case 7: {return "aout";}
		case 8: {return "septembre";}
		case 9: {return "octobre";} 
		case 10: {return "novembre";}
		case 11: {return "décembre";}
	}
}

//renoi une chaine de caractère avec l'heure
function stringHour(d)
{
	return d.getHours() + "h" + d.getMinutes();
}

//renoi une chaine de caractère avec la date
function stringDate(d)
{
	return d.getDate() + " " + getMonthName(d.getMonth()) + " " + d.getFullYear();
}

//lien historique
function htmlHistLink(pageName)
{
	return "<a title='historique' href='/w/index.php?title=" + pageName + "&action=history'>(h)</a>";
}

//lien diff
function htmlDiffLink(pageName, revid)
{
	return "<a title='diff' href='/w/index.php?title=" + pageName + "&diff=prev&oldid=" + revid + "'>(d)</a>";
}

//lien article
function htmlLink(pageName)
{
	return "<a href='/wiki/" + pageName + "'>" + pageName + "</a>";
}

//lien delete
function htmlDeleteLink(pageName)
{
	return "<a title='supprimer' href='/w/index.php?title=" + pageName + "&action=delete'>(s)</a>";
}

//lien user
function htmluserLink(User)
{
	return "<a title='Utilisateur:" + User + "' href='/wiki/Utilisateur:" + User + "'>" + User + "</a><small>&nbsp;(" +
			"<a href='/wiki/Discussion_Utilisateur:" + User + "'>d</a>&nbsp;" +
			"<a href='/wiki/Special:Contributions/" + User + "'>c</a>&nbsp;" +
			"<a href='/wiki/Special:Blockip/" + User + "'>b</a>)</small>";
			
			
}

//charge l'objet XML vers les objets
function getContribs(Doc)
{
	var XmlContribs = Doc.getElementsByTagName('contributions')[0].childNodes;
	var Contribs = new Array();
	
	for(i=0;i!=XmlContribs.length;i++)
	{
		Contribs[i] = new Object();
				
		Contribs[i].comment = XmlContribs[i].attributes.comment.value;
		Contribs[i].revid = XmlContribs[i].attributes.revid.value;
		Contribs[i].page = XmlContribs[i].textContent;
		Contribs[i].date = parseDate( XmlContribs[i].attributes.timestamp.value);
		if(XmlContribs[i].attributes.getNamedItem('top'))  Contribs[i].top = true;
		else Contribs[i].top = false;
		
		if(XmlContribs[i].attributes.getNamedItem('new'))  Contribs[i].new = true;
		else Contribs[i].new = false;
		
	}
	
	
	return Contribs;
}

//parse la chaine de caratère date
function parseDate(str)
{
	var a,b,c;
	var date = new Date();
	
	a = str.split('T');
	b = a[0].split('-');
	c = a[1].split(':');
		
	c[2]=c[2].split('Z')[0];
	
	date.setFullYear((b[0]),(b[1])-1, (b[2]));
	date.setHours((c[0]),(c[1]), (c[2]),0);
	
	return date;
}