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.
// Utilisateur:Edit Counter
// Utilisateur:Edit Counter/Estrie
// Utilisateur:Edit Counter/Edit Counter
// Utilisateur:Edit Counter/Letartean
// Utilisateur:Edit Counter/Tinodela
// Utilisateur:Edit Counter/Cantons-de-l'Est

/**
 * Edit Counter
 *
 * Ensemble de fonctions servant à générer différents diagrammes à 
 * partir d'un historique de contributions. Plus de détails sur 
 * https://fr.wikipedia.org/wiki/Utilisateur:Edit_Counter
 *
 * Auteurs : Cantons-de-l'Est (usually active on the French Wikipedia)
 * Date de dernière révision : 2011-12-26
 */
 
// Documentation :
//   To do
 
//////////////////////ZONE PERSONNALISABLE//////////////////////
var EC = {
  UnknowUser : "Ce pseudonyme est inconnu de ce wiki",
  UnknownDate : "(date inconnue)",
  MissingTag : "Edit Counter ne peut afficher les informations qu'il a recueillies, car il manque une balise dans la page",
  ProvidePseudonym : "Prière de fournir un pseudonyme",
  AcknowledgeCommand : "Edit Counter devrait compléter votre commande d'ici MMM minutes et SSS secondes. Il vous avertira quand les informations seront affichées.", // The script will replace MMM with a number of minutes and SSS with a number of seconds.
  UserInfos : "Informations sur le contributeur",
  Pseudonym : "Pseudonyme",
  RegisteredOn : "Enregistré le",
  DecimalSeparator : ",",  // Is the number written like "123 456,78" (in French) ? This variable is set in order to get around a bug within some browsers.
  ThousandSeparator : " ", // Is it like "123,456.78" (in English) or like "123.456,78" (German) ?  This variable is set in order to get around a bug within some browsers.
  FirstEdits : "Premières contributions",
  TotalEdits : "Contributions totales",
  NamespacesEdits : "Contributions par espace de noms",
  NamespacesMain : "(Principal)", // The name given to the main namespace
  NamespacesTitle : "Espace de noms",
  Total : "Total",
  BarChartPerMonth : "Contributions par mois",
  EditsTitle : "Contri-<br/>butions",
  YearMonthsTitle : "Année-<br/>mois",
  DrawComplete : "Les données sont affichées.",
  NamespaceCountBug : "Il y a une erreur dans le décompte des espaces de noms. Prière d'avertir le programmeur responsable d'Edit Counter.",
  NewArticles : "Articles créés",
  NewArticlesDisplayHideList : "Il y a NNN articles en tout. Cliquer sur le titre de chaque section pour afficher ou masquer la liste des articles d'un espace de noms.", // The script will replace NNN with the number of new articles.
  NewArticlesNone : "(Aucun)", // If the user has not created any article
  NewArticlesDate : "Date de création",
  NewRedirects : "Redirections créées",
  NewRedirectsDisplayHideList : "Il y a NNN redirections en tout. Cliquer sur le titre de chaque section pour afficher ou cacher la liste des redirections d'un espace de noms.", // The script will replace NNN with the number of new redirects.
  NewRedirectsNone : "(Aucune)", // If the user has not created any redirect
  DisclaimerTitle : "Avertissement",
  DisclaimerContent : "Les données sur les contributions de tous les participants aux projets de la <i>Wikimedia Foundation</i> sont mises à la disposition du public selon les termes de sa politique de confidentialité (un lien vers cette politique apparaît dans le bas de cette page). Il n'y a aucune information affichée dans cette page qui ne soit déjà publiée dans la liste des contributions de chaque participant à ce wiki, ou qui ne soit publiée dans les <i>dumps</i> périodiques des bases de données de la <i>Wikimedia Foundation</i>." // This text is partly a translation of the first sentences of the disclaimer displayed in http://toolserver.org/~vvv/yaec.php?user=Estrie&wiki=frwiki_p.
};
/////////////////FIN DE LA ZONE PERSONNALISABLE/////////////////


// JavaScript Raphaël library (see http://raphaeljs.com)
importScript("User:Cantons-de-l'Est/raphael.js");

// If XMLHttpRequest undefined, then define it
importScript("User:Cantons-de-l'Est/XMLHttpRequest.js");

// If JSON methods undefined, then define them
importScript("User:Cantons-de-l'Est/json2.js");

// Create something like https://fr.wikipedia.org/w/api.php?format=json
EC.szUrlPrefix = mw.config.get('wgServer') + "/w/api.php?format=json";

EC.DrawFace = function()
{
  var obj = document.getElementById("edit_counter");
  
  // Append <canvas width="150" height="150"></canvas>
  ////////////////////////////////////////////////////
  var elem = document.createElement('canvas');
  elem.width = 150;
  elem.height = 150;
  var canvas = obj.appendChild(elem);
  
  if (canvas.getContext) 
  {
  var ctx = canvas.getContext('2d');
  
  ctx.beginPath();
  ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle
  ctx.moveTo(110,75);
  ctx.arc(75,75,35,0,Math.PI,false);   // Mouth (clockwise)
  ctx.moveTo(65,65);
  ctx.arc(60,65,5,0,Math.PI*2,true);  // Left eye
  ctx.moveTo(95,65);
  ctx.arc(90,65,5,0,Math.PI*2,true);  // Right eye
  ctx.stroke();
  }
}

///////////////////////////
// Helper functions - begin
///////////////////
EC.ConvertToIso8601Date = function(sz)
{
// Expect something like '20090526011210'
// Return something like '2009-05-26 01:12:10'
  var szDate = "";
  szDate += sz.substring(0,4) + '-' ;
  szDate += sz.substring(4,6) + '-';
  szDate += sz.substring(6,8) + ' ';
  szDate += sz.substring(8,10) + ':';
  szDate += sz.substring(10,12) + ':';
  szDate += sz.substring(12,14);
  
  return szDate;
}

EC.ZeroPad = function(sz)
{
// If the number is lower than 10, then insert a zero
  if( sz < '10') sz = '0' + sz;
  
  return sz;
}

EC.ReplaceDecimalSeparator = function(n)
{
  var sz = '' + n;
  
  return sz.split('.').join(EC.DecimalSeparator);
}

EC.InsertThousandSeparator = function(n)
{
// Insert a thousand separator at every three digits.
// Does not work if there is a sign in front of the number

  // Work on the integer part
  ///////////////////////////
  var sz = '' + Math.floor(n);
  var a = sz.split('');
  a = EC.RevertArray(a);
  for(var i = 0; i < a.length; i++)
  {
  if(i > 0 && i % 3 == 0) a[i] = a[i] + EC.ThousandSeparator;
  }
  a = EC.RevertArray(a);
  sz = a.join('');
  
  // Append the decimal part
  //////////////////////////
  var sz2 = '' + n;
  var b = sz2.split('.');
  if( b && b[1] ) sz = sz + '.' + b[1];
  
  return sz;
}

EC.dateUtc = null;
EC.SetNow = function()
{
  var d = new Date();
  EC.dateUtc = EC.ConvertToIso8601Date('' + d.getUTCFullYear() + EC.ZeroPad(d.getUTCMonth() + 1) + EC.ZeroPad(d.getUTCDate()) + EC.ZeroPad(d.getUTCHours()) + EC.ZeroPad(d.getUTCMinutes()) + EC.ZeroPad(d.getUTCSeconds()) );
}
EC.SetNow();

