////
// POJOs
////
function SortObject(){
	this._visible = true;
}

function Head(id,value){
    this.id = id;
    this.value = value;
}

////
// Constants
////
var NO_SORTABLE_FIELD_ID = 'NO_SORTABLE_FIELD_ID';
var ASCENDING_ORDER = -1;
var DESCENDING_ORDER = 1;

////
// Global variables
////
var sortObjects = null;
var styles = null;
var umbral = 0;
var sortFieldName = "";
var sortDirection = ASCENDING_ORDER;
var sortField = null;
var headers = null;
var comparators = null;
var noSortFieldCounter = 0;
// TODO: with different cssPreffix you can set diferent styles for
//       different sorted tables
var cssPreffix = "default";

// Localized month names array
var months = new Array();
months[0] = ['enero','febrero','marzo','abril','mayo','junio','julio','agosto',
		'septiembre','octubre','noviembre','diciembre'];
months[1] = ['january','february','march','april','may','june','july','august',
		'september','october','november','december'];


////
// Regular expressions to detect data type
////
// Time count
var erConnectionTime = /^\s*([\d]+)[^\d]+([\d]+)[^\d]+([\d]+)[^\d]+$/; //  Total time: "0h, 0min, 0seg"
// Percent number
var erPercentValue = /^[ ]*([\+\-]?[ ]*[\d]+[\.\,]?\d*)[ ]*\%$/; // /^\d+\s*(\d+)\s*\%\s*$/; //  Percent: "12%"
// Dates in different format
var erDate = /^[\s]*(\d+)[^\d]+(\d+)[^\d]+(\d+)[\s]*$/; //  Date: "31/01/2007"
var erDateSpanish = /^[^\s]+\s+(\d+)\s+\w+\s+(\w+)[^\d]+(\d+)\s+(\d+):(\d+)$/; //  Date: "lunes 13 de noviembre de 2006 23:07
var erDateEnglish = /^[^\s]+\s+(\w+)\s+(\d+)[^\d]+(\d+)[^\d]+(\d+):(\d+).*$/; // Friday, November 17, 2006 12:43 PM"
// Integer
var erNumber = /^\s*[\-\+]?(\d+)\s*$/;
// HTML select
var erSelected = /^[ ]*<select(.+)$/;
var erSelectedValue = /^[ ]*(.+)selected=\"true\">(.*?)<(.+)$/;
// HTML input
var erInput = /^[ ]*<input(.+)$/;
var erInputValue = /^[ ]*(.+)checked=\"checked\"(.+)$/;
// Custom format, v.g.: 98-Calidad En Servicios
var erIdText = /^\s*(\d+)\s*\-\s*(.+)\s*$/;

////
// Javascript API extensions required to improve code readability
////

/**
 * Checks if the string is a date
 */
String.prototype.isDate = function (){
	return erDate.test(this) || erDateSpanish.test(this) || erDateEnglish.test(this);
}

/**
 * Checks if the string is a integer
 */

String.prototype.isNumber = function (){
	return erNumber.test(this);
}

/**
 * Checks if the string is a time count
 */
String.prototype.isConnectionTime = function (){
	return erConnectionTime.test(this);
}

/**
 * Checks if the string is a HTML select
 */
String.prototype.isSelect = function (){
	return erSelected.test(this);
}

/**
 * Checks if the string is a HTML input
 */
String.prototype.isInput = function (){
	return erInput.test(this);
}

/**
 * Checks if the string is a percent
 */
String.prototype.isPercentValue = function (){
	return erPercentValue.test(this);
}

/**
 * Checks if the string is a custom format ID-text
 */
String.prototype.isIdText = function (){
	return erIdText.test(this);
}

/**
 * Checks if the string is plain text. It is used to set CSS style.
 */
String.prototype.isPlainText = function (){
	return !this.isDate() && !this.isPercentValue() &&
		!this.isConnectionTime() && !this.isIdText();
}
/**
 * Retrieve a Date object from the string expression
 */
