//=============================================================================
//Program:			Table Sort Script
//Version:			1.0
//Date:				3/22/2008
//Last Modified:	3/22/2008
//Author:           Jessica Wilkinson/ CodeProject: Rohith K Rajan
//REQUIREMENTS:
//1. Header row(s) must be contained within the tag <thead></thead>
//2. Sortable header column cells must have the attribute onclick=sortTable(event, [The zero-based column index of this column])
//3. Sortable header columns must be have the attribute type=[The data type of the things to be sorted] 
//4. Sortable ROWS must be contained within the tag <tbody></tbody>
//5. All rows within tbody must have an attribute that is unique amongst themselves. This script is expecting ID, you could use anything and update the script
//6. type="CustomDate" will only allow standard formatted date OR text with the word now. 
//7. IF you have two span's with the classes up and down in the header cells the script will toggle them on and off appropriatly. So the content will appear/ dissappear. 
//	 The script toggles both the display and visibility attributes so hidden elements collapse properly.
//=============================================================================
var type;
//index of the column to be sorted.
var columnIndex,lastIndex,isDescending;

function getAttribute(obj, att)
{
	for(var i =0; i < obj.attributes.length; i++)
	{
		if(obj.attributes[i].nodeName == att){
			return obj.attributes[i].value;
		}
	}
	return '';
}
function getObjTag(obj, tag)
{
	if(obj.tagName.toLowerCase() == tag)
		return obj;
	do
	{
		obj = obj.parentElement?obj.parentElement:obj.parentNode;
		if(obj.tagName.toLowerCase() == tag)
			return obj;
	}
	while(parent.parentElement || obj.parentNode)
	return null;
}
//function used for sorting table rows.
function  sortTable(eventclick,index)
{
     //get the TD element. 
	 var cell=eventclick.srcElement? eventclick.srcElement: eventclick.target;
	 cell = getObjTag(cell, 'td');
     //if the user clicks on the "SPAN" tag which was added dynamically.
     if (cell.type==undefined)
        type=getAttribute(cell, 'type');
     else
     	type=cell.type;
     //set the current index.
     columnIndex=index;
     //get the head tag.
	 if(!cell)
	 	return;
     var thead= getObjTag(cell, 'thead'); 
	 
	 var table = getObjTag(thead, 'table'); 
	
     //if clicked on span tag go one level up.
     if(!table.tBodies)
     {
        table=table.parentElement;  
        thead=thead.parentElement;  
     }
     var tblBody = table.tBodies[0];
     var tblRows = tblBody.rows;
     
     //set the direction opposite of last time
     if(columnIndex==lastIndex)
     {
        if(isDescending==true)
            isDescending=false;
        else
            isDescending=true;
      }
      else 
        isDescending=true;
      //make the array of rows.
     var rowArray=new Array();
	 var sortRowArray = new Array();
     
     //add each row to array.
	 //assume first row to always have the sortable data any following rows without the same number of columns is considered extra data for the parent row to be displayed not sorted by
     var iCells = 0;
	 for(var i=0;i<tblRows.length;i++)
     {
	 	if(i==0)
		{
			iCells = tblRows[i].cells.length;
		}
		if(tblRows[i].cells.length == iCells)
		{
			sortRowArray[i]=tblRows[i];
		}	
		//master row table should contain all rows
       	rowArray[i]=tblRows[i];			
     }    
     //call the generic function to sort the array.
     //custom comare will return another function whih compares the value passed.
     sortRowArray.sort(customCompare(type,isDescending,columnIndex));
             
     //append the sorted array to table.
     for(var i=0;i<sortRowArray.length;i++)
     {
	 	 if(!sortRowArray[i])
		 {
		 	break;
		 }	
         tblBody.appendChild(sortRowArray[i]);

		//check for  children
		var bFound = false;
		for(var j=0; j < rowArray.length; j++)
		{
			//find this row
			if(rowArray[j].id == sortRowArray[i].id)
			{
				bFound = true;
			}
			else if(bFound)
			{
				if(rowArray[j].cells.length != sortRowArray[i].cells.length)
				{
					//this is a child row
					tblBody.appendChild(rowArray[j]);
				}
				else
				{
					//no more child rows
					break;
				}
			}
		}
     }
	 
	 toggleArrow(thead, index);

     lastIndex=index;
}
function toggleArrow(thead, onIndex)
{
	var row = thead.rows[0];
	var cells = row.children?row.children:row.cells;
	for(var i = 0; i < cells.length; i++)
	{
		var cell = cells[i];
		var spans = cell.getElementsByTagName('span');
		if(!spans)
			return;
		for(var j = 0; j < spans.length; j++)
		{
			var span = spans[j];
			if(span.className == "up" || span.className == "down")
			{
				if(onIndex == i)
				{
					//current one
					if(span.className == "up")
					{
						span.style.display = isDescending?"none":"inline";
					}
					else
					{
						span.style.display = isDescending?"inline":"none";
					}
				}
				else
				{
					//not current
					span.style.display = "none";
				}
			}
		}
	}
}
function toString(value)
{
    if (value) {
        return value.toUpperCase();
    }

    return value;
}
function toDate(value)
{
    return Date.parse(value);  
}
function Custom(a,b)
{
          
   if(a==undefined||b==undefined)
     return 0;
     
   var val1=getRowValue(a, columnIndex); 
   var val2=getRowValue(b, columnIndex); 
   
    var a1=val1.split('.');
    var b1=val2.split('.');
    //get the maximum length of array.
    var maxlength;
    if(a1.length>b1.length)
     maxlength=a1.length;
     else
     maxlength=b1.length;
     //iterate through array item to get individual order no .
     for(var i=0;i<maxlength;i++)
     {
     //if both order number is same conitue to next order.else return the difference
      if(a1[i]!=undefined && b1[i]!=undefined)
      {
          if(parseInt(a1[i])==parseInt(b1[i]))
          {
           continue;
          }
          else
          {
            if(parseInt(a1[i])<parseInt(b1[i]))
                return isDescending?-1:+1;
           if(parseInt(a1[i])>parseInt(b1[i]))
                return isDescending ?+1:-1;
            return 0;               
            
          }
      }
      //if one of the order does not exists,return 1 or -1.
      else
      {
        if(a1[i]==undefined)
          return -1;
        else
          return 1;
          
      }
      
     }
     
     return 0; 
}
function IntRange(a,b)
{
          
   if(a==undefined||b==undefined)
     return 0;
     
   var val1=getRowValue(a, columnIndex); //.children[columnIndex].innerText;
   var val2=getRowValue(b, columnIndex); //.children[columnIndex].innerText;
   
    var a1=val1.replace(/,/g, "").split('-');
    var b1=val2.replace(/,/g, "").split('-');
	
	if(a1[0] && b1[0])
	{
		  if(parseInt(a1[0])==parseInt(b1[0]))
          {
          	if(a1[1] && b1[1])
			{
			 	if(parseInt(a1[1])<parseInt(b1[1]))
                	return isDescending?-1:+1;
           		if(parseInt(a1[1])>parseInt(b1[1]))
                	return isDescending ?+1:-1;
            	return 0;
			}
			else
			{
				if(a1[1]==undefined)
          			return isDescending?-1:+1;
       			else
          			return isDescending?+1:-1;
			}
          }
          else
          {
            if(parseInt(a1[0])<parseInt(b1[0]))
                return isDescending?-1:+1;
           	if(parseInt(a1[0])>parseInt(b1[0]))
                return isDescending ?+1:-1;
            return 0;    
          }
	}
	else
	{
		if(a1[0]==undefined)
          return isDescending?-1:+1;
        else
          return isDescending?+1:-1;
	}
    return 0;
}
function CustomPriceRange(a,b)
{
          
   if(a==undefined||b==undefined)
     return 0;
     
   var val1=getRowValue(a, columnIndex); //.children[columnIndex].innerText;
   var val2=getRowValue(b, columnIndex); //.children[columnIndex].innerText;
   
    var a1=val1.replace(/\$/g, "").replace(/,/g, "").split('-');
    var b1=val2.replace(/\$/g, "").replace(/,/g, "").split('-');
	
	if(a1[0] && b1[0])
	{
		  if(parseInt(a1[0])==parseInt(b1[0]))
          {
          	if(a1[1] && b1[1])
			{
			 	if(parseInt(a1[1])<parseInt(b1[1]))
                	return isDescending?-1:+1;
           		if(parseInt(a1[1])>parseInt(b1[1]))
                	return isDescending ?+1:-1;
            	return 0;
			}
			else
			{
				if(a1[1]==undefined)
          			return isDescending?-1:+1;
       			else
          			return isDescending?+1:-1;
			}
          }
          else
          {
            if(parseInt(a1[0])<parseInt(b1[0]))
                return isDescending?-1:+1;
           	if(parseInt(a1[0])>parseInt(b1[0]))
                return isDescending ?+1:-1;
            return 0;    
          }
	}
	else
	{
		if(a1[0]==undefined)
          return isDescending?-1:+1;
        else
          return isDescending?+1:-1;
	}
    return 0;
}
function CustomDecimal(a,b)
{
          
   if(a==undefined||b==undefined)
     return 0;
     
   var val1=getRowValue(a, columnIndex); //.children[columnIndex].innerText;
   var val2=getRowValue(b, columnIndex); //.children[columnIndex].innerText;
   
    var a1=val1.split('.');
    var b1=val2.split('.');
	
	if(a1[0] && b1[0])
	{
		  if(parseInt(a1[0])==parseInt(b1[0]))
          {
          	if(a1[1] && b1[1])
			{
			 	if(parseInt(a1[1])<parseInt(b1[1]))
                	return isDescending?-1:+1;
           		if(parseInt(a1[1])>parseInt(b1[1]))
                	return isDescending ?+1:-1;
            	return 0;
			}
			else
			{
				if(a1[1]==undefined)
          			return isDescending?-1:+1;
       			else
          			return isDescending?+1:-1;
			}
          }
          else
          {
            if(parseInt(a1[0])<parseInt(b1[0]))
                return isDescending?-1:+1;
           	if(parseInt(a1[0])>parseInt(b1[0]))
                return isDescending ?+1:-1;
            return 0;    
          }
	}
	else
	{
		if(a1[0]==undefined)
          return isDescending?-1:+1;
        else
          return isDescending?+1:-1;
	}
    return 0;
}
function CustomDate(a,b)
{
    var TypeCast = toDate;
    var val1,val2;
    var d = new Date();
    var strD = (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear();
    val1=getRowValue(a, columnIndex); 
    if(val1.toLowerCase().indexOf("now") > -1){val1 = strD;}
    val2=getRowValue(b, columnIndex); 
    if(val2.toLowerCase().indexOf("now") > -1){val2 = strD;}

    if(TypeCast(val1)<TypeCast(val2))
        return isDescending ?-1:+1;
    if(TypeCast(val1)>TypeCast(val2))
        return isDescending ?+1:-1
    return 0;
}
//function returning another function.
function customCompare(type,isDescend,columnIndex)
{
    var TypeCast;
    //assign the typecast to point to the cast function.
    //if type is 'Custom' then return the Custom() function.
    if(type=="Number")
        TypeCast=Number;
      else if(type=="Date")
          TypeCast=toDate;
        else if(type=="Custom")
            return Custom;
	       else if(type=="CustomDate")
	   	      return CustomDate;
		     else if(type=="CustomPriceRange")
	   	       return CustomPriceRange;
		   	      else if(type=="CustomDecimal")
                    return CustomDecimal;
       	             else
         	           TypeCast=toString;

    return function(row1,row2)
    {
        var val1,val2;

        val1=getRowValue(row1, columnIndex); 
        val2=getRowValue(row2, columnIndex); 
        if(TypeCast(val1)<TypeCast(val2))
            return isDescend ?-1:+1;
        if(TypeCast(val1)>TypeCast(val2))
            return isDescend ?+1:-1;
        return 0;
    } 
}
function getRowValue(row, index)
{
    return row.children?stripHTML(row.children[index].innerText):stripHTML(row.cells[index].innerHTML); 
}
function stripHTML(strIn)
{
    return strIn.replace(/(<([^>]+)>)/ig,""); 
}