EC.RevertArray = function(a)
{
  var b = new Array();
  for (var i = 0; i < a.length; i++) b[i] = '';
  for (var i = 0; i < a.length; i++) b[a.length - i - 1] = a[i];
  return b;
}

// A timer used to compute the time between the command and the display. 
////////////////////////////////////////////////////////////////////////
EC.TimeLapse = {};
EC.TimeLapse.Id = null;
EC.TimeLapse.n = 0;
EC.TimeLapse.Compute = function()
{
  EC.TimeLapse.n++;
  EC.TimeLapse.Id = setTimeout("EC.TimeLapse.Compute()", 1000);
}
///////////////
// Helper functions - end
/////////////////////////

/////////////////////
// Parameters - begin
//////////////
EC.szTitlePrefix = "";
EC.aParams = new Array();
EC.GetParametersFromUrl = function()
{
  // Example URL ending :
  //   User:Edit Counter/Cantons-de-l'Est&chart=pie|bar&refresh=yes
  //
  // The URL must contain the pseudonym :
  // - Cantons-de-l'Est
  //
  // It may contain :
  // - chart=pie|bar
  // - refresh=yes|no
  // - namespace=first|second|third|...
  // - startdate=2010-01-12
  // - stopdate=2010-12-31
  //
  // Anything else is useless
  //

  EC.aParams['pseudonym'] = "";
  EC.aParams['bar'] = false;
  EC.aParams['pie'] = false;
  EC.aParams['new_articles'] = false;
  EC.aParams['refresh'] = false;
  
  var sz = mw.config.get('wgTitle');
  
  if( sz.indexOf('Edit Counter/') == 0 )
  {
    EC.szTitlePrefix = 'Edit Counter/';
    sz = sz.substring('Edit Counter/'.length);
    
    // Get parameters
    /////////////////
    var a = sz.split('&');
    EC.aParams['pseudonym'] = a[0];
    for(var i = 1; i < a.length; i++)
    {
      var b = a[i].split('=');
      if( b && b.length > 0 )
      {
        if( b[0].indexOf('chart') == 0)
        {
          var c = b[1].split('|');
          for(var j = 0; j < c.length; j++)
          {
            if( c[j] && c[j] == 'pie') EC.aParams['bar'] = true;
            if( c[j] && c[j] == 'bar') EC.aParams['pie'] = true;
            if( c[j] && c[j] == 'new_articles') EC.aParams['new_articles'] = true;
          }
        }
        if( b[0].indexOf('refresh') == 0)
        {
          if( b[1] && b[1] == 'yes') EC.aParams['refresh'] = true;
        }
      }
    }
  }
}
//////////////
// Parameters - end
////////////////////

/////////////////////
// Data - begin
///////////////
EC.szSummaryToStore = "";
// Note the array elements computed/extracted from DB queries
// This function serves to group the JavaScript variables.
EC.Stringify = function(szName, a)
{
  var myJSONText = null;
  if(szName != 'EC.aUserInfos')
  {
    myJSONText = JSON.stringify(a);
  }
  else
  {
    myJSONText = JSON.stringify(a, function(key, value) 
    { 
      if(value instanceof Array) 
      { 
        var obj = {}; 
        for(var ind in value) 
        { 
          if(value.hasOwnProperty(ind)) 
          { 
            obj[ind] = value[ind]; 
          }
        } 
        return obj; 
      } 
      return value; 
    });
  }
  EC.szSummaryToStore += szName + " = " + myJSONText + "; ";
}

EC.GetDataInHtml = function()
{
  var sz = "";
  
  sz += "\n";
  sz += "<!-- Computed/extracted data on " + EC.dateUtc + " -->"
  sz += "<div style=\"display:none;\" id=\"edit_counter_date_" + EC.dateUtc + "\">";
  sz += "var date = \"" + EC.dateUtc + "\";";
  sz += EC.szSummaryToStore;
  sz += "</div>";
  sz += "\n";
  
  return sz;
}
/////////////
// Data - end
/////////////////////


EC.anNamespaceIds = new Array();
EC.aszNamespaceNames = new Array();
EC.GetNamespaceInfos = function()
{
  var i = 0;
  for(var NSnb in mw.config.get('wgFormattedNamespaces'))
  {
    if(NSnb >= 0)
    {
      EC.anNamespaceIds[i] = NSnb;
      EC.aszNamespaceNames[i] = mw.config.get('wgFormattedNamespaces')[NSnb];
      if(NSnb == '0')
      {
        // Use the name provided for the main namespace
        EC.aszNamespaceNames[i] = EC.NamespacesMain;
      }
      i++;
    }
  }
}

EC.aUserInfos = new Array();
EC.GetTotalContribs = function() 
{
  var szUrl = EC.szUrlPrefix + "&action=userdailycontribs&daysago=10000&user=" + EC.aParams['pseudonym'];

  var objJson = {};
  var objHttpRequest = new XMLHttpRequest();
  objHttpRequest.open("GET", szUrl, true);
  objHttpRequest.onreadystatechange = function () 
  {
    var done = 4, ok = 200;
    if(objHttpRequest.readyState == done)
    {
      if(objHttpRequest.status == ok) 
      {
        objJson = JSON.parse(objHttpRequest.responseText);
        
//{"userdailycontribs"://{"id":18624,"registration":0,"timeFrameEdits":674,"pastYearEdits":164,"totalEdits":"50676"}}
        EC.aUserInfos['user_id'] = objJson.userdailycontribs.id;
        EC.aUserInfos['registration_date'] = objJson.userdailycontribs.registration;
        EC.aUserInfos['gross_contributions'] = objJson.userdailycontribs.totalEdits;
        EC.ConfirmPseudonym();
      }
      else
      {
        alert('Error in EC.GetTotalContribs() : ' + objHttpRequest.status);
      }
    }
  };
  objHttpRequest.send(null);
}


EC.GetProcessingSpeed = function()
{
// The speed depends on the browser and its version
  var nSpeed = 200; 
  
  return nSpeed;
}

EC.ConfirmPseudonym = function()
{
  if( EC.aUserInfos['user_id'] == "0" )
  {
    alert(EC.UnknowUser + " : " + EC.aParams['pseudonym']);
  }
  else
  {
    var n = EC.aUserInfos['gross_contributions'] / EC.GetProcessingSpeed(); 
    n = Math.ceil(n / 10) * 10;
    var nMinutes = Math.floor(n / 60);
    var nSeconds = n % 60;
    var sz = EC.AcknowledgeCommand;
    var a = sz.split('MMM');
    sz = a[0] + nMinutes + a[1];
    a = sz.split('SSS');
    sz = a[0] + nSeconds + a[1];
    if( confirm(sz) == false ) return ; // No further processing
    EC.GetEdits();
  }
}