String.prototype.getDate = function (){
	if(erDate.test(this)){
		return eval(this.replace(erDate,"newDate($1,$2,$3,0,0)")).getTime();
	}
	if(erDateSpanish.test(this)){
		return eval(this.replace(erDateSpanish,"newDate($1,'$2',$3,$4,$5)")).getTime();
	}
	if(erDateEnglish.test(this)){
		return eval(this.replace(erDateEnglish,"newDate($2,'$1',$3,$4,$5)")).getTime();
	}
	return 0;
}

/**
 * Retrieve a base 10 integer from the string expression
 */
String.prototype.getNumber = function (){
	if(this.isNumber()){
		return parseInt(this.replace(erNumber,"$1"),10);
	}
}

/**
 * Retrieve a time count from the string expression
 */
String.prototype.getConnectionTime = function(){
	if(erConnectionTime.test(this)){
		return parseInt(this.replace(erConnectionTime,"$1"),10)*3600
				+ parseInt(this.replace(erConnectionTime,"$2"),10)*60
				+ parseInt(this.replace(erConnectionTime,"$3"),10);
	}
	return -1;
}

/**
 * Retrieve a real number from the string expression
 */
String.prototype.getPercentValue = function(){
	if(erPercentValue.test(this)){
		if(this.lastIndexOf(',')>0 && this.indexOf(',')>this.indexOf('.')) {
			return parseFloat(this.replace(erPercentValue,
					this.substring(0,this.lastIndexOf(','))+'.'
						+this.substring(this.lastIndexOf(',')+1),"$1"));
		}
		return parseFloat(this.replace(erPercentValue,"$1"));
	}
	return -1;
}

/**
 * Retrieve a HTML select value from the string expression
 */
String.prototype.getSelectedValue = function(){
	if(erSelected.test(this)){
		return this.replace(erSelectedValue,'$2');
	}
	return -1;
}

/**
 * Retrieve a HTML input value from the string expression
 */
String.prototype.getInputValue = function(){
	if(erInput.test(this)){
		return erInputValue.test(this);
	}
	return -1;
}

/**
 * Retrieve a custom ID-text value from the string expression
 */
String.prototype.getIdTextValueObject = function(){
	if(erIdText.test(this)){
		// Returns an object literal
		return {id: parseInt(this.replace(erIdText,"$1"),10),
				text:this.replace(erIdText,"$2"),
				type:'IdTextObject'}
	}
	return -1;
}

////
// Miscellanous functions
////

/**
 * Retrieve the months array index corresponding to this string
 */
function getMonthNumber(value){
	value = value.toLowerCase();
	for(i=0; i<months.length; i++){
		for (j=0; j<months[i].length; j++) {
			if (months[i][j].toLowerCase() == value) {
				return j;
			}
		}
	}
	return -1;
};

/**
 * Create a Date object from parameters.
 * @param month in String format or number format (from 1 to 12)
 */
function newDate(day,month,year,hours,minutes){
	var d = Number(day);
	var m = (isNaN(Number(month)))?getMonthNumber(month):(Number(month)-1);
	var y = Number(year);
	var hs = Number(hours);
	var ms = Number(minutes);

	var date = new Date(y,m,d,hs,ms);

	return date;
}

/**
 * Hide rows visibility
 * @param Node container HTML object to be filter
 * @param String field Filter field
 * @param Object filterValue Filter function
 */
function changeVisibleState(container,field,filterValue){
	// Do the filter if filterValue is not empty. Else, remove the node. 
	if(filterValue != null && !/^\s*$/.test(filterValue) &&
			 /^\s*[<>=]=?.+$/.test(filterValue)){
		for(i=0;i<sortObjects.length;i++){
			comparator = filterValue.replace(/^\s*([<>=]=?).+$/,'$1');
			compareValue = getComparableValue(
				filterValue.replace(/^\s*[<>=]=?\s*(.+)\s*$/,'$1'));
			sortObjects[i]._visible = eval (getComparableFilterValue(
					sortObjects[i][field]) + comparator + compareValue ) ;
		}
	} else {
		for(i=0;i<sortObjects.length;i++){
			sortObjects[i]._visible = true;
		}
	}

	sortByColumnId(container, ((sortDirection == ASCENDING_ORDER)?'ASC':'DESC'), sortField);
}

