User:Tcncv/sorttablesdev.js

/* * Draft fix for: https://bugzilla.wikimedia.org/show_bug.cgi?id=8028 * * Enhancements: *  1.  Will explode rowspans, so that rows are self contained and can be sorted *      without garbling the table structure. *  2.  Will recognize colspans, so that the proper value is retrieved from each *      row. Each column in a colspan range is treated as having the same value. *      Also, colspans are preserved, they are not split. *  3.  After sorting, some cell ranges may be recombined under certain restrictive *      conditions (still being refined). Also, the class="autorowspan" option can *      be applied to column headers or the entire table to enable more aggressive *      rowspan combines, such as combining cells in the currently sorted column *      that were not originally combined. Current merge rules: *        a. Only merge cells in adjacent sorted columns, selected right to left. *        b. Only merge if cells to left also merged, or if leftmost sorted column. *        c. Only merge if cells have same ID or if class="autorowspan" is active. *        d. And of course, cells must be equivalent (content and attributes). *        e. Do not merge header, footer (sortbottom) or fixed (unsortable) rows. *  4.  Supports multi-row headers with a mix of rowspans and colspans. Clicking on *      the sort icon in a colspan'd header will perform a multi-column sort. (The *      "unsortable" class name can be used in the header to limit sort icon creation.) * * Bugs/Limitations: *  1.  Conflicting (partially overlapping) rowspans/colspans are not supported. *  2.  Rowspans that add extra table rows are not supported. *  3.  Table row attributes are not compared when combining cells. *  4.  Some intermittent problems observed very early in developmentwhen using table *      sorting + Twinkle + IE + with complex pages having large amounts of table data. *      (This problem has not been duplicated.) * * Todo: *  1.  Restore thead/tbody/tfoot support (see bugzilla bug id 4740) *  2.  Merge with any recent released source changes. *  3.  Test on other browser configurations. *  4.  Solicit feature discussion and code review. *  5.  Prepare formal test cases and submit to bugzilla. * * Related: *  1.  Help:Sorting - Document new capabilities with examples. *  2.  Catalogue of CSS classes - Add new "autorowspan" class and *      update "unsortable" to document new use. * * Other possible enhancements (extra complexity may not be justified): *  1.  Consider if it be useful to apply autorowspan to the original table before *      its initial display? (Might same some tedious table formatting effort.) *  2.  Automatically apply initial sort. *  3.  Implement mixed mode sorting option (numbers, dates, and text) in a *       similar to Excel, and possibly compound sorting of mixed data (9Z < 10A). *  4.  Support fixed columns whose cells do noot move with the sort (a lot of work). * */

/* The following is based on code extracted from wikibits * (/trunk/phase3/skins/common/wikibits.js) revision 61023, 13 Jan 2010. * All global variables and functions were renamed from a ts_ prefix to * tsx_ prefix. The table class affected was changed from "sortable" * to "tcncv_sortable" */

/* * Table sorting script based on one (c) 1997-2006 Stuart Langridge and Joost * de Valk: * http://www.joostdevalk.nl/code/sortable-table/ * http://www.kryogenix.org/code/browser/sorttable/ * * @todo don't break on colspans/rowspans (bug 8028) * @todo language-specific digit grouping/decimals (bug 8063) * @todo support all accepted date formats (bug 8226) */ var tsx_image_path = stylepath + '/common/images/'; var tsx_image_up = 'sort_up.gif'; var tsx_image_down = 'sort_down.gif'; var tsx_image_none = 'sort_none.gif'; var tsx_europeandate = mw.config.get('wgContentLanguage') != 'en'; // The non-American-inclined can change to "true" var tsx_alternate_row_colors = false; var tsx_number_transform_table = null; var tsx_number_regex = null; var tsx_SortedColumnRanges = new Array;