// This variable is updated by EC.WalkEditsRecursive()
EC.nEditsId = 0;
// This function calls EC.WalkEditsRecursive()
// This function is called back by EC.WalkEditsRecursive() once it has queried
// all data from the DB.
EC.GetEdits = function()
{
  if( EC.nEditsId < EC.anNamespaceIds.length )
  {
    var nId = EC.nEditsId;
    var szUrl = EC.szUrlPrefix + 
                "&action=query" + 
                "&list=usercontribs" +
                "&uclimit=5000" +
                "&ucuser=" + EC.aParams['pseudonym'] +
                "&ucnamespace=" + EC.anNamespaceIds[nId];
    EC.anEditCounts[nId] = 0;
    EC.aszUcstart[nId] = "";
    EC.abIsEditCountComplete[nId] = false;
    
    new EC.WalkEditsRecursive(szUrl, "", nId);
  }
  else
  {
    EC.WaitForData();
  }
}


EC.anEditCounts = new Array();
EC.aszUcstart = new Array();
EC.abIsEditCountComplete = new Array();
EC.nSentinel = 1000;
// EC.aYearMonthEdits structure :
// [ '2009-01',     Year month where the edit is done
//   [
//     [0],    Edit count within namespace 0
//     [1],    Edit count within namespace 1
//     ...
//     [21],   Edit count within namespace 105
//     ...
//   ],
//   Total contributions,
//   Relative Weight
// ];
EC.aYearMonthEdits = new Array();

// EC.aNewArticles structure :
// [
//  [ [<title>, <timestamp>, IsArticle, <size>], [<title>, <timestamp>, IsArticle, <size>]... ], // 0
//  [ [<title>, <timestamp>, IsArticle, <size>], [<title>, <timestamp>, IsArticle, <size>]... ], // 1
//  ...
// ];
EC.aNewArticles = new Array();

// EC.aanDowHours structure :
// [
//   [ <0th hour>, <1st hour>, <2nd hour>... ], // Sunday
//   [ <0th hour>, <1st hour>, <2nd hour>... ], // Monday
//   ...
// ];
EC.aanDowHours = new Array();

EC.WalkEditsRecursive = function(szUrl, szSuffix, nId)
{
  var a = null;
  var objJson = {};
  var szTimestamp = "";
  var nYearMonthId = 0;
  var szYearMonth = "";
  var nLength = 0;
  var szDateHour = "";
  var date = null;
  var szHour = "";
  var objHttpRequest = new XMLHttpRequest();
  objHttpRequest.open("GET", szUrl + szSuffix, true);
  objHttpRequest.onreadystatechange = function () 
  {
    var done = 4, ok = 200;
    if (objHttpRequest.readyState == done)
    {
      if (objHttpRequest.status == ok) 
      {
        objJson = JSON.parse(objHttpRequest.responseText);
        
        nLength = objJson.query.usercontribs.length;
        for(var i = 0; i < nLength; i++)
        {
          szTitle = objJson.query.usercontribs[i].title;
          szDateHour = objJson.query.usercontribs[i].timestamp;
          
          // Note each new article within each namespace
          for(var item in objJson.query.usercontribs[i])
          {
            if( item == 'new' )
            {
              a = new Array(szTitle, szDateHour, true, objJson.query.usercontribs[i].size);
              EC.aNewArticles[nId].push(a);
              break; // From for
            }
          }
          
          // Compare with the last known year-month
          // If the same, use the same YearMonth ID
          //   If not, seek for a known year-month and update the YearMonth ID
          szTimestamp = szDateHour.substring(0, 7);
          if( szYearMonth != szTimestamp )
          {
            for(var j = 0; j < EC.aYearMonthEdits.length; j++)
            {
              if( EC.aYearMonthEdits[j][0] == szTimestamp )
              {
                szYearMonth = szTimestamp;
                nYearMonthId = j;
                break; // From for
              }
            }
          }
          
          // Count each entry within each namespace
          EC.aYearMonthEdits[nYearMonthId][1][nId]++;
          
          // Fill the Dow-hours
          /////////////////////
          date = EC.ConvertToDateObject(szDateHour);
          szHour = szDateHour.substring(11,13);
          EC.aanDowHours[date.getDay()][Math.floor(szHour)]++;
        }
        
        // Get edits by namespaces
        //////////////////////////
        EC.anEditCounts[nId] += nLength;
//alert(EC.nSentinel + ", EC.anEditCounts[" + nId + "] : " + EC.anEditCounts[nId]);
        
        EC.aszUcstart[nId] = "";
        try { 
          EC.aszUcstart[nId] = objJson["query-continue"]["usercontribs"]["uccontinue"]; 
        } catch(e) { /* Nothing here */ }
        
        if( EC.aszUcstart[nId] != "" && EC.nSentinel-- >= 0)
        {
          EC.WalkEditsRecursive(szUrl, "&uccontinue=" + EC.aszUcstart[nId], nId)
        }
        else
        {
          EC.abIsEditCountComplete[nId] = true;
          EC.nEditsId++;
          // Call back for next namespace
          EC.GetEdits();
        }
        delete objJson;
      }
      else
      {
        alert('Error in EC.WalkEditsRecursive() : ' + objHttpRequest.status);
      }
    }
  };
  objHttpRequest.send(null);
}


EC.IdTimeoutData = null;
EC.WaitForData = function()
{
  var bIsAllCompleted = true;
  for(var j = 0; j < EC.anNamespaceIds.length; j++)
  {
    bIsAllCompleted &= EC.abIsEditCountComplete[j];
  }
  
  if( bIsAllCompleted == true )
  {
    clearTimeout(EC.IdTimeoutData);
    
    EC.TagRedirects();
  }
  else
  {
    EC.IdTimeoutData = setTimeout("EC.WaitForData()", 1000);
  }
}


////////////////////////////////////
// Redirects - begin
/////////////////////
EC.abIsTagRedirectsComplete = new Array();
EC.TagRedirects = function()
{
  for(var i = 0; i < EC.aNewArticles.length; i++)
  {
    EC.abIsTagRedirectsComplete[i] = new Array();
    for(var j = 0; j < EC.aNewArticles[i].length; j++)
    {
      EC.abIsTagRedirectsComplete[i][j] = false;
    }
  }
  
  var szUrl = EC.szUrlPrefix + 
              "&action=query" + 
              "&list=allpages" +
              "&aplimit=1" +
              "&apfilterredir=redirects";
  var sz = "";
  for(var i = 0; i < EC.aNewArticles.length; i++)
  {
    for(var j = 0; j < EC.aNewArticles[i].length; j++)
    {
      // Remove the prefix if not in the main namespace
      if( i != 0 )          
      {
        sz = EC.aNewArticles[i][j][0].substring(EC.aszNamespaceNames[i].length + 1);
      }
      else
      {
        sz = EC.aNewArticles[i][j][0];
      }
// alert(szUrl + "&apnamespace=" + EC.anNamespaceIds[i] + "&apfrom=" + sz +"&apto=" + sz);
      EC.TestTagRedirects(szUrl + "&apnamespace=" + EC.anNamespaceIds[i] + "&apfrom=" + sz +"&apto=" + sz, i, j);
    }
  }  
  EC.WaitForTagRedirects();
}