/**
 * Filter by count time
 * @param String containerName name to the HTML element to be filtered
 * @param String fieldName name of the field to be filtered
 * @param String comparator '<', '>', '<=', '>=', '=='
 * @param String filter Filter value
 */
function updateFilterByConnectionTime(containerName, fieldName, comparator, filter){
	var evalFilter = "";
	// If it is empty remove last filter. Else, filter.
	if(!/^\s*$/.test(filter)){
		// If it is a number convert to seconds. Else, use the functions.
		if(filter.isNumber()){
			evalFilter = filter.getNumber() * 60 * 60;
		} else if(filter.isConnectionTime()){
			evalFilter = getComparableValue(filter);
		}
	}
	if(evalFilter+"" != ""){
		changeVisibleState(containerName,fieldName,comparator + evalFilter);
	}
	else{
		changeVisibleState(containerName,fieldName,"");
	}
}

/**
 * Filter by number
 * @param String containerName name to the HTML element to be filtered
 * @param String fieldName name of the field to be filtered
 * @param String comparator '<', '>', '<=', '>=', '=='
 * @param String filter Filter value
 */
function updateFilterByNumber(containerName, fieldName, comparator, filter){
	var evalFilter = "";
	// If it is empty remove last filter. Else, filter.
	if(!/^\s*$/.test(filter)){
		// If it is a number convert to seconds. Else, use the functions.
		if(filter.isNumber()){
			evalFilter = filter.getNumber()
		}
	}
	if(evalFilter+"" != ""){
		changeVisibleState(containerName,fieldName,comparator + evalFilter);
	}
	else{
		changeVisibleState(containerName,fieldName,"");
	}
}

/**
 * Filter by percentage
 * @param String comparator '<', '>', '<=', '>=', '=='
 * @param String filter Filter value
 */
function updateFilterByPercentValue(containerName, fieldName, comparator, filter){
	var evalFilter = "";
	// If it is empty remove last filter. Else, filter.
	if(!/^\s*$/.test(filter)){
		// If it is a number convert to seconds. Else, use the functions.
		if(filter.isPercentValue()){
			evalFilter = filter.getPercentValue()
		}
	}
	if(evalFilter+"" != ""){
		changeVisibleState(containerName,fieldName,comparator + evalFilter);
	}
	else{
		changeVisibleState(containerName,fieldName,"");
	}
}

/**
 * Initialize global variables
 * @param String containerId Id of HTML element to be sorted
 */
