/**

Sorting tables, short and fast.

© 2010, fictif.org, licence CeCILL-C (LGPL compatible droit français) Examples
<table class="Sortable">
<!-- ... -->
</table>
<!-- Will make sortable all tables with the sortable className -->
<script src="../diple/js/Sortable.js">//</script>

On a today (2011) laptop, less than 1s. on 10 000 rows.

onLoad onClick Credit : */
var Sortable = { /** last key */ lastKey : 'key0',
/** call on the load document event, loop on table to put sort arrows */
load: function() { // Find all tables with class sortable and make them sortable if (!document.getElementsByTagName) return; tables = document.getElementsByTagName("table"); for (var i=tables.length - 1; i >= 0; i--) { table = tables[i]; if (((' ' + table.className.toLowerCase() + ' ').indexOf("sortable") != -1)) { Sortable.create(table); } } },
/** Sort a prepared table. For strink values, the trick is to modify the String.toString() method so that we can give what we want for table row <tr>, especially a prepared sort key for the requested row. row[key] is a 10 chars string prepared with this.key(). */
sort: function(table, key, reverse) { var tbody; for (var i = 0; i < table.tBodies.length; i++) { tbody=table.tBodies[i]; // waited object not found, go out if (!tbody.lines) continue; // numerical key if (tbody.lines[0][key] === +tbody.lines[0][key]) { var comparator=function(a, b) { return a[key] - b[key]; } if (reverse) tbody.lines.reverse(comparator); else tbody.lines.sort(comparator); } else { // save native String.toString() var save = String.prototype.toString; // special case, a second sort, return 2 keys to sort on 2 columns if (Sortable.lastKey && Sortable.lastKey != key) String.prototype.toString = function () { return this[key]+this[Sortable.lastKey];}; // set the method else String.prototype.toString = function () { return this[key];}; // do the sort if (reverse) tbody.lines.reverse(); else tbody.lines.sort(); // restore native String.toString() String.prototype.toString = save; } // affect the sorted <tr> to the table as a innerHTML tbody.innerHTML=tbody.lines.join("\n"); } // zebra the table after sort Sortable.zebra(table); },
/** normalize table, add events to top cells, take a copy of the table as an array of string */
create: function(table) { if (table.sortable) return false; // not enough rows, go away if (table.rows.length < 2) return false; // if no tHead, create it with first row if (!table.tHead) { table.createTHead().appendChild(table.rows[0]); } // loop on tbodies var row, s; for (var i = 0; i < table.tBodies.length; i++) { // create the array of lines table.tBodies[i].lines=new Array(); for (j=table.tBodies[i].rows.length-1; j >=0; j--) { row=table.tBodies[i].rows[j]; Sortable.paint(row, j); // get the <tr> html as a String object s=new String(row.outerHTML || new XMLSerializer().serializeToString(row).replace(' xmlns="http://www.w3.org/1999/xhtml"', '')); for (k=row.cells.length -1; k>-1; k--) s['key'+k]=Sortable.key(row.cells[k]); table.tBodies[i].lines[j]=s; } } firstRow = table.tHead.rows[0]; // table.tHead.rows.length-1 // last ? // We have a first row: assume it's the header, and make its contents clickable links var cell; for (var i=0;i<firstRow.cells.length;i++) { cell = firstRow.cells[i]; var text = cell.innerText || cell.textContent; if (cell.className.indexOf("unsort") != -1 || cell.className.indexOf("nosort") != -1 || Sortable.trim(text) == '') continue; cell.table=table; cell.innerHTML = '<a href="#" class="sortheader" onclick="Sortable.sort(this.parentNode.table, \'key'+i+'\', this.reverse); this.reverse=!this.reverse ;return false;">↓'+text+'↑</a>'; // } // do it one time table.sortable=true; return true; },
/** build sortable key from element content */
key: function(text) { if (typeof text == 'string'); else if (text.textContent) text=text.textContent; else text=text.innerHTML.replace(/<.+>/g, ''); // incredibly slow in chrome // else if (text.innerText) text=text.innerText; // return text.substring(0, 10) ; text=Sortable.trim(text); // num n=parseFloat(text.replace(/,/g, '.').replace(/[  ]/g, '')); // text if (isNaN(n)) { text=text.toLowerCase().replace(/œ/g, 'oe').replace(/æ/g, 'ae').replace(/ç/g, 'c').replace(/ñ/g, 'n').replace(/[éèêë]/g, 'e').replace(/[àâä]/g, 'a').replace(/[ïîí]/g, 'i').replace(/úûü/, 'u') +"__________"; return text.substring(0, 10) ; } else { return n; } },
/** add alternate even odd classes on table rows */
zebra: function (table) { for (var i = 0; i < table.tBodies.length; i++) { for (j=table.tBodies[i].rows.length -1; j >= 0; j--) this.paint(table.tBodies[i].rows[j], j); } },
/** Paint a row according to its index */
paint: function(row, i) { row.className=" "+row.className+" "; row.className=row.className.replace(/ *(odd|even|mod5|mod10) */g, ' '); if ( (i % 2) == 0 ) row.className+=" even"; if ( (i % 2) == 1 ) row.className+=" odd"; if ( (i % 5) == 0 ) row.className+=" mod5"; if ( (i % 10) == 0 ) row.className+=" mod10"; // row.className=row.className.replace(/^\s\s*|\s(?=\s)|\s\s*$/g, ""); // normalize-space, will bug a bit on \n\t },
/** A clever and fast trim, http://flesler.blogspot.com/2008/11/fast-trim-function-for-javascript.html */
trim : function ( s ){ var start = -1, end = s.length; while( s.charCodeAt(--end) < 33 ); while( s.charCodeAt(++start) < 33 ); return s.slice( start, end + 1 ); } } // if loaded as bottom script, create tables if(window.document.body) Sortable.load();