EC.TestTagRedirects = function(szUrl, i, j)
{
  var objJson = {};
  var objHttpRequest = new XMLHttpRequest();
  
  objHttpRequest.open("GET", szUrl, true);
  objHttpRequest.onreadystatechange = function () 
  {
    var done = 4, ok = 200;
    if (objHttpRequest.readyState == done)
    {
      if (objHttpRequest.status == ok) 
      {
        objJson = JSON.parse(objHttpRequest.responseText);
        
        // If the query comes back with one element,
        // then it is a redirect, NOT an article
        if( objJson.query.allpages.length > 0 )
        {
          EC.aNewArticles[i][j][2] = false;
//alert(EC.aNewArticles[i][j] + ' : ' + 'Redirect');
        }
        EC.abIsTagRedirectsComplete[i][j] = true;
      }
      else
      {
        alert('Error in EC.TestTagRedirects() : ' + objHttpRequest.status);
      }
    }
  }
  objHttpRequest.send(null);
}

EC.IdTimeoutTagRedirects = null;
EC.WaitForTagRedirects = function()
{
  var bIsAllCompleted = true;
  for(var i = 0; i < EC.aNewArticles.length; i++)
  {
    for(var j = 0; j < EC.aNewArticles[i].length; j++)
    {
      bIsAllCompleted &= EC.abIsTagRedirectsComplete[i][j];
    }
  }
  
  if( bIsAllCompleted == true )
  {
    clearTimeout(EC.IdTimeoutTagRedirects);
    
    EC.Draw();
  }
  else
  {
    EC.IdTimeoutTagRedirects = setTimeout("EC.WaitForTagRedirects()", 1000);
  }
}

/////////////////////
// Redirects - end
////////////////////////////////////

////////////////////////////////////
// User infos - begin
/////////////////////

EC.GetUserInfosInHtml = function()
{
  var sz = "";
  var nSum = 0;
  for(var i = 0; i < EC.anEditCounts.length; i++)
  {
    nSum += EC.anEditCounts[i];
  }
  
  sz += '<table width="100%">';
  sz += '<tr>';
  sz += '<td style="border-bottom-style:solid; border-width:2px; font-weight:900;">' + EC.UserInfos + '</td>';
  sz += '</tr>';
  sz += '</table>';
  sz += '<table width="80%" align="center">';
  sz += '<tr>';
  sz += '<td>' + EC.Pseudonym + ' : ' + '</td>';
  sz += '<td><span style="font-weight:900;">' + EC.aParams['pseudonym'] + '</span></td>';
  sz += '</tr>';
  sz += '<tr>';
  sz += '<td>' + EC.RegisteredOn + ' : ' + '</td>';
  sz += '<td>'
  if(EC.aUserInfos['registration_date'] != 0)
  {
    sz += EC.ConvertToIso8601Date(EC.aUserInfos['registration_date']) + ' (UTC)';
  }
  else
  {
    sz += EC.UnknownDate;
  }
  sz += ' &nbsp; (' + '<a href="' + mw.config.get('wgServer') + '/w/index.php?title=Special:Contributions&dir=prev&limit=5&target=' +
EC.aParams['pseudonym'] + '">' + EC.FirstEdits + '</a>)';
  sz += '</td>';
  sz += '</tr>';
  sz += '<tr>';
  sz += '<td>' + EC.TotalEdits + ' : ' + '</td>';
  sz += '<td>' + EC.InsertThousandSeparator(nSum) + '</td>';
  sz += '</tr>';
  sz += '<tr>';
  sz += '<td> &#160; </td>';
  sz += '<td> &#160; </td>';
  sz += '</tr>';
  sz += '</table>';
  
  EC.Stringify('EC.aUserInfos', EC.aUserInfos);
  
  return sz;
}
////////////////////
// User infos - end
///////////////////////////////////


////////////////////////////////////
// Pie charts - begin
/////////////////////
EC.GetPieChartPaths = function(anValues, cx, cy, r)
{
// SVG explanations at http://www.w3.org/TR/SVG/paths.html#PathData

// Compute a partial sector, the side and the arc. Each partial sector adds up
// on the screen to form a pie chart.

  // Compute the angles in degrees
  ////////////////////////////////
  var nTotal = 0;
  for(var i = 0; i < anValues.length; i++) 
  {
    nTotal += anValues[i];
  }
  
  var anAngles = new Array();
  for(var i = 0; i < anValues.length; i++) 
  {
    anAngles[i] = anValues[i] / nTotal * 360 ;
  }
  
  // Compute sectors
  //////////////////
  var rad = Math.PI / 180;
  var aszCoord = new Array();
  
  var nAnglesSum = 0;
  var rx = r;
  var ry = 0;
  for(var i = 0; i < anValues.length; i++)
  {
    aszCoord[i] = "M " + cx + "," + cy + " " +
                  "L " + (cx + rx) + "," + (cy + ry) + " " +
                  "A " + r + "," + r 
    nAnglesSum += anAngles[i];
    rx = Math.round(Math.cos( -nAnglesSum * Math.PI/180 ) * r);
    ry = Math.round(Math.sin( -nAnglesSum * Math.PI/180 ) * r);
    
    if(anAngles[i] < 180)
    {
      aszCoord[i] += " 0 " + "0,0 "; // Shortest path 
    }
    else
    {
      aszCoord[i] += " 0 " + "1,0 "; // Longest path 
    }
    
    aszCoord[i] += (cx + rx) + "," + (cy + ry);
    aszCoord[i] += " z";
//alert(aszCoord[i]);
  }
  return aszCoord;
}

// Namespace ID
// Color
EC.anGraphParams = [
   ['0', 
        "rgb(255, 85, 85)",
   ]
  ,['1', 
        "rgb(85, 255, 85)",
   ]
  ,['2', 
        "rgb(255, 255, 85)",
   ]
  ,['3', 
         "rgb(255, 85, 255)",
   ]
  ,['4', 
         "rgb(85, 85, 255)",
   ]
  ,['5', 
         "rgb(85, 255, 255)",
   ]
  ,['6', 
         "rgb(192, 0, 0)",
   ]
  ,['7', // Maybe same as X!'s Edit Counter
         "rgb(210, 105, 30)",
   ]
  ,['8', // Maybe same as X!'s Edit Counter
         "rgb(64, 61, 61)",
   ]
  ,['9', 
         "rgb(0, 192, 192)",
   ]
  ,['10', 
         "rgb(255, 175, 175)",
   ]
  ,['11', 
         "rgb(128, 128, 128)",
   ]
  ,['12', 
         "rgb(0, 192, 0)",
   ]
  ,['13', 
         "rgb(64, 64, 64)",
   ]
  ,['14', 
         "rgb(192, 192, 0)",
   ]
  ,['15', 
         "rgb(192, 0, 192)",
   ]
  ,['100', 
         "rgb(117, 163, 209)",
   ]
  ,['101', 
         "rgb(166, 121, 210)",
   ]
  ,['102', 
         "rgb(102, 0, 0)",
   ]
  ,['103', 
         "rgb(0, 0, 102)",
   ]
  ,['104', 
         "rgb(250, 255, 175)",
   ]
  ,['105', // Maybe same as X!'s Edit Counter
         "rgb(0, 255, 127)",
   ]
  ,['108', // Maybe same as X!'s Edit Counter
         "rgb(255, 99, 71)",
   ]
  ,['109', // Maybe same as X!'s Edit Counter
         "rgb(128, 128, 0)",
   ]
];