function initSortObjects(containerId){
	container = document.getElementById(containerId);
	rows = container.getElementsByTagName("tr");

	comparators = new Array();
	styles = new Array();

	// Aux array
	var values = new Array();
	
	// Table headers
	headers = new Array();
	rowHeader = rows[0].getElementsByTagName("th");
	for(j=0;j<rowHeader.length;j++){
		if(rowHeader[j].firstChild!=null && rowHeader[j].firstChild
				.nodeValue.replace(/[\n\t ]/g,'')!=''){
			// If ID does not exist, use the field name position
			id = (rowHeader[j].id!='' &&
					 !/^\s+$/.test(rowHeader[j].getAttribute('id')))
						? rowHeader[j].getAttribute('id') : ("C_"+j);

			aux = rowHeader[j];
			aux = rowHeader[j].innerHTML;
			aux = rowHeader[j].innerHTML.replace(/[\n\t]/g,'');
			aux = rowHeader[j].innerHTML.replace(/[\n\t]/g,'')
				.replace(/^[ ]*(.+)[ ]*$/,'$1');

			headers[headers.length]=new Head(id ,rowHeader[j]
					.innerHTML.replace(/[\n\t]/g,'')
					.replace(/^[ ]*(.+)[ ]*$/,'$1'));				
		}
	}

	// Retrieve table body (ignore table foot)
	total = (container.getElementsByTagName("tfoot").length == 0)
			? (rows.length) : (rows.length-1);
			
	for(i=1; i<total; i++){
		values[i-1] = new Array();
		styles[i-1] = new Array();
		
                // Preserve original CSS style
		values[i-1]._originalStyle = getNodeClass(rows[i]);

                // Table rows
		row = rows[i].getElementsByTagName("td");

		for(j=0;j<row.length;j++){
			var value = "";
			if(row[j].firstChild!=null && row[j].innerHTML
					.replace(/[\n\t ]/g,'')!=''){					
				value = row[j].innerHTML.replace(/[\n\t]/g,'')
					.replace(/^[ ]*(.+)[ ]*$/,'$1');
			}
			values[i-1][headers[j].id]= value;
			// Preserve original CSS style
			styles[i-1][headers[j].id]= getNodeClass(row[j]);

			if(comparators[headers[j].id] == undefined) {
				comparators[headers[j].id] = getComparableType(value.replace(/<.*?>/g,'').replace(/&.+?;/g,'').replace(/ +?/g,''));			
			} else {
				// Any different type is considered as a String
				comparableType = getComparableType(value.replace(/<.*?>/g,'').replace(/&.+?;/g,'').replace(/ +?/g,''));			
				if(comparators[headers[j].id] != 'string' &&
						comparableType == 'string' &&
						comparableType != undefined) {
					comparators[headers[j].id] = 'string';
				}
			}
			p='q';
		}
	}

	// Filling the sortObjects array
	sortObjects = new Array(values.length);
	for(i=0;i<values.length;i++){
		sortObjects[i] = new SortObject();
		for(var field in values[i]){
			sortObjects[i][field] = values[i][field];
		}
		sortObjects[i]._visible = true;
	}
}

/**
 * Set global variables sortDirection and sortField
 * @param String direction sort direction {'ASC', 'DESC'}
 * @param String fieldName next sort name
 */
function setSortPreferences(direction,fieldName){
	// Sentido en el orden
        sortDirection = (direction.toUpperCase() == "ASC") ?
			ASCENDING_ORDER : DESCENDING_ORDER;
	sortField = fieldName;
}

/**
 * Get node's original CSS style
 * @param element Node DOM node
 */
function getOriginalStyle(element) {
	if (getNodeClass(element)!= '') {	
		return(getNodeClass(element).replace(/ +.*/g,""));
	}
}

/**
 * Set sort events and headers' style
 * @param container Node DOM node (usually table) with data.
 */
function setHeaders(container){
	// Headers
	thead = document.createElement("thead");
	tr = document.createElement("tr");
	for(i=0;i<headers.length;i++){
		th = document.createElement("th");
		th.setAttribute("id",headers[i].id);				
		th.appendChild(document.createTextNode(headers[i].value));

		if(sortField == headers[i].id){
			if(sortDirection == ASCENDING_ORDER /*'ASC'*/){
				if(th.id.indexOf(NO_SORTABLE_FIELD_ID)!=0){
					th.onclick = function(){
                                            sortByColumnId(container.id, 'DESC', this.id); }
				}
				setNodeClass(th, ((getOriginalStyle(document.getElementById(headers[i].id))!=null)?getOriginalStyle(document.getElementById(headers[i].id)) + " " + cssPreffix:cssPreffix) + "SortAscending");
			} else {
				if(th.id.indexOf(NO_SORTABLE_FIELD_ID)!=0){
					th.onclick = function(){
                                            sortByColumnId(container.id, 'ASC', this.id); }
				}
				setNodeClass(th, ((getOriginalStyle(document.getElementById(headers[i].id))!=null)?getOriginalStyle(document.getElementById(headers[i].id)) + " " + cssPreffix:cssPreffix) + "SortDescending");
			}
		} else {
			if(th.id.indexOf(NO_SORTABLE_FIELD_ID)!=0){
				th.onclick = function(){
                                    sortByColumnId(container.id, 'ASC', this.id); }
			}
			if (getOriginalStyle(document.getElementById(headers[i].id))!=null) {
				setNodeClass(th, getOriginalStyle(document.getElementById(headers[i].id)));
			}
		}
		if (getNodeStyle(document.getElementById(headers[i].id))!='') {
			setNodeStyle(th, getNodeStyle(document.getElementById(headers[i].id)));			
		}
		if (getOriginalStyle(rows[0])!=null) {
			setNodeClass(tr, getOriginalStyle(rows[0]));	
		}
		tr.appendChild(th);

	}
	thead.appendChild(tr);

	//container.appendChild(thead);
	container.replaceChild(thead,container.getElementsByTagName("thead")[0]);
}

