docs: function _fnCalculateColumnWidths ( oSettings )
  		{
  			var iTableWidth = oSettings.nTable.offsetWidth;
  			var iUserInputs = 0;
  			var iTmpWidth;
  			var iVisibleColumns = 0;
  			var iColums = oSettings.aoColumns.length;
  			var i, iIndex, iCorrector, iWidth;
  			var oHeaders = $('th', oSettings.nTHead);
  			var widthAttr = oSettings.nTable.getAttribute('width');
  			
  			/* Convert any user input sizes into pixel sizes */
  			for ( i=0 ; i<iColums ; i++ )
  			{
  				if ( oSettings.aoColumns[i].bVisible )
  				{
  					iVisibleColumns++;
  					
  					if ( oSettings.aoColumns[i].sWidth !== null )
  					{
  						iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidthOrig, 
  							oSettings.nTable.parentNode );
  						if ( iTmpWidth !== null )
  						{
  							oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
  						}
  							
  						iUserInputs++;
  					}
  				}
  			}
  			
  			/* If the number of columns in the DOM equals the number that we have to process in 
  			 * DataTables, then we can use the offsets that are created by the web-browser. No custom 
  			 * sizes can be set in order for this to happen, nor scrolling used
  			 */
  			if ( iColums == oHeaders.length && iUserInputs === 0 && iVisibleColumns == iColums &&
  				oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
  			{
  				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
  				{
  					iTmpWidth = $(oHeaders[i]).width();
  					if ( iTmpWidth !== null )
  					{
  						oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
  					}
  				}
  			}
  			else
  			{
  				/* Otherwise we are going to have to do some calculations to get the width of each column.
  				 * Construct a 1 row table with the widest node in the data, and any user defined widths,
  				 * then insert it into the DOM and allow the browser to do all the hard work of
  				 * calculating table widths.
  				 */
  				var
  					nCalcTmp = oSettings.nTable.cloneNode( false ),
  					nTheadClone = oSettings.nTHead.cloneNode(true),
  					nBody = document.createElement( 'tbody' ),
  					nTr = document.createElement( 'tr' ),
  					nDivSizing;
  				
  				nCalcTmp.removeAttribute( "id" );
  				nCalcTmp.appendChild( nTheadClone );
  				if ( oSettings.nTFoot !== null )
  				{
  					nCalcTmp.appendChild( oSettings.nTFoot.cloneNode(true) );
  					_fnApplyToChildren( function(n) {
  						n.style.width = "";
  					}, nCalcTmp.getElementsByTagName('tr') );
  				}
  				
  				nCalcTmp.appendChild( nBody );
  				nBody.appendChild( nTr );
  				
  				/* Remove any sizing that was previously applied by the styles */
  				var jqColSizing = $('thead th', nCalcTmp);
  				if ( jqColSizing.length === 0 )
  				{
  					jqColSizing = $('tbody tr:eq(0)>td', nCalcTmp);
  				}
  		
  				/* Apply custom sizing to the cloned header */
  				var nThs = _fnGetUniqueThs( oSettings, nTheadClone );
  				iCorrector = 0;
  				for ( i=0 ; i<iColums ; i++ )
  				{
  					var oColumn = oSettings.aoColumns[i];
  					if ( oColumn.bVisible && oColumn.sWidthOrig !== null && oColumn.sWidthOrig !== "" )
  					{
  						nThs[i-iCorrector].style.width = _fnStringToCss( oColumn.sWidthOrig );
  					}
  					else if ( oColumn.bVisible )
  					{
  						nThs[i-iCorrector].style.width = "";
  					}
  					else
  					{
  						iCorrector++;
  					}
  				}
  		
  				/* Find the biggest td for each column and put it into the table */
  				for ( i=0 ; i<iColums ; i++ )
  				{
  					if ( oSettings.aoColumns[i].bVisible )
  					{
  						var nTd = _fnGetWidestNode( oSettings, i );
  						if ( nTd !== null )
  						{
  							nTd = nTd.cloneNode(true);
  							if ( oSettings.aoColumns[i].sContentPadding !== "" )
  							{
  								nTd.innerHTML += oSettings.aoColumns[i].sContentPadding;
  							}
  							nTr.appendChild( nTd );
  						}
  					}
  				}
  				
  				/* Build the table and 'display' it */
  				var nWrapper = oSettings.nTable.parentNode;
  				nWrapper.appendChild( nCalcTmp );
  				
  				/* When scrolling (X or Y) we want to set the width of the table as appropriate. However,
  				 * when not scrolling leave the table width as it is. This results in slightly different,
  				 * but I think correct behaviour
  				 */
  				if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" )
  				{
  					nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner);
  				}
  				else if ( oSettings.oScroll.sX !== "" )
  				{
  					nCalcTmp.style.width = "";
  					if ( $(nCalcTmp).width() < nWrapper.offsetWidth )
  					{
  						nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
  					}
  				}
  				else if ( oSettings.oScroll.sY !== "" )
  				{
  					nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
  				}
  				else if ( widthAttr )
  				{
  					nCalcTmp.style.width = _fnStringToCss( widthAttr );
  				}
  				nCalcTmp.style.visibility = "hidden";
  				
  				/* Scrolling considerations */
  				_fnScrollingWidthAdjust( oSettings, nCalcTmp );
  				
  				/* Read the width's calculated by the browser and store them for use by the caller. We
  				 * first of all try to use the elements in the body, but it is possible that there are
  				 * no elements there, under which circumstances we use the header elements
  				 */
  				var oNodes = $("tbody tr:eq(0)", nCalcTmp).children();
  				if ( oNodes.length === 0 )
  				{
  					oNodes = _fnGetUniqueThs( oSettings, $('thead', nCalcTmp)[0] );
  				}
  		
  				/* Browsers need a bit of a hand when a width is assigned to any columns when 
  				 * x-scrolling as they tend to collapse the table to the min-width, even if
  				 * we sent the column widths. So we need to keep track of what the table width
  				 * should be by summing the user given values, and the automatic values
  				 */
  				if ( oSettings.oScroll.sX !== "" )
  				{
  					var iTotal = 0;
  					iCorrector = 0;
  					for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
  					{
  						if ( oSettings.aoColumns[i].bVisible )
  						{
  							if ( oSettings.aoColumns[i].sWidthOrig === null )
  							{
  								iTotal += $(oNodes[iCorrector]).outerWidth();
  							}
  							else
  							{
  								iTotal += parseInt(oSettings.aoColumns[i].sWidth.replace('px',''), 10) +
  									($(oNodes[iCorrector]).outerWidth() - $(oNodes[iCorrector]).width());
  							}
  							iCorrector++;
  						}
  					}
  					
  					nCalcTmp.style.width = _fnStringToCss( iTotal );
  					oSettings.nTable.style.width = _fnStringToCss( iTotal );
  				}
  		
  				iCorrector = 0;
  				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
  				{
  					if ( oSettings.aoColumns[i].bVisible )
  					{
  						iWidth = $(oNodes[iCorrector]).width();
  						if ( iWidth !== null && iWidth > 0 )
  						{
  							oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth );
  						}
  						iCorrector++;
  					}
  				}
  		
  				var cssWidth = $(nCalcTmp).css('width');
  				oSettings.nTable.style.width = (cssWidth.indexOf('%') !== -1) ?
  				    cssWidth : _fnStringToCss( $(nCalcTmp).outerWidth() );
  				nCalcTmp.parentNode.removeChild( nCalcTmp );
  			}
  		
  			if ( widthAttr )
  			{
  				oSettings.nTable.style.width = _fnStringToCss( widthAttr );
  			}
  		}