EC.DrawPieChart = function(szHtmlId, anIds, anValues)
{
  var nSum = 0;
  for(var i = 0; i < anValues.length; i++)
  {
    nSum += anValues[i];
  }
  
  // Get the percentages rounded to 2 digits after 
  // the local decimal separator
  ////////////////////////////////////////////////
  var aszPercent = new Array();
  for(var i = 0; i < anValues.length; i++)
  {
    aszPercent[i] = anValues[i] / nSum * 100;
    aszPercent[i] = Math.round(aszPercent[i] * 100)
    aszPercent[i] = aszPercent[i] / 100;
    aszPercent[i] = aszPercent[i].toFixed(2);
    aszPercent[i] = EC.ReplaceDecimalSeparator(aszPercent[i]);
  }
  
  // Canvas dimensions
  var nWidth = 400; // 400px
  var nHeight = 400; // 400px
  
  // Creates canvas at <div id="pie_chart"...
  // This canvas is larger than the side lengths in order to accommodate 
  // some display problems within IE 8.0 and Google Chrome 16.0
  var paper = Raphael(szHtmlId, nWidth + 5, nHeight + 13);
  
  var r = Math.floor(nHeight / 2), 
    cx = Math.floor(nWidth / 2) + 2,
    cy = Math.floor(nHeight / 2) + 4;
  var aszCoord = EC.GetPieChartPaths(anValues, cx, cy, r);
  
  var path = null;
  for(var i = 0; i < aszCoord.length; i++)
  {
//alert(aszCoord[i]);
    path = paper.path(aszCoord[i]);
//    path.attr("stroke", "blue");
//    path.attr("stroke-width", "1");
    path.attr("stroke-width", "0");
    for(var j = 0; j < EC.anGraphParams.length; j++)
    {
      if( anIds[i] == EC.anGraphParams[j][0] )
      {
        path.attr({ 
          "fill": EC.anGraphParams[j][1], /* yyyyy */
          "title": EC.aszNamespaceNames[j] /* Tooltip */ +
                  ' (' + EC.InsertThousandSeparator(anValues[i]) + 
                  '; ' + aszPercent[i] + ' %)' 
        });
        break; // From for
      }
    }
  }
}


EC.GetCategoriesInHtml = function(anIds, anValues)
{
  var nSum = 0;
  for(var i = 0; i < anValues.length; i++)
  {
    nSum += anValues[i];
  }
  
  // Get the percentages rounded to 2 digits after 
  // the local decimal separator
  ////////////////////////////////////////////////
  var aszPercent = new Array();
  for(var i = 0; i < anValues.length; i++)
  {
    aszPercent[i] = anValues[i] / nSum * 100;
    aszPercent[i] = Math.round(aszPercent[i] * 100)
    aszPercent[i] = aszPercent[i] / 100;
    aszPercent[i] = aszPercent[i].toFixed(2);
    aszPercent[i] = EC.ReplaceDecimalSeparator(aszPercent[i]);
  }
  
  var sz = '';
  sz += '<table>';
  sz += '<tr style="font-weight:900; font-size:90%;">';
  sz += '<th align="center">' + EC.NamespacesTitle + '</td>';
  sz += '<th width="2px"> </td>';
  sz += '<th align="center">' + EC.EditsTitle + '</td>';
  sz += '<th width="4px"> </td>';
  sz += '<th align="center"> % </td>';
  sz += '</tr>';
  
  var szDefault = '<td style="background-color:white;">(???)</td>';
  for(var i = 0; i < anValues.length; i++)
  {
    sz += '<tr>';
    for(var j = 0; j < EC.anGraphParams.length; j++)
    {
      if( EC.anGraphParams[j][0] == anIds[i] )
      {
        szDefault = '<td style="background-color:' 
                    + EC.anGraphParams[j][1] 
                    + ';">' 
                    + '<a href="' + mw.config.get('wgServer') + '/w/index.php?limit=50&tagFilter=&title=Special:Contributions&contribs=user&target=' + EC.aParams['pseudonym'] + '&namespace=' + EC.anGraphParams[j][0] + '&tagfilter=&year=&month=-1">' + EC.aszNamespaceNames[j] + '</a>'
/* + ' <' + anIds[i] + '> ' */
                    + '</td>';
        break; // From for
      }
    }
    sz += szDefault;
    sz += '<td width="2px"> </td>';
    sz += '<td align="right">' + EC.InsertThousandSeparator(anValues[i]) + '</td>';
    sz += '<td width="4px"> </td>';
    sz += '<td align="right">' + aszPercent[i] + '</td>';
    sz += '</tr>';
  }
  
  sz += '<tr style="font-weight:900;">';
  sz += '<td align="right"><a href="' + mw.config.get('wgServer') + '/wiki/Special:Contributions/' + EC.aParams['pseudonym'] + '">' + EC.Total + '</a> :</td>';
  sz += '<td width="2px"> </td>';
  sz += '<td align="right">' + EC.InsertThousandSeparator(nSum) + '</td>';
  sz += '<td width="4px"> </td>';
  sz += '<td align="center">100</td>';
  sz += '</tr>';
  sz += '</table>';
  
  return sz;
}




EC.DrawPieInfos = function(szIdHeader, szIdCategories, szIdPieChart)
{
  var sz = "";
  sz += '<table width="100%">';
  sz += '<tr>';
  sz += '<td style="border-bottom-style:solid; border-width:2px; font-weight:900;">' + EC.NamespacesEdits  + ' &nbsp; ' + EC.dateUtc + ' (UTC)</td>';
  sz += '</tr>';
  sz += '</table>';
  var objPieHeader = document.getElementById(szIdHeader);
  objPieHeader.innerHTML = sz;
  
  // Remove empty categories before drawing
  /////////////////////////////////////////
  var anIds = EC.anNamespaceIds.slice();
  var anEditCounts = EC.anEditCounts.slice();
  for(var i = EC.anEditCounts.length - 1; i >= 0; i--)
  {
    if( EC.anEditCounts[i] == 0 )
    {
      anIds.splice(i, 1);
      anEditCounts.splice(i, 1);
    }
  }
  
  EC.Stringify('EC.anNamespaceIds', anIds);
  EC.Stringify('EC.anEditCounts', anEditCounts);

  sz = "";
  sz += EC.GetCategoriesInHtml(anIds, anEditCounts);
  var objPieCategories = document.getElementById(szIdCategories);
  objPieCategories.innerHTML = sz;
  
  // Draw directly the pie chart
  EC.DrawPieChart(szIdPieChart, anIds, anEditCounts);
}

////////////////////
// Pie charts - end
///////////////////////////////////

////////////////////////////////////
// Bar charts - begin
/////////////////////

EC.FillYearMonthArrayWithDefaults = function()
{
// Create the data array as if there were edits every month
// since Wikipedia started (to my knowledge, it is the first known
// entity within the Wikimedia ecosystem).

  // Set the first year where an edit was done for the first time
  // (January 15, 2001) and today's year
  ///////////////////////////////////////////////////////////////
  var nLowestYear = 2001;
  var Today = new Date();
  var nHighestYear = Today.getFullYear();

  // Create a list of year-months for all years
  /////////////////////////////////////////////
  var aszMonths = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12' ];
  var aszYearMonths = new Array();
  var k = 0;
  for(var i = nLowestYear; i <= nHighestYear ; i++)
  {
    for(var j = 0; j < aszMonths.length; j++)
    {
      aszYearMonths[k++] = i + '-' + aszMonths[j];
    }
  }
  
  // Insert default values
  ////////////////////////
  var b = null;
  for(var i = 0; i < aszYearMonths.length; i++)
  {
    b = new Array();
    for (var k = 0; k < EC.anGraphParams.length; k++) b[k] = 0;
    EC.aYearMonthEdits[i] = [ aszYearMonths[i], b, 0, 0];
  }
}