/**
 * Update table view
 * @param container Node DOM node (usually table) with data.
 */
function updateDataView(container){
	// Table style
	if (getOriginalStyle(container)!=null) {
		setNodeClass(container, getOriginalStyle(container));
	}

	// Headers
	setHeaders(container);

	tbody = document.createElement("tbody");

	for(i=0,j=0;i<sortObjects.length;i++){
		if(sortObjects[i]._visible){		
			tr = document.createElement("tr");

			for (var field in sortObjects[i]){
				// Ignore metainformation
				if(field.charAt(0)!='_'){
					td = document.createElement("td");				
					td.innerHTML = sortObjects[i][field];		
					if (styles[i][field]!=null && styles[i][field]!='') {			
						setNodeClass(td, styles[i][field]);
					}
					tr.appendChild(td);					
				}
			}

			// Style alternance preserving TR style
			var originalStyle = (sortObjects[i]._originalStyle != undefined &&
				sortObjects[i]._originalStyle != null) ? (" " + sortObjects[i]._originalStyle) : "";
			if((j++)%2!=0){
				setNodeClass(tr, cssPreffix + "SortDark" + originalStyle);
			} else {
				setNodeClass(tr, cssPreffix + "SortLight" + originalStyle);
			}

			tbody.appendChild(tr);
		}
	}
	//container.appendChild(tbody);
	container.replaceChild(tbody,container.getElementsByTagName("tbody")[0]);

	// TODO: replaceChild for tfoot style or set in at init time to set tfoot style
}

/**
 * Set an ID to ignore this column for sorting. This function has to be invoked 
 * before initialization.
 * @param String containerId
 * @param Integer index Index of column to be excluded. Indexing starts at 0, and
 *        goes from left to right.
 */
function setNoSortColumnByIndex(containerId,columnIndex){
	ths = document.getElementById(containerId).getElementsByTagName("th");
	// Verify existence of index
	if(columnIndex>=0 && columnIndex<ths.length){
		ths[columnIndex].id =
			NO_SORTABLE_FIELD_ID + "_"+ (++noSortFieldCounter);
	}
}

/**
 * Set an ID to ignore this column for sorting. This function has to be invoked 
 * before initialization.
 * @param String containerId
 * @param Integer index Index of column to be excluded. Indexing starts at 0, and
 *        goes from left to right.
 */
function setNoSortColumnById(containerId,columnId){
	th = document.getElementById(containerId).getElementsByTagName("th");
	for(i=0; i<th.length; i++){
		if(th[i].id == columnId){
			th[i].id = NO_SORTABLE_FIELD_ID + "_"+ (++noSortFieldCounter);
			break;
		}
	}
}

/**
 * Sort when column index is not known or does not exist
 * @param containerId table ID
 * @param direction Sorting order {'ASC', 'DESC'}
 * @param columnIndex Index of column to be sorted. Indexing starts at 0, and
 *        goes from left to right.
function sortByColumnIndex(containerId, direction, columnIndex) {
	// Se cargan los valores a partir de la tabla si no se ha hecho antes
	if(sortObjects == undefined || sortObjects == null){
		initSortObjects(containerId);
	}
	// Se verifica que el indice pedido exista en la tabla
	if(columnIndex>=0 && columnIndex<headers.length){
		sortByColumnId(containerId, direction, headers[columnIndex].id);
	}
}

/**
 * Sort by column id
 * @param containerId table ID
 * @param direction Sorting order {'ASC', 'DESC'}
 * @param String fieldName name of the field to sort by
 */
