docs: function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback )
  		{
  			_fnBindAction( nNode, {}, function (e) {
  				/* If the column is not sortable - don't to anything */
  				if ( oSettings.aoColumns[iDataIndex].bSortable === false )
  				{
  					return;
  				}
  				
  				/*
  				 * This is a little bit odd I admit... I declare a temporary function inside the scope of
  				 * _fnBuildHead and the click handler in order that the code presented here can be used 
  				 * twice - once for when bProcessing is enabled, and another time for when it is 
  				 * disabled, as we need to perform slightly different actions.
  				 *   Basically the issue here is that the Javascript engine in modern browsers don't 
  				 * appear to allow the rendering engine to update the display while it is still excuting
  				 * it's thread (well - it does but only after long intervals). This means that the 
  				 * 'processing' display doesn't appear for a table sort. To break the js thread up a bit
  				 * I force an execution break by using setTimeout - but this breaks the expected 
  				 * thread continuation for the end-developer's point of view (their code would execute
  				 * too early), so we on;y do it when we absolutely have to.
  				 */
  				var fnInnerSorting = function () {
  					var iColumn, iNextSort;
  					
  					/* If the shift key is pressed then we are multipe column sorting */
  					if ( e.shiftKey )
  					{
  						/* Are we already doing some kind of sort on this column? */
  						var bFound = false;
  						for ( var i=0 ; i<oSettings.aaSorting.length ; i++ )
  						{
  							if ( oSettings.aaSorting[i][0] == iDataIndex )
  							{
  								bFound = true;
  								iColumn = oSettings.aaSorting[i][0];
  								iNextSort = oSettings.aaSorting[i][2]+1;
  								
  								if ( !oSettings.aoColumns[iColumn].asSorting[iNextSort] )
  								{
  									/* Reached the end of the sorting options, remove from multi-col sort */
  									oSettings.aaSorting.splice( i, 1 );
  								}
  								else
  								{
  									/* Move onto next sorting direction */
  									oSettings.aaSorting[i][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
  									oSettings.aaSorting[i][2] = iNextSort;
  								}
  								break;
  							}
  						}
  						
  						/* No sort yet - add it in */
  						if ( bFound === false )
  						{
  							oSettings.aaSorting.push( [ iDataIndex, 
  								oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
  						}
  					}
  					else
  					{
  						/* If no shift key then single column sort */
  						if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex )
  						{
  							iColumn = oSettings.aaSorting[0][0];
  							iNextSort = oSettings.aaSorting[0][2]+1;
  							if ( !oSettings.aoColumns[iColumn].asSorting[iNextSort] )
  							{
  								iNextSort = 0;
  							}
  							oSettings.aaSorting[0][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
  							oSettings.aaSorting[0][2] = iNextSort;
  						}
  						else
  						{
  							oSettings.aaSorting.splice( 0, oSettings.aaSorting.length );
  							oSettings.aaSorting.push( [ iDataIndex, 
  								oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
  						}
  					}
  					
  					/* Run the sort */
  					_fnSort( oSettings );
  				}; /* /fnInnerSorting */
  				
  				if ( !oSettings.oFeatures.bProcessing )
  				{
  					fnInnerSorting();
  				}
  				else
  				{
  					_fnProcessingDisplay( oSettings, true );
  					setTimeout( function() {
  						fnInnerSorting();
  						if ( !oSettings.oFeatures.bServerSide )
  						{
  							_fnProcessingDisplay( oSettings, false );
  						}
  					}, 0 );
  				}
  				
  				/* Call the user specified callback function - used for async user interaction */
  				if ( typeof fnCallback == 'function' )
  				{
  					fnCallback( oSettings );
  				}
  			} );
  		}