EC.YearMonthComputeTotals = function(a)
{
  var nSum = 0;
  for(var i = 0; i < a.length; i++)
  {
    nSum = 0;
    for(var j = 0; j < a[i][1].length; j++) nSum += a[i][1][j];
    a[i][2] = nSum;
  }
  
  return a;
}


EC.YearMonthTrimDataArray = function(a)
{
  // Starting with the oldest year-month, remove entry with no edit
  while( a[0][2] == 0 ) 
  { 
    a.shift();
  }
  
  // Remove future years-months
  /////////////////////////////
  var d = new Date();
  var CurrentYearMonth = d.getFullYear() + '-' + EC.ZeroPad(d.getMonth() + 1);
  a = EC.RevertArray(a);
  while( a[0][0] > CurrentYearMonth )
  {
    a.shift();
  }
  a = EC.RevertArray(a);
  
  return a;
}


EC.YearMonthComputeRelativeWeights = function(a)
{
  var nMax = 0;
  for(var i = 0; i < a.length; i++)
  {
    if ( a[i][2] > nMax ) nMax = a[i][2];
  }
  
  for(var i = 0; i < a.length; i++)
  {
    a[i][3] = a[i][2] / nMax;
  }
  
  return a;
}


EC.DrawBarChartInfos = function(szIdHeader)
{
  EC.aYearMonthEdits = EC.YearMonthComputeTotals(EC.aYearMonthEdits);
  EC.aYearMonthEdits = EC.YearMonthTrimDataArray(EC.aYearMonthEdits);
  EC.aYearMonthEdits = EC.YearMonthComputeRelativeWeights(EC.aYearMonthEdits);
  
  EC.Stringify('EC.aYearMonthEdits', EC.aYearMonthEdits);
  
  var sz = "";
  
  // The bars' length
  var nRowWidth = 580; // 580px
  
  // Insert the first two columns of the table
  ////////////////////////////////////////////
  sz += '<table width="100%">';
  sz += '<tr>';
  sz += '<td style="border-bottom-style:solid; border-width:2px; font-weight:900;">' + EC.BarChartPerMonth + ' &nbsp; ' + EC.dateUtc + ' (UTC)</td>';
  sz += '</tr>';
  sz += '</table>';
  sz += '<table width="100%" align="left" cellpadding="0px" border="0px">';
  sz += '<tr style="font-weight:900; width:100%; font-size:90%;">';
  sz += '<th style="width:42px; align="center"">' + EC.YearMonthsTitle + '</th>';
  sz += '<th style="width:2px; "> </th>';
  sz += '<th style="width:45px;" align="center">' + EC.EditsTitle + '</th>';
  sz += '<th style="width:2px; "> </th>';
  sz += '<th style="width:' + nRowWidth + 'px; "> </th>';
  sz += '</tr>';

  for(var i = 0; i < EC.aYearMonthEdits.length; i++)
  {
    sz += '<tr style="width:100%; font-size:70%;">';
    sz += '<td style="width:42px; height:12px;">';
    // Insert an hyperlink only if the year-month total is greater than zero
    if( EC.aYearMonthEdits[i][2] > 0 )
    {
      sz += '<a href="' + mw.config.get('wgServer') + '/w/index.php?limit=50&tagFilter=&title=Special:Contributions&contribs=user&target=' + EC.aParams['pseudonym'] + '&tagfilter=&year=' + EC.aYearMonthEdits[i][0].split('-')[0] + '&month=' + EC.aYearMonthEdits[i][0].split('-')[1] + '">' + EC.aYearMonthEdits[i][0] + '</a>';
    }
    else
    {
      sz += EC.aYearMonthEdits[i][0];
    }
    sz += '</td>';
    sz += '<td style="width:2px; height:12px;"> &#160; </td>';
    sz += '<td style="width:45px; height:12px;" align="right">' + EC.aYearMonthEdits[i][2] + '</td>';
    sz += '<td style="width:2px; height:12px;"> &#160; </td>';
    sz += '<td style="width:' + nRowWidth + 'px; height:12px;" id="' + EC.aYearMonthEdits[i][0] + '">' + '</td>';
    sz += '</tr>';
  }
  
  sz += '</table>';
  
  var objBarChartHeader = document.getElementById(szIdHeader);
  objBarChartHeader.innerHTML = sz;
  
  // Bar charts
  /////////////
  // Canvas dimensions
  var nHeight = 12;
  
  // Creates canvas at <td id="2001-07"...
  ////////////////////////////////////////
  var paper = null;
  var path = null;
  var objHtmlId = null;
  var nItemWidth = 0;
  var nOffset = 0;
  
  for(var i = 0; i < EC.aYearMonthEdits.length; i++)
  {
    paper = Raphael(EC.aYearMonthEdits[i][0], nRowWidth, nHeight);
    nOffset = 0;
    
    for(var j = 0; j < EC.aYearMonthEdits[i][1].length; j++)
    {
      if( EC.aYearMonthEdits[i][2] > 0 )
      {
        nItemWidth = Math.floor(
                    nRowWidth *
                    EC.aYearMonthEdits[i][3] *
                    EC.aYearMonthEdits[i][1][j] /
                    EC.aYearMonthEdits[i][2]     );
        
        // Creates multi-colored rectangle at <td id="2001-07"...
        path = paper.rect(nOffset, 0, nItemWidth, nHeight);
    path.attr("stroke-width", "0");
    path.attr({ 
            "fill": EC.anGraphParams[j][1], /* zzzzz */
            "title": EC.aszNamespaceNames[j] /* Tooltip */ +
                  ' : ' + EC.InsertThousandSeparator(EC.aYearMonthEdits[i][1][j])
            });
        nOffset += nItemWidth;
      
        paper.path("M0,0 l" + nOffset + ",0");
        paper.path("M0,12 l" + nOffset + ",0");
/*      
        // For unknown reason, this command displays vertical lines
        // after *each* small rectangle. This is true for Raphael 1.5.2
        // and Raphael 2.0.0 . (2011-10-03)
        //paper.path("M0,0 l0,12");
        
        paper.path("M" + nOffset + ",12 l0,-12");
*/      }
    }
  }
  
  // To do : Create tooltips with colored text
  // 
  // 
  
}
////////////////////
// Bar charts - end
///////////////////////////////////

////////////////////////////////////
// New articles - begin
///////////////////////

EC.FillNewArticlesArrayWithDefaults = function()
{
  for(var i = 0; i < EC.anNamespaceIds.length; i++)
  {
    EC.aNewArticles[i] = new Array();
  }
}


EC.DisplayHide = function(szId)
{
  var obj = document.getElementById(szId);
  if( obj == null) return;
  if (obj.style.display == 'none')
  {
    obj.style.display = 'block';
  }
  else
  {
    obj.style.display = 'none';
  }
}