function sortByColumnId(containerId, direction, columnId) {
	// Load data values, if necessary
	if(sortObjects == undefined || sortObjects == null){
		initSortObjects(containerId);
	}
	container = document.getElementById(containerId);
	// Sorting order
	setSortPreferences(direction, columnId);	
	// Use standard array sorting with a comparator
	sortObjects.sort(compareObjects);	
	// Update data view
	updateDataView(container);			
}

/**
 * Match sequences using regular expressions
 * @param Object value Object to extract value from
 * @return value related with the element passed as an argument
 */
function getComparableValue(value){
	switch(comparators[sortField]) {
		case 'select': return value.getSelectedValue();
		case 'input': return value.getInputValue();
		case 'percent': return value.getPercentValue();
		case 'date': return value.getDate();
		case 'connectiontime': return value.getConnectionTime();
		case 'number': if (value.getNumber() == undefined) { 
							return 0;
					   } else {
							return value.getNumber();
					   }
		case 'idtext': return value.getIdTextValueObject();
		// TODO: dates, hours, ips, etc.
		default: return value;
	}
}

/**
 * Match sequences using regular expressions
 * @param Object value Object to extract value from
 * @return value related with the element passed as an argument
 */
function getComparableFilterValue(value){
	// Convertions required to compare
	// empty string
	if(/^\s*$/.test(value)){
		return undefined;
	}
	// select
	if(value.isSelect()) {
		return value.getSelectedValue();
	}
	// input
	if(value.isInput()) {
		return value.getInputValue();
	}
	// %
	if(value.isPercentValue()) {
		return value.getPercentValue();
	}
	// date
	if(value.isDate()) {
		return value.getDate();
	}
	// connectionTime
	if(value.isConnectionTime()) {
		return value.getConnectionTime();
	}
	// number
	if(value.isNumber()) {
		return value.getNumber();
	}
	// id-text
	if(value.isIdText()) {
		return value.getIdTextValueObject();
	}
	// TODO: dates, hours, ips, etc.
	return undefined;
}

/**
 * Match sequences using regular expressions
 * @param Object value Object to extract value from
 * @return numeric transformation for sorting criteria
 */
function getComparableType(value){
	if(typeof(value)=='string' || value instanceof String){
		// Convertions required to compare
		// special characters
		if (value=='+' || value=='?'||value=='n') {	
			return 'number';
		}
		// empty string
		if(/^\s*$/.test(value)){	
			return undefined;
		}
		// select
		if(value.isSelect()) {		
			return 'select';
		}
		// input
		if(value.isInput()) {	
			return 'input';
		}
		// %
		if(value.isPercentValue()) {		
			return 'percent';
		}
		// date
		if(value.isDate()) {
			return 'date';
		}
		// connectionTime
		if(value.isConnectionTime()) {		
			return 'connectiontime';
		}
		// number
		if(value.isNumber()) {			
			return 'number';
		}
		// id-text
		if(value.isIdText()) {		
			return 'idtext';
		}
		// TODO: dates, hours, ips, etc.
	}
	return 'string';
}


/**
 * Remove HTML tags
 * @param value Node DOM node to be processed
 * @return modified node
 */
function stripHtmlTags(value){
	if(value.isSelect() || value.isInput()) {
		return (value);
	}else{
		return (value.replace(/<[\/]?[^>]+>/g,""));
	}
}

/**
 * Compare arrays
 * @param a Object First array to compare
 * @param b Object Second array to compare
 * @return
 *   -1  'a' before 'b' in the sorted array,
 *   +1  'b' before 'a' in the sorted array,
 *    0  in case of equality
 */
