Utilisateur:Phe/sortable table.js

Un article de Wikipédia, l'encyclopédie libre.

Note : Après avoir publié la page, vous devrez forcer son rechargement pour voir les changements : Mozilla / Konqueror / Firefox : Shift-Ctrl-R, Internet Explorer / Opera : Ctrl-F5, Safari : Cmd-R.

// <pre>
 
/**
 * Adapté de  http://kryogenix.org/code/browser/sorttable/ par Phe
 * MIT licence, auteur Stuart Langridge, November 2003
 *
 * Principales modifications :
 *
 *  Il est possible de spécifier qu'on ne peut pas effectuer de tri
 * par rapport à certaines colonnes en leur donnant la class
 * "non_triable".
 *
 *  Lorsque des données sont manquantes il est difficile de deviner le
 * type de données, on peut dans ce cas sélectionner l'algo utilisé
 * en spécifiant l'algo comme nom de class de la colonne e.g.
 * « ! class='tri_numerique' | intitulé colonne », les classes
 * tri_numerique, tri_chaine et tri_date sont les seules reconnues
 * actuellement.
 *
 *  Le code a été assez nettement optimisé en vitesse en pre
 * contruisant les chaînes a trié en dehors des fonctions de
 * comparaison.
 *
 *  Le code gérant la classe de ligne sortbottom a été supprimé,
 * possible qu'il y ait des utilisations de ce truc mais c'est peu
 * intuitif comme mode de tri.
 *
 * Problème connu:
 *
 *  Les caractères unicode utilisés dans les entêtes de colonnes sont
 * très moches sous win98.
 *
 * Localisation :
 *  le tri alpha ne trie pas correctement les accentués, que ce soit
 * avec un linux localisé en en.US ou un win98 localisé en français.
 * Le problème est (très) partiellement corrigé par tsfr_fixDiacritic()
 *  même problème pour les formats en virgule flottante, le script
 * transforme 2,52 en 2.52 avant de faire les comparaisons.
 */
 
var tsfr_image_path = stylepath+"/common/images/";
var tsfr_image_up = "sort_up.gif";
var tsfr_image_down = "sort_down.gif";
var tsfr_image_none = "sort_none.gif";
 
function ts_parseFloat(num) {
	if (!num) return 0;
        num = num.replace(/ /, "");
	num = parseFloat(num.replace(/,/, ""));
	return (isNaN(num) ? 0 : num);
}
 
function tsfr_sortables_init()
{
  // Find all tables with class sortable and make them sortable
  if (!document.getElementsByTagName) return;
  var tbls = document.getElementsByTagName("table");
  for (var ti=0;ti<tbls.length;ti++) {
    if ((' '+tbls[ti].className+' ').indexOf(" triable ") != -1) {
      tsfr_makeSortable(tbls[ti]);
    }
  }
}
 
function tsfr_makeSortable(table)
{
  if (table.rows && table.rows.length > 0) {
    var firstRow = table.rows[0];
  }
  if (!firstRow) return;
 
  // We have a first row: assume it's the header, and make its contents clickable links
  for (var i=0;i<firstRow.cells.length;i++) {
    var cell = firstRow.cells[i];
    if ((" "+cell.className+" ").indexOf(" non_triable ") == -1) {
        cell.innerHTML += '&nbsp;&nbsp;<a href="#" class="sortheader" onclick="tsfr_resortTable(this);return false;"><span class="sortarrow"><img src="'+ tsfr_image_path + tsfr_image_none + '" alt="&darr;"/></span></a>';
    }
  }
}
 
function tsfr_getInnerText(el) {
  if (typeof el == "string") return el;
  if (typeof el == "undefined") return el;
  if (el.innerText) return el.innerText;	//Not needed but it is faster
  var str = "";
 
  var cs = el.childNodes;
  var l = cs.length;
  for (var i = 0; i < l; i++) {
    switch (cs[i].nodeType) {
    case 1: //ELEMENT_NODE
      str += tsfr_getInnerText(cs[i]);
      break;
    case 3:	//TEXT_NODE
      str += cs[i].nodeValue;
      break;
    }
  }
 
  return str;
}
 
// used only on lowered string, so now need to handle upper case
// diacritic except in a few special case...
function tsfr_fixDiacritic(str)
{
  // Il en manque plein...
  str = str.replace(/[áàâä]/g,'a');
  str = str.replace(/[éèêë]/g,'e');
  str = str.replace(/[íîï]/g,'i');
  str = str.replace(/[óôö]/g,'o');
  str = str.replace(/[úûü]/g,'u');
  str = str.replace(/[ýÿ]/g,'y');
  str = str.replace(/[ç]/g,'c');
  str = str.replace(/[Œœ]/g, 'oe');
  str = str.replace(/[Ææ]/g, 'ae');
  return str;
}
 