function tsx_sortables_init {

var idnum = 0; // Find all tables with class sortable and make them sortable var tables = getElementsByClassName( document, 'table', 'tcncv_sortable' ); for (var ti = 0; ti < tables.length ; ti++) { if (!tables[ti].id) { tables[ti].setAttribute('id','sortable_table_id_tsx_'+idnum); ++idnum; }		tsx_makeSortable(tables[ti]); } } $(tsx_sortables_init);

function tsx_makeSortable(table) { if (!table.rows || table.rows.length == 0) return;

// Count header rows. First row is always considered part of the header. // Also include rows having class=sortheader" or containing only TH cells.	var numHeaders = 1; // Also equals rowStart	var isHeader = true;	for (var r = 1; r < table.rows.length && isHeader; r++) {		if ( (' ' + table.rows[r].className + ' ').indexOf(' sortheader ') == -1 ) {			for (var i = 0; i < table.rows[r].cells.length && isHeader; i++) {				//alert("makeSortable (headerrows scan: r=" + r + ", i=" + i );				if (table.rows[r].cells[i].nodeName.toUpperCase != "TH") {					isHeader = false;				}			}		}		if (isHeader) {			numHeaders = r + 1;		}	}	//alert("numHeaders=" + numHeaders);	if (table.rows.length - numHeaders < 2) {		return;	}

var repeatedCells = new Array; for (var r = 0; r < numHeaders; r++) { var row = table.rows[r]; var c = 0; // column number and repeatedCells index var i = 0; // cells index (may be less than column number) while (i < row.cells.length || c < repeatedCells.length) { //alert("makeSortable (add sort link): r=" + r + ", i=" + i + ", c=" + c); if (c < repeatedCells.length && repeatedCells[c] && repeatedCells[c].remaining > 0) { // Use repeated cell repeatedCells[c].remaining--; // remaining_repeats c += repeatedCells[c].cell.colSpan; }			else if (i < row.cells.length ) { // Use existing defined cell. If rowspan, save for later duplication. var cell = row.cells[i]; if ((" "+cell.className+" ").indexOf(" unsortable ") == -1) { cell.innerHTML += '' + ' '						+ ' '; //alert("makeSortable: " + cell.innerHTML); }				if (cell.rowSpan > 1) { repeatedCells[c] = new tsx_RepeatedCell(cell); }				c += cell.colSpan; i++; }			else { c += 1; //undefined cell i++; }		}	}

if (tsx_alternate_row_colors) { tsx_alternate(table); } }

// Debug function to extract cell attributes for display function tsx_cellAttributes(cell) { var msg = " " + cell.nodeName + " id=" + cell.id; msg += "\n innerHTML=" + cell.innerHTML; for (var i = 0; i < cell.attributes.length; i++) { var nodeName = cell.attributes[i].nodeName; var nodeValue = cell.getAttribute(nodeName); var nodeValueType = typeof nodeValue; if (nodeValue != null			&& (nodeValueType == "string" || nodeValueType == "number" || nodeValueType == "boolean")) {			msg += "\n " + nodeName + "(" + nodeValueType + ")=" + nodeValue; }	}	return msg; }

function tsx_copyCell(to_cell, from_cell) { //if (tsx_debug_alert_count < 5) { //	tsx_debug_alert_count++; //	alert("From_cell:\n" + tsx_cellAttributes(from_cell)); //}	to_cell.innerHTML = from_cell.innerHTML; from_cell.innerHTML = from_cell.innerHTML;  // Copy to self - IE morphs some values for (var i = 0; i < from_cell.attributes.length; i++) { var nodeName = from_cell.attributes[i].nodeName; var nodeValue = from_cell.getAttribute(nodeName); var nodeValueType = typeof nodeValue; if (nodeValue!=null			&& (nodeValueType == "string" || nodeValueType == "number" || nodeValueType == "boolean")) {			to_cell.setAttribute(nodeName, nodeValue); }	}	to_cell.innerHTML = from_cell.innerHTML;   // Overkill from_cell.innerHTML = from_cell.innerHTML; // Overkill }

var tsx_debug_compare_cells_trace = false; function tsx_compareCells(lhs, rhs) { if (tsx_debug_compare_cells_trace) { alert("tsx_debug_compare_cells_trace:\nlhs=" + lhs.toString + "\nrhs=" + rhs.toString); alert("lhs:{" + tsx_cellAttributes(lhs) + "}"			+ "\n\nrhs:{" + tsx_cellAttributes(rhs) + "}"); }

if (lhs.innerHTML != rhs.innerHTML) return false;

for (var i = 0; i < lhs.attributes.length; i++) { var nodeName = lhs.attributes[i].nodeName; var nodeNameLower = nodeName.toLowerCase; /* IE uses mixed case */ var nodeValue = lhs.attributes[i].nodeValue; var nodeValueType = typeof nodeValue; if (nodeNameLower != "id" && nodeNameLower != "rowspan" 			&& nodeValue!=null			&& (nodeValueType == "string" || nodeValueType == "number" || nodeValueType == "boolean")) {			if (rhs.getAttribute(nodeName) != lhs.getAttribute(nodeName)) { if (true || tsx_debug_compare_cells_trace) { var msg = "compare_cells misatch on attribute " + nodeName msg += "\nrhs(" + typeof (rhs.getAttribute(nodeName))					msg += ")="+rhs.getAttribute(nodeName) msg += "\nlhs(" + typeof (lhs.getAttribute(nodeName))					msg += ")="+lhs.getAttribute(nodeName) msg += "\n\nlhs:{" + tsx_cellAttributes(lhs) + "}"; msg += "\n\nrhs:{" + tsx_cellAttributes(rhs) + "}"; alert(msg); }				return false; }		}	}	for (var i = 0; i < rhs.attributes.length; i++) { var nodeName = rhs.attributes[i].nodeName; var nodeNameLower = nodeName.toLowerCase; /* IE uses mixed case */ var nodeValue = rhs.attributes[i].nodeValue; var nodeValueType = typeof nodeValue; if (nodeNameLower != "id" && nodeNameLower != "rowspan" 			&& nodeValue!=null			&& (nodeValueType == "string" || nodeValueType == "number" || nodeValueType == "boolean")) {			if (rhs.getAttribute(nodeName) != lhs.getAttribute(nodeName)) { if (true || tsx_debug_compare_cells_trace) { var msg = "compare_cells misatch on attribute " + nodeName msg += "\nrhs(" + typeof (rhs.getAttribute(nodeName))					msg += ")="+rhs.getAttribute(nodeName) msg += "\nlhs(" + typeof (lhs.getAttribute(nodeName))					msg += ")="+lhs.getAttribute(nodeName) msg += "\n\nlhs:{" + tsx_cellAttributes(lhs) + "}"; msg += "\n\nrhs:{" + tsx_cellAttributes(rhs) + "}"; alert(msg); }				return false; }		}	}	if (tsx_debug_compare_cells_trace) alert("compare_cells: OK"); return true; }

// Construct object to track remaining occurrences or rowspanned cell function tsx_RepeatedCell(cell) { this.cell = cell; this.remaining = cell.rowSpan - 1; }

// Identify and duplicate rowspanned cells so that each row has its own copy function tsx_explodeRowspans(table, rowStart) { //tsx_debug_alert_count = 0; //alert("tsx_explode_rowspans: table.id=" + table.id);

var rowspangroup_seq = 0; // Used to generate ids for rowspan cell groups var repeatedCells = new Array; for (var r = rowStart; r < table.rows.length; r++) { var row = table.rows[r]; var c = 0; // column number and repeatedCells index var i = 0; // cells index (may be less than column number) while (i < row.cells.length || c < repeatedCells.length) { if (c < repeatedCells.length && repeatedCells[c] && repeatedCells[c].remaining > 0) { // Use repeated cell row.insertCell(i); tsx_copyCell(row.cells[i], repeatedCells[c].cell); row.cells[i].rowSpan = 1; repeatedCells[c].remaining--; // remaining_repeats }			else if (i < row.cells.length ) { // Use existing defined cell. If rowspan, save for later duplication. if (row.cells[i].rowSpan > 1) { if (row.cells[i].id == "" ) { row.cells[i].id = table.id + ".rowspangroup." + (++rowspangroup_seq); }					repeatedCells[c] = new tsx_RepeatedCell(row.cells[i]); row.cells[i].rowSpan = 1; }			}			else { // Insert filler cell row.insertCell(i); }

c += row.cells[i].colSpan; // Note: Conflicting rowspan/colspan are not supported i++; }

// Trim any trailing completed rowspans (and trailing null elements) while (repeatedCells.length > 0			&& (!repeatedCells[repeatedCells.length-1] || repeatedCells[repeatedCells.length-1].remaining == 0)) repeatedCells.length--; }	//alert("After tsx_explode_rowspans"); }

// Construct object to hold range of adjacent sorted columns function tsx_SortedColumnRange(table, sortColumn, sortSpan) { this.id = table.id; this.from = sortColumn; this.thru = sortColumn + sortSpan - 1; this.extend = function tsx_SortedColumnRange_extend(sortColumn, sortSpan) { // Track columns sorted in sequence from right to left. Reset if jump var sortThru = sortColumn + sortSpan - 1; if (sortThru < this.from - 1 || sortThru  > this.thru ) this.thru = sortThru; this.from = sortColumn; return this; } }

// Get and extend range of sorted columns function tsx_GetSortedColumnRange(table, sortColumn, sortSpan) { for (var i = 0; i < tsx_SortedColumnRanges.length; i++) { if (table.id == tsx_SortedColumnRanges[i].id) { return tsx_SortedColumnRanges[i].extend(sortColumn, sortSpan); }	}	tsx_SortedColumnRanges.push(new tsx_SortedColumnRange(table, sortColumn, sortSpan)); return tsx_SortedColumnRanges[tsx_SortedColumnRanges.length-1]; }

// Build array, indexed by column number, with a flag indicating if autorowspan is enabled function tsx_GetAutoRowSpanColumns(table, rowStart, sortColumn, sortSpan) { var autoRowSpanTable = ((" "+table.className+" ").indexOf(" autorowspan ") >= 0); var autoRowSpanColumns = new Array;

var repeatedCells = new Array; for (var r = 0; r < rowStart; r++) { var row = table.rows[r]; var c = 0; // column number and repeatedCells index var i = 0; // cells index (may be less than column number) while (i < row.cells.length || c < repeatedCells.length) { if (c < repeatedCells.length && repeatedCells[c] && repeatedCells[c].remaining > 0) { // Repeat prior rowspanned cell repeatedCells[c].remaining--; // remaining_repeats c += repeatedCells[c].cell.colSpan; }			else if (i < row.cells.length ) { // Use given cell var cell = row.cells[i]; if ( autoRowSpanTable || (" "+cell.className+" ").indexOf(" autorowspan ") >= 0 ) { for (var j = 0; j < cell.colSpan; j++) { autoRowSpanColumns[c+j] = true; }				}				if (cell.rowSpan > 1) { repeatedCells[c] = new tsx_RepeatedCell(cell); }				c += cell.colSpan; i++; }			else { // Skip undefined cell c += 1; i++; }		}	}

return autoRowSpanColumns; }

// After sorting, scan for and combine repeated cells, where allowed function tsx_combineRowspans(table, rowStart, sortColumn, sortSpan) { //alert("before tsx_combine_rowspans");

var SortedColumnRange = tsx_GetSortedColumnRange(table, sortColumn, sortSpan); //alert("SortedColumnRange="+SortedColumnRange.from+".."+SortedColumnRange.thru); var autoRowSpanColumns = tsx_GetAutoRowSpanColumns(table, rowStart, sortColumn, sortSpan); //alert("autoRowSpanColumns="+autoRowSpanColumns.join(', '));

var priorCells = new Array; for (var r = rowStart; r < table.rows.length; r++) { var row = table.rows[r]; if ((" "+row.className+" ").indexOf(" unsortable ") != -1 ||		   (" "+row.className+" ").indexOf(" sortbottom ") != -1) {			priorCells.length = 0; // Reset - Do skip and not span across fixed rows }		else { var c = 0; // column number and priorCells index var i = 0; // cells index (may be less than column number) var merging = false; while (i < row.cells.length) { // (1) Only merge cells in adjacent sorted columns, selected right to left. // (2) Only merge if cells to left also merged, or if leftmost sorted column. // (3) Merge only if cells have same ID or class="autorowspan" is active // (4) And of course, cells must be equivalent. if (c >= SortedColumnRange.from && c <= SortedColumnRange.thru					&& (c == sortColumn || merging)					&& c < priorCells.length && priorCells[c]					&& ( (autoRowSpanColumns.length > c && autoRowSpanColumns[c]) || (row.cells[i].id != "" && row.cells[i].id == priorCells[c].id) )					&& tsx_compareCells(row.cells[i],priorCells[c]) ) {					merging = true; // Match - update rowspan in prior row's tableCell and delete current. priorCells[c].rowSpan++; for (var j = 1; j < row.cells[i].colSpan; j++) priorCells[c+j] = null; // Skipped c += row.cells[i].colSpan; row.deleteCell(i); }				else { merging = false; // No match or not allowed - save, but leave unchanged. priorCells[c] = row.cells[i]; for (var j = 1; j < row.cells[i].colSpan; j++) priorCells[c+j] = null; c += row.cells[i].colSpan; i++; }			}			priorCells.length = c;		} }	//alert("after tsx_combine_rowspans"); } function tsx_getInnerText(row,column) { var i = 0; var c = 0; var ncells = row.cells.length; while (i < ncells && c <= column) { if (column >= c && column < c + row.cells[i].colSpan) { return getInnerText( row.cells[i] ); }		c += row.cells[i].colSpan; i++; }	return ""; } function tsx_resortTable(lnk, rowStart, sortColumn, sortSpan) { //alert("resortTable: rowStart=" + rowStart + ", sortColumn=" + sortColumn + ", sortSpan=" + sortSpan); // Get the span containing the sort icon and determine sort direction var span = lnk.getElementsByTagName('span')[0]; var reverse = ( span.getAttribute( 'sortdir' ) == 'down' );

// Get the table var td = lnk.parentNode; var tr = td.parentNode; var table = tr.parentNode; while ( table && !( table.tagName && table.tagName.toLowerCase == 'table' ) ) { table = table.parentNode; }	if ( !table ) { return; }	// Generate the number transform table if it's not done already if ( tsx_number_transform_table == null ) { tsx_initTransformTable; }

// Expand any rowspan'ed cells that could potentially be split by sort tsx_explodeRowspans(table,rowStart);

// Sort each column in the selected range from right to left for (var i = sortSpan - 1; i >= 0 ; i--) { tsx_sortColumn(table, rowStart, sortColumn + i, reverse); }

// Merge cells into rowspans, where possible tsx_combineRowspans(table, rowStart, sortColumn, sortSpan);

var arrowHTML; if (reverse) { arrowHTML = ''; span.setAttribute('sortdir','up'); } else { arrowHTML = ''; span.setAttribute('sortdir','down'); }

// Delete any other arrows there may be showing for (var r = 0; r < rowStart; r++) { var spans = getElementsByClassName(table.rows[r], 'span', 'sortarrow'); for (var i = 0; i < spans.length; i++) { spans[i].innerHTML = ''; }	}	span.innerHTML = arrowHTML; if (tsx_alternate_row_colors) { tsx_alternate(table); } }

function tsx_sortColumn(table, rowStart, column, reverse) { //alert("sortColumn: rowStart=" + rowStart + ", column=" + column + ", reverse=" + reverse); // Work out a type for the column var itm = ''; for (var i = rowStart; i < table.rows.length; i++) { if (table.rows[i].cells.length > column) { itm = tsx_getInnerText(table.rows[i],column); itm = itm.replace(/^[\s\xa0]+/, ).replace(/[\s\xa0]+$/, ); if ( itm != '' ) { break; }		}	}

// TODO: bug 8226, localised date formats var sortfn = tsx_sort_generic; var preprocessor = tsx_toLowerCase; if (/^\d\d[\/. -][a-zA-Z]{3}[\/. -]\d\d\d\d$/.test(itm)) { preprocessor = tsx_dateToSortKey; } else if (/^\d\d[\/.-]\d\d[\/.-]\d\d\d\d$/.test(itm)) { preprocessor = tsx_dateToSortKey; } else if (/^\d\d[\/.-]\d\d[\/.-]\d\d$/.test(itm)) { preprocessor = tsx_dateToSortKey; // (minus sign)([pound dollar euro yen currency]|cents) } else if ( /(^([-\u2212] *)?[\u00a3$\u20ac\u00a4\u00a5]|\u00a2$)/.test( itm ) ) { preprocessor = tsx_currencyToSortKey; } else if (tsx_number_regex.test(itm)) { preprocessor = tsx_parseFloat; }

var newRows = new Array; var staticRows = new Array; for (var j = rowStart; j < table.rows.length; j++) { var row = table.rows[j]; if( (' ' + row.className + ' ').indexOf(' unsortable ') < 0 ) { var keyText = tsx_getInnerText(row,column); if( keyText === undefined ) { keyText = ''; }			var oldIndex = (reverse ? -j : j); var preprocessed = preprocessor( keyText.replace(/^[\s\xa0]+/, ).replace(/[\s\xa0]+$/, ) ); newRows[newRows.length] = new Array(row, preprocessed, oldIndex); } else { staticRows[staticRows.length] = new Array( row, false, j-rowStart ); }	}

newRows.sort(sortfn); if (reverse) newRows.reverse;

for (var i = 0; i < staticRows.length; i++) { var row = staticRows[i]; newRows.splice(row[2], 0, row); }

// We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones // don't do sortbottom rows for (var i = 0; i < newRows.length; i++) { if ( ( ' ' + newRows[i][0].className + ' ').indexOf(' sortbottom ') == -1 ) { table.tBodies[0].appendChild(newRows[i][0]); }	}

// do sortbottom rows only for (var i = 0; i < newRows.length; i++) { if ( ( ' ' + newRows[i][0].className + ' ').indexOf(' sortbottom ') != -1 ) { table.tBodies[0].appendChild(newRows[i][0]); }	} }

function tsx_initTransformTable { if ( typeof mw.config.get('wgSeparatorTransformTable') == 'undefined'			|| ( mw.config.get('wgSeparatorTransformTable')[0] ==  && mw.config.get('wgDigitTransformTable')[2] ==  ) ) {		digitClass = "[0-9,.]"; tsx_number_transform_table = false; } else { tsx_number_transform_table = {}; // Unpack the transform table // Separators ascii = mw.config.get('wgSeparatorTransformTable')[0].split("\t"); localised = mw.config.get('wgSeparatorTransformTable')[1].split("\t"); for ( var i = 0; i < ascii.length; i++ ) { tsx_number_transform_table[localised[i]] = ascii[i]; }		// Digits ascii = mw.config.get('wgDigitTransformTable')[0].split("\t"); localised = mw.config.get('wgDigitTransformTable')[1].split("\t"); for ( var i = 0; i < ascii.length; i++ ) { tsx_number_transform_table[localised[i]] = ascii[i]; }		// Construct regex for number identification digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '\\.']; maxDigitLength = 1; for ( var digit in tsx_number_transform_table ) { // Escape regex metacharacters digits.push( 				digit.replace( /[\\\\$\*\+\?\.\(\)\|\{\}\[\]\-]/, function( s ) { return '\\' + s; } )			); if (digit.length > maxDigitLength) { maxDigitLength = digit.length; }		}		if ( maxDigitLength > 1 ) { digitClass = '[' + digits.join( '', digits ) + ']'; } else { digitClass = '(' + digits.join( '|', digits ) + ')'; }	}	// We allow a trailing percent sign, which we just strip. This works fine // if percents and regular numbers aren't being mixed. tsx_number_regex = new RegExp(		"^(" + "[-+\u2212]?[0-9][0-9,]*(\\.[0-9,]*)?(E[-+\u2212]?[0-9][0-9,]*)?" + // Fortran-style scientific "|" +			"[-+\u2212]?" + digitClass + "+%?" + // Generic localised ")$", "i"	); } function tsx_toLowerCase( s ) { return s.toLowerCase; } function tsx_dateToSortKey(date) { // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX if (date.length == 11) { switch (date.substr(3,3).toLowerCase) { case 'jan': var month = '01'; break; case 'feb': var month = '02'; break; case 'mar': var month = '03'; break; case 'apr': var month = '04'; break; case 'may': var month = '05'; break; case 'jun': var month = '06'; break; case 'jul': var month = '07'; break; case 'aug': var month = '08'; break; case 'sep': var month = '09'; break; case 'oct': var month = '10'; break; case 'nov': var month = '11'; break; case 'dec': var month = '12'; break; // default: var month = '00'; }		return date.substr(7,4)+month+date.substr(0,2); } else if (date.length == 10) { if (tsx_europeandate == false) { return date.substr(6,4)+date.substr(0,2)+date.substr(3,2); } else { return date.substr(6,4)+date.substr(3,2)+date.substr(0,2); }	} else if (date.length == 8) { yr = date.substr(6,2); if (parseInt(yr) < 50) { yr = '20'+yr; } else { yr = '19'+yr; }		if (tsx_europeandate == true) { return yr+date.substr(3,2)+date.substr(0,2); } else { return yr+date.substr(0,2)+date.substr(3,2); }	}	return '00000000'; } function tsx_parseFloat( s ) { if ( !s ) { return 0; }	if (tsx_number_transform_table != false) { var newNum = '', c;		for ( var p = 0; p < s.length; p++ ) { c = s.charAt( p ); if (c in tsx_number_transform_table) { newNum += tsx_number_transform_table[c]; } else { newNum += c;			} }		s = newNum; }	num = parseFloat( s.replace(/[, ]/g, '').replace("\u2212", '-') ); return ( isNaN( num ) ? -Infinity : num ); } function tsx_currencyToSortKey( s ) { return tsx_parseFloat(s.replace(/[^-\u22120-9.,]/g,'')); } function tsx_sort_generic(a, b) { return a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : a[2] - b[2]; } function tsx_alternate(table) { // Take object table and get all it's tbodies. var tableBodies = table.getElementsByTagName( 'tbody' ); // Loop through these tbodies for (var i = 0; i < tableBodies.length; i++) { // Take the tbody, and get all it's rows var tableRows = tableBodies[i].getElementsByTagName( 'tr' ); // Loop through these rows // Start at 1 because we want to leave the heading row untouched for (var j = 0; j < tableRows.length; j++) { // Check if j is even, and apply classes for both possible results var oldClasses = tableRows[j].className.split(' '); var newClassName = ''; for (var k = 0; k < oldClasses.length; k++) { if ( oldClasses[k] != '' && oldClasses[k] != 'even' && oldClasses[k] != 'odd' ) { newClassName += oldClasses[k] + ' '; }			}			tableRows[j].className = newClassName + ( j % 2 == 0 ? 'even' : 'odd' ); }	} } /* * End of table sorting code */