// Set the number of items in the sub-lists
EC.nSTEP = 50;
EC.GetNewArticlesInHtml = function()
{
  var sz = '';
  // Vertical space for IE 8.0  
  sz += '<br style="clear: all;" />';
  sz += '<table width="100%">';
  sz += '<tr>';
  sz += '<td style="border-bottom-style:solid; border-width:2px; font-weight:900;">' + EC.NewArticles + ' &nbsp; ' + EC.dateUtc + ' (UTC)</td>';
  sz += '</tr>';
  sz += '</table>';
  
  // Count the new articles
  /////////////////////////
  var anCnt = new Array();
  var n = 0;
  for(var i = 0; i < EC.aNewArticles.length ; i++)
  {
    anCnt[i] = 0;
    for(var j = 0; j < EC.aNewArticles[i].length ; j++)
    {
      if( EC.aNewArticles[i][j][2] == true )
      {
        anCnt[i]++;
        n++;
      }
    }
  }
  
  // Display only in namespace where there are new articles
  /////////////////////////////////////////////////////////
  if( n > 0)
  {
    var a = EC.NewArticlesDisplayHideList.split('NNN');
    EC.NewArticlesDisplayHideList = a[0] + '<span style="font-weight:900;">' + n + '</span>' + a[1];
 
    sz += '<div>' + EC.NewArticlesDisplayHideList + '</div>';
 
    var nCnt = 0;
    var szTableId = "";
    for(var i = 0; i < EC.aNewArticles.length ; i++)
    {
      nCnt = 0;
      if( anCnt[i] > 0 )
      {
        for(var j = 0; j < EC.aNewArticles[i].length; j++)
        {
          if( EC.aNewArticles[i][j][2] == true )
          {
            if( nCnt % EC.nSTEP == 0 )
            {
              // Header section
              /////////////////
              szTableId = 'edit_counter_new_articles_table_' + i + '_' + nCnt;
              sz += '<table cellborder="0px" cellspacing="0px" style="border-bottom-width: 1px; border-bottom:thin dotted black;" onClick="JavaScript:EC.DisplayHide(\'' + szTableId + '\');">';
              sz += '<tr style="background-color:' + EC.anGraphParams[i][1] + ';">';
              if( anCnt[i] > EC.nSTEP )
              {
                sz += '<th width="40px" align="left">' + (nCnt + 1) + '-</th>';
              }
              else
              {
                sz += '<th width="40px">&nbsp;</th>';
              }

              if( nCnt == 0)
              {
                sz += '<th width="520px" align="center">' + EC.aszNamespaceNames[i] + ' (' + anCnt[i] + ')</th>';
                sz += '<th width="140px">' + EC.NewArticlesDate + '</th>';
              }
              else
              {
                sz += '<th width="520px" align="center">&nbsp;</th>';
                sz += '<th width="140px">&nbsp;</th>';
              }
              sz += '</tr>';
              sz += '</table>';
              
              // Articles section
              ///////////////////
              sz += '<table style="display:none;" cellborder="0px" cellspacing="0px" id="' + szTableId + '">';
            }
            
            sz += '<tr>';
            sz += '<td width="40px"> ' +  (nCnt + 1) + ' </td>';
            sz += '<td width="520px"> ' + '<a href="' + mw.config.get('wgServer') + '/wiki/' + EC.aNewArticles[i][j][0] + '">';
            // Remove the prefix if not in the main namespace
            if( i != 0 )
            {
               sz += EC.aNewArticles[i][j][0].substring(EC.aszNamespaceNames[i].length + 1);
            }
            else
            {
               sz += EC.aNewArticles[i][j][0];
            }
            sz += '</a> [' + EC.aNewArticles[i][j][3] + '] </td>';
            sz += '<td width="140px"> ' + '<a href="' + mw.config.get('wgServer') + '/w/index.php?title=' + EC.aNewArticles[i][j][0] + '&dir=prev&action=history">' + EC.aNewArticles[i][j][1].split('T').join(' ').split('Z').join('') + '</td>';
            sz += '</tr>';
 
            if( nCnt % EC.nSTEP == (EC.nSTEP - 1) )
            {
                sz += '</table>';
            }
            nCnt++;
          }
        }
 
        // Close table if needed
        if( nCnt % EC.nSTEP == 0 )
        {
          sz += '</table>';
        }
      }
    }
  }
  else
  {
    sz += EC.NewArticlesNone + '<br/>';
  }
  
  return sz;
}
/////////////////////
// New articles - end
////////////////////////////////////

////////////////////////////////////
// New redirects - begin
///////////////////////

// Using EC.DisplayHide() since it has the same structure

EC.GetNewRedirectsInHtml = function()
{
  var sz = '';
  sz += '<table width="100%">';
  sz += '<tr>';
  sz += '<td style="border-bottom-style:solid; border-width:2px; font-weight:900;">' + EC.NewRedirects + ' &nbsp; ' + EC.dateUtc + ' (UTC)</td>';
  sz += '</tr>';
  sz += '</table>';
  
  // Count the new redirects
  //////////////////////////
  var anCnt = new Array();
  var n = 0;
  for(var i = 0; i < EC.aNewArticles.length ; i++)
  {
    anCnt[i] = 0;
    for(var j = 0; j < EC.aNewArticles[i].length ; j++)
    {
      if( EC.aNewArticles[i][j][2] == false )
      {
        anCnt[i]++;
        n++;
      }
    }
  }
  
  // Display only in namespace where there are new redirects
  //////////////////////////////////////////////////////////
  if( n > 0)
  {
    var a = EC.NewRedirectsDisplayHideList.split('NNN');
    EC.NewRedirectsDisplayHideList = a[0] + '<span style="font-weight:900;">' + n + '</span>' + a[1];
 
    sz += '<div>' + EC.NewRedirectsDisplayHideList + '</div>';
 
    var nCnt = 0;
    for(var i = 0; i < EC.aNewArticles.length ; i++)
    {
      nCnt = 0;
      if( anCnt[i] > 0 )
      {
        for(var j = 0; j < EC.aNewArticles[i].length; j++)
        {
          if( EC.aNewArticles[i][j][2] == false )
          {
            if( nCnt % EC.nSTEP == 0 )
            {
              // Header section
              /////////////////
              sz += '<table cellborder="0px" cellspacing="0px" style="border-bottom-width: 1px; border-bottom:thin dotted black;" onClick="JavaScript:EC.DisplayHide(\'edit_counter_new_redirects_table_' + i + '_' + nCnt + '\');">';
              sz += '<tr style="background-color:' + EC.anGraphParams[i][1] + ';">';
              if( anCnt[i] > EC.nSTEP )
              {
                sz += '<th width="40px" align="left">' + (nCnt + 1) + '-</th>';
              }
              else
              {
                sz += '<th width="40px" align="left">&nbsp;</th>';
              }
              if( nCnt == 0)
              {
                sz += '<th width="520px" align="center">' + EC.aszNamespaceNames[i] + ' (' + anCnt[i] + ')</th>';
                sz += '<th width="140px">' + EC.NewArticlesDate + '</th>';
              }
              else
              {
                sz += '<th width="520px" align="center">&nbsp;</th>';
                sz += '<th width="140px">&nbsp;</th>';
              }
              sz += '</tr>';
              sz += '</table>';
               
              // Redirects section
              ////////////////////
              sz += '<table style="display:none;" cellborder="0px" cellspacing="0px" id="edit_counter_new_redirects_table_' + i + '_' + nCnt + '">';
            }
            
            sz += '<tr>';
            sz += '<td width="40px"> ' +  (nCnt + 1) + ' </td>';
            sz += '<td width="520px"> ' + '<a href="' + mw.config.get('wgServer') + '/wiki/' + EC.aNewArticles[i][j][0] + '">';
            // Remove the prefix if not in the main namespace
            if( i != 0 )
            {
               sz += EC.aNewArticles[i][j][0].substring(EC.aszNamespaceNames[i].length + 1);
            }
            else
            {
               sz += EC.aNewArticles[i][j][0];
            }
            sz += '</a>' + ' </td>';
            sz += '<td width="140px"> ' + '<a href="' + mw.config.get('wgServer') + '/w/index.php?title=' + EC.aNewArticles[i][j][0] + '&dir=prev&action=history">' + EC.aNewArticles[i][j][1].split('T').join(' ').split('Z').join('') + '</td>';
            sz += '</tr>';
 
            if( nCnt % EC.nSTEP == (EC.nSTEP - 1) )
            {
              sz += '</table>';
            }
            nCnt++;
          }
        }
 
        // Close table if needed
        if( nCnt % EC.nSTEP == 0 )
        {
          sz += '</table>';
        }
      }
    }
  }
  else
  {
    sz += EC.NewRedirectsNone + '<br/>';
  }
  
  return sz;
}
/////////////////////
// New redirects - end
////////////////////////////////////