function tsfr_getSortFn(text, column_classname)
{
  var sortfn = null;
  if (column_classname) {
     switch(column_classname) {
         case 'tri_numerique':
           sortfn = tsfr_sortNumeric;
           break;
         case 'tri_chaine':
           sortfn = tsfr_sortCaseInsensitive;
           break;
         case 'tri_date':
           sortfn = tsfr_sortDate;
           break;
     }
  }
 
  if (sortfn == null) {
    sortfn = tsfr_sortCaseInsensitive;
    // recognize / or - as date separator
    if (text.match(/^\d\d[\/-]\d\d[\/-]\d\d\d\d$/)) {
      sortfn = tsfr_sortDate;
    // all string starting with digit are sorted as numeric
    } else if (text.match(/^[-]?[\d]+/)) {
      sortfn = tsfr_sortNumeric;
    }
    // prolly problematic in some case.
    if (text.length == 0) sortfn = tsfr_sortNumeric;
  }
  return sortfn
}
 
// Used to speedup sort by pre-building the string to compare rather
// to do it in the sort function.
function tsfr_buildSortableText(cell, sortfn)
{
  var aa = tsfr_getInnerText(cell);
  if (sortfn == tsfr_sortCaseInsensitive) {
    aa = aa.toLowerCase();
    aa = tsfr_fixDiacritic(aa);
  } else if (sortfn == tsfr_sortNumeric) {
    // FIXME: is this locale dependant ?
    aa = aa.replace(/,/g,'.');
    aa = aa.replace(/ /g,'');
    aa = parseFloat(aa);
    if (isNaN(aa))
      aa = -1E200;
  } else if (sortfn == tsfr_sortDate) {
    // y2k notes: all date must use four digits years format
    aa = aa.substr(6,4) + aa.substr(3,2) + aa.substr(0,2);
  }
  return aa;
}
 
function tsfr_resortTable(lnk) {
  //begin_time();
 
  // get the span
  var span;
  for (var ci=0;ci<lnk.childNodes.length;ci++) {
    if (lnk.childNodes[ci].tagName && lnk.childNodes[ci].tagName.toLowerCase() == 'span')
      span = lnk.childNodes[ci];
  }
  var td = lnk.parentNode;
  var column = td.cellIndex;
  var table = getParent(td, "table");
 
  // Work out a type for the column
  if (table.rows.length <= 1) return;
 
  var rowStart = (table.tHead && table.tHead.rows.length > 0 ? 0 : 1);
 
  var itm = "";
  for (var i = rowStart; i < table.rows.length; i++) {
     if (table.rows[i].cells.length > column) {
	itm = tsfr_getInnerText(table.rows[i].cells[column]);
	itm = itm.replace(/^[\s\xa0]+/, "").replace(/[\s\xa0]+$/, "");
	if (itm != "") break;
	}
  }
 
  var sortfn = tsfr_getSortFn(itm, table.rows[0].cells[column].className);
 
  var newRows = new Array();
  for (j=1;j<table.rows.length;j++) {
    newRows[j-1] = new Array(table.rows[j], tsfr_buildSortableText(table.rows[j].cells[column], sortfn));
  }
 
  newRows.sort(sortfn);
 
  var arrow; 
  if (span.getAttribute("sortdir") == 'down') {
    arrow = '<img src="'+ tsfr_image_path + tsfr_image_down + '" alt="&darr;"/>';
    newRows.reverse();
    span.setAttribute('sortdir','up');
  } else {
    arrow = '<img src="'+ tsfr_image_path + tsfr_image_up + '" alt="&uarr;"/>';
    span.setAttribute('sortdir','down');
  }
 
  // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
  for (i = 0; i < newRows.length; i++) {
    table.tBodies[0].appendChild(newRows[i][0]);
  }
 
  // Delete any other arrows there may be showing
  var allspans = document.getElementsByTagName("span");
  for (var ci=0;ci<allspans.length;ci++) {
    if (allspans[ci].className == 'sortarrow') {
      // in the same table as us?
      if (getParent(allspans[ci],"table") == getParent(lnk,"table")) {
	allspans[ci].innerHTML = '<img src="'+ tsfr_image_path + tsfr_image_none + '" alt="&darr;"/>';
      }
    }
  }
 
  span.innerHTML = arrow;
 
  //end_time();
}
 
// FIXME: what's the exact purpose of this ?
function getParent(el, pTagName) {
  if (el == null) return null;
  else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName)	// Gecko bug, supposed to be uppercase
    return el;
  else
    return getParent(el.parentNode, pTagName);
}
 
function tsfr_sortDate(a,b) {
  if (a[1]>b[1]) return 1;
  if (a[1]<b[1]) return -1;
  return 0;
}
 
function tsfr_sortNumeric(a,b) {
  return a[1]-b[1];
}
 
function tsfr_sortCaseInsensitive(a,b) {
  if (a[1]>b[1]) return 1;
  if (a[1]<b[1]) return -1;
  return 0;
}
 
//------- end sortable table
 
addOnloadHook(tsfr_sortables_init);
 
var millisecs;
var secs;
function begin_time()
{
  date = new Date();
  millisecs = date.getMilliseconds();
  secs = date.getSeconds();
}
 
function end_time()
{
  date = new Date();
  var msec = date.getMilliseconds() - millisecs;
  if (msec < 0) { msec += 1000; secs += 1; }
  alert((date.getSeconds() - secs) + '.' + msec);
}
 
// </pre>