function compareObjects(a, b) {
	var valorA = null;
	var valorB = null;

	// Verify if exists a field to sort by
	if(sortField!=undefined && sortField!=null){
		valorA = (a[sortField] instanceof String) ? a[sortField].toLowerCase() : a[sortField];
		valorB = (b[sortField] instanceof String) ? b[sortField].toLowerCase() : b[sortField];
	} else {
		valorA = (a instanceof String) ? a.toLowerCase() : a;
		valorB = (b instanceof String) ? b.toLowerCase() : b;
	}

	// Set the data type of data to be sorted
	valorA = getComparableValue(stripHtmlTags(valorA));
	valorB = getComparableValue(stripHtmlTags(valorB));

	if(valorA instanceof Object && valorB instanceof Object && valorA.type == valorB.type){
		// compare objects
		switch(valorA.type){
			case 'IdTextObject':
				if(valorA.id != valorB.id){
					return sortDirection * (valorB.id - valorA.id);
				}
				return sortDirection * ((valorA < valorB) ? 1 :
						 ((valorA > valorB) ? -1 : 0));
				break;
		}

	}

	// compare primitive values or unreconigzed objects or objects of different type
	return sortDirection * ((valorA < valorB) ? 1 :
			 ((valorA > valorB) ? -1 : 0));
	
}

function setSortCssPreffix(preffix) {
	cssPreffix = preffix;
}


////
// Utilities. TODO: move to another file utils.js
////

/**
 * Solution to the problem of class dynamic setting to a DOM node
 * @param Node node DOM node that is to be assigned new class
 * @param String className new CSS style name
 */
function setNodeClass(node,className){
    if(node != undefined && node != null){
		if (node.getAttributeNode("class")) {
			// IE, FF>=2
			for (var i = 0; i < node.attributes.length; i++) {
				if (node.attributes[i].name.toLowerCase() == 'class') {
					// TODO: cleanPreviousAttributes(node.attributes[i].value);

					node.attributes[i].value = className;
                                        //break;
				}
			}
		} else {
			// Other browsers
			// TODO: cleanPreviousAttributes(node.getAttribute("class"));
			node.setAttribute("class", className);
		}
    }
}

/**
 * Solution to the problem of style dynamic setting to a DOM node
 * @param Node node DOM node that is to be assigned new class
 * @param String styleName new node's style
 */
function setNodeStyle(node,styleName){
    if(node != undefined && node != null){
		if (node.getAttributeNode("style")) {
			// IE, FF>=2			
			node.style.cssText = styleName;                            
		} else {
			// Otros navegadores
			node.setAttribute("style", styleName);
		}
    }
}

/**
 * Get CSS class of a DOM node
 * @param Node node
 * @return String CSS class name
 */
function getNodeClass(node){
    var className = null;
    if(node != undefined && node != null){
	if (node.getAttributeNode("class")) {
		// IE, FF>=2
		for (var i = 0; i < node.attributes.length; i++) {
			if (node.attributes[i].name.toLowerCase() == 'class') {
				className = node.attributes[i].value;
				break;
			}
		}
	} else {
		// Other browsers
		// TODO: cleanPreviousAttributes(node.getAttribute("class"));
		className = node.getAttribute("class");
	}
    }
    return (className != undefined && className != null) ? className : '';
}

/**
 * Gets the DOM node's style
 * @param node Node DOM node
 * @return String node's style
 */
function getNodeStyle(node){
    var styleName = null;
    if(node != undefined && node != null){
    
	    if (typeof node.getAttribute("style") == 'string') {
	    	styleName = node.getAttribute("style");
	    } else if (node.getAttribute("style") != null) {
	    	// IE
	    	styleName = node.getAttribute("style").cssText;	
	    }
    }
    return (styleName != undefined && styleName != null) ? styleName : '';
}


// TODO: Create sorting objects to define ER, alignment and another styles,
//       required convertion to compare (compareTo), toString, etc. These objects
//       would be created out of this library, what would let separate particularities 
//       from general sorting library