////////////////////////////////////////
// Day of the week, hours charts - begin
//////////////////////
EC.ConvertToDateObject = function(sz)
{
// Expect something like '2011-12-09T17:13:03Z'
// Return a date object
// Day of the week :
// - Sunday : 0
// - Monday : 1
// ...
// - Saturday : 6

  var date = new Date();
  date.setYear(sz.substring(0,4));
  date.setMonth(sz.substring(5,7) - 1);
  date.setDate(sz.substring(8,10));
  date.setHours(sz.substring(11,13));
  
  return date;
}

EC.FillDowHoursArrayWithDefaults = function()
{
  // Create the DowHours array 
  // - 7 days a week
  // - 24 hours a day
  for(var i = 0; i < 7 ; i++)
  {
    EC.aanDowHours[i] = new Array();
    for(var j = 0; j < 24 ; j++)
    {
      EC.aanDowHours[i][j] = 0;
    }
  }
}

//////////////////////
// Day of the week, hours charts - end
////////////////////////////////////////


EC.GetDisclaimer = function()
{
  var sz = "";
  sz += '<fieldset><legend><b>' + EC.DisclaimerTitle + '</b></legend>' + EC.DisclaimerContent + '</fieldset>';
  return sz;
}


EC.Draw = function()
{
  var objPage = document.getElementById("edit_counter");
  var sz = "";
  // Insert page layout code
  //////////////////////////
  sz = '';
  sz += '<div id="edit_counter_user_infos"></div>';
  sz += '<div id="edit_counter_pie_header"></div>';
  sz += '<table width="100%">';
  sz += '<tr>';
  sz += '<td width="40%" id="edit_counter_pie_categories"></td>';
  sz += '<td width="50%" id="edit_counter_pie_chart"></td>';
  sz += '<td width="10%">&nbsp;</td>';
  sz += '</tr>';
  sz += '</table>';
  sz += '<div id="edit_counter_bar_chart"></div>';
  sz += '<p>&nbsp;</p>';
  sz += '<div id="edit_counter_new_articles"></div>';
  sz += '<p>&nbsp;</p>';
  sz += '<div id="edit_counter_new_redirects"></div>';
  sz += '<div id="edit_counter_disclaimer"></div>';
  objPage.innerHTML = sz;
  
  // Insert user's infos
  //////////////////////
  var objUserInfos = document.getElementById("edit_counter_user_infos");
  objUserInfos.innerHTML = EC.GetUserInfosInHtml();
  
  // Insert pie chart infos
  // Raphael library insert SVG code directly
  /////////////////////////
  EC.DrawPieInfos("edit_counter_pie_header", 
                  "edit_counter_pie_categories", 
                  "edit_counter_pie_chart");
 
  // Insert bar chart
  // Raphael library inserts SVG code directly
  ////////////////////////////////////////////
  EC.DrawBarChartInfos("edit_counter_bar_chart");
  
  // Insert dow-hours chart infos
  ///////////////////////////////
var sz = "";
  for(var i = 0; i < 7; i++)
  {
    sz += i + ' : ';
    for(var j = 0; j < 24; j++)
    {
      sz += EC.aanDowHours[i][j] + ' | ';
    }
    sz += '\n';
  }
alert('Répartition horaire de chaque journée de la semaine : \n\n' + sz);
  
  // Insert dow chart infos
  //////////////////////////

  // Insert top articles infos
  ////////////////////////////
  
  // Insert new articles
  //////////////////////
  var objNewArticles = document.getElementById("edit_counter_new_articles");
  objNewArticles.innerHTML = EC.GetNewArticlesInHtml();
  
  // Insert new redirects
  ///////////////////////
  var objNewRedirects = document.getElementById("edit_counter_new_redirects");
  objNewRedirects.innerHTML = EC.GetNewRedirectsInHtml();
  
  // Insert disclaimer
  ////////////////////
  var objDisclaimer = document.getElementById("edit_counter_disclaimer");
  objDisclaimer.innerHTML = EC.GetDisclaimer();
  
  // Alert the data in HTML
  /////////////////////////
  var szData = "";
  szData = EC.GetDataInHtml();
  //alert(szData);
  
  // Reduction of executed code
  /////////////////////////////
  delete EC.aNewArticles;
  
  try {
    clearTimeout(EC.TimeLapse.Id); 
// Display elapsed time between the call and the completion
//alert(EC.TimeLapse.n + ' secondes');
    } catch(e) { /* Nothing here */ }
  alert(EC.DrawComplete);
}


EC.EditCounter = function()
{
  // Invoke this extension only from a user namespace in view mode.
  // The page must be "under" User:Edit Counter.
  if( mw.config.get('wgNamespaceNumber') == 2 && 
      mw.config.get('wgTitle').indexOf('Edit Counter/') == 0 &&
      mw.config.get('wgAction') == "view" &&
      document.getElementById('ca-nstab-user').className == 'selected'
      )
  {
    if( document.getElementById("edit_counter") )
    {
      // Look for something like 
      // "User:Edit Counter/Cantons-de-l'Est&chart=pie|bar&refresh=yes"
      // before querying the DB and creating any graph
      //////////////////////////////////////////////////////////////////
      EC.GetParametersFromUrl();
      
      if(EC.szTitlePrefix.length > 0)
      {
        if ( EC.aParams['pseudonym'].length > 0)
        {
          EC.GetNamespaceInfos();
          EC.FillYearMonthArrayWithDefaults();
          EC.FillNewArticlesArrayWithDefaults();
          EC.FillDowHoursArrayWithDefaults();
          EC.GetTotalContribs();
          EC.TimeLapse.Compute();
        }
        else
        {
          alert(EC.ProvidePseudonym);
          return;
        }
      }
    }
    else
    {
      alert(EC.MissingTag + " : <div id='edit_counter'></div>");
    }
  }
}

addOnloadHook(function(){
//  EC.DrawFace();
  EC.EditCounter();
});