github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/public/root/js/jquery.dataTables.min.js (about)

     1  /**
     2   * @summary     DataTables
     3   * @description Paginate, search and sort HTML tables
     4   * @version     1.9.4
     5   * @file        jquery.dataTables.js
     6   * @author      Allan Jardine (www.sprymedia.co.uk)
     7   * @contact     www.sprymedia.co.uk/contact
     8   *
     9   * @copyright Copyright 2008-2012 Allan Jardine, all rights reserved.
    10   *
    11   * This source file is free software, under either the GPL v2 license or a
    12   * BSD style license, available at:
    13   *   http://datatables.net/license_gpl2
    14   *   http://datatables.net/license_bsd
    15   * 
    16   * This source file is distributed in the hope that it will be useful, but 
    17   * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
    18   * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
    19   * 
    20   * For details please refer to: http://www.datatables.net
    21   */
    22  
    23  /*jslint evil: true, undef: true, browser: true */
    24  /*globals $, jQuery,define,_fnExternApiFunc,_fnInitialise,_fnInitComplete,_fnLanguageCompat,_fnAddColumn,_fnColumnOptions,_fnAddData,_fnCreateTr,_fnGatherData,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnServerParams,_fnAddOptionsHtml,_fnFeatureHtmlTable,_fnScrollDraw,_fnAdjustColumnSizing,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnBuildSearchArray,_fnBuildSearchRow,_fnFilterCreateSearch,_fnDataToSearch,_fnSort,_fnSortAttachListener,_fnSortingClasses,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnFeatureHtmlLength,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnNodeToDataIndex,_fnVisbleColumns,_fnCalculateEnd,_fnConvertToWidth,_fnCalculateColumnWidths,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnDetectType,_fnSettingsFromNode,_fnGetDataMaster,_fnGetTrNodes,_fnGetTdNodes,_fnEscapeRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnDetectHeader,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap,_fnGetRowData,_fnGetCellData,_fnSetCellData,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnApplyColumnDefs,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnJsonString,_fnRender,_fnNodeToColumnIndex,_fnInfoMacros,_fnBrowserDetect,_fnGetColumns*/
    25  
    26  (/** @lends <global> */function( window, document, undefined ) {
    27  
    28  (function( factory ) {
    29  	"use strict";
    30  
    31  	// Define as an AMD module if possible
    32  	if ( typeof define === 'function' && define.amd )
    33  	{
    34  		define( ['jquery'], factory );
    35  	}
    36  	/* Define using browser globals otherwise
    37  	 * Prevent multiple instantiations if the script is loaded twice
    38  	 */
    39  	else if ( jQuery && !jQuery.fn.dataTable )
    40  	{
    41  		factory( jQuery );
    42  	}
    43  }
    44  (/** @lends <global> */function( $ ) {
    45  	"use strict";
    46  	/** 
    47  	 * DataTables is a plug-in for the jQuery Javascript library. It is a 
    48  	 * highly flexible tool, based upon the foundations of progressive 
    49  	 * enhancement, which will add advanced interaction controls to any 
    50  	 * HTML table. For a full list of features please refer to
    51  	 * <a href="http://datatables.net">DataTables.net</a>.
    52  	 *
    53  	 * Note that the <i>DataTable</i> object is not a global variable but is
    54  	 * aliased to <i>jQuery.fn.DataTable</i> and <i>jQuery.fn.dataTable</i> through which 
    55  	 * it may be  accessed.
    56  	 *
    57  	 *  @class
    58  	 *  @param {object} [oInit={}] Configuration object for DataTables. Options
    59  	 *    are defined by {@link DataTable.defaults}
    60  	 *  @requires jQuery 1.3+
    61  	 * 
    62  	 *  @example
    63  	 *    // Basic initialisation
    64  	 *    $(document).ready( function {
    65  	 *      $('#example').dataTable();
    66  	 *    } );
    67  	 *  
    68  	 *  @example
    69  	 *    // Initialisation with configuration options - in this case, disable
    70  	 *    // pagination and sorting.
    71  	 *    $(document).ready( function {
    72  	 *      $('#example').dataTable( {
    73  	 *        "bPaginate": false,
    74  	 *        "bSort": false 
    75  	 *      } );
    76  	 *    } );
    77  	 */
    78  	var DataTable = function( oInit )
    79  	{
    80  		
    81  		
    82  		/**
    83  		 * Add a column to the list used for the table with default values
    84  		 *  @param {object} oSettings dataTables settings object
    85  		 *  @param {node} nTh The th element for this column
    86  		 *  @memberof DataTable#oApi
    87  		 */
    88  		function _fnAddColumn( oSettings, nTh )
    89  		{
    90  			var oDefaults = DataTable.defaults.columns;
    91  			var iCol = oSettings.aoColumns.length;
    92  			var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
    93  				"sSortingClass": oSettings.oClasses.sSortable,
    94  				"sSortingClassJUI": oSettings.oClasses.sSortJUI,
    95  				"nTh": nTh ? nTh : document.createElement('th'),
    96  				"sTitle":    oDefaults.sTitle    ? oDefaults.sTitle    : nTh ? nTh.innerHTML : '',
    97  				"aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
    98  				"mData": oDefaults.mData ? oDefaults.oDefaults : iCol
    99  			} );
   100  			oSettings.aoColumns.push( oCol );
   101  			
   102  			/* Add a column specific filter */
   103  			if ( oSettings.aoPreSearchCols[ iCol ] === undefined || oSettings.aoPreSearchCols[ iCol ] === null )
   104  			{
   105  				oSettings.aoPreSearchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch );
   106  			}
   107  			else
   108  			{
   109  				var oPre = oSettings.aoPreSearchCols[ iCol ];
   110  				
   111  				/* Don't require that the user must specify bRegex, bSmart or bCaseInsensitive */
   112  				if ( oPre.bRegex === undefined )
   113  				{
   114  					oPre.bRegex = true;
   115  				}
   116  				
   117  				if ( oPre.bSmart === undefined )
   118  				{
   119  					oPre.bSmart = true;
   120  				}
   121  				
   122  				if ( oPre.bCaseInsensitive === undefined )
   123  				{
   124  					oPre.bCaseInsensitive = true;
   125  				}
   126  			}
   127  			
   128  			/* Use the column options function to initialise classes etc */
   129  			_fnColumnOptions( oSettings, iCol, null );
   130  		}
   131  		
   132  		
   133  		/**
   134  		 * Apply options for a column
   135  		 *  @param {object} oSettings dataTables settings object
   136  		 *  @param {int} iCol column index to consider
   137  		 *  @param {object} oOptions object with sType, bVisible and bSearchable etc
   138  		 *  @memberof DataTable#oApi
   139  		 */
   140  		function _fnColumnOptions( oSettings, iCol, oOptions )
   141  		{
   142  			var oCol = oSettings.aoColumns[ iCol ];
   143  			
   144  			/* User specified column options */
   145  			if ( oOptions !== undefined && oOptions !== null )
   146  			{
   147  				/* Backwards compatibility for mDataProp */
   148  				if ( oOptions.mDataProp && !oOptions.mData )
   149  				{
   150  					oOptions.mData = oOptions.mDataProp;
   151  				}
   152  		
   153  				if ( oOptions.sType !== undefined )
   154  				{
   155  					oCol.sType = oOptions.sType;
   156  					oCol._bAutoType = false;
   157  				}
   158  				
   159  				$.extend( oCol, oOptions );
   160  				_fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
   161  		
   162  				/* iDataSort to be applied (backwards compatibility), but aDataSort will take
   163  				 * priority if defined
   164  				 */
   165  				if ( oOptions.iDataSort !== undefined )
   166  				{
   167  					oCol.aDataSort = [ oOptions.iDataSort ];
   168  				}
   169  				_fnMap( oCol, oOptions, "aDataSort" );
   170  			}
   171  		
   172  			/* Cache the data get and set functions for speed */
   173  			var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
   174  			var mData = _fnGetObjectDataFn( oCol.mData );
   175  		
   176  			oCol.fnGetData = function (oData, sSpecific) {
   177  				var innerData = mData( oData, sSpecific );
   178  		
   179  				if ( oCol.mRender && (sSpecific && sSpecific !== '') )
   180  				{
   181  					return mRender( innerData, sSpecific, oData );
   182  				}
   183  				return innerData;
   184  			};
   185  			oCol.fnSetData = _fnSetObjectDataFn( oCol.mData );
   186  			
   187  			/* Feature sorting overrides column specific when off */
   188  			if ( !oSettings.oFeatures.bSort )
   189  			{
   190  				oCol.bSortable = false;
   191  			}
   192  			
   193  			/* Check that the class assignment is correct for sorting */
   194  			if ( !oCol.bSortable ||
   195  				 ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) )
   196  			{
   197  				oCol.sSortingClass = oSettings.oClasses.sSortableNone;
   198  				oCol.sSortingClassJUI = "";
   199  			}
   200  			else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1 )
   201  			{
   202  				oCol.sSortingClass = oSettings.oClasses.sSortable;
   203  				oCol.sSortingClassJUI = oSettings.oClasses.sSortJUI;
   204  			}
   205  			else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 )
   206  			{
   207  				oCol.sSortingClass = oSettings.oClasses.sSortableAsc;
   208  				oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed;
   209  			}
   210  			else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 )
   211  			{
   212  				oCol.sSortingClass = oSettings.oClasses.sSortableDesc;
   213  				oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed;
   214  			}
   215  		}
   216  		
   217  		
   218  		/**
   219  		 * Adjust the table column widths for new data. Note: you would probably want to 
   220  		 * do a redraw after calling this function!
   221  		 *  @param {object} oSettings dataTables settings object
   222  		 *  @memberof DataTable#oApi
   223  		 */
   224  		function _fnAdjustColumnSizing ( oSettings )
   225  		{
   226  			/* Not interested in doing column width calculation if auto-width is disabled */
   227  			if ( oSettings.oFeatures.bAutoWidth === false )
   228  			{
   229  				return false;
   230  			}
   231  			
   232  			_fnCalculateColumnWidths( oSettings );
   233  			for ( var i=0 , iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
   234  			{
   235  				oSettings.aoColumns[i].nTh.style.width = oSettings.aoColumns[i].sWidth;
   236  			}
   237  		}
   238  		
   239  		
   240  		/**
   241  		 * Covert the index of a visible column to the index in the data array (take account
   242  		 * of hidden columns)
   243  		 *  @param {object} oSettings dataTables settings object
   244  		 *  @param {int} iMatch Visible column index to lookup
   245  		 *  @returns {int} i the data index
   246  		 *  @memberof DataTable#oApi
   247  		 */
   248  		function _fnVisibleToColumnIndex( oSettings, iMatch )
   249  		{
   250  			var aiVis = _fnGetColumns( oSettings, 'bVisible' );
   251  		
   252  			return typeof aiVis[iMatch] === 'number' ?
   253  				aiVis[iMatch] :
   254  				null;
   255  		}
   256  		
   257  		
   258  		/**
   259  		 * Covert the index of an index in the data array and convert it to the visible
   260  		 *   column index (take account of hidden columns)
   261  		 *  @param {int} iMatch Column index to lookup
   262  		 *  @param {object} oSettings dataTables settings object
   263  		 *  @returns {int} i the data index
   264  		 *  @memberof DataTable#oApi
   265  		 */
   266  		function _fnColumnIndexToVisible( oSettings, iMatch )
   267  		{
   268  			var aiVis = _fnGetColumns( oSettings, 'bVisible' );
   269  			var iPos = $.inArray( iMatch, aiVis );
   270  		
   271  			return iPos !== -1 ? iPos : null;
   272  		}
   273  		
   274  		
   275  		/**
   276  		 * Get the number of visible columns
   277  		 *  @param {object} oSettings dataTables settings object
   278  		 *  @returns {int} i the number of visible columns
   279  		 *  @memberof DataTable#oApi
   280  		 */
   281  		function _fnVisbleColumns( oSettings )
   282  		{
   283  			return _fnGetColumns( oSettings, 'bVisible' ).length;
   284  		}
   285  		
   286  		
   287  		/**
   288  		 * Get an array of column indexes that match a given property
   289  		 *  @param {object} oSettings dataTables settings object
   290  		 *  @param {string} sParam Parameter in aoColumns to look for - typically 
   291  		 *    bVisible or bSearchable
   292  		 *  @returns {array} Array of indexes with matched properties
   293  		 *  @memberof DataTable#oApi
   294  		 */
   295  		function _fnGetColumns( oSettings, sParam )
   296  		{
   297  			var a = [];
   298  		
   299  			$.map( oSettings.aoColumns, function(val, i) {
   300  				if ( val[sParam] ) {
   301  					a.push( i );
   302  				}
   303  			} );
   304  		
   305  			return a;
   306  		}
   307  		
   308  		
   309  		/**
   310  		 * Get the sort type based on an input string
   311  		 *  @param {string} sData data we wish to know the type of
   312  		 *  @returns {string} type (defaults to 'string' if no type can be detected)
   313  		 *  @memberof DataTable#oApi
   314  		 */
   315  		function _fnDetectType( sData )
   316  		{
   317  			var aTypes = DataTable.ext.aTypes;
   318  			var iLen = aTypes.length;
   319  			
   320  			for ( var i=0 ; i<iLen ; i++ )
   321  			{
   322  				var sType = aTypes[i]( sData );
   323  				if ( sType !== null )
   324  				{
   325  					return sType;
   326  				}
   327  			}
   328  			
   329  			return 'string';
   330  		}
   331  		
   332  		
   333  		/**
   334  		 * Figure out how to reorder a display list
   335  		 *  @param {object} oSettings dataTables settings object
   336  		 *  @returns array {int} aiReturn index list for reordering
   337  		 *  @memberof DataTable#oApi
   338  		 */
   339  		function _fnReOrderIndex ( oSettings, sColumns )
   340  		{
   341  			var aColumns = sColumns.split(',');
   342  			var aiReturn = [];
   343  			
   344  			for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
   345  			{
   346  				for ( var j=0 ; j<iLen ; j++ )
   347  				{
   348  					if ( oSettings.aoColumns[i].sName == aColumns[j] )
   349  					{
   350  						aiReturn.push( j );
   351  						break;
   352  					}
   353  				}
   354  			}
   355  			
   356  			return aiReturn;
   357  		}
   358  		
   359  		
   360  		/**
   361  		 * Get the column ordering that DataTables expects
   362  		 *  @param {object} oSettings dataTables settings object
   363  		 *  @returns {string} comma separated list of names
   364  		 *  @memberof DataTable#oApi
   365  		 */
   366  		function _fnColumnOrdering ( oSettings )
   367  		{
   368  			var sNames = '';
   369  			for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
   370  			{
   371  				sNames += oSettings.aoColumns[i].sName+',';
   372  			}
   373  			if ( sNames.length == iLen )
   374  			{
   375  				return "";
   376  			}
   377  			return sNames.slice(0, -1);
   378  		}
   379  		
   380  		
   381  		/**
   382  		 * Take the column definitions and static columns arrays and calculate how
   383  		 * they relate to column indexes. The callback function will then apply the
   384  		 * definition found for a column to a suitable configuration object.
   385  		 *  @param {object} oSettings dataTables settings object
   386  		 *  @param {array} aoColDefs The aoColumnDefs array that is to be applied
   387  		 *  @param {array} aoCols The aoColumns array that defines columns individually
   388  		 *  @param {function} fn Callback function - takes two parameters, the calculated
   389  		 *    column index and the definition for that column.
   390  		 *  @memberof DataTable#oApi
   391  		 */
   392  		function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
   393  		{
   394  			var i, iLen, j, jLen, k, kLen;
   395  		
   396  			// Column definitions with aTargets
   397  			if ( aoColDefs )
   398  			{
   399  				/* Loop over the definitions array - loop in reverse so first instance has priority */
   400  				for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
   401  				{
   402  					/* Each definition can target multiple columns, as it is an array */
   403  					var aTargets = aoColDefs[i].aTargets;
   404  					if ( !$.isArray( aTargets ) )
   405  					{
   406  						_fnLog( oSettings, 1, 'aTargets must be an array of targets, not a '+(typeof aTargets) );
   407  					}
   408  		
   409  					for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
   410  					{
   411  						if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
   412  						{
   413  							/* Add columns that we don't yet know about */
   414  							while( oSettings.aoColumns.length <= aTargets[j] )
   415  							{
   416  								_fnAddColumn( oSettings );
   417  							}
   418  		
   419  							/* Integer, basic index */
   420  							fn( aTargets[j], aoColDefs[i] );
   421  						}
   422  						else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
   423  						{
   424  							/* Negative integer, right to left column counting */
   425  							fn( oSettings.aoColumns.length+aTargets[j], aoColDefs[i] );
   426  						}
   427  						else if ( typeof aTargets[j] === 'string' )
   428  						{
   429  							/* Class name matching on TH element */
   430  							for ( k=0, kLen=oSettings.aoColumns.length ; k<kLen ; k++ )
   431  							{
   432  								if ( aTargets[j] == "_all" ||
   433  								     $(oSettings.aoColumns[k].nTh).hasClass( aTargets[j] ) )
   434  								{
   435  									fn( k, aoColDefs[i] );
   436  								}
   437  							}
   438  						}
   439  					}
   440  				}
   441  			}
   442  		
   443  			// Statically defined columns array
   444  			if ( aoCols )
   445  			{
   446  				for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
   447  				{
   448  					fn( i, aoCols[i] );
   449  				}
   450  			}
   451  		}
   452  		
   453  		/**
   454  		 * Add a data array to the table, creating DOM node etc. This is the parallel to 
   455  		 * _fnGatherData, but for adding rows from a Javascript source, rather than a
   456  		 * DOM source.
   457  		 *  @param {object} oSettings dataTables settings object
   458  		 *  @param {array} aData data array to be added
   459  		 *  @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
   460  		 *  @memberof DataTable#oApi
   461  		 */
   462  		function _fnAddData ( oSettings, aDataSupplied )
   463  		{
   464  			var oCol;
   465  			
   466  			/* Take an independent copy of the data source so we can bash it about as we wish */
   467  			var aDataIn = ($.isArray(aDataSupplied)) ?
   468  				aDataSupplied.slice() :
   469  				$.extend( true, {}, aDataSupplied );
   470  			
   471  			/* Create the object for storing information about this new row */
   472  			var iRow = oSettings.aoData.length;
   473  			var oData = $.extend( true, {}, DataTable.models.oRow );
   474  			oData._aData = aDataIn;
   475  			oSettings.aoData.push( oData );
   476  		
   477  			/* Create the cells */
   478  			var nTd, sThisType;
   479  			for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
   480  			{
   481  				oCol = oSettings.aoColumns[i];
   482  		
   483  				/* Use rendered data for filtering / sorting */
   484  				if ( typeof oCol.fnRender === 'function' && oCol.bUseRendered && oCol.mData !== null )
   485  				{
   486  					_fnSetCellData( oSettings, iRow, i, _fnRender(oSettings, iRow, i) );
   487  				}
   488  				else
   489  				{
   490  					_fnSetCellData( oSettings, iRow, i, _fnGetCellData( oSettings, iRow, i ) );
   491  				}
   492  				
   493  				/* See if we should auto-detect the column type */
   494  				if ( oCol._bAutoType && oCol.sType != 'string' )
   495  				{
   496  					/* Attempt to auto detect the type - same as _fnGatherData() */
   497  					var sVarType = _fnGetCellData( oSettings, iRow, i, 'type' );
   498  					if ( sVarType !== null && sVarType !== '' )
   499  					{
   500  						sThisType = _fnDetectType( sVarType );
   501  						if ( oCol.sType === null )
   502  						{
   503  							oCol.sType = sThisType;
   504  						}
   505  						else if ( oCol.sType != sThisType && oCol.sType != "html" )
   506  						{
   507  							/* String is always the 'fallback' option */
   508  							oCol.sType = 'string';
   509  						}
   510  					}
   511  				}
   512  			}
   513  			
   514  			/* Add to the display array */
   515  			oSettings.aiDisplayMaster.push( iRow );
   516  		
   517  			/* Create the DOM information */
   518  			if ( !oSettings.oFeatures.bDeferRender )
   519  			{
   520  				_fnCreateTr( oSettings, iRow );
   521  			}
   522  		
   523  			return iRow;
   524  		}
   525  		
   526  		
   527  		/**
   528  		 * Read in the data from the target table from the DOM
   529  		 *  @param {object} oSettings dataTables settings object
   530  		 *  @memberof DataTable#oApi
   531  		 */
   532  		function _fnGatherData( oSettings )
   533  		{
   534  			var iLoop, i, iLen, j, jLen, jInner,
   535  			 	nTds, nTrs, nTd, nTr, aLocalData, iThisIndex,
   536  				iRow, iRows, iColumn, iColumns, sNodeName,
   537  				oCol, oData;
   538  			
   539  			/*
   540  			 * Process by row first
   541  			 * Add the data object for the whole table - storing the tr node. Note - no point in getting
   542  			 * DOM based data if we are going to go and replace it with Ajax source data.
   543  			 */
   544  			if ( oSettings.bDeferLoading || oSettings.sAjaxSource === null )
   545  			{
   546  				nTr = oSettings.nTBody.firstChild;
   547  				while ( nTr )
   548  				{
   549  					if ( nTr.nodeName.toUpperCase() == "TR" )
   550  					{
   551  						iThisIndex = oSettings.aoData.length;
   552  						nTr._DT_RowIndex = iThisIndex;
   553  						oSettings.aoData.push( $.extend( true, {}, DataTable.models.oRow, {
   554  							"nTr": nTr
   555  						} ) );
   556  		
   557  						oSettings.aiDisplayMaster.push( iThisIndex );
   558  						nTd = nTr.firstChild;
   559  						jInner = 0;
   560  						while ( nTd )
   561  						{
   562  							sNodeName = nTd.nodeName.toUpperCase();
   563  							if ( sNodeName == "TD" || sNodeName == "TH" )
   564  							{
   565  								_fnSetCellData( oSettings, iThisIndex, jInner, $.trim(nTd.innerHTML) );
   566  								jInner++;
   567  							}
   568  							nTd = nTd.nextSibling;
   569  						}
   570  					}
   571  					nTr = nTr.nextSibling;
   572  				}
   573  			}
   574  			
   575  			/* Gather in the TD elements of the Table - note that this is basically the same as
   576  			 * fnGetTdNodes, but that function takes account of hidden columns, which we haven't yet
   577  			 * setup!
   578  			 */
   579  			nTrs = _fnGetTrNodes( oSettings );
   580  			nTds = [];
   581  			for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
   582  			{
   583  				nTd = nTrs[i].firstChild;
   584  				while ( nTd )
   585  				{
   586  					sNodeName = nTd.nodeName.toUpperCase();
   587  					if ( sNodeName == "TD" || sNodeName == "TH" )
   588  					{
   589  						nTds.push( nTd );
   590  					}
   591  					nTd = nTd.nextSibling;
   592  				}
   593  			}
   594  			
   595  			/* Now process by column */
   596  			for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
   597  			{
   598  				oCol = oSettings.aoColumns[iColumn];
   599  		
   600  				/* Get the title of the column - unless there is a user set one */
   601  				if ( oCol.sTitle === null )
   602  				{
   603  					oCol.sTitle = oCol.nTh.innerHTML;
   604  				}
   605  				
   606  				var
   607  					bAutoType = oCol._bAutoType,
   608  					bRender = typeof oCol.fnRender === 'function',
   609  					bClass = oCol.sClass !== null,
   610  					bVisible = oCol.bVisible,
   611  					nCell, sThisType, sRendered, sValType;
   612  				
   613  				/* A single loop to rule them all (and be more efficient) */
   614  				if ( bAutoType || bRender || bClass || !bVisible )
   615  				{
   616  					for ( iRow=0, iRows=oSettings.aoData.length ; iRow<iRows ; iRow++ )
   617  					{
   618  						oData = oSettings.aoData[iRow];
   619  						nCell = nTds[ (iRow*iColumns) + iColumn ];
   620  						
   621  						/* Type detection */
   622  						if ( bAutoType && oCol.sType != 'string' )
   623  						{
   624  							sValType = _fnGetCellData( oSettings, iRow, iColumn, 'type' );
   625  							if ( sValType !== '' )
   626  							{
   627  								sThisType = _fnDetectType( sValType );
   628  								if ( oCol.sType === null )
   629  								{
   630  									oCol.sType = sThisType;
   631  								}
   632  								else if ( oCol.sType != sThisType && 
   633  								          oCol.sType != "html" )
   634  								{
   635  									/* String is always the 'fallback' option */
   636  									oCol.sType = 'string';
   637  								}
   638  							}
   639  						}
   640  		
   641  						if ( oCol.mRender )
   642  						{
   643  							// mRender has been defined, so we need to get the value and set it
   644  							nCell.innerHTML = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
   645  						}
   646  						else if ( oCol.mData !== iColumn )
   647  						{
   648  							// If mData is not the same as the column number, then we need to
   649  							// get the dev set value. If it is the column, no point in wasting
   650  							// time setting the value that is already there!
   651  							nCell.innerHTML = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
   652  						}
   653  						
   654  						/* Rendering */
   655  						if ( bRender )
   656  						{
   657  							sRendered = _fnRender( oSettings, iRow, iColumn );
   658  							nCell.innerHTML = sRendered;
   659  							if ( oCol.bUseRendered )
   660  							{
   661  								/* Use the rendered data for filtering / sorting */
   662  								_fnSetCellData( oSettings, iRow, iColumn, sRendered );
   663  							}
   664  						}
   665  						
   666  						/* Classes */
   667  						if ( bClass )
   668  						{
   669  							nCell.className += ' '+oCol.sClass;
   670  						}
   671  						
   672  						/* Column visibility */
   673  						if ( !bVisible )
   674  						{
   675  							oData._anHidden[iColumn] = nCell;
   676  							nCell.parentNode.removeChild( nCell );
   677  						}
   678  						else
   679  						{
   680  							oData._anHidden[iColumn] = null;
   681  						}
   682  		
   683  						if ( oCol.fnCreatedCell )
   684  						{
   685  							oCol.fnCreatedCell.call( oSettings.oInstance,
   686  								nCell, _fnGetCellData( oSettings, iRow, iColumn, 'display' ), oData._aData, iRow, iColumn
   687  							);
   688  						}
   689  					}
   690  				}
   691  			}
   692  		
   693  			/* Row created callbacks */
   694  			if ( oSettings.aoRowCreatedCallback.length !== 0 )
   695  			{
   696  				for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
   697  				{
   698  					oData = oSettings.aoData[i];
   699  					_fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [oData.nTr, oData._aData, i] );
   700  				}
   701  			}
   702  		}
   703  		
   704  		
   705  		/**
   706  		 * Take a TR element and convert it to an index in aoData
   707  		 *  @param {object} oSettings dataTables settings object
   708  		 *  @param {node} n the TR element to find
   709  		 *  @returns {int} index if the node is found, null if not
   710  		 *  @memberof DataTable#oApi
   711  		 */
   712  		function _fnNodeToDataIndex( oSettings, n )
   713  		{
   714  			return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
   715  		}
   716  		
   717  		
   718  		/**
   719  		 * Take a TD element and convert it into a column data index (not the visible index)
   720  		 *  @param {object} oSettings dataTables settings object
   721  		 *  @param {int} iRow The row number the TD/TH can be found in
   722  		 *  @param {node} n The TD/TH element to find
   723  		 *  @returns {int} index if the node is found, -1 if not
   724  		 *  @memberof DataTable#oApi
   725  		 */
   726  		function _fnNodeToColumnIndex( oSettings, iRow, n )
   727  		{
   728  			var anCells = _fnGetTdNodes( oSettings, iRow );
   729  		
   730  			for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
   731  			{
   732  				if ( anCells[i] === n )
   733  				{
   734  					return i;
   735  				}
   736  			}
   737  			return -1;
   738  		}
   739  		
   740  		
   741  		/**
   742  		 * Get an array of data for a given row from the internal data cache
   743  		 *  @param {object} oSettings dataTables settings object
   744  		 *  @param {int} iRow aoData row id
   745  		 *  @param {string} sSpecific data get type ('type' 'filter' 'sort')
   746  		 *  @param {array} aiColumns Array of column indexes to get data from
   747  		 *  @returns {array} Data array
   748  		 *  @memberof DataTable#oApi
   749  		 */
   750  		function _fnGetRowData( oSettings, iRow, sSpecific, aiColumns )
   751  		{
   752  			var out = [];
   753  			for ( var i=0, iLen=aiColumns.length ; i<iLen ; i++ )
   754  			{
   755  				out.push( _fnGetCellData( oSettings, iRow, aiColumns[i], sSpecific ) );
   756  			}
   757  			return out;
   758  		}
   759  		
   760  		
   761  		/**
   762  		 * Get the data for a given cell from the internal cache, taking into account data mapping
   763  		 *  @param {object} oSettings dataTables settings object
   764  		 *  @param {int} iRow aoData row id
   765  		 *  @param {int} iCol Column index
   766  		 *  @param {string} sSpecific data get type ('display', 'type' 'filter' 'sort')
   767  		 *  @returns {*} Cell data
   768  		 *  @memberof DataTable#oApi
   769  		 */
   770  		function _fnGetCellData( oSettings, iRow, iCol, sSpecific )
   771  		{
   772  			var sData;
   773  			var oCol = oSettings.aoColumns[iCol];
   774  			var oData = oSettings.aoData[iRow]._aData;
   775  		
   776  			if ( (sData=oCol.fnGetData( oData, sSpecific )) === undefined )
   777  			{
   778  				if ( oSettings.iDrawError != oSettings.iDraw && oCol.sDefaultContent === null )
   779  				{
   780  					_fnLog( oSettings, 0, "Requested unknown parameter "+
   781  						(typeof oCol.mData=='function' ? '{mData function}' : "'"+oCol.mData+"'")+
   782  						" from the data source for row "+iRow );
   783  					oSettings.iDrawError = oSettings.iDraw;
   784  				}
   785  				return oCol.sDefaultContent;
   786  			}
   787  		
   788  			/* When the data source is null, we can use default column data */
   789  			if ( sData === null && oCol.sDefaultContent !== null )
   790  			{
   791  				sData = oCol.sDefaultContent;
   792  			}
   793  			else if ( typeof sData === 'function' )
   794  			{
   795  				/* If the data source is a function, then we run it and use the return */
   796  				return sData();
   797  			}
   798  		
   799  			if ( sSpecific == 'display' && sData === null )
   800  			{
   801  				return '';
   802  			}
   803  			return sData;
   804  		}
   805  		
   806  		
   807  		/**
   808  		 * Set the value for a specific cell, into the internal data cache
   809  		 *  @param {object} oSettings dataTables settings object
   810  		 *  @param {int} iRow aoData row id
   811  		 *  @param {int} iCol Column index
   812  		 *  @param {*} val Value to set
   813  		 *  @memberof DataTable#oApi
   814  		 */
   815  		function _fnSetCellData( oSettings, iRow, iCol, val )
   816  		{
   817  			var oCol = oSettings.aoColumns[iCol];
   818  			var oData = oSettings.aoData[iRow]._aData;
   819  		
   820  			oCol.fnSetData( oData, val );
   821  		}
   822  		
   823  		
   824  		// Private variable that is used to match array syntax in the data property object
   825  		var __reArray = /\[.*?\]$/;
   826  		
   827  		/**
   828  		 * Return a function that can be used to get data from a source object, taking
   829  		 * into account the ability to use nested objects as a source
   830  		 *  @param {string|int|function} mSource The data source for the object
   831  		 *  @returns {function} Data get function
   832  		 *  @memberof DataTable#oApi
   833  		 */
   834  		function _fnGetObjectDataFn( mSource )
   835  		{
   836  			if ( mSource === null )
   837  			{
   838  				/* Give an empty string for rendering / sorting etc */
   839  				return function (data, type) {
   840  					return null;
   841  				};
   842  			}
   843  			else if ( typeof mSource === 'function' )
   844  			{
   845  				return function (data, type, extra) {
   846  					return mSource( data, type, extra );
   847  				};
   848  			}
   849  			else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || mSource.indexOf('[') !== -1) )
   850  			{
   851  				/* If there is a . in the source string then the data source is in a 
   852  				 * nested object so we loop over the data for each level to get the next
   853  				 * level down. On each loop we test for undefined, and if found immediately
   854  				 * return. This allows entire objects to be missing and sDefaultContent to
   855  				 * be used if defined, rather than throwing an error
   856  				 */
   857  				var fetchData = function (data, type, src) {
   858  					var a = src.split('.');
   859  					var arrayNotation, out, innerSrc;
   860  		
   861  					if ( src !== "" )
   862  					{
   863  						for ( var i=0, iLen=a.length ; i<iLen ; i++ )
   864  						{
   865  							// Check if we are dealing with an array notation request
   866  							arrayNotation = a[i].match(__reArray);
   867  		
   868  							if ( arrayNotation ) {
   869  								a[i] = a[i].replace(__reArray, '');
   870  		
   871  								// Condition allows simply [] to be passed in
   872  								if ( a[i] !== "" ) {
   873  									data = data[ a[i] ];
   874  								}
   875  								out = [];
   876  								
   877  								// Get the remainder of the nested object to get
   878  								a.splice( 0, i+1 );
   879  								innerSrc = a.join('.');
   880  		
   881  								// Traverse each entry in the array getting the properties requested
   882  								for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
   883  									out.push( fetchData( data[j], type, innerSrc ) );
   884  								}
   885  		
   886  								// If a string is given in between the array notation indicators, that
   887  								// is used to join the strings together, otherwise an array is returned
   888  								var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
   889  								data = (join==="") ? out : out.join(join);
   890  		
   891  								// The inner call to fetchData has already traversed through the remainder
   892  								// of the source requested, so we exit from the loop
   893  								break;
   894  							}
   895  		
   896  							if ( data === null || data[ a[i] ] === undefined )
   897  							{
   898  								return undefined;
   899  							}
   900  							data = data[ a[i] ];
   901  						}
   902  					}
   903  		
   904  					return data;
   905  				};
   906  		
   907  				return function (data, type) {
   908  					return fetchData( data, type, mSource );
   909  				};
   910  			}
   911  			else
   912  			{
   913  				/* Array or flat object mapping */
   914  				return function (data, type) {
   915  					return data[mSource];	
   916  				};
   917  			}
   918  		}
   919  		
   920  		
   921  		/**
   922  		 * Return a function that can be used to set data from a source object, taking
   923  		 * into account the ability to use nested objects as a source
   924  		 *  @param {string|int|function} mSource The data source for the object
   925  		 *  @returns {function} Data set function
   926  		 *  @memberof DataTable#oApi
   927  		 */
   928  		function _fnSetObjectDataFn( mSource )
   929  		{
   930  			if ( mSource === null )
   931  			{
   932  				/* Nothing to do when the data source is null */
   933  				return function (data, val) {};
   934  			}
   935  			else if ( typeof mSource === 'function' )
   936  			{
   937  				return function (data, val) {
   938  					mSource( data, 'set', val );
   939  				};
   940  			}
   941  			else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || mSource.indexOf('[') !== -1) )
   942  			{
   943  				/* Like the get, we need to get data from a nested object */
   944  				var setData = function (data, val, src) {
   945  					var a = src.split('.'), b;
   946  					var arrayNotation, o, innerSrc;
   947  		
   948  					for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
   949  					{
   950  						// Check if we are dealing with an array notation request
   951  						arrayNotation = a[i].match(__reArray);
   952  		
   953  						if ( arrayNotation )
   954  						{
   955  							a[i] = a[i].replace(__reArray, '');
   956  							data[ a[i] ] = [];
   957  							
   958  							// Get the remainder of the nested object to set so we can recurse
   959  							b = a.slice();
   960  							b.splice( 0, i+1 );
   961  							innerSrc = b.join('.');
   962  		
   963  							// Traverse each entry in the array setting the properties requested
   964  							for ( var j=0, jLen=val.length ; j<jLen ; j++ )
   965  							{
   966  								o = {};
   967  								setData( o, val[j], innerSrc );
   968  								data[ a[i] ].push( o );
   969  							}
   970  		
   971  							// The inner call to setData has already traversed through the remainder
   972  							// of the source and has set the data, thus we can exit here
   973  							return;
   974  						}
   975  		
   976  						// If the nested object doesn't currently exist - since we are
   977  						// trying to set the value - create it
   978  						if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
   979  						{
   980  							data[ a[i] ] = {};
   981  						}
   982  						data = data[ a[i] ];
   983  					}
   984  		
   985  					// If array notation is used, we just want to strip it and use the property name
   986  					// and assign the value. If it isn't used, then we get the result we want anyway
   987  					data[ a[a.length-1].replace(__reArray, '') ] = val;
   988  				};
   989  		
   990  				return function (data, val) {
   991  					return setData( data, val, mSource );
   992  				};
   993  			}
   994  			else
   995  			{
   996  				/* Array or flat object mapping */
   997  				return function (data, val) {
   998  					data[mSource] = val;	
   999  				};
  1000  			}
  1001  		}
  1002  		
  1003  		
  1004  		/**
  1005  		 * Return an array with the full table data
  1006  		 *  @param {object} oSettings dataTables settings object
  1007  		 *  @returns array {array} aData Master data array
  1008  		 *  @memberof DataTable#oApi
  1009  		 */
  1010  		function _fnGetDataMaster ( oSettings )
  1011  		{
  1012  			var aData = [];
  1013  			var iLen = oSettings.aoData.length;
  1014  			for ( var i=0 ; i<iLen; i++ )
  1015  			{
  1016  				aData.push( oSettings.aoData[i]._aData );
  1017  			}
  1018  			return aData;
  1019  		}
  1020  		
  1021  		
  1022  		/**
  1023  		 * Nuke the table
  1024  		 *  @param {object} oSettings dataTables settings object
  1025  		 *  @memberof DataTable#oApi
  1026  		 */
  1027  		function _fnClearTable( oSettings )
  1028  		{
  1029  			oSettings.aoData.splice( 0, oSettings.aoData.length );
  1030  			oSettings.aiDisplayMaster.splice( 0, oSettings.aiDisplayMaster.length );
  1031  			oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length );
  1032  			_fnCalculateEnd( oSettings );
  1033  		}
  1034  		
  1035  		
  1036  		 /**
  1037  		 * Take an array of integers (index array) and remove a target integer (value - not 
  1038  		 * the key!)
  1039  		 *  @param {array} a Index array to target
  1040  		 *  @param {int} iTarget value to find
  1041  		 *  @memberof DataTable#oApi
  1042  		 */
  1043  		function _fnDeleteIndex( a, iTarget )
  1044  		{
  1045  			var iTargetIndex = -1;
  1046  			
  1047  			for ( var i=0, iLen=a.length ; i<iLen ; i++ )
  1048  			{
  1049  				if ( a[i] == iTarget )
  1050  				{
  1051  					iTargetIndex = i;
  1052  				}
  1053  				else if ( a[i] > iTarget )
  1054  				{
  1055  					a[i]--;
  1056  				}
  1057  			}
  1058  			
  1059  			if ( iTargetIndex != -1 )
  1060  			{
  1061  				a.splice( iTargetIndex, 1 );
  1062  			}
  1063  		}
  1064  		
  1065  		
  1066  		 /**
  1067  		 * Call the developer defined fnRender function for a given cell (row/column) with
  1068  		 * the required parameters and return the result.
  1069  		 *  @param {object} oSettings dataTables settings object
  1070  		 *  @param {int} iRow aoData index for the row
  1071  		 *  @param {int} iCol aoColumns index for the column
  1072  		 *  @returns {*} Return of the developer's fnRender function
  1073  		 *  @memberof DataTable#oApi
  1074  		 */
  1075  		function _fnRender( oSettings, iRow, iCol )
  1076  		{
  1077  			var oCol = oSettings.aoColumns[iCol];
  1078  		
  1079  			return oCol.fnRender( {
  1080  				"iDataRow":    iRow,
  1081  				"iDataColumn": iCol,
  1082  				"oSettings":   oSettings,
  1083  				"aData":       oSettings.aoData[iRow]._aData,
  1084  				"mDataProp":   oCol.mData
  1085  			}, _fnGetCellData(oSettings, iRow, iCol, 'display') );
  1086  		}
  1087  		/**
  1088  		 * Create a new TR element (and it's TD children) for a row
  1089  		 *  @param {object} oSettings dataTables settings object
  1090  		 *  @param {int} iRow Row to consider
  1091  		 *  @memberof DataTable#oApi
  1092  		 */
  1093  		function _fnCreateTr ( oSettings, iRow )
  1094  		{
  1095  			var oData = oSettings.aoData[iRow];
  1096  			var nTd;
  1097  		
  1098  			if ( oData.nTr === null )
  1099  			{
  1100  				oData.nTr = document.createElement('tr');
  1101  		
  1102  				/* Use a private property on the node to allow reserve mapping from the node
  1103  				 * to the aoData array for fast look up
  1104  				 */
  1105  				oData.nTr._DT_RowIndex = iRow;
  1106  		
  1107  				/* Special parameters can be given by the data source to be used on the row */
  1108  				if ( oData._aData.DT_RowId )
  1109  				{
  1110  					oData.nTr.id = oData._aData.DT_RowId;
  1111  				}
  1112  		
  1113  				if ( oData._aData.DT_RowClass )
  1114  				{
  1115  					oData.nTr.className = oData._aData.DT_RowClass;
  1116  				}
  1117  		
  1118  				/* Process each column */
  1119  				for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
  1120  				{
  1121  					var oCol = oSettings.aoColumns[i];
  1122  					nTd = document.createElement( oCol.sCellType );
  1123  		
  1124  					/* Render if needed - if bUseRendered is true then we already have the rendered
  1125  					 * value in the data source - so can just use that
  1126  					 */
  1127  					nTd.innerHTML = (typeof oCol.fnRender === 'function' && (!oCol.bUseRendered || oCol.mData === null)) ?
  1128  						_fnRender( oSettings, iRow, i ) :
  1129  						_fnGetCellData( oSettings, iRow, i, 'display' );
  1130  				
  1131  					/* Add user defined class */
  1132  					if ( oCol.sClass !== null )
  1133  					{
  1134  						nTd.className = oCol.sClass;
  1135  					}
  1136  					
  1137  					if ( oCol.bVisible )
  1138  					{
  1139  						oData.nTr.appendChild( nTd );
  1140  						oData._anHidden[i] = null;
  1141  					}
  1142  					else
  1143  					{
  1144  						oData._anHidden[i] = nTd;
  1145  					}
  1146  		
  1147  					if ( oCol.fnCreatedCell )
  1148  					{
  1149  						oCol.fnCreatedCell.call( oSettings.oInstance,
  1150  							nTd, _fnGetCellData( oSettings, iRow, i, 'display' ), oData._aData, iRow, i
  1151  						);
  1152  					}
  1153  				}
  1154  		
  1155  				_fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [oData.nTr, oData._aData, iRow] );
  1156  			}
  1157  		}
  1158  		
  1159  		
  1160  		/**
  1161  		 * Create the HTML header for the table
  1162  		 *  @param {object} oSettings dataTables settings object
  1163  		 *  @memberof DataTable#oApi
  1164  		 */
  1165  		function _fnBuildHead( oSettings )
  1166  		{
  1167  			var i, nTh, iLen, j, jLen;
  1168  			var iThs = $('th, td', oSettings.nTHead).length;
  1169  			var iCorrector = 0;
  1170  			var jqChildren;
  1171  			
  1172  			/* If there is a header in place - then use it - otherwise it's going to get nuked... */
  1173  			if ( iThs !== 0 )
  1174  			{
  1175  				/* We've got a thead from the DOM, so remove hidden columns and apply width to vis cols */
  1176  				for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
  1177  				{
  1178  					nTh = oSettings.aoColumns[i].nTh;
  1179  					nTh.setAttribute('role', 'columnheader');
  1180  					if ( oSettings.aoColumns[i].bSortable )
  1181  					{
  1182  						nTh.setAttribute('tabindex', oSettings.iTabIndex);
  1183  						nTh.setAttribute('aria-controls', oSettings.sTableId);
  1184  					}
  1185  		
  1186  					if ( oSettings.aoColumns[i].sClass !== null )
  1187  					{
  1188  						$(nTh).addClass( oSettings.aoColumns[i].sClass );
  1189  					}
  1190  					
  1191  					/* Set the title of the column if it is user defined (not what was auto detected) */
  1192  					if ( oSettings.aoColumns[i].sTitle != nTh.innerHTML )
  1193  					{
  1194  						nTh.innerHTML = oSettings.aoColumns[i].sTitle;
  1195  					}
  1196  				}
  1197  			}
  1198  			else
  1199  			{
  1200  				/* We don't have a header in the DOM - so we are going to have to create one */
  1201  				var nTr = document.createElement( "tr" );
  1202  				
  1203  				for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
  1204  				{
  1205  					nTh = oSettings.aoColumns[i].nTh;
  1206  					nTh.innerHTML = oSettings.aoColumns[i].sTitle;
  1207  					nTh.setAttribute('tabindex', '0');
  1208  					
  1209  					if ( oSettings.aoColumns[i].sClass !== null )
  1210  					{
  1211  						$(nTh).addClass( oSettings.aoColumns[i].sClass );
  1212  					}
  1213  					
  1214  					nTr.appendChild( nTh );
  1215  				}
  1216  				$(oSettings.nTHead).html( '' )[0].appendChild( nTr );
  1217  				_fnDetectHeader( oSettings.aoHeader, oSettings.nTHead );
  1218  			}
  1219  			
  1220  			/* ARIA role for the rows */	
  1221  			$(oSettings.nTHead).children('tr').attr('role', 'row');
  1222  			
  1223  			/* Add the extra markup needed by jQuery UI's themes */
  1224  			if ( oSettings.bJUI )
  1225  			{
  1226  				for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
  1227  				{
  1228  					nTh = oSettings.aoColumns[i].nTh;
  1229  					
  1230  					var nDiv = document.createElement('div');
  1231  					nDiv.className = oSettings.oClasses.sSortJUIWrapper;
  1232  					$(nTh).contents().appendTo(nDiv);
  1233  					
  1234  					var nSpan = document.createElement('span');
  1235  					nSpan.className = oSettings.oClasses.sSortIcon;
  1236  					nDiv.appendChild( nSpan );
  1237  					nTh.appendChild( nDiv );
  1238  				}
  1239  			}
  1240  			
  1241  			if ( oSettings.oFeatures.bSort )
  1242  			{
  1243  				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
  1244  				{
  1245  					if ( oSettings.aoColumns[i].bSortable !== false )
  1246  					{
  1247  						_fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i );
  1248  					}
  1249  					else
  1250  					{
  1251  						$(oSettings.aoColumns[i].nTh).addClass( oSettings.oClasses.sSortableNone );
  1252  					}
  1253  				}
  1254  			}
  1255  			
  1256  			/* Deal with the footer - add classes if required */
  1257  			if ( oSettings.oClasses.sFooterTH !== "" )
  1258  			{
  1259  				$(oSettings.nTFoot).children('tr').children('th').addClass( oSettings.oClasses.sFooterTH );
  1260  			}
  1261  			
  1262  			/* Cache the footer elements */
  1263  			if ( oSettings.nTFoot !== null )
  1264  			{
  1265  				var anCells = _fnGetUniqueThs( oSettings, null, oSettings.aoFooter );
  1266  				for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
  1267  				{
  1268  					if ( anCells[i] )
  1269  					{
  1270  						oSettings.aoColumns[i].nTf = anCells[i];
  1271  						if ( oSettings.aoColumns[i].sClass )
  1272  						{
  1273  							$(anCells[i]).addClass( oSettings.aoColumns[i].sClass );
  1274  						}
  1275  					}
  1276  				}
  1277  			}
  1278  		}
  1279  		
  1280  		
  1281  		/**
  1282  		 * Draw the header (or footer) element based on the column visibility states. The
  1283  		 * methodology here is to use the layout array from _fnDetectHeader, modified for
  1284  		 * the instantaneous column visibility, to construct the new layout. The grid is
  1285  		 * traversed over cell at a time in a rows x columns grid fashion, although each 
  1286  		 * cell insert can cover multiple elements in the grid - which is tracks using the
  1287  		 * aApplied array. Cell inserts in the grid will only occur where there isn't
  1288  		 * already a cell in that position.
  1289  		 *  @param {object} oSettings dataTables settings object
  1290  		 *  @param array {objects} aoSource Layout array from _fnDetectHeader
  1291  		 *  @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc, 
  1292  		 *  @memberof DataTable#oApi
  1293  		 */
  1294  		function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
  1295  		{
  1296  			var i, iLen, j, jLen, k, kLen, n, nLocalTr;
  1297  			var aoLocal = [];
  1298  			var aApplied = [];
  1299  			var iColumns = oSettings.aoColumns.length;
  1300  			var iRowspan, iColspan;
  1301  		
  1302  			if (  bIncludeHidden === undefined )
  1303  			{
  1304  				bIncludeHidden = false;
  1305  			}
  1306  		
  1307  			/* Make a copy of the master layout array, but without the visible columns in it */
  1308  			for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
  1309  			{
  1310  				aoLocal[i] = aoSource[i].slice();
  1311  				aoLocal[i].nTr = aoSource[i].nTr;
  1312  		
  1313  				/* Remove any columns which are currently hidden */
  1314  				for ( j=iColumns-1 ; j>=0 ; j-- )
  1315  				{
  1316  					if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
  1317  					{
  1318  						aoLocal[i].splice( j, 1 );
  1319  					}
  1320  				}
  1321  		
  1322  				/* Prep the applied array - it needs an element for each row */
  1323  				aApplied.push( [] );
  1324  			}
  1325  		
  1326  			for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
  1327  			{
  1328  				nLocalTr = aoLocal[i].nTr;
  1329  				
  1330  				/* All cells are going to be replaced, so empty out the row */
  1331  				if ( nLocalTr )
  1332  				{
  1333  					while( (n = nLocalTr.firstChild) )
  1334  					{
  1335  						nLocalTr.removeChild( n );
  1336  					}
  1337  				}
  1338  		
  1339  				for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
  1340  				{
  1341  					iRowspan = 1;
  1342  					iColspan = 1;
  1343  		
  1344  					/* Check to see if there is already a cell (row/colspan) covering our target
  1345  					 * insert point. If there is, then there is nothing to do.
  1346  					 */
  1347  					if ( aApplied[i][j] === undefined )
  1348  					{
  1349  						nLocalTr.appendChild( aoLocal[i][j].cell );
  1350  						aApplied[i][j] = 1;
  1351  		
  1352  						/* Expand the cell to cover as many rows as needed */
  1353  						while ( aoLocal[i+iRowspan] !== undefined &&
  1354  						        aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
  1355  						{
  1356  							aApplied[i+iRowspan][j] = 1;
  1357  							iRowspan++;
  1358  						}
  1359  		
  1360  						/* Expand the cell to cover as many columns as needed */
  1361  						while ( aoLocal[i][j+iColspan] !== undefined &&
  1362  						        aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
  1363  						{
  1364  							/* Must update the applied array over the rows for the columns */
  1365  							for ( k=0 ; k<iRowspan ; k++ )
  1366  							{
  1367  								aApplied[i+k][j+iColspan] = 1;
  1368  							}
  1369  							iColspan++;
  1370  						}
  1371  		
  1372  						/* Do the actual expansion in the DOM */
  1373  						aoLocal[i][j].cell.rowSpan = iRowspan;
  1374  						aoLocal[i][j].cell.colSpan = iColspan;
  1375  					}
  1376  				}
  1377  			}
  1378  		}
  1379  		
  1380  		
  1381  		/**
  1382  		 * Insert the required TR nodes into the table for display
  1383  		 *  @param {object} oSettings dataTables settings object
  1384  		 *  @memberof DataTable#oApi
  1385  		 */
  1386  		function _fnDraw( oSettings )
  1387  		{
  1388  			/* Provide a pre-callback function which can be used to cancel the draw is false is returned */
  1389  			var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
  1390  			if ( $.inArray( false, aPreDraw ) !== -1 )
  1391  			{
  1392  				_fnProcessingDisplay( oSettings, false );
  1393  				return;
  1394  			}
  1395  			
  1396  			var i, iLen, n;
  1397  			var anRows = [];
  1398  			var iRowCount = 0;
  1399  			var iStripes = oSettings.asStripeClasses.length;
  1400  			var iOpenRows = oSettings.aoOpenRows.length;
  1401  			
  1402  			oSettings.bDrawing = true;
  1403  			
  1404  			/* Check and see if we have an initial draw position from state saving */
  1405  			if ( oSettings.iInitDisplayStart !== undefined && oSettings.iInitDisplayStart != -1 )
  1406  			{
  1407  				if ( oSettings.oFeatures.bServerSide )
  1408  				{
  1409  					oSettings._iDisplayStart = oSettings.iInitDisplayStart;
  1410  				}
  1411  				else
  1412  				{
  1413  					oSettings._iDisplayStart = (oSettings.iInitDisplayStart >= oSettings.fnRecordsDisplay()) ?
  1414  						0 : oSettings.iInitDisplayStart;
  1415  				}
  1416  				oSettings.iInitDisplayStart = -1;
  1417  				_fnCalculateEnd( oSettings );
  1418  			}
  1419  			
  1420  			/* Server-side processing draw intercept */
  1421  			if ( oSettings.bDeferLoading )
  1422  			{
  1423  				oSettings.bDeferLoading = false;
  1424  				oSettings.iDraw++;
  1425  			}
  1426  			else if ( !oSettings.oFeatures.bServerSide )
  1427  			{
  1428  				oSettings.iDraw++;
  1429  			}
  1430  			else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
  1431  			{
  1432  				return;
  1433  			}
  1434  			
  1435  			if ( oSettings.aiDisplay.length !== 0 )
  1436  			{
  1437  				var iStart = oSettings._iDisplayStart;
  1438  				var iEnd = oSettings._iDisplayEnd;
  1439  				
  1440  				if ( oSettings.oFeatures.bServerSide )
  1441  				{
  1442  					iStart = 0;
  1443  					iEnd = oSettings.aoData.length;
  1444  				}
  1445  				
  1446  				for ( var j=iStart ; j<iEnd ; j++ )
  1447  				{
  1448  					var aoData = oSettings.aoData[ oSettings.aiDisplay[j] ];
  1449  					if ( aoData.nTr === null )
  1450  					{
  1451  						_fnCreateTr( oSettings, oSettings.aiDisplay[j] );
  1452  					}
  1453  		
  1454  					var nRow = aoData.nTr;
  1455  					
  1456  					/* Remove the old striping classes and then add the new one */
  1457  					if ( iStripes !== 0 )
  1458  					{
  1459  						var sStripe = oSettings.asStripeClasses[ iRowCount % iStripes ];
  1460  						if ( aoData._sRowStripe != sStripe )
  1461  						{
  1462  							$(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
  1463  							aoData._sRowStripe = sStripe;
  1464  						}
  1465  					}
  1466  					
  1467  					/* Row callback functions - might want to manipulate the row */
  1468  					_fnCallbackFire( oSettings, 'aoRowCallback', null, 
  1469  						[nRow, oSettings.aoData[ oSettings.aiDisplay[j] ]._aData, iRowCount, j] );
  1470  					
  1471  					anRows.push( nRow );
  1472  					iRowCount++;
  1473  					
  1474  					/* If there is an open row - and it is attached to this parent - attach it on redraw */
  1475  					if ( iOpenRows !== 0 )
  1476  					{
  1477  						for ( var k=0 ; k<iOpenRows ; k++ )
  1478  						{
  1479  							if ( nRow == oSettings.aoOpenRows[k].nParent )
  1480  							{
  1481  								anRows.push( oSettings.aoOpenRows[k].nTr );
  1482  								break;
  1483  							}
  1484  						}
  1485  					}
  1486  				}
  1487  			}
  1488  			else
  1489  			{
  1490  				/* Table is empty - create a row with an empty message in it */
  1491  				anRows[ 0 ] = document.createElement( 'tr' );
  1492  				
  1493  				if ( oSettings.asStripeClasses[0] )
  1494  				{
  1495  					anRows[ 0 ].className = oSettings.asStripeClasses[0];
  1496  				}
  1497  		
  1498  				var oLang = oSettings.oLanguage;
  1499  				var sZero = oLang.sZeroRecords;
  1500  				if ( oSettings.iDraw == 1 && oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide )
  1501  				{
  1502  					sZero = oLang.sLoadingRecords;
  1503  				}
  1504  				else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
  1505  				{
  1506  					sZero = oLang.sEmptyTable;
  1507  				}
  1508  		
  1509  				var nTd = document.createElement( 'td' );
  1510  				nTd.setAttribute( 'valign', "top" );
  1511  				nTd.colSpan = _fnVisbleColumns( oSettings );
  1512  				nTd.className = oSettings.oClasses.sRowEmpty;
  1513  				nTd.innerHTML = _fnInfoMacros( oSettings, sZero );
  1514  				
  1515  				anRows[ iRowCount ].appendChild( nTd );
  1516  			}
  1517  			
  1518  			/* Header and footer callbacks */
  1519  			_fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], 
  1520  				_fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), oSettings.aiDisplay ] );
  1521  			
  1522  			_fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], 
  1523  				_fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), oSettings.aiDisplay ] );
  1524  			
  1525  			/* 
  1526  			 * Need to remove any old row from the display - note we can't just empty the tbody using
  1527  			 * $().html('') since this will unbind the jQuery event handlers (even although the node 
  1528  			 * still exists!) - equally we can't use innerHTML, since IE throws an exception.
  1529  			 */
  1530  			var
  1531  				nAddFrag = document.createDocumentFragment(),
  1532  				nRemoveFrag = document.createDocumentFragment(),
  1533  				nBodyPar, nTrs;
  1534  			
  1535  			if ( oSettings.nTBody )
  1536  			{
  1537  				nBodyPar = oSettings.nTBody.parentNode;
  1538  				nRemoveFrag.appendChild( oSettings.nTBody );
  1539  				
  1540  				/* When doing infinite scrolling, only remove child rows when sorting, filtering or start
  1541  				 * up. When not infinite scroll, always do it.
  1542  				 */
  1543  				if ( !oSettings.oScroll.bInfinite || !oSettings._bInitComplete ||
  1544  				 	oSettings.bSorted || oSettings.bFiltered )
  1545  				{
  1546  					while( (n = oSettings.nTBody.firstChild) )
  1547  					{
  1548  						oSettings.nTBody.removeChild( n );
  1549  					}
  1550  				}
  1551  				
  1552  				/* Put the draw table into the dom */
  1553  				for ( i=0, iLen=anRows.length ; i<iLen ; i++ )
  1554  				{
  1555  					nAddFrag.appendChild( anRows[i] );
  1556  				}
  1557  				
  1558  				oSettings.nTBody.appendChild( nAddFrag );
  1559  				if ( nBodyPar !== null )
  1560  				{
  1561  					nBodyPar.appendChild( oSettings.nTBody );
  1562  				}
  1563  			}
  1564  			
  1565  			/* Call all required callback functions for the end of a draw */
  1566  			_fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
  1567  			
  1568  			/* Draw is complete, sorting and filtering must be as well */
  1569  			oSettings.bSorted = false;
  1570  			oSettings.bFiltered = false;
  1571  			oSettings.bDrawing = false;
  1572  			
  1573  			if ( oSettings.oFeatures.bServerSide )
  1574  			{
  1575  				_fnProcessingDisplay( oSettings, false );
  1576  				if ( !oSettings._bInitComplete )
  1577  				{
  1578  					_fnInitComplete( oSettings );
  1579  				}
  1580  			}
  1581  		}
  1582  		
  1583  		
  1584  		/**
  1585  		 * Redraw the table - taking account of the various features which are enabled
  1586  		 *  @param {object} oSettings dataTables settings object
  1587  		 *  @memberof DataTable#oApi
  1588  		 */
  1589  		function _fnReDraw( oSettings )
  1590  		{
  1591  			if ( oSettings.oFeatures.bSort )
  1592  			{
  1593  				/* Sorting will refilter and draw for us */
  1594  				_fnSort( oSettings, oSettings.oPreviousSearch );
  1595  			}
  1596  			else if ( oSettings.oFeatures.bFilter )
  1597  			{
  1598  				/* Filtering will redraw for us */
  1599  				_fnFilterComplete( oSettings, oSettings.oPreviousSearch );
  1600  			}
  1601  			else
  1602  			{
  1603  				_fnCalculateEnd( oSettings );
  1604  				_fnDraw( oSettings );
  1605  			}
  1606  		}
  1607  		
  1608  		
  1609  		/**
  1610  		 * Add the options to the page HTML for the table
  1611  		 *  @param {object} oSettings dataTables settings object
  1612  		 *  @memberof DataTable#oApi
  1613  		 */
  1614  		function _fnAddOptionsHtml ( oSettings )
  1615  		{
  1616  			/*
  1617  			 * Create a temporary, empty, div which we can later on replace with what we have generated
  1618  			 * we do it this way to rendering the 'options' html offline - speed :-)
  1619  			 */
  1620  			var nHolding = $('<div></div>')[0];
  1621  			oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable );
  1622  			
  1623  			/* 
  1624  			 * All DataTables are wrapped in a div
  1625  			 */
  1626  			oSettings.nTableWrapper = $('<div id="'+oSettings.sTableId+'_wrapper" class="'+oSettings.oClasses.sWrapper+'" role="grid"></div>')[0];
  1627  			oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
  1628  		
  1629  			/* Track where we want to insert the option */
  1630  			var nInsertNode = oSettings.nTableWrapper;
  1631  			
  1632  			/* Loop over the user set positioning and place the elements as needed */
  1633  			var aDom = oSettings.sDom.split('');
  1634  			var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j;
  1635  			for ( var i=0 ; i<aDom.length ; i++ )
  1636  			{
  1637  				iPushFeature = 0;
  1638  				cOption = aDom[i];
  1639  				
  1640  				if ( cOption == '<' )
  1641  				{
  1642  					/* New container div */
  1643  					nNewNode = $('<div></div>')[0];
  1644  					
  1645  					/* Check to see if we should append an id and/or a class name to the container */
  1646  					cNext = aDom[i+1];
  1647  					if ( cNext == "'" || cNext == '"' )
  1648  					{
  1649  						sAttr = "";
  1650  						j = 2;
  1651  						while ( aDom[i+j] != cNext )
  1652  						{
  1653  							sAttr += aDom[i+j];
  1654  							j++;
  1655  						}
  1656  						
  1657  						/* Replace jQuery UI constants */
  1658  						if ( sAttr == "H" )
  1659  						{
  1660  							sAttr = oSettings.oClasses.sJUIHeader;
  1661  						}
  1662  						else if ( sAttr == "F" )
  1663  						{
  1664  							sAttr = oSettings.oClasses.sJUIFooter;
  1665  						}
  1666  						
  1667  						/* The attribute can be in the format of "#id.class", "#id" or "class" This logic
  1668  						 * breaks the string into parts and applies them as needed
  1669  						 */
  1670  						if ( sAttr.indexOf('.') != -1 )
  1671  						{
  1672  							var aSplit = sAttr.split('.');
  1673  							nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
  1674  							nNewNode.className = aSplit[1];
  1675  						}
  1676  						else if ( sAttr.charAt(0) == "#" )
  1677  						{
  1678  							nNewNode.id = sAttr.substr(1, sAttr.length-1);
  1679  						}
  1680  						else
  1681  						{
  1682  							nNewNode.className = sAttr;
  1683  						}
  1684  						
  1685  						i += j; /* Move along the position array */
  1686  					}
  1687  					
  1688  					nInsertNode.appendChild( nNewNode );
  1689  					nInsertNode = nNewNode;
  1690  				}
  1691  				else if ( cOption == '>' )
  1692  				{
  1693  					/* End container div */
  1694  					nInsertNode = nInsertNode.parentNode;
  1695  				}
  1696  				else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange )
  1697  				{
  1698  					/* Length */
  1699  					nTmp = _fnFeatureHtmlLength( oSettings );
  1700  					iPushFeature = 1;
  1701  				}
  1702  				else if ( cOption == 'f' && oSettings.oFeatures.bFilter )
  1703  				{
  1704  					/* Filter */
  1705  					nTmp = _fnFeatureHtmlFilter( oSettings );
  1706  					iPushFeature = 1;
  1707  				}
  1708  				else if ( cOption == 'r' && oSettings.oFeatures.bProcessing )
  1709  				{
  1710  					/* pRocessing */
  1711  					nTmp = _fnFeatureHtmlProcessing( oSettings );
  1712  					iPushFeature = 1;
  1713  				}
  1714  				else if ( cOption == 't' )
  1715  				{
  1716  					/* Table */
  1717  					nTmp = _fnFeatureHtmlTable( oSettings );
  1718  					iPushFeature = 1;
  1719  				}
  1720  				else if ( cOption ==  'i' && oSettings.oFeatures.bInfo )
  1721  				{
  1722  					/* Info */
  1723  					nTmp = _fnFeatureHtmlInfo( oSettings );
  1724  					iPushFeature = 1;
  1725  				}
  1726  				else if ( cOption == 'p' && oSettings.oFeatures.bPaginate )
  1727  				{
  1728  					/* Pagination */
  1729  					nTmp = _fnFeatureHtmlPaginate( oSettings );
  1730  					iPushFeature = 1;
  1731  				}
  1732  				else if ( DataTable.ext.aoFeatures.length !== 0 )
  1733  				{
  1734  					/* Plug-in features */
  1735  					var aoFeatures = DataTable.ext.aoFeatures;
  1736  					for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
  1737  					{
  1738  						if ( cOption == aoFeatures[k].cFeature )
  1739  						{
  1740  							nTmp = aoFeatures[k].fnInit( oSettings );
  1741  							if ( nTmp )
  1742  							{
  1743  								iPushFeature = 1;
  1744  							}
  1745  							break;
  1746  						}
  1747  					}
  1748  				}
  1749  				
  1750  				/* Add to the 2D features array */
  1751  				if ( iPushFeature == 1 && nTmp !== null )
  1752  				{
  1753  					if ( typeof oSettings.aanFeatures[cOption] !== 'object' )
  1754  					{
  1755  						oSettings.aanFeatures[cOption] = [];
  1756  					}
  1757  					oSettings.aanFeatures[cOption].push( nTmp );
  1758  					nInsertNode.appendChild( nTmp );
  1759  				}
  1760  			}
  1761  			
  1762  			/* Built our DOM structure - replace the holding div with what we want */
  1763  			nHolding.parentNode.replaceChild( oSettings.nTableWrapper, nHolding );
  1764  		}
  1765  		
  1766  		
  1767  		/**
  1768  		 * Use the DOM source to create up an array of header cells. The idea here is to
  1769  		 * create a layout grid (array) of rows x columns, which contains a reference
  1770  		 * to the cell that that point in the grid (regardless of col/rowspan), such that
  1771  		 * any column / row could be removed and the new grid constructed
  1772  		 *  @param array {object} aLayout Array to store the calculated layout in
  1773  		 *  @param {node} nThead The header/footer element for the table
  1774  		 *  @memberof DataTable#oApi
  1775  		 */
  1776  		function _fnDetectHeader ( aLayout, nThead )
  1777  		{
  1778  			var nTrs = $(nThead).children('tr');
  1779  			var nTr, nCell;
  1780  			var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
  1781  			var bUnique;
  1782  			var fnShiftCol = function ( a, i, j ) {
  1783  				var k = a[i];
  1784  		                while ( k[j] ) {
  1785  					j++;
  1786  				}
  1787  				return j;
  1788  			};
  1789  		
  1790  			aLayout.splice( 0, aLayout.length );
  1791  			
  1792  			/* We know how many rows there are in the layout - so prep it */
  1793  			for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
  1794  			{
  1795  				aLayout.push( [] );
  1796  			}
  1797  			
  1798  			/* Calculate a layout array */
  1799  			for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
  1800  			{
  1801  				nTr = nTrs[i];
  1802  				iColumn = 0;
  1803  				
  1804  				/* For every cell in the row... */
  1805  				nCell = nTr.firstChild;
  1806  				while ( nCell ) {
  1807  					if ( nCell.nodeName.toUpperCase() == "TD" ||
  1808  					     nCell.nodeName.toUpperCase() == "TH" )
  1809  					{
  1810  						/* Get the col and rowspan attributes from the DOM and sanitise them */
  1811  						iColspan = nCell.getAttribute('colspan') * 1;
  1812  						iRowspan = nCell.getAttribute('rowspan') * 1;
  1813  						iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
  1814  						iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
  1815  		
  1816  						/* There might be colspan cells already in this row, so shift our target 
  1817  						 * accordingly
  1818  						 */
  1819  						iColShifted = fnShiftCol( aLayout, i, iColumn );
  1820  						
  1821  						/* Cache calculation for unique columns */
  1822  						bUnique = iColspan === 1 ? true : false;
  1823  						
  1824  						/* If there is col / rowspan, copy the information into the layout grid */
  1825  						for ( l=0 ; l<iColspan ; l++ )
  1826  						{
  1827  							for ( k=0 ; k<iRowspan ; k++ )
  1828  							{
  1829  								aLayout[i+k][iColShifted+l] = {
  1830  									"cell": nCell,
  1831  									"unique": bUnique
  1832  								};
  1833  								aLayout[i+k].nTr = nTr;
  1834  							}
  1835  						}
  1836  					}
  1837  					nCell = nCell.nextSibling;
  1838  				}
  1839  			}
  1840  		}
  1841  		
  1842  		
  1843  		/**
  1844  		 * Get an array of unique th elements, one for each column
  1845  		 *  @param {object} oSettings dataTables settings object
  1846  		 *  @param {node} nHeader automatically detect the layout from this node - optional
  1847  		 *  @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
  1848  		 *  @returns array {node} aReturn list of unique th's
  1849  		 *  @memberof DataTable#oApi
  1850  		 */
  1851  		function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
  1852  		{
  1853  			var aReturn = [];
  1854  			if ( !aLayout )
  1855  			{
  1856  				aLayout = oSettings.aoHeader;
  1857  				if ( nHeader )
  1858  				{
  1859  					aLayout = [];
  1860  					_fnDetectHeader( aLayout, nHeader );
  1861  				}
  1862  			}
  1863  		
  1864  			for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
  1865  			{
  1866  				for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
  1867  				{
  1868  					if ( aLayout[i][j].unique && 
  1869  						 (!aReturn[j] || !oSettings.bSortCellsTop) )
  1870  					{
  1871  						aReturn[j] = aLayout[i][j].cell;
  1872  					}
  1873  				}
  1874  			}
  1875  			
  1876  			return aReturn;
  1877  		}
  1878  		
  1879  		
  1880  		
  1881  		/**
  1882  		 * Update the table using an Ajax call
  1883  		 *  @param {object} oSettings dataTables settings object
  1884  		 *  @returns {boolean} Block the table drawing or not
  1885  		 *  @memberof DataTable#oApi
  1886  		 */
  1887  		function _fnAjaxUpdate( oSettings )
  1888  		{
  1889  			if ( oSettings.bAjaxDataGet )
  1890  			{
  1891  				oSettings.iDraw++;
  1892  				_fnProcessingDisplay( oSettings, true );
  1893  				var iColumns = oSettings.aoColumns.length;
  1894  				var aoData = _fnAjaxParameters( oSettings );
  1895  				_fnServerParams( oSettings, aoData );
  1896  				
  1897  				oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aoData,
  1898  					function(json) {
  1899  						_fnAjaxUpdateDraw( oSettings, json );
  1900  					}, oSettings );
  1901  				return false;
  1902  			}
  1903  			else
  1904  			{
  1905  				return true;
  1906  			}
  1907  		}
  1908  		
  1909  		
  1910  		/**
  1911  		 * Build up the parameters in an object needed for a server-side processing request
  1912  		 *  @param {object} oSettings dataTables settings object
  1913  		 *  @returns {bool} block the table drawing or not
  1914  		 *  @memberof DataTable#oApi
  1915  		 */
  1916  		function _fnAjaxParameters( oSettings )
  1917  		{
  1918  			var iColumns = oSettings.aoColumns.length;
  1919  			var aoData = [], mDataProp, aaSort, aDataSort;
  1920  			var i, j;
  1921  			
  1922  			aoData.push( { "name": "sEcho",          "value": oSettings.iDraw } );
  1923  			aoData.push( { "name": "iColumns",       "value": iColumns } );
  1924  			aoData.push( { "name": "sColumns",       "value": _fnColumnOrdering(oSettings) } );
  1925  			aoData.push( { "name": "iDisplayStart",  "value": oSettings._iDisplayStart } );
  1926  			aoData.push( { "name": "iDisplayLength", "value": oSettings.oFeatures.bPaginate !== false ?
  1927  				oSettings._iDisplayLength : -1 } );
  1928  				
  1929  			for ( i=0 ; i<iColumns ; i++ )
  1930  			{
  1931  			  mDataProp = oSettings.aoColumns[i].mData;
  1932  				aoData.push( { "name": "mDataProp_"+i, "value": typeof(mDataProp)==="function" ? 'function' : mDataProp } );
  1933  			}
  1934  			
  1935  			/* Filtering */
  1936  			if ( oSettings.oFeatures.bFilter !== false )
  1937  			{
  1938  				aoData.push( { "name": "sSearch", "value": oSettings.oPreviousSearch.sSearch } );
  1939  				aoData.push( { "name": "bRegex",  "value": oSettings.oPreviousSearch.bRegex } );
  1940  				for ( i=0 ; i<iColumns ; i++ )
  1941  				{
  1942  					aoData.push( { "name": "sSearch_"+i,     "value": oSettings.aoPreSearchCols[i].sSearch } );
  1943  					aoData.push( { "name": "bRegex_"+i,      "value": oSettings.aoPreSearchCols[i].bRegex } );
  1944  					aoData.push( { "name": "bSearchable_"+i, "value": oSettings.aoColumns[i].bSearchable } );
  1945  				}
  1946  			}
  1947  			
  1948  			/* Sorting */
  1949  			if ( oSettings.oFeatures.bSort !== false )
  1950  			{
  1951  				var iCounter = 0;
  1952  		
  1953  				aaSort = ( oSettings.aaSortingFixed !== null ) ?
  1954  					oSettings.aaSortingFixed.concat( oSettings.aaSorting ) :
  1955  					oSettings.aaSorting.slice();
  1956  				
  1957  				for ( i=0 ; i<aaSort.length ; i++ )
  1958  				{
  1959  					aDataSort = oSettings.aoColumns[ aaSort[i][0] ].aDataSort;
  1960  					
  1961  					for ( j=0 ; j<aDataSort.length ; j++ )
  1962  					{
  1963  						aoData.push( { "name": "iSortCol_"+iCounter,  "value": aDataSort[j] } );
  1964  						aoData.push( { "name": "sSortDir_"+iCounter,  "value": aaSort[i][1] } );
  1965  						iCounter++;
  1966  					}
  1967  				}
  1968  				aoData.push( { "name": "iSortingCols",   "value": iCounter } );
  1969  				
  1970  				for ( i=0 ; i<iColumns ; i++ )
  1971  				{
  1972  					aoData.push( { "name": "bSortable_"+i,  "value": oSettings.aoColumns[i].bSortable } );
  1973  				}
  1974  			}
  1975  			
  1976  			return aoData;
  1977  		}
  1978  		
  1979  		
  1980  		/**
  1981  		 * Add Ajax parameters from plug-ins
  1982  		 *  @param {object} oSettings dataTables settings object
  1983  		 *  @param array {objects} aoData name/value pairs to send to the server
  1984  		 *  @memberof DataTable#oApi
  1985  		 */
  1986  		function _fnServerParams( oSettings, aoData )
  1987  		{
  1988  			_fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [aoData] );
  1989  		}
  1990  		
  1991  		
  1992  		/**
  1993  		 * Data the data from the server (nuking the old) and redraw the table
  1994  		 *  @param {object} oSettings dataTables settings object
  1995  		 *  @param {object} json json data return from the server.
  1996  		 *  @param {string} json.sEcho Tracking flag for DataTables to match requests
  1997  		 *  @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
  1998  		 *  @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
  1999  		 *  @param {array} json.aaData The data to display on this page
  2000  		 *  @param {string} [json.sColumns] Column ordering (sName, comma separated)
  2001  		 *  @memberof DataTable#oApi
  2002  		 */
  2003  		function _fnAjaxUpdateDraw ( oSettings, json )
  2004  		{
  2005  			if ( json.sEcho !== undefined )
  2006  			{
  2007  				/* Protect against old returns over-writing a new one. Possible when you get
  2008  				 * very fast interaction, and later queries are completed much faster
  2009  				 */
  2010  				if ( json.sEcho*1 < oSettings.iDraw )
  2011  				{
  2012  					return;
  2013  				}
  2014  				else
  2015  				{
  2016  					oSettings.iDraw = json.sEcho * 1;
  2017  				}
  2018  			}
  2019  			
  2020  			if ( !oSettings.oScroll.bInfinite ||
  2021  				   (oSettings.oScroll.bInfinite && (oSettings.bSorted || oSettings.bFiltered)) )
  2022  			{
  2023  				_fnClearTable( oSettings );
  2024  			}
  2025  			oSettings._iRecordsTotal = parseInt(json.iTotalRecords, 10);
  2026  			oSettings._iRecordsDisplay = parseInt(json.iTotalDisplayRecords, 10);
  2027  			
  2028  			/* Determine if reordering is required */
  2029  			var sOrdering = _fnColumnOrdering(oSettings);
  2030  			var bReOrder = (json.sColumns !== undefined && sOrdering !== "" && json.sColumns != sOrdering );
  2031  			var aiIndex;
  2032  			if ( bReOrder )
  2033  			{
  2034  				aiIndex = _fnReOrderIndex( oSettings, json.sColumns );
  2035  			}
  2036  			
  2037  			var aData = _fnGetObjectDataFn( oSettings.sAjaxDataProp )( json );
  2038  			for ( var i=0, iLen=aData.length ; i<iLen ; i++ )
  2039  			{
  2040  				if ( bReOrder )
  2041  				{
  2042  					/* If we need to re-order, then create a new array with the correct order and add it */
  2043  					var aDataSorted = [];
  2044  					for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ )
  2045  					{
  2046  						aDataSorted.push( aData[i][ aiIndex[j] ] );
  2047  					}
  2048  					_fnAddData( oSettings, aDataSorted );
  2049  				}
  2050  				else
  2051  				{
  2052  					/* No re-order required, sever got it "right" - just straight add */
  2053  					_fnAddData( oSettings, aData[i] );
  2054  				}
  2055  			}
  2056  			oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
  2057  			
  2058  			oSettings.bAjaxDataGet = false;
  2059  			_fnDraw( oSettings );
  2060  			oSettings.bAjaxDataGet = true;
  2061  			_fnProcessingDisplay( oSettings, false );
  2062  		}
  2063  		
  2064  		
  2065  		
  2066  		/**
  2067  		 * Generate the node required for filtering text
  2068  		 *  @returns {node} Filter control element
  2069  		 *  @param {object} oSettings dataTables settings object
  2070  		 *  @memberof DataTable#oApi
  2071  		 */
  2072  		function _fnFeatureHtmlFilter ( oSettings )
  2073  		{
  2074  			var oPreviousSearch = oSettings.oPreviousSearch;
  2075  			
  2076  			var sSearchStr = oSettings.oLanguage.sSearch;
  2077  			sSearchStr = (sSearchStr.indexOf('_INPUT_') !== -1) ?
  2078  			  sSearchStr.replace('_INPUT_', '<input type="text" />') :
  2079  			  sSearchStr==="" ? '<input type="text" />' : sSearchStr+' <input type="text" />';
  2080  			
  2081  			var nFilter = document.createElement( 'div' );
  2082  			nFilter.className = oSettings.oClasses.sFilter;
  2083  			nFilter.innerHTML = '<label>'+sSearchStr+'</label>';
  2084  			if ( !oSettings.aanFeatures.f )
  2085  			{
  2086  				nFilter.id = oSettings.sTableId+'_filter';
  2087  			}
  2088  			
  2089  			var jqFilter = $('input[type="text"]', nFilter);
  2090  		
  2091  			// Store a reference to the input element, so other input elements could be
  2092  			// added to the filter wrapper if needed (submit button for example)
  2093  			nFilter._DT_Input = jqFilter[0];
  2094  		
  2095  			jqFilter.val( oPreviousSearch.sSearch.replace('"','&quot;') );
  2096  			jqFilter.bind( 'keyup.DT', function(e) {
  2097  				/* Update all other filter input elements for the new display */
  2098  				var n = oSettings.aanFeatures.f;
  2099  				var val = this.value==="" ? "" : this.value; // mental IE8 fix :-(
  2100  		
  2101  				for ( var i=0, iLen=n.length ; i<iLen ; i++ )
  2102  				{
  2103  					if ( n[i] != $(this).parents('div.dataTables_filter')[0] )
  2104  					{
  2105  						$(n[i]._DT_Input).val( val );
  2106  					}
  2107  				}
  2108  				
  2109  				/* Now do the filter */
  2110  				if ( val != oPreviousSearch.sSearch )
  2111  				{
  2112  					_fnFilterComplete( oSettings, { 
  2113  						"sSearch": val, 
  2114  						"bRegex": oPreviousSearch.bRegex,
  2115  						"bSmart": oPreviousSearch.bSmart ,
  2116  						"bCaseInsensitive": oPreviousSearch.bCaseInsensitive 
  2117  					} );
  2118  				}
  2119  			} );
  2120  		
  2121  			jqFilter
  2122  				.attr('aria-controls', oSettings.sTableId)
  2123  				.bind( 'keypress.DT', function(e) {
  2124  					/* Prevent form submission */
  2125  					if ( e.keyCode == 13 )
  2126  					{
  2127  						return false;
  2128  					}
  2129  				}
  2130  			);
  2131  			
  2132  			return nFilter;
  2133  		}
  2134  		
  2135  		
  2136  		/**
  2137  		 * Filter the table using both the global filter and column based filtering
  2138  		 *  @param {object} oSettings dataTables settings object
  2139  		 *  @param {object} oSearch search information
  2140  		 *  @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
  2141  		 *  @memberof DataTable#oApi
  2142  		 */
  2143  		function _fnFilterComplete ( oSettings, oInput, iForce )
  2144  		{
  2145  			var oPrevSearch = oSettings.oPreviousSearch;
  2146  			var aoPrevSearch = oSettings.aoPreSearchCols;
  2147  			var fnSaveFilter = function ( oFilter ) {
  2148  				/* Save the filtering values */
  2149  				oPrevSearch.sSearch = oFilter.sSearch;
  2150  				oPrevSearch.bRegex = oFilter.bRegex;
  2151  				oPrevSearch.bSmart = oFilter.bSmart;
  2152  				oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
  2153  			};
  2154  		
  2155  			/* In server-side processing all filtering is done by the server, so no point hanging around here */
  2156  			if ( !oSettings.oFeatures.bServerSide )
  2157  			{
  2158  				/* Global filter */
  2159  				_fnFilter( oSettings, oInput.sSearch, iForce, oInput.bRegex, oInput.bSmart, oInput.bCaseInsensitive );
  2160  				fnSaveFilter( oInput );
  2161  		
  2162  				/* Now do the individual column filter */
  2163  				for ( var i=0 ; i<oSettings.aoPreSearchCols.length ; i++ )
  2164  				{
  2165  					_fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, aoPrevSearch[i].bRegex, 
  2166  						aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
  2167  				}
  2168  				
  2169  				/* Custom filtering */
  2170  				_fnFilterCustom( oSettings );
  2171  			}
  2172  			else
  2173  			{
  2174  				fnSaveFilter( oInput );
  2175  			}
  2176  			
  2177  			/* Tell the draw function we have been filtering */
  2178  			oSettings.bFiltered = true;
  2179  			$(oSettings.oInstance).trigger('filter', oSettings);
  2180  			
  2181  			/* Redraw the table */
  2182  			oSettings._iDisplayStart = 0;
  2183  			_fnCalculateEnd( oSettings );
  2184  			_fnDraw( oSettings );
  2185  			
  2186  			/* Rebuild search array 'offline' */
  2187  			_fnBuildSearchArray( oSettings, 0 );
  2188  		}
  2189  		
  2190  		
  2191  		/**
  2192  		 * Apply custom filtering functions
  2193  		 *  @param {object} oSettings dataTables settings object
  2194  		 *  @memberof DataTable#oApi
  2195  		 */
  2196  		function _fnFilterCustom( oSettings )
  2197  		{
  2198  			var afnFilters = DataTable.ext.afnFiltering;
  2199  			var aiFilterColumns = _fnGetColumns( oSettings, 'bSearchable' );
  2200  		
  2201  			for ( var i=0, iLen=afnFilters.length ; i<iLen ; i++ )
  2202  			{
  2203  				var iCorrector = 0;
  2204  				for ( var j=0, jLen=oSettings.aiDisplay.length ; j<jLen ; j++ )
  2205  				{
  2206  					var iDisIndex = oSettings.aiDisplay[j-iCorrector];
  2207  					var bTest = afnFilters[i](
  2208  						oSettings,
  2209  						_fnGetRowData( oSettings, iDisIndex, 'filter', aiFilterColumns ),
  2210  						iDisIndex
  2211  					);
  2212  					
  2213  					/* Check if we should use this row based on the filtering function */
  2214  					if ( !bTest )
  2215  					{
  2216  						oSettings.aiDisplay.splice( j-iCorrector, 1 );
  2217  						iCorrector++;
  2218  					}
  2219  				}
  2220  			}
  2221  		}
  2222  		
  2223  		
  2224  		/**
  2225  		 * Filter the table on a per-column basis
  2226  		 *  @param {object} oSettings dataTables settings object
  2227  		 *  @param {string} sInput string to filter on
  2228  		 *  @param {int} iColumn column to filter
  2229  		 *  @param {bool} bRegex treat search string as a regular expression or not
  2230  		 *  @param {bool} bSmart use smart filtering or not
  2231  		 *  @param {bool} bCaseInsensitive Do case insenstive matching or not
  2232  		 *  @memberof DataTable#oApi
  2233  		 */
  2234  		function _fnFilterColumn ( oSettings, sInput, iColumn, bRegex, bSmart, bCaseInsensitive )
  2235  		{
  2236  			if ( sInput === "" )
  2237  			{
  2238  				return;
  2239  			}
  2240  			
  2241  			var iIndexCorrector = 0;
  2242  			var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart, bCaseInsensitive );
  2243  			
  2244  			for ( var i=oSettings.aiDisplay.length-1 ; i>=0 ; i-- )
  2245  			{
  2246  				var sData = _fnDataToSearch( _fnGetCellData( oSettings, oSettings.aiDisplay[i], iColumn, 'filter' ),
  2247  					oSettings.aoColumns[iColumn].sType );
  2248  				if ( ! rpSearch.test( sData ) )
  2249  				{
  2250  					oSettings.aiDisplay.splice( i, 1 );
  2251  					iIndexCorrector++;
  2252  				}
  2253  			}
  2254  		}
  2255  		
  2256  		
  2257  		/**
  2258  		 * Filter the data table based on user input and draw the table
  2259  		 *  @param {object} oSettings dataTables settings object
  2260  		 *  @param {string} sInput string to filter on
  2261  		 *  @param {int} iForce optional - force a research of the master array (1) or not (undefined or 0)
  2262  		 *  @param {bool} bRegex treat as a regular expression or not
  2263  		 *  @param {bool} bSmart perform smart filtering or not
  2264  		 *  @param {bool} bCaseInsensitive Do case insenstive matching or not
  2265  		 *  @memberof DataTable#oApi
  2266  		 */
  2267  		function _fnFilter( oSettings, sInput, iForce, bRegex, bSmart, bCaseInsensitive )
  2268  		{
  2269  			var i;
  2270  			var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart, bCaseInsensitive );
  2271  			var oPrevSearch = oSettings.oPreviousSearch;
  2272  			
  2273  			/* Check if we are forcing or not - optional parameter */
  2274  			if ( !iForce )
  2275  			{
  2276  				iForce = 0;
  2277  			}
  2278  			
  2279  			/* Need to take account of custom filtering functions - always filter */
  2280  			if ( DataTable.ext.afnFiltering.length !== 0 )
  2281  			{
  2282  				iForce = 1;
  2283  			}
  2284  			
  2285  			/*
  2286  			 * If the input is blank - we want the full data set
  2287  			 */
  2288  			if ( sInput.length <= 0 )
  2289  			{
  2290  				oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
  2291  				oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
  2292  			}
  2293  			else
  2294  			{
  2295  				/*
  2296  				 * We are starting a new search or the new search string is smaller 
  2297  				 * then the old one (i.e. delete). Search from the master array
  2298  			 	 */
  2299  				if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length ||
  2300  					   oPrevSearch.sSearch.length > sInput.length || iForce == 1 ||
  2301  					   sInput.indexOf(oPrevSearch.sSearch) !== 0 )
  2302  				{
  2303  					/* Nuke the old display array - we are going to rebuild it */
  2304  					oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
  2305  					
  2306  					/* Force a rebuild of the search array */
  2307  					_fnBuildSearchArray( oSettings, 1 );
  2308  					
  2309  					/* Search through all records to populate the search array
  2310  					 * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1 
  2311  					 * mapping
  2312  					 */
  2313  					for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ )
  2314  					{
  2315  						if ( rpSearch.test(oSettings.asDataSearch[i]) )
  2316  						{
  2317  							oSettings.aiDisplay.push( oSettings.aiDisplayMaster[i] );
  2318  						}
  2319  					}
  2320  			  }
  2321  			  else
  2322  				{
  2323  			  	/* Using old search array - refine it - do it this way for speed
  2324  			  	 * Don't have to search the whole master array again
  2325  					 */
  2326  			  	var iIndexCorrector = 0;
  2327  			  	
  2328  			  	/* Search the current results */
  2329  			  	for ( i=0 ; i<oSettings.asDataSearch.length ; i++ )
  2330  					{
  2331  			  		if ( ! rpSearch.test(oSettings.asDataSearch[i]) )
  2332  						{
  2333  			  			oSettings.aiDisplay.splice( i-iIndexCorrector, 1 );
  2334  			  			iIndexCorrector++;
  2335  			  		}
  2336  			  	}
  2337  			  }
  2338  			}
  2339  		}
  2340  		
  2341  		
  2342  		/**
  2343  		 * Create an array which can be quickly search through
  2344  		 *  @param {object} oSettings dataTables settings object
  2345  		 *  @param {int} iMaster use the master data array - optional
  2346  		 *  @memberof DataTable#oApi
  2347  		 */
  2348  		function _fnBuildSearchArray ( oSettings, iMaster )
  2349  		{
  2350  			if ( !oSettings.oFeatures.bServerSide )
  2351  			{
  2352  				/* Clear out the old data */
  2353  				oSettings.asDataSearch = [];
  2354  		
  2355  				var aiFilterColumns = _fnGetColumns( oSettings, 'bSearchable' );
  2356  				var aiIndex = (iMaster===1) ?
  2357  				 	oSettings.aiDisplayMaster :
  2358  				 	oSettings.aiDisplay;
  2359  				
  2360  				for ( var i=0, iLen=aiIndex.length ; i<iLen ; i++ )
  2361  				{
  2362  					oSettings.asDataSearch[i] = _fnBuildSearchRow(
  2363  						oSettings,
  2364  						_fnGetRowData( oSettings, aiIndex[i], 'filter', aiFilterColumns )
  2365  					);
  2366  				}
  2367  			}
  2368  		}
  2369  		
  2370  		
  2371  		/**
  2372  		 * Create a searchable string from a single data row
  2373  		 *  @param {object} oSettings dataTables settings object
  2374  		 *  @param {array} aData Row data array to use for the data to search
  2375  		 *  @memberof DataTable#oApi
  2376  		 */
  2377  		function _fnBuildSearchRow( oSettings, aData )
  2378  		{
  2379  			var sSearch = aData.join('  ');
  2380  			
  2381  			/* If it looks like there is an HTML entity in the string, attempt to decode it */
  2382  			if ( sSearch.indexOf('&') !== -1 )
  2383  			{
  2384  				sSearch = $('<div>').html(sSearch).text();
  2385  			}
  2386  			
  2387  			// Strip newline characters
  2388  			return sSearch.replace( /[\n\r]/g, " " );
  2389  		}
  2390  		
  2391  		/**
  2392  		 * Build a regular expression object suitable for searching a table
  2393  		 *  @param {string} sSearch string to search for
  2394  		 *  @param {bool} bRegex treat as a regular expression or not
  2395  		 *  @param {bool} bSmart perform smart filtering or not
  2396  		 *  @param {bool} bCaseInsensitive Do case insensitive matching or not
  2397  		 *  @returns {RegExp} constructed object
  2398  		 *  @memberof DataTable#oApi
  2399  		 */
  2400  		function _fnFilterCreateSearch( sSearch, bRegex, bSmart, bCaseInsensitive )
  2401  		{
  2402  			var asSearch, sRegExpString;
  2403  			
  2404  			if ( bSmart )
  2405  			{
  2406  				/* Generate the regular expression to use. Something along the lines of:
  2407  				 * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$
  2408  				 */
  2409  				asSearch = bRegex ? sSearch.split( ' ' ) : _fnEscapeRegex( sSearch ).split( ' ' );
  2410  				sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$';
  2411  				return new RegExp( sRegExpString, bCaseInsensitive ? "i" : "" );
  2412  			}
  2413  			else
  2414  			{
  2415  				sSearch = bRegex ? sSearch : _fnEscapeRegex( sSearch );
  2416  				return new RegExp( sSearch, bCaseInsensitive ? "i" : "" );
  2417  			}
  2418  		}
  2419  		
  2420  		
  2421  		/**
  2422  		 * Convert raw data into something that the user can search on
  2423  		 *  @param {string} sData data to be modified
  2424  		 *  @param {string} sType data type
  2425  		 *  @returns {string} search string
  2426  		 *  @memberof DataTable#oApi
  2427  		 */
  2428  		function _fnDataToSearch ( sData, sType )
  2429  		{
  2430  			if ( typeof DataTable.ext.ofnSearch[sType] === "function" )
  2431  			{
  2432  				return DataTable.ext.ofnSearch[sType]( sData );
  2433  			}
  2434  			else if ( sData === null )
  2435  			{
  2436  				return '';
  2437  			}
  2438  			else if ( sType == "html" )
  2439  			{
  2440  				return sData.replace(/[\r\n]/g," ").replace( /<.*?>/g, "" );
  2441  			}
  2442  			else if ( typeof sData === "string" )
  2443  			{
  2444  				return sData.replace(/[\r\n]/g," ");
  2445  			}
  2446  			return sData;
  2447  		}
  2448  		
  2449  		
  2450  		/**
  2451  		 * scape a string such that it can be used in a regular expression
  2452  		 *  @param {string} sVal string to escape
  2453  		 *  @returns {string} escaped string
  2454  		 *  @memberof DataTable#oApi
  2455  		 */
  2456  		function _fnEscapeRegex ( sVal )
  2457  		{
  2458  			var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ];
  2459  			var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' );
  2460  			return sVal.replace(reReplace, '\\$1');
  2461  		}
  2462  		
  2463  		
  2464  		/**
  2465  		 * Generate the node required for the info display
  2466  		 *  @param {object} oSettings dataTables settings object
  2467  		 *  @returns {node} Information element
  2468  		 *  @memberof DataTable#oApi
  2469  		 */
  2470  		function _fnFeatureHtmlInfo ( oSettings )
  2471  		{
  2472  			var nInfo = document.createElement( 'div' );
  2473  			nInfo.className = oSettings.oClasses.sInfo;
  2474  			
  2475  			/* Actions that are to be taken once only for this feature */
  2476  			if ( !oSettings.aanFeatures.i )
  2477  			{
  2478  				/* Add draw callback */
  2479  				oSettings.aoDrawCallback.push( {
  2480  					"fn": _fnUpdateInfo,
  2481  					"sName": "information"
  2482  				} );
  2483  				
  2484  				/* Add id */
  2485  				nInfo.id = oSettings.sTableId+'_info';
  2486  			}
  2487  			oSettings.nTable.setAttribute( 'aria-describedby', oSettings.sTableId+'_info' );
  2488  			
  2489  			return nInfo;
  2490  		}
  2491  		
  2492  		
  2493  		/**
  2494  		 * Update the information elements in the display
  2495  		 *  @param {object} oSettings dataTables settings object
  2496  		 *  @memberof DataTable#oApi
  2497  		 */
  2498  		function _fnUpdateInfo ( oSettings )
  2499  		{
  2500  			/* Show information about the table */
  2501  			if ( !oSettings.oFeatures.bInfo || oSettings.aanFeatures.i.length === 0 )
  2502  			{
  2503  				return;
  2504  			}
  2505  			
  2506  			var
  2507  				oLang = oSettings.oLanguage,
  2508  				iStart = oSettings._iDisplayStart+1,
  2509  				iEnd = oSettings.fnDisplayEnd(),
  2510  				iMax = oSettings.fnRecordsTotal(),
  2511  				iTotal = oSettings.fnRecordsDisplay(),
  2512  				sOut;
  2513  			
  2514  			if ( iTotal === 0 )
  2515  			{
  2516  				/* Empty record set */
  2517  				sOut = oLang.sInfoEmpty;
  2518  			}
  2519  			else {
  2520  				/* Normal record set */
  2521  				sOut = oLang.sInfo;
  2522  			}
  2523  		
  2524  			if ( iTotal != iMax )
  2525  			{
  2526  				/* Record set after filtering */
  2527  				sOut += ' ' + oLang.sInfoFiltered;
  2528  			}
  2529  		
  2530  			// Convert the macros
  2531  			sOut += oLang.sInfoPostFix;
  2532  			sOut = _fnInfoMacros( oSettings, sOut );
  2533  			
  2534  			if ( oLang.fnInfoCallback !== null )
  2535  			{
  2536  				sOut = oLang.fnInfoCallback.call( oSettings.oInstance, 
  2537  					oSettings, iStart, iEnd, iMax, iTotal, sOut );
  2538  			}
  2539  			
  2540  			var n = oSettings.aanFeatures.i;
  2541  			for ( var i=0, iLen=n.length ; i<iLen ; i++ )
  2542  			{
  2543  				$(n[i]).html( sOut );
  2544  			}
  2545  		}
  2546  		
  2547  		
  2548  		function _fnInfoMacros ( oSettings, str )
  2549  		{
  2550  			var
  2551  				iStart = oSettings._iDisplayStart+1,
  2552  				sStart = oSettings.fnFormatNumber( iStart ),
  2553  				iEnd = oSettings.fnDisplayEnd(),
  2554  				sEnd = oSettings.fnFormatNumber( iEnd ),
  2555  				iTotal = oSettings.fnRecordsDisplay(),
  2556  				sTotal = oSettings.fnFormatNumber( iTotal ),
  2557  				iMax = oSettings.fnRecordsTotal(),
  2558  				sMax = oSettings.fnFormatNumber( iMax );
  2559  		
  2560  			// When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
  2561  			// internally
  2562  			if ( oSettings.oScroll.bInfinite )
  2563  			{
  2564  				sStart = oSettings.fnFormatNumber( 1 );
  2565  			}
  2566  		
  2567  			return str.
  2568  				replace(/_START_/g, sStart).
  2569  				replace(/_END_/g,   sEnd).
  2570  				replace(/_TOTAL_/g, sTotal).
  2571  				replace(/_MAX_/g,   sMax);
  2572  		}
  2573  		
  2574  		
  2575  		
  2576  		/**
  2577  		 * Draw the table for the first time, adding all required features
  2578  		 *  @param {object} oSettings dataTables settings object
  2579  		 *  @memberof DataTable#oApi
  2580  		 */
  2581  		function _fnInitialise ( oSettings )
  2582  		{
  2583  			var i, iLen, iAjaxStart=oSettings.iInitDisplayStart;
  2584  			
  2585  			/* Ensure that the table data is fully initialised */
  2586  			if ( oSettings.bInitialised === false )
  2587  			{
  2588  				setTimeout( function(){ _fnInitialise( oSettings ); }, 200 );
  2589  				return;
  2590  			}
  2591  			
  2592  			/* Show the display HTML options */
  2593  			_fnAddOptionsHtml( oSettings );
  2594  			
  2595  			/* Build and draw the header / footer for the table */
  2596  			_fnBuildHead( oSettings );
  2597  			_fnDrawHead( oSettings, oSettings.aoHeader );
  2598  			if ( oSettings.nTFoot )
  2599  			{
  2600  				_fnDrawHead( oSettings, oSettings.aoFooter );
  2601  			}
  2602  		
  2603  			/* Okay to show that something is going on now */
  2604  			_fnProcessingDisplay( oSettings, true );
  2605  			
  2606  			/* Calculate sizes for columns */
  2607  			if ( oSettings.oFeatures.bAutoWidth )
  2608  			{
  2609  				_fnCalculateColumnWidths( oSettings );
  2610  			}
  2611  			
  2612  			for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
  2613  			{
  2614  				if ( oSettings.aoColumns[i].sWidth !== null )
  2615  				{
  2616  					oSettings.aoColumns[i].nTh.style.width = _fnStringToCss( oSettings.aoColumns[i].sWidth );
  2617  				}
  2618  			}
  2619  			
  2620  			/* If there is default sorting required - let's do it. The sort function will do the
  2621  			 * drawing for us. Otherwise we draw the table regardless of the Ajax source - this allows
  2622  			 * the table to look initialised for Ajax sourcing data (show 'loading' message possibly)
  2623  			 */
  2624  			if ( oSettings.oFeatures.bSort )
  2625  			{
  2626  				_fnSort( oSettings );
  2627  			}
  2628  			else if ( oSettings.oFeatures.bFilter )
  2629  			{
  2630  				_fnFilterComplete( oSettings, oSettings.oPreviousSearch );
  2631  			}
  2632  			else
  2633  			{
  2634  				oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
  2635  				_fnCalculateEnd( oSettings );
  2636  				_fnDraw( oSettings );
  2637  			}
  2638  			
  2639  			/* if there is an ajax source load the data */
  2640  			if ( oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide )
  2641  			{
  2642  				var aoData = [];
  2643  				_fnServerParams( oSettings, aoData );
  2644  				oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aoData, function(json) {
  2645  					var aData = (oSettings.sAjaxDataProp !== "") ?
  2646  					 	_fnGetObjectDataFn( oSettings.sAjaxDataProp )(json) : json;
  2647  		
  2648  					/* Got the data - add it to the table */
  2649  					for ( i=0 ; i<aData.length ; i++ )
  2650  					{
  2651  						_fnAddData( oSettings, aData[i] );
  2652  					}
  2653  					
  2654  					/* Reset the init display for cookie saving. We've already done a filter, and
  2655  					 * therefore cleared it before. So we need to make it appear 'fresh'
  2656  					 */
  2657  					oSettings.iInitDisplayStart = iAjaxStart;
  2658  					
  2659  					if ( oSettings.oFeatures.bSort )
  2660  					{
  2661  						_fnSort( oSettings );
  2662  					}
  2663  					else
  2664  					{
  2665  						oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
  2666  						_fnCalculateEnd( oSettings );
  2667  						_fnDraw( oSettings );
  2668  					}
  2669  					
  2670  					_fnProcessingDisplay( oSettings, false );
  2671  					_fnInitComplete( oSettings, json );
  2672  				}, oSettings );
  2673  				return;
  2674  			}
  2675  			
  2676  			/* Server-side processing initialisation complete is done at the end of _fnDraw */
  2677  			if ( !oSettings.oFeatures.bServerSide )
  2678  			{
  2679  				_fnProcessingDisplay( oSettings, false );
  2680  				_fnInitComplete( oSettings );
  2681  			}
  2682  		}
  2683  		
  2684  		
  2685  		/**
  2686  		 * Draw the table for the first time, adding all required features
  2687  		 *  @param {object} oSettings dataTables settings object
  2688  		 *  @param {object} [json] JSON from the server that completed the table, if using Ajax source
  2689  		 *    with client-side processing (optional)
  2690  		 *  @memberof DataTable#oApi
  2691  		 */
  2692  		function _fnInitComplete ( oSettings, json )
  2693  		{
  2694  			oSettings._bInitComplete = true;
  2695  			_fnCallbackFire( oSettings, 'aoInitComplete', 'init', [oSettings, json] );
  2696  		}
  2697  		
  2698  		
  2699  		/**
  2700  		 * Language compatibility - when certain options are given, and others aren't, we
  2701  		 * need to duplicate the values over, in order to provide backwards compatibility
  2702  		 * with older language files.
  2703  		 *  @param {object} oSettings dataTables settings object
  2704  		 *  @memberof DataTable#oApi
  2705  		 */
  2706  		function _fnLanguageCompat( oLanguage )
  2707  		{
  2708  			var oDefaults = DataTable.defaults.oLanguage;
  2709  		
  2710  			/* Backwards compatibility - if there is no sEmptyTable given, then use the same as
  2711  			 * sZeroRecords - assuming that is given.
  2712  			 */
  2713  			if ( !oLanguage.sEmptyTable && oLanguage.sZeroRecords &&
  2714  				oDefaults.sEmptyTable === "No data available in table" )
  2715  			{
  2716  				_fnMap( oLanguage, oLanguage, 'sZeroRecords', 'sEmptyTable' );
  2717  			}
  2718  		
  2719  			/* Likewise with loading records */
  2720  			if ( !oLanguage.sLoadingRecords && oLanguage.sZeroRecords &&
  2721  				oDefaults.sLoadingRecords === "Loading..." )
  2722  			{
  2723  				_fnMap( oLanguage, oLanguage, 'sZeroRecords', 'sLoadingRecords' );
  2724  			}
  2725  		}
  2726  		
  2727  		
  2728  		
  2729  		/**
  2730  		 * Generate the node required for user display length changing
  2731  		 *  @param {object} oSettings dataTables settings object
  2732  		 *  @returns {node} Display length feature node
  2733  		 *  @memberof DataTable#oApi
  2734  		 */
  2735  		function _fnFeatureHtmlLength ( oSettings )
  2736  		{
  2737  			if ( oSettings.oScroll.bInfinite )
  2738  			{
  2739  				return null;
  2740  			}
  2741  			
  2742  			/* This can be overruled by not using the _MENU_ var/macro in the language variable */
  2743  			var sName = 'name="'+oSettings.sTableId+'_length"';
  2744  			var sStdMenu = '<select size="1" '+sName+'>';
  2745  			var i, iLen;
  2746  			var aLengthMenu = oSettings.aLengthMenu;
  2747  			
  2748  			if ( aLengthMenu.length == 2 && typeof aLengthMenu[0] === 'object' && 
  2749  					typeof aLengthMenu[1] === 'object' )
  2750  			{
  2751  				for ( i=0, iLen=aLengthMenu[0].length ; i<iLen ; i++ )
  2752  				{
  2753  					sStdMenu += '<option value="'+aLengthMenu[0][i]+'">'+aLengthMenu[1][i]+'</option>';
  2754  				}
  2755  			}
  2756  			else
  2757  			{
  2758  				for ( i=0, iLen=aLengthMenu.length ; i<iLen ; i++ )
  2759  				{
  2760  					sStdMenu += '<option value="'+aLengthMenu[i]+'">'+aLengthMenu[i]+'</option>';
  2761  				}
  2762  			}
  2763  			sStdMenu += '</select>';
  2764  			
  2765  			var nLength = document.createElement( 'div' );
  2766  			if ( !oSettings.aanFeatures.l )
  2767  			{
  2768  				nLength.id = oSettings.sTableId+'_length';
  2769  			}
  2770  			nLength.className = oSettings.oClasses.sLength;
  2771  			nLength.innerHTML = '<label>'+oSettings.oLanguage.sLengthMenu.replace( '_MENU_', sStdMenu )+'</label>';
  2772  			
  2773  			/*
  2774  			 * Set the length to the current display length - thanks to Andrea Pavlovic for this fix,
  2775  			 * and Stefan Skopnik for fixing the fix!
  2776  			 */
  2777  			$('select option[value="'+oSettings._iDisplayLength+'"]', nLength).attr("selected", true);
  2778  			
  2779  			$('select', nLength).bind( 'change.DT', function(e) {
  2780  				var iVal = $(this).val();
  2781  				
  2782  				/* Update all other length options for the new display */
  2783  				var n = oSettings.aanFeatures.l;
  2784  				for ( i=0, iLen=n.length ; i<iLen ; i++ )
  2785  				{
  2786  					if ( n[i] != this.parentNode )
  2787  					{
  2788  						$('select', n[i]).val( iVal );
  2789  					}
  2790  				}
  2791  				
  2792  				/* Redraw the table */
  2793  				oSettings._iDisplayLength = parseInt(iVal, 10);
  2794  				_fnCalculateEnd( oSettings );
  2795  				
  2796  				/* If we have space to show extra rows (backing up from the end point - then do so */
  2797  				if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() )
  2798  				{
  2799  					oSettings._iDisplayStart = oSettings.fnDisplayEnd() - oSettings._iDisplayLength;
  2800  					if ( oSettings._iDisplayStart < 0 )
  2801  					{
  2802  						oSettings._iDisplayStart = 0;
  2803  					}
  2804  				}
  2805  				
  2806  				if ( oSettings._iDisplayLength == -1 )
  2807  				{
  2808  					oSettings._iDisplayStart = 0;
  2809  				}
  2810  				
  2811  				_fnDraw( oSettings );
  2812  			} );
  2813  		
  2814  		
  2815  			$('select', nLength).attr('aria-controls', oSettings.sTableId);
  2816  			
  2817  			return nLength;
  2818  		}
  2819  		
  2820  		
  2821  		/**
  2822  		 * Recalculate the end point based on the start point
  2823  		 *  @param {object} oSettings dataTables settings object
  2824  		 *  @memberof DataTable#oApi
  2825  		 */
  2826  		function _fnCalculateEnd( oSettings )
  2827  		{
  2828  			if ( oSettings.oFeatures.bPaginate === false )
  2829  			{
  2830  				oSettings._iDisplayEnd = oSettings.aiDisplay.length;
  2831  			}
  2832  			else
  2833  			{
  2834  				/* Set the end point of the display - based on how many elements there are
  2835  				 * still to display
  2836  				 */
  2837  				if ( oSettings._iDisplayStart + oSettings._iDisplayLength > oSettings.aiDisplay.length ||
  2838  					   oSettings._iDisplayLength == -1 )
  2839  				{
  2840  					oSettings._iDisplayEnd = oSettings.aiDisplay.length;
  2841  				}
  2842  				else
  2843  				{
  2844  					oSettings._iDisplayEnd = oSettings._iDisplayStart + oSettings._iDisplayLength;
  2845  				}
  2846  			}
  2847  		}
  2848  		
  2849  		
  2850  		
  2851  		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2852  		 * Note that most of the paging logic is done in 
  2853  		 * DataTable.ext.oPagination
  2854  		 */
  2855  		
  2856  		/**
  2857  		 * Generate the node required for default pagination
  2858  		 *  @param {object} oSettings dataTables settings object
  2859  		 *  @returns {node} Pagination feature node
  2860  		 *  @memberof DataTable#oApi
  2861  		 */
  2862  		function _fnFeatureHtmlPaginate ( oSettings )
  2863  		{
  2864  			if ( oSettings.oScroll.bInfinite )
  2865  			{
  2866  				return null;
  2867  			}
  2868  			
  2869  			var nPaginate = document.createElement( 'div' );
  2870  			nPaginate.className = oSettings.oClasses.sPaging+oSettings.sPaginationType;
  2871  			
  2872  			DataTable.ext.oPagination[ oSettings.sPaginationType ].fnInit( oSettings, nPaginate, 
  2873  				function( oSettings ) {
  2874  					_fnCalculateEnd( oSettings );
  2875  					_fnDraw( oSettings );
  2876  				}
  2877  			);
  2878  			
  2879  			/* Add a draw callback for the pagination on first instance, to update the paging display */
  2880  			if ( !oSettings.aanFeatures.p )
  2881  			{
  2882  				oSettings.aoDrawCallback.push( {
  2883  					"fn": function( oSettings ) {
  2884  						DataTable.ext.oPagination[ oSettings.sPaginationType ].fnUpdate( oSettings, function( oSettings ) {
  2885  							_fnCalculateEnd( oSettings );
  2886  							_fnDraw( oSettings );
  2887  						} );
  2888  					},
  2889  					"sName": "pagination"
  2890  				} );
  2891  			}
  2892  			return nPaginate;
  2893  		}
  2894  		
  2895  		
  2896  		/**
  2897  		 * Alter the display settings to change the page
  2898  		 *  @param {object} oSettings dataTables settings object
  2899  		 *  @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
  2900  		 *    or page number to jump to (integer)
  2901  		 *  @returns {bool} true page has changed, false - no change (no effect) eg 'first' on page 1
  2902  		 *  @memberof DataTable#oApi
  2903  		 */
  2904  		function _fnPageChange ( oSettings, mAction )
  2905  		{
  2906  			var iOldStart = oSettings._iDisplayStart;
  2907  			
  2908  			if ( typeof mAction === "number" )
  2909  			{
  2910  				oSettings._iDisplayStart = mAction * oSettings._iDisplayLength;
  2911  				if ( oSettings._iDisplayStart > oSettings.fnRecordsDisplay() )
  2912  				{
  2913  					oSettings._iDisplayStart = 0;
  2914  				}
  2915  			}
  2916  			else if ( mAction == "first" )
  2917  			{
  2918  				oSettings._iDisplayStart = 0;
  2919  			}
  2920  			else if ( mAction == "previous" )
  2921  			{
  2922  				oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ?
  2923  					oSettings._iDisplayStart - oSettings._iDisplayLength :
  2924  					0;
  2925  				
  2926  				/* Correct for under-run */
  2927  				if ( oSettings._iDisplayStart < 0 )
  2928  				{
  2929  				  oSettings._iDisplayStart = 0;
  2930  				}
  2931  			}
  2932  			else if ( mAction == "next" )
  2933  			{
  2934  				if ( oSettings._iDisplayLength >= 0 )
  2935  				{
  2936  					/* Make sure we are not over running the display array */
  2937  					if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() )
  2938  					{
  2939  						oSettings._iDisplayStart += oSettings._iDisplayLength;
  2940  					}
  2941  				}
  2942  				else
  2943  				{
  2944  					oSettings._iDisplayStart = 0;
  2945  				}
  2946  			}
  2947  			else if ( mAction == "last" )
  2948  			{
  2949  				if ( oSettings._iDisplayLength >= 0 )
  2950  				{
  2951  					var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1;
  2952  					oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength;
  2953  				}
  2954  				else
  2955  				{
  2956  					oSettings._iDisplayStart = 0;
  2957  				}
  2958  			}
  2959  			else
  2960  			{
  2961  				_fnLog( oSettings, 0, "Unknown paging action: "+mAction );
  2962  			}
  2963  			$(oSettings.oInstance).trigger('page', oSettings);
  2964  			
  2965  			return iOldStart != oSettings._iDisplayStart;
  2966  		}
  2967  		
  2968  		
  2969  		
  2970  		/**
  2971  		 * Generate the node required for the processing node
  2972  		 *  @param {object} oSettings dataTables settings object
  2973  		 *  @returns {node} Processing element
  2974  		 *  @memberof DataTable#oApi
  2975  		 */
  2976  		function _fnFeatureHtmlProcessing ( oSettings )
  2977  		{
  2978  			var nProcessing = document.createElement( 'div' );
  2979  			
  2980  			if ( !oSettings.aanFeatures.r )
  2981  			{
  2982  				nProcessing.id = oSettings.sTableId+'_processing';
  2983  			}
  2984  			nProcessing.innerHTML = oSettings.oLanguage.sProcessing;
  2985  			nProcessing.className = oSettings.oClasses.sProcessing;
  2986  			oSettings.nTable.parentNode.insertBefore( nProcessing, oSettings.nTable );
  2987  			
  2988  			return nProcessing;
  2989  		}
  2990  		
  2991  		
  2992  		/**
  2993  		 * Display or hide the processing indicator
  2994  		 *  @param {object} oSettings dataTables settings object
  2995  		 *  @param {bool} bShow Show the processing indicator (true) or not (false)
  2996  		 *  @memberof DataTable#oApi
  2997  		 */
  2998  		function _fnProcessingDisplay ( oSettings, bShow )
  2999  		{
  3000  			if ( oSettings.oFeatures.bProcessing )
  3001  			{
  3002  				var an = oSettings.aanFeatures.r;
  3003  				for ( var i=0, iLen=an.length ; i<iLen ; i++ )
  3004  				{
  3005  					an[i].style.visibility = bShow ? "visible" : "hidden";
  3006  				}
  3007  			}
  3008  		
  3009  			$(oSettings.oInstance).trigger('processing', [oSettings, bShow]);
  3010  		}
  3011  		
  3012  		/**
  3013  		 * Add any control elements for the table - specifically scrolling
  3014  		 *  @param {object} oSettings dataTables settings object
  3015  		 *  @returns {node} Node to add to the DOM
  3016  		 *  @memberof DataTable#oApi
  3017  		 */
  3018  		function _fnFeatureHtmlTable ( oSettings )
  3019  		{
  3020  			/* Check if scrolling is enabled or not - if not then leave the DOM unaltered */
  3021  			if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
  3022  			{
  3023  				return oSettings.nTable;
  3024  			}
  3025  			
  3026  			/*
  3027  			 * The HTML structure that we want to generate in this function is:
  3028  			 *  div - nScroller
  3029  			 *    div - nScrollHead
  3030  			 *      div - nScrollHeadInner
  3031  			 *        table - nScrollHeadTable
  3032  			 *          thead - nThead
  3033  			 *    div - nScrollBody
  3034  			 *      table - oSettings.nTable
  3035  			 *        thead - nTheadSize
  3036  			 *        tbody - nTbody
  3037  			 *    div - nScrollFoot
  3038  			 *      div - nScrollFootInner
  3039  			 *        table - nScrollFootTable
  3040  			 *          tfoot - nTfoot
  3041  			 */
  3042  			var
  3043  			 	nScroller = document.createElement('div'),
  3044  			 	nScrollHead = document.createElement('div'),
  3045  			 	nScrollHeadInner = document.createElement('div'),
  3046  			 	nScrollBody = document.createElement('div'),
  3047  			 	nScrollFoot = document.createElement('div'),
  3048  			 	nScrollFootInner = document.createElement('div'),
  3049  			 	nScrollHeadTable = oSettings.nTable.cloneNode(false),
  3050  			 	nScrollFootTable = oSettings.nTable.cloneNode(false),
  3051  				nThead = oSettings.nTable.getElementsByTagName('thead')[0],
  3052  			 	nTfoot = oSettings.nTable.getElementsByTagName('tfoot').length === 0 ? null : 
  3053  					oSettings.nTable.getElementsByTagName('tfoot')[0],
  3054  				oClasses = oSettings.oClasses;
  3055  			
  3056  			nScrollHead.appendChild( nScrollHeadInner );
  3057  			nScrollFoot.appendChild( nScrollFootInner );
  3058  			nScrollBody.appendChild( oSettings.nTable );
  3059  			nScroller.appendChild( nScrollHead );
  3060  			nScroller.appendChild( nScrollBody );
  3061  			nScrollHeadInner.appendChild( nScrollHeadTable );
  3062  			nScrollHeadTable.appendChild( nThead );
  3063  			if ( nTfoot !== null )
  3064  			{
  3065  				nScroller.appendChild( nScrollFoot );
  3066  				nScrollFootInner.appendChild( nScrollFootTable );
  3067  				nScrollFootTable.appendChild( nTfoot );
  3068  			}
  3069  			
  3070  			nScroller.className = oClasses.sScrollWrapper;
  3071  			nScrollHead.className = oClasses.sScrollHead;
  3072  			nScrollHeadInner.className = oClasses.sScrollHeadInner;
  3073  			nScrollBody.className = oClasses.sScrollBody;
  3074  			nScrollFoot.className = oClasses.sScrollFoot;
  3075  			nScrollFootInner.className = oClasses.sScrollFootInner;
  3076  			
  3077  			if ( oSettings.oScroll.bAutoCss )
  3078  			{
  3079  				nScrollHead.style.overflow = "hidden";
  3080  				nScrollHead.style.position = "relative";
  3081  				nScrollFoot.style.overflow = "hidden";
  3082  				nScrollBody.style.overflow = "auto";
  3083  			}
  3084  			
  3085  			nScrollHead.style.border = "0";
  3086  			nScrollHead.style.width = "100%";
  3087  			nScrollFoot.style.border = "0";
  3088  			nScrollHeadInner.style.width = oSettings.oScroll.sXInner !== "" ?
  3089  				oSettings.oScroll.sXInner : "100%"; /* will be overwritten */
  3090  			
  3091  			/* Modify attributes to respect the clones */
  3092  			nScrollHeadTable.removeAttribute('id');
  3093  			nScrollHeadTable.style.marginLeft = "0";
  3094  			oSettings.nTable.style.marginLeft = "0";
  3095  			if ( nTfoot !== null )
  3096  			{
  3097  				nScrollFootTable.removeAttribute('id');
  3098  				nScrollFootTable.style.marginLeft = "0";
  3099  			}
  3100  			
  3101  			/* Move caption elements from the body to the header, footer or leave where it is
  3102  			 * depending on the configuration. Note that the DTD says there can be only one caption */
  3103  			var nCaption = $(oSettings.nTable).children('caption');
  3104  			if ( nCaption.length > 0 )
  3105  			{
  3106  				nCaption = nCaption[0];
  3107  				if ( nCaption._captionSide === "top" )
  3108  				{
  3109  					nScrollHeadTable.appendChild( nCaption );
  3110  				}
  3111  				else if ( nCaption._captionSide === "bottom" && nTfoot )
  3112  				{
  3113  					nScrollFootTable.appendChild( nCaption );
  3114  				}
  3115  			}
  3116  			
  3117  			/*
  3118  			 * Sizing
  3119  			 */
  3120  			/* When x-scrolling add the width and a scroller to move the header with the body */
  3121  			if ( oSettings.oScroll.sX !== "" )
  3122  			{
  3123  				nScrollHead.style.width = _fnStringToCss( oSettings.oScroll.sX );
  3124  				nScrollBody.style.width = _fnStringToCss( oSettings.oScroll.sX );
  3125  				
  3126  				if ( nTfoot !== null )
  3127  				{
  3128  					nScrollFoot.style.width = _fnStringToCss( oSettings.oScroll.sX );	
  3129  				}
  3130  				
  3131  				/* When the body is scrolled, then we also want to scroll the headers */
  3132  				$(nScrollBody).scroll( function (e) {
  3133  					nScrollHead.scrollLeft = this.scrollLeft;
  3134  					
  3135  					if ( nTfoot !== null )
  3136  					{
  3137  						nScrollFoot.scrollLeft = this.scrollLeft;
  3138  					}
  3139  				} );
  3140  			}
  3141  			
  3142  			/* When yscrolling, add the height */
  3143  			if ( oSettings.oScroll.sY !== "" )
  3144  			{
  3145  				nScrollBody.style.height = _fnStringToCss( oSettings.oScroll.sY );
  3146  			}
  3147  			
  3148  			/* Redraw - align columns across the tables */
  3149  			oSettings.aoDrawCallback.push( {
  3150  				"fn": _fnScrollDraw,
  3151  				"sName": "scrolling"
  3152  			} );
  3153  			
  3154  			/* Infinite scrolling event handlers */
  3155  			if ( oSettings.oScroll.bInfinite )
  3156  			{
  3157  				$(nScrollBody).scroll( function() {
  3158  					/* Use a blocker to stop scrolling from loading more data while other data is still loading */
  3159  					if ( !oSettings.bDrawing && $(this).scrollTop() !== 0 )
  3160  					{
  3161  						/* Check if we should load the next data set */
  3162  						if ( $(this).scrollTop() + $(this).height() > 
  3163  							$(oSettings.nTable).height() - oSettings.oScroll.iLoadGap )
  3164  						{
  3165  							/* Only do the redraw if we have to - we might be at the end of the data */
  3166  							if ( oSettings.fnDisplayEnd() < oSettings.fnRecordsDisplay() )
  3167  							{
  3168  								_fnPageChange( oSettings, 'next' );
  3169  								_fnCalculateEnd( oSettings );
  3170  								_fnDraw( oSettings );
  3171  							}
  3172  						}
  3173  					}
  3174  				} );
  3175  			}
  3176  			
  3177  			oSettings.nScrollHead = nScrollHead;
  3178  			oSettings.nScrollFoot = nScrollFoot;
  3179  			
  3180  			return nScroller;
  3181  		}
  3182  		
  3183  		
  3184  		/**
  3185  		 * Update the various tables for resizing. It's a bit of a pig this function, but
  3186  		 * basically the idea to:
  3187  		 *   1. Re-create the table inside the scrolling div
  3188  		 *   2. Take live measurements from the DOM
  3189  		 *   3. Apply the measurements
  3190  		 *   4. Clean up
  3191  		 *  @param {object} o dataTables settings object
  3192  		 *  @returns {node} Node to add to the DOM
  3193  		 *  @memberof DataTable#oApi
  3194  		 */
  3195  		function _fnScrollDraw ( o )
  3196  		{
  3197  			var
  3198  				nScrollHeadInner = o.nScrollHead.getElementsByTagName('div')[0],
  3199  				nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
  3200  				nScrollBody = o.nTable.parentNode,
  3201  				i, iLen, j, jLen, anHeadToSize, anHeadSizers, anFootSizers, anFootToSize, oStyle, iVis,
  3202  				nTheadSize, nTfootSize,
  3203  				iWidth, aApplied=[], aAppliedFooter=[], iSanityWidth,
  3204  				nScrollFootInner = (o.nTFoot !== null) ? o.nScrollFoot.getElementsByTagName('div')[0] : null,
  3205  				nScrollFootTable = (o.nTFoot !== null) ? nScrollFootInner.getElementsByTagName('table')[0] : null,
  3206  				ie67 = o.oBrowser.bScrollOversize,
  3207  				zeroOut = function(nSizer) {
  3208  					oStyle = nSizer.style;
  3209  					oStyle.paddingTop = "0";
  3210  					oStyle.paddingBottom = "0";
  3211  					oStyle.borderTopWidth = "0";
  3212  					oStyle.borderBottomWidth = "0";
  3213  					oStyle.height = 0;
  3214  				};
  3215  			
  3216  			/*
  3217  			 * 1. Re-create the table inside the scrolling div
  3218  			 */
  3219  			
  3220  			/* Remove the old minimised thead and tfoot elements in the inner table */
  3221  			$(o.nTable).children('thead, tfoot').remove();
  3222  		
  3223  			/* Clone the current header and footer elements and then place it into the inner table */
  3224  			nTheadSize = $(o.nTHead).clone()[0];
  3225  			o.nTable.insertBefore( nTheadSize, o.nTable.childNodes[0] );
  3226  			anHeadToSize = o.nTHead.getElementsByTagName('tr');
  3227  			anHeadSizers = nTheadSize.getElementsByTagName('tr');
  3228  			
  3229  			if ( o.nTFoot !== null )
  3230  			{
  3231  				nTfootSize = $(o.nTFoot).clone()[0];
  3232  				o.nTable.insertBefore( nTfootSize, o.nTable.childNodes[1] );
  3233  				anFootToSize = o.nTFoot.getElementsByTagName('tr');
  3234  				anFootSizers = nTfootSize.getElementsByTagName('tr');
  3235  			}
  3236  			
  3237  			/*
  3238  			 * 2. Take live measurements from the DOM - do not alter the DOM itself!
  3239  			 */
  3240  			
  3241  			/* Remove old sizing and apply the calculated column widths
  3242  			 * Get the unique column headers in the newly created (cloned) header. We want to apply the
  3243  			 * calculated sizes to this header
  3244  			 */
  3245  			if ( o.oScroll.sX === "" )
  3246  			{
  3247  				nScrollBody.style.width = '100%';
  3248  				nScrollHeadInner.parentNode.style.width = '100%';
  3249  			}
  3250  			
  3251  			var nThs = _fnGetUniqueThs( o, nTheadSize );
  3252  			for ( i=0, iLen=nThs.length ; i<iLen ; i++ )
  3253  			{
  3254  				iVis = _fnVisibleToColumnIndex( o, i );
  3255  				nThs[i].style.width = o.aoColumns[iVis].sWidth;
  3256  			}
  3257  			
  3258  			if ( o.nTFoot !== null )
  3259  			{
  3260  				_fnApplyToChildren( function(n) {
  3261  					n.style.width = "";
  3262  				}, anFootSizers );
  3263  			}
  3264  		
  3265  			// If scroll collapse is enabled, when we put the headers back into the body for sizing, we
  3266  			// will end up forcing the scrollbar to appear, making our measurements wrong for when we
  3267  			// then hide it (end of this function), so add the header height to the body scroller.
  3268  			if ( o.oScroll.bCollapse && o.oScroll.sY !== "" )
  3269  			{
  3270  				nScrollBody.style.height = (nScrollBody.offsetHeight + o.nTHead.offsetHeight)+"px";
  3271  			}
  3272  			
  3273  			/* Size the table as a whole */
  3274  			iSanityWidth = $(o.nTable).outerWidth();
  3275  			if ( o.oScroll.sX === "" )
  3276  			{
  3277  				/* No x scrolling */
  3278  				o.nTable.style.width = "100%";
  3279  				
  3280  				/* I know this is rubbish - but IE7 will make the width of the table when 100% include
  3281  				 * the scrollbar - which is shouldn't. When there is a scrollbar we need to take this
  3282  				 * into account.
  3283  				 */
  3284  				if ( ie67 && ($('tbody', nScrollBody).height() > nScrollBody.offsetHeight || 
  3285  					$(nScrollBody).css('overflow-y') == "scroll")  )
  3286  				{
  3287  					o.nTable.style.width = _fnStringToCss( $(o.nTable).outerWidth() - o.oScroll.iBarWidth);
  3288  				}
  3289  			}
  3290  			else
  3291  			{
  3292  				if ( o.oScroll.sXInner !== "" )
  3293  				{
  3294  					/* x scroll inner has been given - use it */
  3295  					o.nTable.style.width = _fnStringToCss(o.oScroll.sXInner);
  3296  				}
  3297  				else if ( iSanityWidth == $(nScrollBody).width() &&
  3298  				   $(nScrollBody).height() < $(o.nTable).height() )
  3299  				{
  3300  					/* There is y-scrolling - try to take account of the y scroll bar */
  3301  					o.nTable.style.width = _fnStringToCss( iSanityWidth-o.oScroll.iBarWidth );
  3302  					if ( $(o.nTable).outerWidth() > iSanityWidth-o.oScroll.iBarWidth )
  3303  					{
  3304  						/* Not possible to take account of it */
  3305  						o.nTable.style.width = _fnStringToCss( iSanityWidth );
  3306  					}
  3307  				}
  3308  				else
  3309  				{
  3310  					/* All else fails */
  3311  					o.nTable.style.width = _fnStringToCss( iSanityWidth );
  3312  				}
  3313  			}
  3314  			
  3315  			/* Recalculate the sanity width - now that we've applied the required width, before it was
  3316  			 * a temporary variable. This is required because the column width calculation is done
  3317  			 * before this table DOM is created.
  3318  			 */
  3319  			iSanityWidth = $(o.nTable).outerWidth();
  3320  			
  3321  			/* We want the hidden header to have zero height, so remove padding and borders. Then
  3322  			 * set the width based on the real headers
  3323  			 */
  3324  			
  3325  			// Apply all styles in one pass. Invalidates layout only once because we don't read any 
  3326  			// DOM properties.
  3327  			_fnApplyToChildren( zeroOut, anHeadSizers );
  3328  			 
  3329  			// Read all widths in next pass. Forces layout only once because we do not change 
  3330  			// any DOM properties.
  3331  			_fnApplyToChildren( function(nSizer) {
  3332  				aApplied.push( _fnStringToCss( $(nSizer).width() ) );
  3333  			}, anHeadSizers );
  3334  			 
  3335  			// Apply all widths in final pass. Invalidates layout only once because we do not
  3336  			// read any DOM properties.
  3337  			_fnApplyToChildren( function(nToSize, i) {
  3338  				nToSize.style.width = aApplied[i];
  3339  			}, anHeadToSize );
  3340  		
  3341  			$(anHeadSizers).height(0);
  3342  			
  3343  			/* Same again with the footer if we have one */
  3344  			if ( o.nTFoot !== null )
  3345  			{
  3346  				_fnApplyToChildren( zeroOut, anFootSizers );
  3347  				 
  3348  				_fnApplyToChildren( function(nSizer) {
  3349  					aAppliedFooter.push( _fnStringToCss( $(nSizer).width() ) );
  3350  				}, anFootSizers );
  3351  				 
  3352  				_fnApplyToChildren( function(nToSize, i) {
  3353  					nToSize.style.width = aAppliedFooter[i];
  3354  				}, anFootToSize );
  3355  		
  3356  				$(anFootSizers).height(0);
  3357  			}
  3358  			
  3359  			/*
  3360  			 * 3. Apply the measurements
  3361  			 */
  3362  			
  3363  			/* "Hide" the header and footer that we used for the sizing. We want to also fix their width
  3364  			 * to what they currently are
  3365  			 */
  3366  			_fnApplyToChildren( function(nSizer, i) {
  3367  				nSizer.innerHTML = "";
  3368  				nSizer.style.width = aApplied[i];
  3369  			}, anHeadSizers );
  3370  			
  3371  			if ( o.nTFoot !== null )
  3372  			{
  3373  				_fnApplyToChildren( function(nSizer, i) {
  3374  					nSizer.innerHTML = "";
  3375  					nSizer.style.width = aAppliedFooter[i];
  3376  				}, anFootSizers );
  3377  			}
  3378  			
  3379  			/* Sanity check that the table is of a sensible width. If not then we are going to get
  3380  			 * misalignment - try to prevent this by not allowing the table to shrink below its min width
  3381  			 */
  3382  			if ( $(o.nTable).outerWidth() < iSanityWidth )
  3383  			{
  3384  				/* The min width depends upon if we have a vertical scrollbar visible or not */
  3385  				var iCorrection = ((nScrollBody.scrollHeight > nScrollBody.offsetHeight || 
  3386  					$(nScrollBody).css('overflow-y') == "scroll")) ?
  3387  						iSanityWidth+o.oScroll.iBarWidth : iSanityWidth;
  3388  				
  3389  				/* IE6/7 are a law unto themselves... */
  3390  				if ( ie67 && (nScrollBody.scrollHeight > 
  3391  					nScrollBody.offsetHeight || $(nScrollBody).css('overflow-y') == "scroll")  )
  3392  				{
  3393  					o.nTable.style.width = _fnStringToCss( iCorrection-o.oScroll.iBarWidth );
  3394  				}
  3395  				
  3396  				/* Apply the calculated minimum width to the table wrappers */
  3397  				nScrollBody.style.width = _fnStringToCss( iCorrection );
  3398  				o.nScrollHead.style.width = _fnStringToCss( iCorrection );
  3399  				
  3400  				if ( o.nTFoot !== null )
  3401  				{
  3402  					o.nScrollFoot.style.width = _fnStringToCss( iCorrection );
  3403  				}
  3404  				
  3405  				/* And give the user a warning that we've stopped the table getting too small */
  3406  				if ( o.oScroll.sX === "" )
  3407  				{
  3408  					_fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
  3409  						" misalignment. The table has been drawn at its minimum possible width." );
  3410  				}
  3411  				else if ( o.oScroll.sXInner !== "" )
  3412  				{
  3413  					_fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
  3414  						" misalignment. Increase the sScrollXInner value or remove it to allow automatic"+
  3415  						" calculation" );
  3416  				}
  3417  			}
  3418  			else
  3419  			{
  3420  				nScrollBody.style.width = _fnStringToCss( '100%' );
  3421  				o.nScrollHead.style.width = _fnStringToCss( '100%' );
  3422  				
  3423  				if ( o.nTFoot !== null )
  3424  				{
  3425  					o.nScrollFoot.style.width = _fnStringToCss( '100%' );
  3426  				}
  3427  			}
  3428  			
  3429  			
  3430  			/*
  3431  			 * 4. Clean up
  3432  			 */
  3433  			if ( o.oScroll.sY === "" )
  3434  			{
  3435  				/* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
  3436  				 * the scrollbar height from the visible display, rather than adding it on. We need to
  3437  				 * set the height in order to sort this. Don't want to do it in any other browsers.
  3438  				 */
  3439  				if ( ie67 )
  3440  				{
  3441  					nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+o.oScroll.iBarWidth );
  3442  				}
  3443  			}
  3444  			
  3445  			if ( o.oScroll.sY !== "" && o.oScroll.bCollapse )
  3446  			{
  3447  				nScrollBody.style.height = _fnStringToCss( o.oScroll.sY );
  3448  				
  3449  				var iExtra = (o.oScroll.sX !== "" && o.nTable.offsetWidth > nScrollBody.offsetWidth) ?
  3450  				 	o.oScroll.iBarWidth : 0;
  3451  				if ( o.nTable.offsetHeight < nScrollBody.offsetHeight )
  3452  				{
  3453  					nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+iExtra );
  3454  				}
  3455  			}
  3456  			
  3457  			/* Finally set the width's of the header and footer tables */
  3458  			var iOuterWidth = $(o.nTable).outerWidth();
  3459  			nScrollHeadTable.style.width = _fnStringToCss( iOuterWidth );
  3460  			nScrollHeadInner.style.width = _fnStringToCss( iOuterWidth );
  3461  		
  3462  			// Figure out if there are scrollbar present - if so then we need a the header and footer to
  3463  			// provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
  3464  			var bScrolling = $(o.nTable).height() > nScrollBody.clientHeight || $(nScrollBody).css('overflow-y') == "scroll";
  3465  			nScrollHeadInner.style.paddingRight = bScrolling ? o.oScroll.iBarWidth+"px" : "0px";
  3466  			
  3467  			if ( o.nTFoot !== null )
  3468  			{
  3469  				nScrollFootTable.style.width = _fnStringToCss( iOuterWidth );
  3470  				nScrollFootInner.style.width = _fnStringToCss( iOuterWidth );
  3471  				nScrollFootInner.style.paddingRight = bScrolling ? o.oScroll.iBarWidth+"px" : "0px";
  3472  			}
  3473  		
  3474  			/* Adjust the position of the header in case we loose the y-scrollbar */
  3475  			$(nScrollBody).scroll();
  3476  			
  3477  			/* If sorting or filtering has occurred, jump the scrolling back to the top */
  3478  			if ( o.bSorted || o.bFiltered )
  3479  			{
  3480  				nScrollBody.scrollTop = 0;
  3481  			}
  3482  		}
  3483  		
  3484  		
  3485  		/**
  3486  		 * Apply a given function to the display child nodes of an element array (typically
  3487  		 * TD children of TR rows
  3488  		 *  @param {function} fn Method to apply to the objects
  3489  		 *  @param array {nodes} an1 List of elements to look through for display children
  3490  		 *  @param array {nodes} an2 Another list (identical structure to the first) - optional
  3491  		 *  @memberof DataTable#oApi
  3492  		 */
  3493  		function _fnApplyToChildren( fn, an1, an2 )
  3494  		{
  3495  			var index=0, i=0, iLen=an1.length;
  3496  			var nNode1, nNode2;
  3497  		
  3498  			while ( i < iLen )
  3499  			{
  3500  				nNode1 = an1[i].firstChild;
  3501  				nNode2 = an2 ? an2[i].firstChild : null;
  3502  				while ( nNode1 )
  3503  				{
  3504  					if ( nNode1.nodeType === 1 )
  3505  					{
  3506  						if ( an2 )
  3507  						{
  3508  							fn( nNode1, nNode2, index );
  3509  						}
  3510  						else
  3511  						{
  3512  							fn( nNode1, index );
  3513  						}
  3514  						index++;
  3515  					}
  3516  					nNode1 = nNode1.nextSibling;
  3517  					nNode2 = an2 ? nNode2.nextSibling : null;
  3518  				}
  3519  				i++;
  3520  			}
  3521  		}
  3522  		
  3523  		/**
  3524  		 * Convert a CSS unit width to pixels (e.g. 2em)
  3525  		 *  @param {string} sWidth width to be converted
  3526  		 *  @param {node} nParent parent to get the with for (required for relative widths) - optional
  3527  		 *  @returns {int} iWidth width in pixels
  3528  		 *  @memberof DataTable#oApi
  3529  		 */
  3530  		function _fnConvertToWidth ( sWidth, nParent )
  3531  		{
  3532  			if ( !sWidth || sWidth === null || sWidth === '' )
  3533  			{
  3534  				return 0;
  3535  			}
  3536  			
  3537  			if ( !nParent )
  3538  			{
  3539  				nParent = document.body;
  3540  			}
  3541  			
  3542  			var iWidth;
  3543  			var nTmp = document.createElement( "div" );
  3544  			nTmp.style.width = _fnStringToCss( sWidth );
  3545  			
  3546  			nParent.appendChild( nTmp );
  3547  			iWidth = nTmp.offsetWidth;
  3548  			nParent.removeChild( nTmp );
  3549  			
  3550  			return ( iWidth );
  3551  		}
  3552  		
  3553  		
  3554  		/**
  3555  		 * Calculate the width of columns for the table
  3556  		 *  @param {object} oSettings dataTables settings object
  3557  		 *  @memberof DataTable#oApi
  3558  		 */
  3559  		function _fnCalculateColumnWidths ( oSettings )
  3560  		{
  3561  			var iTableWidth = oSettings.nTable.offsetWidth;
  3562  			var iUserInputs = 0;
  3563  			var iTmpWidth;
  3564  			var iVisibleColumns = 0;
  3565  			var iColums = oSettings.aoColumns.length;
  3566  			var i, iIndex, iCorrector, iWidth;
  3567  			var oHeaders = $('th', oSettings.nTHead);
  3568  			var widthAttr = oSettings.nTable.getAttribute('width');
  3569  			var nWrapper = oSettings.nTable.parentNode;
  3570  			
  3571  			/* Convert any user input sizes into pixel sizes */
  3572  			for ( i=0 ; i<iColums ; i++ )
  3573  			{
  3574  				if ( oSettings.aoColumns[i].bVisible )
  3575  				{
  3576  					iVisibleColumns++;
  3577  					
  3578  					if ( oSettings.aoColumns[i].sWidth !== null )
  3579  					{
  3580  						iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidthOrig, 
  3581  							nWrapper );
  3582  						if ( iTmpWidth !== null )
  3583  						{
  3584  							oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
  3585  						}
  3586  							
  3587  						iUserInputs++;
  3588  					}
  3589  				}
  3590  			}
  3591  			
  3592  			/* If the number of columns in the DOM equals the number that we have to process in 
  3593  			 * DataTables, then we can use the offsets that are created by the web-browser. No custom 
  3594  			 * sizes can be set in order for this to happen, nor scrolling used
  3595  			 */
  3596  			if ( iColums == oHeaders.length && iUserInputs === 0 && iVisibleColumns == iColums &&
  3597  				oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
  3598  			{
  3599  				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
  3600  				{
  3601  					iTmpWidth = $(oHeaders[i]).width();
  3602  					if ( iTmpWidth !== null )
  3603  					{
  3604  						oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
  3605  					}
  3606  				}
  3607  			}
  3608  			else
  3609  			{
  3610  				/* Otherwise we are going to have to do some calculations to get the width of each column.
  3611  				 * Construct a 1 row table with the widest node in the data, and any user defined widths,
  3612  				 * then insert it into the DOM and allow the browser to do all the hard work of
  3613  				 * calculating table widths.
  3614  				 */
  3615  				var
  3616  					nCalcTmp = oSettings.nTable.cloneNode( false ),
  3617  					nTheadClone = oSettings.nTHead.cloneNode(true),
  3618  					nBody = document.createElement( 'tbody' ),
  3619  					nTr = document.createElement( 'tr' ),
  3620  					nDivSizing;
  3621  				
  3622  				nCalcTmp.removeAttribute( "id" );
  3623  				nCalcTmp.appendChild( nTheadClone );
  3624  				if ( oSettings.nTFoot !== null )
  3625  				{
  3626  					nCalcTmp.appendChild( oSettings.nTFoot.cloneNode(true) );
  3627  					_fnApplyToChildren( function(n) {
  3628  						n.style.width = "";
  3629  					}, nCalcTmp.getElementsByTagName('tr') );
  3630  				}
  3631  				
  3632  				nCalcTmp.appendChild( nBody );
  3633  				nBody.appendChild( nTr );
  3634  				
  3635  				/* Remove any sizing that was previously applied by the styles */
  3636  				var jqColSizing = $('thead th', nCalcTmp);
  3637  				if ( jqColSizing.length === 0 )
  3638  				{
  3639  					jqColSizing = $('tbody tr:eq(0)>td', nCalcTmp);
  3640  				}
  3641  		
  3642  				/* Apply custom sizing to the cloned header */
  3643  				var nThs = _fnGetUniqueThs( oSettings, nTheadClone );
  3644  				iCorrector = 0;
  3645  				for ( i=0 ; i<iColums ; i++ )
  3646  				{
  3647  					var oColumn = oSettings.aoColumns[i];
  3648  					if ( oColumn.bVisible && oColumn.sWidthOrig !== null && oColumn.sWidthOrig !== "" )
  3649  					{
  3650  						nThs[i-iCorrector].style.width = _fnStringToCss( oColumn.sWidthOrig );
  3651  					}
  3652  					else if ( oColumn.bVisible )
  3653  					{
  3654  						nThs[i-iCorrector].style.width = "";
  3655  					}
  3656  					else
  3657  					{
  3658  						iCorrector++;
  3659  					}
  3660  				}
  3661  		
  3662  				/* Find the biggest td for each column and put it into the table */
  3663  				for ( i=0 ; i<iColums ; i++ )
  3664  				{
  3665  					if ( oSettings.aoColumns[i].bVisible )
  3666  					{
  3667  						var nTd = _fnGetWidestNode( oSettings, i );
  3668  						if ( nTd !== null )
  3669  						{
  3670  							nTd = nTd.cloneNode(true);
  3671  							if ( oSettings.aoColumns[i].sContentPadding !== "" )
  3672  							{
  3673  								nTd.innerHTML += oSettings.aoColumns[i].sContentPadding;
  3674  							}
  3675  							nTr.appendChild( nTd );
  3676  						}
  3677  					}
  3678  				}
  3679  				
  3680  				/* Build the table and 'display' it */
  3681  				nWrapper.appendChild( nCalcTmp );
  3682  				
  3683  				/* When scrolling (X or Y) we want to set the width of the table as appropriate. However,
  3684  				 * when not scrolling leave the table width as it is. This results in slightly different,
  3685  				 * but I think correct behaviour
  3686  				 */
  3687  				if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" )
  3688  				{
  3689  					nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner);
  3690  				}
  3691  				else if ( oSettings.oScroll.sX !== "" )
  3692  				{
  3693  					nCalcTmp.style.width = "";
  3694  					if ( $(nCalcTmp).width() < nWrapper.offsetWidth )
  3695  					{
  3696  						nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
  3697  					}
  3698  				}
  3699  				else if ( oSettings.oScroll.sY !== "" )
  3700  				{
  3701  					nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
  3702  				}
  3703  				else if ( widthAttr )
  3704  				{
  3705  					nCalcTmp.style.width = _fnStringToCss( widthAttr );
  3706  				}
  3707  				nCalcTmp.style.visibility = "hidden";
  3708  				
  3709  				/* Scrolling considerations */
  3710  				_fnScrollingWidthAdjust( oSettings, nCalcTmp );
  3711  				
  3712  				/* Read the width's calculated by the browser and store them for use by the caller. We
  3713  				 * first of all try to use the elements in the body, but it is possible that there are
  3714  				 * no elements there, under which circumstances we use the header elements
  3715  				 */
  3716  				var oNodes = $("tbody tr:eq(0)", nCalcTmp).children();
  3717  				if ( oNodes.length === 0 )
  3718  				{
  3719  					oNodes = _fnGetUniqueThs( oSettings, $('thead', nCalcTmp)[0] );
  3720  				}
  3721  		
  3722  				/* Browsers need a bit of a hand when a width is assigned to any columns when 
  3723  				 * x-scrolling as they tend to collapse the table to the min-width, even if
  3724  				 * we sent the column widths. So we need to keep track of what the table width
  3725  				 * should be by summing the user given values, and the automatic values
  3726  				 */
  3727  				if ( oSettings.oScroll.sX !== "" )
  3728  				{
  3729  					var iTotal = 0;
  3730  					iCorrector = 0;
  3731  					for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
  3732  					{
  3733  						if ( oSettings.aoColumns[i].bVisible )
  3734  						{
  3735  							if ( oSettings.aoColumns[i].sWidthOrig === null )
  3736  							{
  3737  								iTotal += $(oNodes[iCorrector]).outerWidth();
  3738  							}
  3739  							else
  3740  							{
  3741  								iTotal += parseInt(oSettings.aoColumns[i].sWidth.replace('px',''), 10) +
  3742  									($(oNodes[iCorrector]).outerWidth() - $(oNodes[iCorrector]).width());
  3743  							}
  3744  							iCorrector++;
  3745  						}
  3746  					}
  3747  					
  3748  					nCalcTmp.style.width = _fnStringToCss( iTotal );
  3749  					oSettings.nTable.style.width = _fnStringToCss( iTotal );
  3750  				}
  3751  		
  3752  				iCorrector = 0;
  3753  				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
  3754  				{
  3755  					if ( oSettings.aoColumns[i].bVisible )
  3756  					{
  3757  						iWidth = $(oNodes[iCorrector]).width();
  3758  						if ( iWidth !== null && iWidth > 0 )
  3759  						{
  3760  							oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth );
  3761  						}
  3762  						iCorrector++;
  3763  					}
  3764  				}
  3765  		
  3766  				var cssWidth = $(nCalcTmp).css('width');
  3767  				oSettings.nTable.style.width = (cssWidth.indexOf('%') !== -1) ?
  3768  				    cssWidth : _fnStringToCss( $(nCalcTmp).outerWidth() );
  3769  				nCalcTmp.parentNode.removeChild( nCalcTmp );
  3770  			}
  3771  		
  3772  			if ( widthAttr )
  3773  			{
  3774  				oSettings.nTable.style.width = _fnStringToCss( widthAttr );
  3775  			}
  3776  		}
  3777  		
  3778  		
  3779  		/**
  3780  		 * Adjust a table's width to take account of scrolling
  3781  		 *  @param {object} oSettings dataTables settings object
  3782  		 *  @param {node} n table node
  3783  		 *  @memberof DataTable#oApi
  3784  		 */
  3785  		function _fnScrollingWidthAdjust ( oSettings, n )
  3786  		{
  3787  			if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" )
  3788  			{
  3789  				/* When y-scrolling only, we want to remove the width of the scroll bar so the table
  3790  				 * + scroll bar will fit into the area avaialble.
  3791  				 */
  3792  				var iOrigWidth = $(n).width();
  3793  				n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth );
  3794  			}
  3795  			else if ( oSettings.oScroll.sX !== "" )
  3796  			{
  3797  				/* When x-scrolling both ways, fix the table at it's current size, without adjusting */
  3798  				n.style.width = _fnStringToCss( $(n).outerWidth() );
  3799  			}
  3800  		}
  3801  		
  3802  		
  3803  		/**
  3804  		 * Get the widest node
  3805  		 *  @param {object} oSettings dataTables settings object
  3806  		 *  @param {int} iCol column of interest
  3807  		 *  @returns {node} widest table node
  3808  		 *  @memberof DataTable#oApi
  3809  		 */
  3810  		function _fnGetWidestNode( oSettings, iCol )
  3811  		{
  3812  			var iMaxIndex = _fnGetMaxLenString( oSettings, iCol );
  3813  			if ( iMaxIndex < 0 )
  3814  			{
  3815  				return null;
  3816  			}
  3817  		
  3818  			if ( oSettings.aoData[iMaxIndex].nTr === null )
  3819  			{
  3820  				var n = document.createElement('td');
  3821  				n.innerHTML = _fnGetCellData( oSettings, iMaxIndex, iCol, '' );
  3822  				return n;
  3823  			}
  3824  			return _fnGetTdNodes(oSettings, iMaxIndex)[iCol];
  3825  		}
  3826  		
  3827  		
  3828  		/**
  3829  		 * Get the maximum strlen for each data column
  3830  		 *  @param {object} oSettings dataTables settings object
  3831  		 *  @param {int} iCol column of interest
  3832  		 *  @returns {string} max string length for each column
  3833  		 *  @memberof DataTable#oApi
  3834  		 */
  3835  		function _fnGetMaxLenString( oSettings, iCol )
  3836  		{
  3837  			var iMax = -1;
  3838  			var iMaxIndex = -1;
  3839  			
  3840  			for ( var i=0 ; i<oSettings.aoData.length ; i++ )
  3841  			{
  3842  				var s = _fnGetCellData( oSettings, i, iCol, 'display' )+"";
  3843  				s = s.replace( /<.*?>/g, "" );
  3844  				if ( s.length > iMax )
  3845  				{
  3846  					iMax = s.length;
  3847  					iMaxIndex = i;
  3848  				}
  3849  			}
  3850  			
  3851  			return iMaxIndex;
  3852  		}
  3853  		
  3854  		
  3855  		/**
  3856  		 * Append a CSS unit (only if required) to a string
  3857  		 *  @param {array} aArray1 first array
  3858  		 *  @param {array} aArray2 second array
  3859  		 *  @returns {int} 0 if match, 1 if length is different, 2 if no match
  3860  		 *  @memberof DataTable#oApi
  3861  		 */
  3862  		function _fnStringToCss( s )
  3863  		{
  3864  			if ( s === null )
  3865  			{
  3866  				return "0px";
  3867  			}
  3868  			
  3869  			if ( typeof s == 'number' )
  3870  			{
  3871  				if ( s < 0 )
  3872  				{
  3873  					return "0px";
  3874  				}
  3875  				return s+"px";
  3876  			}
  3877  			
  3878  			/* Check if the last character is not 0-9 */
  3879  			var c = s.charCodeAt( s.length-1 );
  3880  			if (c < 0x30 || c > 0x39)
  3881  			{
  3882  				return s;
  3883  			}
  3884  			return s+"px";
  3885  		}
  3886  		
  3887  		
  3888  		/**
  3889  		 * Get the width of a scroll bar in this browser being used
  3890  		 *  @returns {int} width in pixels
  3891  		 *  @memberof DataTable#oApi
  3892  		 */
  3893  		function _fnScrollBarWidth ()
  3894  		{  
  3895  			var inner = document.createElement('p');
  3896  			var style = inner.style;
  3897  			style.width = "100%";
  3898  			style.height = "200px";
  3899  			style.padding = "0px";
  3900  			
  3901  			var outer = document.createElement('div');
  3902  			style = outer.style;
  3903  			style.position = "absolute";
  3904  			style.top = "0px";
  3905  			style.left = "0px";
  3906  			style.visibility = "hidden";
  3907  			style.width = "200px";
  3908  			style.height = "150px";
  3909  			style.padding = "0px";
  3910  			style.overflow = "hidden";
  3911  			outer.appendChild(inner);
  3912  			
  3913  			document.body.appendChild(outer);
  3914  			var w1 = inner.offsetWidth;
  3915  			outer.style.overflow = 'scroll';
  3916  			var w2 = inner.offsetWidth;
  3917  			if ( w1 == w2 )
  3918  			{
  3919  				w2 = outer.clientWidth;
  3920  			}
  3921  			
  3922  			document.body.removeChild(outer);
  3923  			return (w1 - w2);  
  3924  		}
  3925  		
  3926  		/**
  3927  		 * Change the order of the table
  3928  		 *  @param {object} oSettings dataTables settings object
  3929  		 *  @param {bool} bApplyClasses optional - should we apply classes or not
  3930  		 *  @memberof DataTable#oApi
  3931  		 */
  3932  		function _fnSort ( oSettings, bApplyClasses )
  3933  		{
  3934  			var
  3935  				i, iLen, j, jLen, k, kLen,
  3936  				sDataType, nTh,
  3937  				aaSort = [],
  3938  			 	aiOrig = [],
  3939  				oSort = DataTable.ext.oSort,
  3940  				aoData = oSettings.aoData,
  3941  				aoColumns = oSettings.aoColumns,
  3942  				oAria = oSettings.oLanguage.oAria;
  3943  			
  3944  			/* No sorting required if server-side or no sorting array */
  3945  			if ( !oSettings.oFeatures.bServerSide && 
  3946  				(oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) )
  3947  			{
  3948  				aaSort = ( oSettings.aaSortingFixed !== null ) ?
  3949  					oSettings.aaSortingFixed.concat( oSettings.aaSorting ) :
  3950  					oSettings.aaSorting.slice();
  3951  				
  3952  				/* If there is a sorting data type, and a function belonging to it, then we need to
  3953  				 * get the data from the developer's function and apply it for this column
  3954  				 */
  3955  				for ( i=0 ; i<aaSort.length ; i++ )
  3956  				{
  3957  					var iColumn = aaSort[i][0];
  3958  					var iVisColumn = _fnColumnIndexToVisible( oSettings, iColumn );
  3959  					sDataType = oSettings.aoColumns[ iColumn ].sSortDataType;
  3960  					if ( DataTable.ext.afnSortData[sDataType] )
  3961  					{
  3962  						var aData = DataTable.ext.afnSortData[sDataType].call( 
  3963  							oSettings.oInstance, oSettings, iColumn, iVisColumn
  3964  						);
  3965  						if ( aData.length === aoData.length )
  3966  						{
  3967  							for ( j=0, jLen=aoData.length ; j<jLen ; j++ )
  3968  							{
  3969  								_fnSetCellData( oSettings, j, iColumn, aData[j] );
  3970  							}
  3971  						}
  3972  						else
  3973  						{
  3974  							_fnLog( oSettings, 0, "Returned data sort array (col "+iColumn+") is the wrong length" );
  3975  						}
  3976  					}
  3977  				}
  3978  				
  3979  				/* Create a value - key array of the current row positions such that we can use their
  3980  				 * current position during the sort, if values match, in order to perform stable sorting
  3981  				 */
  3982  				for ( i=0, iLen=oSettings.aiDisplayMaster.length ; i<iLen ; i++ )
  3983  				{
  3984  					aiOrig[ oSettings.aiDisplayMaster[i] ] = i;
  3985  				}
  3986  		
  3987  				/* Build an internal data array which is specific to the sort, so we can get and prep
  3988  				 * the data to be sorted only once, rather than needing to do it every time the sorting
  3989  				 * function runs. This make the sorting function a very simple comparison
  3990  				 */
  3991  				var iSortLen = aaSort.length;
  3992  				var fnSortFormat, aDataSort;
  3993  				for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
  3994  				{
  3995  					for ( j=0 ; j<iSortLen ; j++ )
  3996  					{
  3997  						aDataSort = aoColumns[ aaSort[j][0] ].aDataSort;
  3998  		
  3999  						for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
  4000  						{
  4001  							sDataType = aoColumns[ aDataSort[k] ].sType;
  4002  							fnSortFormat = oSort[ (sDataType ? sDataType : 'string')+"-pre" ];
  4003  							
  4004  							aoData[i]._aSortData[ aDataSort[k] ] = fnSortFormat ?
  4005  								fnSortFormat( _fnGetCellData( oSettings, i, aDataSort[k], 'sort' ) ) :
  4006  								_fnGetCellData( oSettings, i, aDataSort[k], 'sort' );
  4007  						}
  4008  					}
  4009  				}
  4010  				
  4011  				/* Do the sort - here we want multi-column sorting based on a given data source (column)
  4012  				 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
  4013  				 * follow on it's own, but this is what we want (example two column sorting):
  4014  				 *  fnLocalSorting = function(a,b){
  4015  				 *  	var iTest;
  4016  				 *  	iTest = oSort['string-asc']('data11', 'data12');
  4017  				 *  	if (iTest !== 0)
  4018  				 *  		return iTest;
  4019  				 *    iTest = oSort['numeric-desc']('data21', 'data22');
  4020  				 *    if (iTest !== 0)
  4021  				 *  		return iTest;
  4022  				 *  	return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
  4023  				 *  }
  4024  				 * Basically we have a test for each sorting column, if the data in that column is equal,
  4025  				 * test the next column. If all columns match, then we use a numeric sort on the row 
  4026  				 * positions in the original data array to provide a stable sort.
  4027  				 */
  4028  				oSettings.aiDisplayMaster.sort( function ( a, b ) {
  4029  					var k, l, lLen, iTest, aDataSort, sDataType;
  4030  					for ( k=0 ; k<iSortLen ; k++ )
  4031  					{
  4032  						aDataSort = aoColumns[ aaSort[k][0] ].aDataSort;
  4033  		
  4034  						for ( l=0, lLen=aDataSort.length ; l<lLen ; l++ )
  4035  						{
  4036  							sDataType = aoColumns[ aDataSort[l] ].sType;
  4037  							
  4038  							iTest = oSort[ (sDataType ? sDataType : 'string')+"-"+aaSort[k][1] ](
  4039  								aoData[a]._aSortData[ aDataSort[l] ],
  4040  								aoData[b]._aSortData[ aDataSort[l] ]
  4041  							);
  4042  						
  4043  							if ( iTest !== 0 )
  4044  							{
  4045  								return iTest;
  4046  							}
  4047  						}
  4048  					}
  4049  					
  4050  					return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
  4051  				} );
  4052  			}
  4053  			
  4054  			/* Alter the sorting classes to take account of the changes */
  4055  			if ( (bApplyClasses === undefined || bApplyClasses) && !oSettings.oFeatures.bDeferRender )
  4056  			{
  4057  				_fnSortingClasses( oSettings );
  4058  			}
  4059  		
  4060  			for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
  4061  			{
  4062  				var sTitle = aoColumns[i].sTitle.replace( /<.*?>/g, "" );
  4063  				nTh = aoColumns[i].nTh;
  4064  				nTh.removeAttribute('aria-sort');
  4065  				nTh.removeAttribute('aria-label');
  4066  				
  4067  				/* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
  4068  				if ( aoColumns[i].bSortable )
  4069  				{
  4070  					if ( aaSort.length > 0 && aaSort[0][0] == i )
  4071  					{
  4072  						nTh.setAttribute('aria-sort', aaSort[0][1]=="asc" ? "ascending" : "descending" );
  4073  						
  4074  						var nextSort = (aoColumns[i].asSorting[ aaSort[0][2]+1 ]) ? 
  4075  							aoColumns[i].asSorting[ aaSort[0][2]+1 ] : aoColumns[i].asSorting[0];
  4076  						nTh.setAttribute('aria-label', sTitle+
  4077  							(nextSort=="asc" ? oAria.sSortAscending : oAria.sSortDescending) );
  4078  					}
  4079  					else
  4080  					{
  4081  						nTh.setAttribute('aria-label', sTitle+
  4082  							(aoColumns[i].asSorting[0]=="asc" ? oAria.sSortAscending : oAria.sSortDescending) );
  4083  					}
  4084  				}
  4085  				else
  4086  				{
  4087  					nTh.setAttribute('aria-label', sTitle);
  4088  				}
  4089  			}
  4090  			
  4091  			/* Tell the draw function that we have sorted the data */
  4092  			oSettings.bSorted = true;
  4093  			$(oSettings.oInstance).trigger('sort', oSettings);
  4094  			
  4095  			/* Copy the master data into the draw array and re-draw */
  4096  			if ( oSettings.oFeatures.bFilter )
  4097  			{
  4098  				/* _fnFilter() will redraw the table for us */
  4099  				_fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
  4100  			}
  4101  			else
  4102  			{
  4103  				oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
  4104  				oSettings._iDisplayStart = 0; /* reset display back to page 0 */
  4105  				_fnCalculateEnd( oSettings );
  4106  				_fnDraw( oSettings );
  4107  			}
  4108  		}
  4109  		
  4110  		
  4111  		/**
  4112  		 * Attach a sort handler (click) to a node
  4113  		 *  @param {object} oSettings dataTables settings object
  4114  		 *  @param {node} nNode node to attach the handler to
  4115  		 *  @param {int} iDataIndex column sorting index
  4116  		 *  @param {function} [fnCallback] callback function
  4117  		 *  @memberof DataTable#oApi
  4118  		 */
  4119  		function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback )
  4120  		{
  4121  			_fnBindAction( nNode, {}, function (e) {
  4122  				/* If the column is not sortable - don't to anything */
  4123  				if ( oSettings.aoColumns[iDataIndex].bSortable === false )
  4124  				{
  4125  					return;
  4126  				}
  4127  				
  4128  				/*
  4129  				 * This is a little bit odd I admit... I declare a temporary function inside the scope of
  4130  				 * _fnBuildHead and the click handler in order that the code presented here can be used 
  4131  				 * twice - once for when bProcessing is enabled, and another time for when it is 
  4132  				 * disabled, as we need to perform slightly different actions.
  4133  				 *   Basically the issue here is that the Javascript engine in modern browsers don't 
  4134  				 * appear to allow the rendering engine to update the display while it is still executing
  4135  				 * it's thread (well - it does but only after long intervals). This means that the 
  4136  				 * 'processing' display doesn't appear for a table sort. To break the js thread up a bit
  4137  				 * I force an execution break by using setTimeout - but this breaks the expected 
  4138  				 * thread continuation for the end-developer's point of view (their code would execute
  4139  				 * too early), so we only do it when we absolutely have to.
  4140  				 */
  4141  				var fnInnerSorting = function () {
  4142  					var iColumn, iNextSort;
  4143  					
  4144  					/* If the shift key is pressed then we are multiple column sorting */
  4145  					if ( e.shiftKey )
  4146  					{
  4147  						/* Are we already doing some kind of sort on this column? */
  4148  						var bFound = false;
  4149  						for ( var i=0 ; i<oSettings.aaSorting.length ; i++ )
  4150  						{
  4151  							if ( oSettings.aaSorting[i][0] == iDataIndex )
  4152  							{
  4153  								bFound = true;
  4154  								iColumn = oSettings.aaSorting[i][0];
  4155  								iNextSort = oSettings.aaSorting[i][2]+1;
  4156  								
  4157  								if ( !oSettings.aoColumns[iColumn].asSorting[iNextSort] )
  4158  								{
  4159  									/* Reached the end of the sorting options, remove from multi-col sort */
  4160  									oSettings.aaSorting.splice( i, 1 );
  4161  								}
  4162  								else
  4163  								{
  4164  									/* Move onto next sorting direction */
  4165  									oSettings.aaSorting[i][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
  4166  									oSettings.aaSorting[i][2] = iNextSort;
  4167  								}
  4168  								break;
  4169  							}
  4170  						}
  4171  						
  4172  						/* No sort yet - add it in */
  4173  						if ( bFound === false )
  4174  						{
  4175  							oSettings.aaSorting.push( [ iDataIndex, 
  4176  								oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
  4177  						}
  4178  					}
  4179  					else
  4180  					{
  4181  						/* If no shift key then single column sort */
  4182  						if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex )
  4183  						{
  4184  							iColumn = oSettings.aaSorting[0][0];
  4185  							iNextSort = oSettings.aaSorting[0][2]+1;
  4186  							if ( !oSettings.aoColumns[iColumn].asSorting[iNextSort] )
  4187  							{
  4188  								iNextSort = 0;
  4189  							}
  4190  							oSettings.aaSorting[0][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
  4191  							oSettings.aaSorting[0][2] = iNextSort;
  4192  						}
  4193  						else
  4194  						{
  4195  							oSettings.aaSorting.splice( 0, oSettings.aaSorting.length );
  4196  							oSettings.aaSorting.push( [ iDataIndex, 
  4197  								oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
  4198  						}
  4199  					}
  4200  					
  4201  					/* Run the sort */
  4202  					_fnSort( oSettings );
  4203  				}; /* /fnInnerSorting */
  4204  				
  4205  				if ( !oSettings.oFeatures.bProcessing )
  4206  				{
  4207  					fnInnerSorting();
  4208  				}
  4209  				else
  4210  				{
  4211  					_fnProcessingDisplay( oSettings, true );
  4212  					setTimeout( function() {
  4213  						fnInnerSorting();
  4214  						if ( !oSettings.oFeatures.bServerSide )
  4215  						{
  4216  							_fnProcessingDisplay( oSettings, false );
  4217  						}
  4218  					}, 0 );
  4219  				}
  4220  				
  4221  				/* Call the user specified callback function - used for async user interaction */
  4222  				if ( typeof fnCallback == 'function' )
  4223  				{
  4224  					fnCallback( oSettings );
  4225  				}
  4226  			} );
  4227  		}
  4228  		
  4229  		
  4230  		/**
  4231  		 * Set the sorting classes on the header, Note: it is safe to call this function 
  4232  		 * when bSort and bSortClasses are false
  4233  		 *  @param {object} oSettings dataTables settings object
  4234  		 *  @memberof DataTable#oApi
  4235  		 */
  4236  		function _fnSortingClasses( oSettings )
  4237  		{
  4238  			var i, iLen, j, jLen, iFound;
  4239  			var aaSort, sClass;
  4240  			var iColumns = oSettings.aoColumns.length;
  4241  			var oClasses = oSettings.oClasses;
  4242  			
  4243  			for ( i=0 ; i<iColumns ; i++ )
  4244  			{
  4245  				if ( oSettings.aoColumns[i].bSortable )
  4246  				{
  4247  					$(oSettings.aoColumns[i].nTh).removeClass( oClasses.sSortAsc +" "+ oClasses.sSortDesc +
  4248  						" "+ oSettings.aoColumns[i].sSortingClass );
  4249  				}
  4250  			}
  4251  			
  4252  			if ( oSettings.aaSortingFixed !== null )
  4253  			{
  4254  				aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
  4255  			}
  4256  			else
  4257  			{
  4258  				aaSort = oSettings.aaSorting.slice();
  4259  			}
  4260  			
  4261  			/* Apply the required classes to the header */
  4262  			for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
  4263  			{
  4264  				if ( oSettings.aoColumns[i].bSortable )
  4265  				{
  4266  					sClass = oSettings.aoColumns[i].sSortingClass;
  4267  					iFound = -1;
  4268  					for ( j=0 ; j<aaSort.length ; j++ )
  4269  					{
  4270  						if ( aaSort[j][0] == i )
  4271  						{
  4272  							sClass = ( aaSort[j][1] == "asc" ) ?
  4273  								oClasses.sSortAsc : oClasses.sSortDesc;
  4274  							iFound = j;
  4275  							break;
  4276  						}
  4277  					}
  4278  					$(oSettings.aoColumns[i].nTh).addClass( sClass );
  4279  					
  4280  					if ( oSettings.bJUI )
  4281  					{
  4282  						/* jQuery UI uses extra markup */
  4283  						var jqSpan = $("span."+oClasses.sSortIcon,  oSettings.aoColumns[i].nTh);
  4284  						jqSpan.removeClass(oClasses.sSortJUIAsc +" "+ oClasses.sSortJUIDesc +" "+ 
  4285  							oClasses.sSortJUI +" "+ oClasses.sSortJUIAscAllowed +" "+ oClasses.sSortJUIDescAllowed );
  4286  						
  4287  						var sSpanClass;
  4288  						if ( iFound == -1 )
  4289  						{
  4290  						 	sSpanClass = oSettings.aoColumns[i].sSortingClassJUI;
  4291  						}
  4292  						else if ( aaSort[iFound][1] == "asc" )
  4293  						{
  4294  							sSpanClass = oClasses.sSortJUIAsc;
  4295  						}
  4296  						else
  4297  						{
  4298  							sSpanClass = oClasses.sSortJUIDesc;
  4299  						}
  4300  						
  4301  						jqSpan.addClass( sSpanClass );
  4302  					}
  4303  				}
  4304  				else
  4305  				{
  4306  					/* No sorting on this column, so add the base class. This will have been assigned by
  4307  					 * _fnAddColumn
  4308  					 */
  4309  					$(oSettings.aoColumns[i].nTh).addClass( oSettings.aoColumns[i].sSortingClass );
  4310  				}
  4311  			}
  4312  			
  4313  			/* 
  4314  			 * Apply the required classes to the table body
  4315  			 * Note that this is given as a feature switch since it can significantly slow down a sort
  4316  			 * on large data sets (adding and removing of classes is always slow at the best of times..)
  4317  			 * Further to this, note that this code is admittedly fairly ugly. It could be made a lot 
  4318  			 * simpler using jQuery selectors and add/removeClass, but that is significantly slower
  4319  			 * (on the order of 5 times slower) - hence the direct DOM manipulation here.
  4320  			 * Note that for deferred drawing we do use jQuery - the reason being that taking the first
  4321  			 * row found to see if the whole column needs processed can miss classes since the first
  4322  			 * column might be new.
  4323  			 */
  4324  			sClass = oClasses.sSortColumn;
  4325  			
  4326  			if ( oSettings.oFeatures.bSort && oSettings.oFeatures.bSortClasses )
  4327  			{
  4328  				var nTds = _fnGetTdNodes( oSettings );
  4329  				
  4330  				/* Determine what the sorting class for each column should be */
  4331  				var iClass, iTargetCol;
  4332  				var asClasses = [];
  4333  				for (i = 0; i < iColumns; i++)
  4334  				{
  4335  					asClasses.push("");
  4336  				}
  4337  				for (i = 0, iClass = 1; i < aaSort.length; i++)
  4338  				{
  4339  					iTargetCol = parseInt( aaSort[i][0], 10 );
  4340  					asClasses[iTargetCol] = sClass + iClass;
  4341  					
  4342  					if ( iClass < 3 )
  4343  					{
  4344  						iClass++;
  4345  					}
  4346  				}
  4347  				
  4348  				/* Make changes to the classes for each cell as needed */
  4349  				var reClass = new RegExp(sClass + "[123]");
  4350  				var sTmpClass, sCurrentClass, sNewClass;
  4351  				for ( i=0, iLen=nTds.length; i<iLen; i++ )
  4352  				{
  4353  					/* Determine which column we're looking at */
  4354  					iTargetCol = i % iColumns;
  4355  					
  4356  					/* What is the full list of classes now */
  4357  					sCurrentClass = nTds[i].className;
  4358  					/* What sorting class should be applied? */
  4359  					sNewClass = asClasses[iTargetCol];
  4360  					/* What would the new full list be if we did a replacement? */
  4361  					sTmpClass = sCurrentClass.replace(reClass, sNewClass);
  4362  					
  4363  					if ( sTmpClass != sCurrentClass )
  4364  					{
  4365  						/* We changed something */
  4366  						nTds[i].className = $.trim( sTmpClass );
  4367  					}
  4368  					else if ( sNewClass.length > 0 && sCurrentClass.indexOf(sNewClass) == -1 )
  4369  					{
  4370  						/* We need to add a class */
  4371  						nTds[i].className = sCurrentClass + " " + sNewClass;
  4372  					}
  4373  				}
  4374  			}
  4375  		}
  4376  		
  4377  		
  4378  		
  4379  		/**
  4380  		 * Save the state of a table in a cookie such that the page can be reloaded
  4381  		 *  @param {object} oSettings dataTables settings object
  4382  		 *  @memberof DataTable#oApi
  4383  		 */
  4384  		function _fnSaveState ( oSettings )
  4385  		{
  4386  			if ( !oSettings.oFeatures.bStateSave || oSettings.bDestroying )
  4387  			{
  4388  				return;
  4389  			}
  4390  		
  4391  			/* Store the interesting variables */
  4392  			var i, iLen, bInfinite=oSettings.oScroll.bInfinite;
  4393  			var oState = {
  4394  				"iCreate":      new Date().getTime(),
  4395  				"iStart":       (bInfinite ? 0 : oSettings._iDisplayStart),
  4396  				"iEnd":         (bInfinite ? oSettings._iDisplayLength : oSettings._iDisplayEnd),
  4397  				"iLength":      oSettings._iDisplayLength,
  4398  				"aaSorting":    $.extend( true, [], oSettings.aaSorting ),
  4399  				"oSearch":      $.extend( true, {}, oSettings.oPreviousSearch ),
  4400  				"aoSearchCols": $.extend( true, [], oSettings.aoPreSearchCols ),
  4401  				"abVisCols":    []
  4402  			};
  4403  		
  4404  			for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
  4405  			{
  4406  				oState.abVisCols.push( oSettings.aoColumns[i].bVisible );
  4407  			}
  4408  		
  4409  			_fnCallbackFire( oSettings, "aoStateSaveParams", 'stateSaveParams', [oSettings, oState] );
  4410  			
  4411  			oSettings.fnStateSave.call( oSettings.oInstance, oSettings, oState );
  4412  		}
  4413  		
  4414  		
  4415  		/**
  4416  		 * Attempt to load a saved table state from a cookie
  4417  		 *  @param {object} oSettings dataTables settings object
  4418  		 *  @param {object} oInit DataTables init object so we can override settings
  4419  		 *  @memberof DataTable#oApi
  4420  		 */
  4421  		function _fnLoadState ( oSettings, oInit )
  4422  		{
  4423  			if ( !oSettings.oFeatures.bStateSave )
  4424  			{
  4425  				return;
  4426  			}
  4427  		
  4428  			var oData = oSettings.fnStateLoad.call( oSettings.oInstance, oSettings );
  4429  			if ( !oData )
  4430  			{
  4431  				return;
  4432  			}
  4433  			
  4434  			/* Allow custom and plug-in manipulation functions to alter the saved data set and
  4435  			 * cancelling of loading by returning false
  4436  			 */
  4437  			var abStateLoad = _fnCallbackFire( oSettings, 'aoStateLoadParams', 'stateLoadParams', [oSettings, oData] );
  4438  			if ( $.inArray( false, abStateLoad ) !== -1 )
  4439  			{
  4440  				return;
  4441  			}
  4442  			
  4443  			/* Store the saved state so it might be accessed at any time */
  4444  			oSettings.oLoadedState = $.extend( true, {}, oData );
  4445  			
  4446  			/* Restore key features */
  4447  			oSettings._iDisplayStart    = oData.iStart;
  4448  			oSettings.iInitDisplayStart = oData.iStart;
  4449  			oSettings._iDisplayEnd      = oData.iEnd;
  4450  			oSettings._iDisplayLength   = oData.iLength;
  4451  			oSettings.aaSorting         = oData.aaSorting.slice();
  4452  			oSettings.saved_aaSorting   = oData.aaSorting.slice();
  4453  			
  4454  			/* Search filtering  */
  4455  			$.extend( oSettings.oPreviousSearch, oData.oSearch );
  4456  			$.extend( true, oSettings.aoPreSearchCols, oData.aoSearchCols );
  4457  			
  4458  			/* Column visibility state
  4459  			 * Pass back visibility settings to the init handler, but to do not here override
  4460  			 * the init object that the user might have passed in
  4461  			 */
  4462  			oInit.saved_aoColumns = [];
  4463  			for ( var i=0 ; i<oData.abVisCols.length ; i++ )
  4464  			{
  4465  				oInit.saved_aoColumns[i] = {};
  4466  				oInit.saved_aoColumns[i].bVisible = oData.abVisCols[i];
  4467  			}
  4468  		
  4469  			_fnCallbackFire( oSettings, 'aoStateLoaded', 'stateLoaded', [oSettings, oData] );
  4470  		}
  4471  		
  4472  		
  4473  		/**
  4474  		 * Create a new cookie with a value to store the state of a table
  4475  		 *  @param {string} sName name of the cookie to create
  4476  		 *  @param {string} sValue the value the cookie should take
  4477  		 *  @param {int} iSecs duration of the cookie
  4478  		 *  @param {string} sBaseName sName is made up of the base + file name - this is the base
  4479  		 *  @param {function} fnCallback User definable function to modify the cookie
  4480  		 *  @memberof DataTable#oApi
  4481  		 */
  4482  		function _fnCreateCookie ( sName, sValue, iSecs, sBaseName, fnCallback )
  4483  		{
  4484  			var date = new Date();
  4485  			date.setTime( date.getTime()+(iSecs*1000) );
  4486  			
  4487  			/* 
  4488  			 * Shocking but true - it would appear IE has major issues with having the path not having
  4489  			 * a trailing slash on it. We need the cookie to be available based on the path, so we
  4490  			 * have to append the file name to the cookie name. Appalling. Thanks to vex for adding the
  4491  			 * patch to use at least some of the path
  4492  			 */
  4493  			var aParts = window.location.pathname.split('/');
  4494  			var sNameFile = sName + '_' + aParts.pop().replace(/[\/:]/g,"").toLowerCase();
  4495  			var sFullCookie, oData;
  4496  			
  4497  			if ( fnCallback !== null )
  4498  			{
  4499  				oData = (typeof $.parseJSON === 'function') ? 
  4500  					$.parseJSON( sValue ) : eval( '('+sValue+')' );
  4501  				sFullCookie = fnCallback( sNameFile, oData, date.toGMTString(),
  4502  					aParts.join('/')+"/" );
  4503  			}
  4504  			else
  4505  			{
  4506  				sFullCookie = sNameFile + "=" + encodeURIComponent(sValue) +
  4507  					"; expires=" + date.toGMTString() +"; path=" + aParts.join('/')+"/";
  4508  			}
  4509  			
  4510  			/* Are we going to go over the cookie limit of 4KiB? If so, try to delete a cookies
  4511  			 * belonging to DataTables.
  4512  			 */
  4513  			var
  4514  				aCookies =document.cookie.split(';'),
  4515  				iNewCookieLen = sFullCookie.split(';')[0].length,
  4516  				aOldCookies = [];
  4517  			
  4518  			if ( iNewCookieLen+document.cookie.length+10 > 4096 ) /* Magic 10 for padding */
  4519  			{
  4520  				for ( var i=0, iLen=aCookies.length ; i<iLen ; i++ )
  4521  				{
  4522  					if ( aCookies[i].indexOf( sBaseName ) != -1 )
  4523  					{
  4524  						/* It's a DataTables cookie, so eval it and check the time stamp */
  4525  						var aSplitCookie = aCookies[i].split('=');
  4526  						try {
  4527  							oData = eval( '('+decodeURIComponent(aSplitCookie[1])+')' );
  4528  		
  4529  							if ( oData && oData.iCreate )
  4530  							{
  4531  								aOldCookies.push( {
  4532  									"name": aSplitCookie[0],
  4533  									"time": oData.iCreate
  4534  								} );
  4535  							}
  4536  						}
  4537  						catch( e ) {}
  4538  					}
  4539  				}
  4540  		
  4541  				// Make sure we delete the oldest ones first
  4542  				aOldCookies.sort( function (a, b) {
  4543  					return b.time - a.time;
  4544  				} );
  4545  		
  4546  				// Eliminate as many old DataTables cookies as we need to
  4547  				while ( iNewCookieLen + document.cookie.length + 10 > 4096 ) {
  4548  					if ( aOldCookies.length === 0 ) {
  4549  						// Deleted all DT cookies and still not enough space. Can't state save
  4550  						return;
  4551  					}
  4552  					
  4553  					var old = aOldCookies.pop();
  4554  					document.cookie = old.name+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+
  4555  						aParts.join('/') + "/";
  4556  				}
  4557  			}
  4558  			
  4559  			document.cookie = sFullCookie;
  4560  		}
  4561  		
  4562  		
  4563  		/**
  4564  		 * Read an old cookie to get a cookie with an old table state
  4565  		 *  @param {string} sName name of the cookie to read
  4566  		 *  @returns {string} contents of the cookie - or null if no cookie with that name found
  4567  		 *  @memberof DataTable#oApi
  4568  		 */
  4569  		function _fnReadCookie ( sName )
  4570  		{
  4571  			var
  4572  				aParts = window.location.pathname.split('/'),
  4573  				sNameEQ = sName + '_' + aParts[aParts.length-1].replace(/[\/:]/g,"").toLowerCase() + '=',
  4574  			 	sCookieContents = document.cookie.split(';');
  4575  			
  4576  			for( var i=0 ; i<sCookieContents.length ; i++ )
  4577  			{
  4578  				var c = sCookieContents[i];
  4579  				
  4580  				while (c.charAt(0)==' ')
  4581  				{
  4582  					c = c.substring(1,c.length);
  4583  				}
  4584  				
  4585  				if (c.indexOf(sNameEQ) === 0)
  4586  				{
  4587  					return decodeURIComponent( c.substring(sNameEQ.length,c.length) );
  4588  				}
  4589  			}
  4590  			return null;
  4591  		}
  4592  		
  4593  		
  4594  		/**
  4595  		 * Return the settings object for a particular table
  4596  		 *  @param {node} nTable table we are using as a dataTable
  4597  		 *  @returns {object} Settings object - or null if not found
  4598  		 *  @memberof DataTable#oApi
  4599  		 */
  4600  		function _fnSettingsFromNode ( nTable )
  4601  		{
  4602  			for ( var i=0 ; i<DataTable.settings.length ; i++ )
  4603  			{
  4604  				if ( DataTable.settings[i].nTable === nTable )
  4605  				{
  4606  					return DataTable.settings[i];
  4607  				}
  4608  			}
  4609  			
  4610  			return null;
  4611  		}
  4612  		
  4613  		
  4614  		/**
  4615  		 * Return an array with the TR nodes for the table
  4616  		 *  @param {object} oSettings dataTables settings object
  4617  		 *  @returns {array} TR array
  4618  		 *  @memberof DataTable#oApi
  4619  		 */
  4620  		function _fnGetTrNodes ( oSettings )
  4621  		{
  4622  			var aNodes = [];
  4623  			var aoData = oSettings.aoData;
  4624  			for ( var i=0, iLen=aoData.length ; i<iLen ; i++ )
  4625  			{
  4626  				if ( aoData[i].nTr !== null )
  4627  				{
  4628  					aNodes.push( aoData[i].nTr );
  4629  				}
  4630  			}
  4631  			return aNodes;
  4632  		}
  4633  		
  4634  		
  4635  		/**
  4636  		 * Return an flat array with all TD nodes for the table, or row
  4637  		 *  @param {object} oSettings dataTables settings object
  4638  		 *  @param {int} [iIndividualRow] aoData index to get the nodes for - optional 
  4639  		 *    if not given then the return array will contain all nodes for the table
  4640  		 *  @returns {array} TD array
  4641  		 *  @memberof DataTable#oApi
  4642  		 */
  4643  		function _fnGetTdNodes ( oSettings, iIndividualRow )
  4644  		{
  4645  			var anReturn = [];
  4646  			var iCorrector;
  4647  			var anTds, nTd;
  4648  			var iRow, iRows=oSettings.aoData.length,
  4649  				iColumn, iColumns, oData, sNodeName, iStart=0, iEnd=iRows;
  4650  			
  4651  			/* Allow the collection to be limited to just one row */
  4652  			if ( iIndividualRow !== undefined )
  4653  			{
  4654  				iStart = iIndividualRow;
  4655  				iEnd = iIndividualRow+1;
  4656  			}
  4657  		
  4658  			for ( iRow=iStart ; iRow<iEnd ; iRow++ )
  4659  			{
  4660  				oData = oSettings.aoData[iRow];
  4661  				if ( oData.nTr !== null )
  4662  				{
  4663  					/* get the TD child nodes - taking into account text etc nodes */
  4664  					anTds = [];
  4665  					nTd = oData.nTr.firstChild;
  4666  					while ( nTd )
  4667  					{
  4668  						sNodeName = nTd.nodeName.toLowerCase();
  4669  						if ( sNodeName == 'td' || sNodeName == 'th' )
  4670  						{
  4671  							anTds.push( nTd );
  4672  						}
  4673  						nTd = nTd.nextSibling;
  4674  					}
  4675  		
  4676  					iCorrector = 0;
  4677  					for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
  4678  					{
  4679  						if ( oSettings.aoColumns[iColumn].bVisible )
  4680  						{
  4681  							anReturn.push( anTds[iColumn-iCorrector] );
  4682  						}
  4683  						else
  4684  						{
  4685  							anReturn.push( oData._anHidden[iColumn] );
  4686  							iCorrector++;
  4687  						}
  4688  					}
  4689  				}
  4690  			}
  4691  		
  4692  			return anReturn;
  4693  		}
  4694  		
  4695  		
  4696  		/**
  4697  		 * Log an error message
  4698  		 *  @param {object} oSettings dataTables settings object
  4699  		 *  @param {int} iLevel log error messages, or display them to the user
  4700  		 *  @param {string} sMesg error message
  4701  		 *  @memberof DataTable#oApi
  4702  		 */
  4703  		function _fnLog( oSettings, iLevel, sMesg )
  4704  		{
  4705  			var sAlert = (oSettings===null) ?
  4706  				"DataTables warning: "+sMesg :
  4707  				"DataTables warning (table id = '"+oSettings.sTableId+"'): "+sMesg;
  4708  			
  4709  			if ( iLevel === 0 )
  4710  			{
  4711  				if ( DataTable.ext.sErrMode == 'alert' )
  4712  				{
  4713  					alert( sAlert );
  4714  				}
  4715  				else
  4716  				{
  4717  					throw new Error(sAlert);
  4718  				}
  4719  				return;
  4720  			}
  4721  			else if ( window.console && console.log )
  4722  			{
  4723  				console.log( sAlert );
  4724  			}
  4725  		}
  4726  		
  4727  		
  4728  		/**
  4729  		 * See if a property is defined on one object, if so assign it to the other object
  4730  		 *  @param {object} oRet target object
  4731  		 *  @param {object} oSrc source object
  4732  		 *  @param {string} sName property
  4733  		 *  @param {string} [sMappedName] name to map too - optional, sName used if not given
  4734  		 *  @memberof DataTable#oApi
  4735  		 */
  4736  		function _fnMap( oRet, oSrc, sName, sMappedName )
  4737  		{
  4738  			if ( sMappedName === undefined )
  4739  			{
  4740  				sMappedName = sName;
  4741  			}
  4742  			if ( oSrc[sName] !== undefined )
  4743  			{
  4744  				oRet[sMappedName] = oSrc[sName];
  4745  			}
  4746  		}
  4747  		
  4748  		
  4749  		/**
  4750  		 * Extend objects - very similar to jQuery.extend, but deep copy objects, and shallow
  4751  		 * copy arrays. The reason we need to do this, is that we don't want to deep copy array
  4752  		 * init values (such as aaSorting) since the dev wouldn't be able to override them, but
  4753  		 * we do want to deep copy arrays.
  4754  		 *  @param {object} oOut Object to extend
  4755  		 *  @param {object} oExtender Object from which the properties will be applied to oOut
  4756  		 *  @returns {object} oOut Reference, just for convenience - oOut === the return.
  4757  		 *  @memberof DataTable#oApi
  4758  		 *  @todo This doesn't take account of arrays inside the deep copied objects.
  4759  		 */
  4760  		function _fnExtend( oOut, oExtender )
  4761  		{
  4762  			var val;
  4763  			
  4764  			for ( var prop in oExtender )
  4765  			{
  4766  				if ( oExtender.hasOwnProperty(prop) )
  4767  				{
  4768  					val = oExtender[prop];
  4769  		
  4770  					if ( typeof oInit[prop] === 'object' && val !== null && $.isArray(val) === false )
  4771  					{
  4772  						$.extend( true, oOut[prop], val );
  4773  					}
  4774  					else
  4775  					{
  4776  						oOut[prop] = val;
  4777  					}
  4778  				}
  4779  			}
  4780  		
  4781  			return oOut;
  4782  		}
  4783  		
  4784  		
  4785  		/**
  4786  		 * Bind an event handers to allow a click or return key to activate the callback.
  4787  		 * This is good for accessibility since a return on the keyboard will have the
  4788  		 * same effect as a click, if the element has focus.
  4789  		 *  @param {element} n Element to bind the action to
  4790  		 *  @param {object} oData Data object to pass to the triggered function
  4791  		 *  @param {function} fn Callback function for when the event is triggered
  4792  		 *  @memberof DataTable#oApi
  4793  		 */
  4794  		function _fnBindAction( n, oData, fn )
  4795  		{
  4796  			$(n)
  4797  				.bind( 'click.DT', oData, function (e) {
  4798  						n.blur(); // Remove focus outline for mouse users
  4799  						fn(e);
  4800  					} )
  4801  				.bind( 'keypress.DT', oData, function (e){
  4802  					if ( e.which === 13 ) {
  4803  						fn(e);
  4804  					} } )
  4805  				.bind( 'selectstart.DT', function () {
  4806  					/* Take the brutal approach to cancelling text selection */
  4807  					return false;
  4808  					} );
  4809  		}
  4810  		
  4811  		
  4812  		/**
  4813  		 * Register a callback function. Easily allows a callback function to be added to
  4814  		 * an array store of callback functions that can then all be called together.
  4815  		 *  @param {object} oSettings dataTables settings object
  4816  		 *  @param {string} sStore Name of the array storage for the callbacks in oSettings
  4817  		 *  @param {function} fn Function to be called back
  4818  		 *  @param {string} sName Identifying name for the callback (i.e. a label)
  4819  		 *  @memberof DataTable#oApi
  4820  		 */
  4821  		function _fnCallbackReg( oSettings, sStore, fn, sName )
  4822  		{
  4823  			if ( fn )
  4824  			{
  4825  				oSettings[sStore].push( {
  4826  					"fn": fn,
  4827  					"sName": sName
  4828  				} );
  4829  			}
  4830  		}
  4831  		
  4832  		
  4833  		/**
  4834  		 * Fire callback functions and trigger events. Note that the loop over the callback
  4835  		 * array store is done backwards! Further note that you do not want to fire off triggers
  4836  		 * in time sensitive applications (for example cell creation) as its slow.
  4837  		 *  @param {object} oSettings dataTables settings object
  4838  		 *  @param {string} sStore Name of the array storage for the callbacks in oSettings
  4839  		 *  @param {string} sTrigger Name of the jQuery custom event to trigger. If null no trigger
  4840  		 *    is fired
  4841  		 *  @param {array} aArgs Array of arguments to pass to the callback function / trigger
  4842  		 *  @memberof DataTable#oApi
  4843  		 */
  4844  		function _fnCallbackFire( oSettings, sStore, sTrigger, aArgs )
  4845  		{
  4846  			var aoStore = oSettings[sStore];
  4847  			var aRet =[];
  4848  		
  4849  			for ( var i=aoStore.length-1 ; i>=0 ; i-- )
  4850  			{
  4851  				aRet.push( aoStore[i].fn.apply( oSettings.oInstance, aArgs ) );
  4852  			}
  4853  		
  4854  			if ( sTrigger !== null )
  4855  			{
  4856  				$(oSettings.oInstance).trigger(sTrigger, aArgs);
  4857  			}
  4858  		
  4859  			return aRet;
  4860  		}
  4861  		
  4862  		
  4863  		/**
  4864  		 * JSON stringify. If JSON.stringify it provided by the browser, json2.js or any other
  4865  		 * library, then we use that as it is fast, safe and accurate. If the function isn't 
  4866  		 * available then we need to built it ourselves - the inspiration for this function comes
  4867  		 * from Craig Buckler ( http://www.sitepoint.com/javascript-json-serialization/ ). It is
  4868  		 * not perfect and absolutely should not be used as a replacement to json2.js - but it does
  4869  		 * do what we need, without requiring a dependency for DataTables.
  4870  		 *  @param {object} o JSON object to be converted
  4871  		 *  @returns {string} JSON string
  4872  		 *  @memberof DataTable#oApi
  4873  		 */
  4874  		var _fnJsonString = (window.JSON) ? JSON.stringify : function( o )
  4875  		{
  4876  			/* Not an object or array */
  4877  			var sType = typeof o;
  4878  			if (sType !== "object" || o === null)
  4879  			{
  4880  				// simple data type
  4881  				if (sType === "string")
  4882  				{
  4883  					o = '"'+o+'"';
  4884  				}
  4885  				return o+"";
  4886  			}
  4887  		
  4888  			/* If object or array, need to recurse over it */
  4889  			var
  4890  				sProp, mValue,
  4891  				json = [],
  4892  				bArr = $.isArray(o);
  4893  			
  4894  			for (sProp in o)
  4895  			{
  4896  				mValue = o[sProp];
  4897  				sType = typeof mValue;
  4898  		
  4899  				if (sType === "string")
  4900  				{
  4901  					mValue = '"'+mValue+'"';
  4902  				}
  4903  				else if (sType === "object" && mValue !== null)
  4904  				{
  4905  					mValue = _fnJsonString(mValue);
  4906  				}
  4907  		
  4908  				json.push((bArr ? "" : '"'+sProp+'":') + mValue);
  4909  			}
  4910  		
  4911  			return (bArr ? "[" : "{") + json + (bArr ? "]" : "}");
  4912  		};
  4913  		
  4914  		
  4915  		/**
  4916  		 * From some browsers (specifically IE6/7) we need special handling to work around browser
  4917  		 * bugs - this function is used to detect when these workarounds are needed.
  4918  		 *  @param {object} oSettings dataTables settings object
  4919  		 *  @memberof DataTable#oApi
  4920  		 */
  4921  		function _fnBrowserDetect( oSettings )
  4922  		{
  4923  			/* IE6/7 will oversize a width 100% element inside a scrolling element, to include the
  4924  			 * width of the scrollbar, while other browsers ensure the inner element is contained
  4925  			 * without forcing scrolling
  4926  			 */
  4927  			var n = $(
  4928  				'<div style="position:absolute; top:0; left:0; height:1px; width:1px; overflow:hidden">'+
  4929  					'<div style="position:absolute; top:1px; left:1px; width:100px; overflow:scroll;">'+
  4930  						'<div id="DT_BrowserTest" style="width:100%; height:10px;"></div>'+
  4931  					'</div>'+
  4932  				'</div>')[0];
  4933  		
  4934  			document.body.appendChild( n );
  4935  			oSettings.oBrowser.bScrollOversize = $('#DT_BrowserTest', n)[0].offsetWidth === 100 ? true : false;
  4936  			document.body.removeChild( n );
  4937  		}
  4938  		
  4939  
  4940  		/**
  4941  		 * Perform a jQuery selector action on the table's TR elements (from the tbody) and
  4942  		 * return the resulting jQuery object.
  4943  		 *  @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
  4944  		 *  @param {object} [oOpts] Optional parameters for modifying the rows to be included
  4945  		 *  @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
  4946  		 *    criterion ("applied") or all TR elements (i.e. no filter).
  4947  		 *  @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
  4948  		 *    Can be either 'current', whereby the current sorting of the table is used, or
  4949  		 *    'original' whereby the original order the data was read into the table is used.
  4950  		 *  @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
  4951  		 *    ("current") or not ("all"). If 'current' is given, then order is assumed to be 
  4952  		 *    'current' and filter is 'applied', regardless of what they might be given as.
  4953  		 *  @returns {object} jQuery object, filtered by the given selector.
  4954  		 *  @dtopt API
  4955  		 *
  4956  		 *  @example
  4957  		 *    $(document).ready(function() {
  4958  		 *      var oTable = $('#example').dataTable();
  4959  		 *
  4960  		 *      // Highlight every second row
  4961  		 *      oTable.$('tr:odd').css('backgroundColor', 'blue');
  4962  		 *    } );
  4963  		 *
  4964  		 *  @example
  4965  		 *    $(document).ready(function() {
  4966  		 *      var oTable = $('#example').dataTable();
  4967  		 *
  4968  		 *      // Filter to rows with 'Webkit' in them, add a background colour and then
  4969  		 *      // remove the filter, thus highlighting the 'Webkit' rows only.
  4970  		 *      oTable.fnFilter('Webkit');
  4971  		 *      oTable.$('tr', {"filter": "applied"}).css('backgroundColor', 'blue');
  4972  		 *      oTable.fnFilter('');
  4973  		 *    } );
  4974  		 */
  4975  		this.$ = function ( sSelector, oOpts )
  4976  		{
  4977  			var i, iLen, a = [], tr;
  4978  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  4979  			var aoData = oSettings.aoData;
  4980  			var aiDisplay = oSettings.aiDisplay;
  4981  			var aiDisplayMaster = oSettings.aiDisplayMaster;
  4982  		
  4983  			if ( !oOpts )
  4984  			{
  4985  				oOpts = {};
  4986  			}
  4987  		
  4988  			oOpts = $.extend( {}, {
  4989  				"filter": "none", // applied
  4990  				"order": "current", // "original"
  4991  				"page": "all" // current
  4992  			}, oOpts );
  4993  		
  4994  			// Current page implies that order=current and fitler=applied, since it is fairly
  4995  			// senseless otherwise
  4996  			if ( oOpts.page == 'current' )
  4997  			{
  4998  				for ( i=oSettings._iDisplayStart, iLen=oSettings.fnDisplayEnd() ; i<iLen ; i++ )
  4999  				{
  5000  					tr = aoData[ aiDisplay[i] ].nTr;
  5001  					if ( tr )
  5002  					{
  5003  						a.push( tr );
  5004  					}
  5005  				}
  5006  			}
  5007  			else if ( oOpts.order == "current" && oOpts.filter == "none" )
  5008  			{
  5009  				for ( i=0, iLen=aiDisplayMaster.length ; i<iLen ; i++ )
  5010  				{
  5011  					tr = aoData[ aiDisplayMaster[i] ].nTr;
  5012  					if ( tr )
  5013  					{
  5014  						a.push( tr );
  5015  					}
  5016  				}
  5017  			}
  5018  			else if ( oOpts.order == "current" && oOpts.filter == "applied" )
  5019  			{
  5020  				for ( i=0, iLen=aiDisplay.length ; i<iLen ; i++ )
  5021  				{
  5022  					tr = aoData[ aiDisplay[i] ].nTr;
  5023  					if ( tr )
  5024  					{
  5025  						a.push( tr );
  5026  					}
  5027  				}
  5028  			}
  5029  			else if ( oOpts.order == "original" && oOpts.filter == "none" )
  5030  			{
  5031  				for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
  5032  				{
  5033  					tr = aoData[ i ].nTr ;
  5034  					if ( tr )
  5035  					{
  5036  						a.push( tr );
  5037  					}
  5038  				}
  5039  			}
  5040  			else if ( oOpts.order == "original" && oOpts.filter == "applied" )
  5041  			{
  5042  				for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
  5043  				{
  5044  					tr = aoData[ i ].nTr;
  5045  					if ( $.inArray( i, aiDisplay ) !== -1 && tr )
  5046  					{
  5047  						a.push( tr );
  5048  					}
  5049  				}
  5050  			}
  5051  			else
  5052  			{
  5053  				_fnLog( oSettings, 1, "Unknown selection options" );
  5054  			}
  5055  		
  5056  			/* We need to filter on the TR elements and also 'find' in their descendants
  5057  			 * to make the selector act like it would in a full table - so we need
  5058  			 * to build both results and then combine them together
  5059  			 */
  5060  			var jqA = $(a);
  5061  			var jqTRs = jqA.filter( sSelector );
  5062  			var jqDescendants = jqA.find( sSelector );
  5063  		
  5064  			return $( [].concat($.makeArray(jqTRs), $.makeArray(jqDescendants)) );
  5065  		};
  5066  		
  5067  		
  5068  		/**
  5069  		 * Almost identical to $ in operation, but in this case returns the data for the matched
  5070  		 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
  5071  		 * rather than any descendants, so the data can be obtained for the row/cell. If matching
  5072  		 * rows are found, the data returned is the original data array/object that was used to  
  5073  		 * create the row (or a generated array if from a DOM source).
  5074  		 *
  5075  		 * This method is often useful in-combination with $ where both functions are given the
  5076  		 * same parameters and the array indexes will match identically.
  5077  		 *  @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
  5078  		 *  @param {object} [oOpts] Optional parameters for modifying the rows to be included
  5079  		 *  @param {string} [oOpts.filter=none] Select elements that meet the current filter
  5080  		 *    criterion ("applied") or all elements (i.e. no filter).
  5081  		 *  @param {string} [oOpts.order=current] Order of the data in the processed array.
  5082  		 *    Can be either 'current', whereby the current sorting of the table is used, or
  5083  		 *    'original' whereby the original order the data was read into the table is used.
  5084  		 *  @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
  5085  		 *    ("current") or not ("all"). If 'current' is given, then order is assumed to be 
  5086  		 *    'current' and filter is 'applied', regardless of what they might be given as.
  5087  		 *  @returns {array} Data for the matched elements. If any elements, as a result of the
  5088  		 *    selector, were not TR, TD or TH elements in the DataTable, they will have a null 
  5089  		 *    entry in the array.
  5090  		 *  @dtopt API
  5091  		 *
  5092  		 *  @example
  5093  		 *    $(document).ready(function() {
  5094  		 *      var oTable = $('#example').dataTable();
  5095  		 *
  5096  		 *      // Get the data from the first row in the table
  5097  		 *      var data = oTable._('tr:first');
  5098  		 *
  5099  		 *      // Do something useful with the data
  5100  		 *      alert( "First cell is: "+data[0] );
  5101  		 *    } );
  5102  		 *
  5103  		 *  @example
  5104  		 *    $(document).ready(function() {
  5105  		 *      var oTable = $('#example').dataTable();
  5106  		 *
  5107  		 *      // Filter to 'Webkit' and get all data for 
  5108  		 *      oTable.fnFilter('Webkit');
  5109  		 *      var data = oTable._('tr', {"filter": "applied"});
  5110  		 *      
  5111  		 *      // Do something with the data
  5112  		 *      alert( data.length+" rows matched the filter" );
  5113  		 *    } );
  5114  		 */
  5115  		this._ = function ( sSelector, oOpts )
  5116  		{
  5117  			var aOut = [];
  5118  			var i, iLen, iIndex;
  5119  			var aTrs = this.$( sSelector, oOpts );
  5120  		
  5121  			for ( i=0, iLen=aTrs.length ; i<iLen ; i++ )
  5122  			{
  5123  				aOut.push( this.fnGetData(aTrs[i]) );
  5124  			}
  5125  		
  5126  			return aOut;
  5127  		};
  5128  		
  5129  		
  5130  		/**
  5131  		 * Add a single new row or multiple rows of data to the table. Please note
  5132  		 * that this is suitable for client-side processing only - if you are using 
  5133  		 * server-side processing (i.e. "bServerSide": true), then to add data, you
  5134  		 * must add it to the data source, i.e. the server-side, through an Ajax call.
  5135  		 *  @param {array|object} mData The data to be added to the table. This can be:
  5136  		 *    <ul>
  5137  		 *      <li>1D array of data - add a single row with the data provided</li>
  5138  		 *      <li>2D array of arrays - add multiple rows in a single call</li>
  5139  		 *      <li>object - data object when using <i>mData</i></li>
  5140  		 *      <li>array of objects - multiple data objects when using <i>mData</i></li>
  5141  		 *    </ul>
  5142  		 *  @param {bool} [bRedraw=true] redraw the table or not
  5143  		 *  @returns {array} An array of integers, representing the list of indexes in 
  5144  		 *    <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to 
  5145  		 *    the table.
  5146  		 *  @dtopt API
  5147  		 *
  5148  		 *  @example
  5149  		 *    // Global var for counter
  5150  		 *    var giCount = 2;
  5151  		 *    
  5152  		 *    $(document).ready(function() {
  5153  		 *      $('#example').dataTable();
  5154  		 *    } );
  5155  		 *    
  5156  		 *    function fnClickAddRow() {
  5157  		 *      $('#example').dataTable().fnAddData( [
  5158  		 *        giCount+".1",
  5159  		 *        giCount+".2",
  5160  		 *        giCount+".3",
  5161  		 *        giCount+".4" ]
  5162  		 *      );
  5163  		 *        
  5164  		 *      giCount++;
  5165  		 *    }
  5166  		 */
  5167  		this.fnAddData = function( mData, bRedraw )
  5168  		{
  5169  			if ( mData.length === 0 )
  5170  			{
  5171  				return [];
  5172  			}
  5173  			
  5174  			var aiReturn = [];
  5175  			var iTest;
  5176  			
  5177  			/* Find settings from table node */
  5178  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5179  			
  5180  			/* Check if we want to add multiple rows or not */
  5181  			if ( typeof mData[0] === "object" && mData[0] !== null )
  5182  			{
  5183  				for ( var i=0 ; i<mData.length ; i++ )
  5184  				{
  5185  					iTest = _fnAddData( oSettings, mData[i] );
  5186  					if ( iTest == -1 )
  5187  					{
  5188  						return aiReturn;
  5189  					}
  5190  					aiReturn.push( iTest );
  5191  				}
  5192  			}
  5193  			else
  5194  			{
  5195  				iTest = _fnAddData( oSettings, mData );
  5196  				if ( iTest == -1 )
  5197  				{
  5198  					return aiReturn;
  5199  				}
  5200  				aiReturn.push( iTest );
  5201  			}
  5202  			
  5203  			oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
  5204  			
  5205  			if ( bRedraw === undefined || bRedraw )
  5206  			{
  5207  				_fnReDraw( oSettings );
  5208  			}
  5209  			return aiReturn;
  5210  		};
  5211  		
  5212  		
  5213  		/**
  5214  		 * This function will make DataTables recalculate the column sizes, based on the data 
  5215  		 * contained in the table and the sizes applied to the columns (in the DOM, CSS or 
  5216  		 * through the sWidth parameter). This can be useful when the width of the table's 
  5217  		 * parent element changes (for example a window resize).
  5218  		 *  @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
  5219  		 *  @dtopt API
  5220  		 *
  5221  		 *  @example
  5222  		 *    $(document).ready(function() {
  5223  		 *      var oTable = $('#example').dataTable( {
  5224  		 *        "sScrollY": "200px",
  5225  		 *        "bPaginate": false
  5226  		 *      } );
  5227  		 *      
  5228  		 *      $(window).bind('resize', function () {
  5229  		 *        oTable.fnAdjustColumnSizing();
  5230  		 *      } );
  5231  		 *    } );
  5232  		 */
  5233  		this.fnAdjustColumnSizing = function ( bRedraw )
  5234  		{
  5235  			var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  5236  			_fnAdjustColumnSizing( oSettings );
  5237  			
  5238  			if ( bRedraw === undefined || bRedraw )
  5239  			{
  5240  				this.fnDraw( false );
  5241  			}
  5242  			else if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
  5243  			{
  5244  				/* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
  5245  				this.oApi._fnScrollDraw(oSettings);
  5246  			}
  5247  		};
  5248  		
  5249  		
  5250  		/**
  5251  		 * Quickly and simply clear a table
  5252  		 *  @param {bool} [bRedraw=true] redraw the table or not
  5253  		 *  @dtopt API
  5254  		 *
  5255  		 *  @example
  5256  		 *    $(document).ready(function() {
  5257  		 *      var oTable = $('#example').dataTable();
  5258  		 *      
  5259  		 *      // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
  5260  		 *      oTable.fnClearTable();
  5261  		 *    } );
  5262  		 */
  5263  		this.fnClearTable = function( bRedraw )
  5264  		{
  5265  			/* Find settings from table node */
  5266  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5267  			_fnClearTable( oSettings );
  5268  			
  5269  			if ( bRedraw === undefined || bRedraw )
  5270  			{
  5271  				_fnDraw( oSettings );
  5272  			}
  5273  		};
  5274  		
  5275  		
  5276  		/**
  5277  		 * The exact opposite of 'opening' a row, this function will close any rows which 
  5278  		 * are currently 'open'.
  5279  		 *  @param {node} nTr the table row to 'close'
  5280  		 *  @returns {int} 0 on success, or 1 if failed (can't find the row)
  5281  		 *  @dtopt API
  5282  		 *
  5283  		 *  @example
  5284  		 *    $(document).ready(function() {
  5285  		 *      var oTable;
  5286  		 *      
  5287  		 *      // 'open' an information row when a row is clicked on
  5288  		 *      $('#example tbody tr').click( function () {
  5289  		 *        if ( oTable.fnIsOpen(this) ) {
  5290  		 *          oTable.fnClose( this );
  5291  		 *        } else {
  5292  		 *          oTable.fnOpen( this, "Temporary row opened", "info_row" );
  5293  		 *        }
  5294  		 *      } );
  5295  		 *      
  5296  		 *      oTable = $('#example').dataTable();
  5297  		 *    } );
  5298  		 */
  5299  		this.fnClose = function( nTr )
  5300  		{
  5301  			/* Find settings from table node */
  5302  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5303  			
  5304  			for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ )
  5305  			{
  5306  				if ( oSettings.aoOpenRows[i].nParent == nTr )
  5307  				{
  5308  					var nTrParent = oSettings.aoOpenRows[i].nTr.parentNode;
  5309  					if ( nTrParent )
  5310  					{
  5311  						/* Remove it if it is currently on display */
  5312  						nTrParent.removeChild( oSettings.aoOpenRows[i].nTr );
  5313  					}
  5314  					oSettings.aoOpenRows.splice( i, 1 );
  5315  					return 0;
  5316  				}
  5317  			}
  5318  			return 1;
  5319  		};
  5320  		
  5321  		
  5322  		/**
  5323  		 * Remove a row for the table
  5324  		 *  @param {mixed} mTarget The index of the row from aoData to be deleted, or
  5325  		 *    the TR element you want to delete
  5326  		 *  @param {function|null} [fnCallBack] Callback function
  5327  		 *  @param {bool} [bRedraw=true] Redraw the table or not
  5328  		 *  @returns {array} The row that was deleted
  5329  		 *  @dtopt API
  5330  		 *
  5331  		 *  @example
  5332  		 *    $(document).ready(function() {
  5333  		 *      var oTable = $('#example').dataTable();
  5334  		 *      
  5335  		 *      // Immediately remove the first row
  5336  		 *      oTable.fnDeleteRow( 0 );
  5337  		 *    } );
  5338  		 */
  5339  		this.fnDeleteRow = function( mTarget, fnCallBack, bRedraw )
  5340  		{
  5341  			/* Find settings from table node */
  5342  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5343  			var i, iLen, iAODataIndex;
  5344  			
  5345  			iAODataIndex = (typeof mTarget === 'object') ? 
  5346  				_fnNodeToDataIndex(oSettings, mTarget) : mTarget;
  5347  			
  5348  			/* Return the data array from this row */
  5349  			var oData = oSettings.aoData.splice( iAODataIndex, 1 );
  5350  		
  5351  			/* Update the _DT_RowIndex parameter */
  5352  			for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
  5353  			{
  5354  				if ( oSettings.aoData[i].nTr !== null )
  5355  				{
  5356  					oSettings.aoData[i].nTr._DT_RowIndex = i;
  5357  				}
  5358  			}
  5359  			
  5360  			/* Remove the target row from the search array */
  5361  			var iDisplayIndex = $.inArray( iAODataIndex, oSettings.aiDisplay );
  5362  			oSettings.asDataSearch.splice( iDisplayIndex, 1 );
  5363  			
  5364  			/* Delete from the display arrays */
  5365  			_fnDeleteIndex( oSettings.aiDisplayMaster, iAODataIndex );
  5366  			_fnDeleteIndex( oSettings.aiDisplay, iAODataIndex );
  5367  			
  5368  			/* If there is a user callback function - call it */
  5369  			if ( typeof fnCallBack === "function" )
  5370  			{
  5371  				fnCallBack.call( this, oSettings, oData );
  5372  			}
  5373  			
  5374  			/* Check for an 'overflow' they case for displaying the table */
  5375  			if ( oSettings._iDisplayStart >= oSettings.fnRecordsDisplay() )
  5376  			{
  5377  				oSettings._iDisplayStart -= oSettings._iDisplayLength;
  5378  				if ( oSettings._iDisplayStart < 0 )
  5379  				{
  5380  					oSettings._iDisplayStart = 0;
  5381  				}
  5382  			}
  5383  			
  5384  			if ( bRedraw === undefined || bRedraw )
  5385  			{
  5386  				_fnCalculateEnd( oSettings );
  5387  				_fnDraw( oSettings );
  5388  			}
  5389  			
  5390  			return oData;
  5391  		};
  5392  		
  5393  		
  5394  		/**
  5395  		 * Restore the table to it's original state in the DOM by removing all of DataTables 
  5396  		 * enhancements, alterations to the DOM structure of the table and event listeners.
  5397  		 *  @param {boolean} [bRemove=false] Completely remove the table from the DOM
  5398  		 *  @dtopt API
  5399  		 *
  5400  		 *  @example
  5401  		 *    $(document).ready(function() {
  5402  		 *      // This example is fairly pointless in reality, but shows how fnDestroy can be used
  5403  		 *      var oTable = $('#example').dataTable();
  5404  		 *      oTable.fnDestroy();
  5405  		 *    } );
  5406  		 */
  5407  		this.fnDestroy = function ( bRemove )
  5408  		{
  5409  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5410  			var nOrig = oSettings.nTableWrapper.parentNode;
  5411  			var nBody = oSettings.nTBody;
  5412  			var i, iLen;
  5413  		
  5414  			bRemove = (bRemove===undefined) ? false : bRemove;
  5415  			
  5416  			/* Flag to note that the table is currently being destroyed - no action should be taken */
  5417  			oSettings.bDestroying = true;
  5418  			
  5419  			/* Fire off the destroy callbacks for plug-ins etc */
  5420  			_fnCallbackFire( oSettings, "aoDestroyCallback", "destroy", [oSettings] );
  5421  		
  5422  			/* If the table is not being removed, restore the hidden columns */
  5423  			if ( !bRemove )
  5424  			{
  5425  				for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
  5426  				{
  5427  					if ( oSettings.aoColumns[i].bVisible === false )
  5428  					{
  5429  						this.fnSetColumnVis( i, true );
  5430  					}
  5431  				}
  5432  			}
  5433  			
  5434  			/* Blitz all DT events */
  5435  			$(oSettings.nTableWrapper).find('*').andSelf().unbind('.DT');
  5436  			
  5437  			/* If there is an 'empty' indicator row, remove it */
  5438  			$('tbody>tr>td.'+oSettings.oClasses.sRowEmpty, oSettings.nTable).parent().remove();
  5439  			
  5440  			/* When scrolling we had to break the table up - restore it */
  5441  			if ( oSettings.nTable != oSettings.nTHead.parentNode )
  5442  			{
  5443  				$(oSettings.nTable).children('thead').remove();
  5444  				oSettings.nTable.appendChild( oSettings.nTHead );
  5445  			}
  5446  			
  5447  			if ( oSettings.nTFoot && oSettings.nTable != oSettings.nTFoot.parentNode )
  5448  			{
  5449  				$(oSettings.nTable).children('tfoot').remove();
  5450  				oSettings.nTable.appendChild( oSettings.nTFoot );
  5451  			}
  5452  			
  5453  			/* Remove the DataTables generated nodes, events and classes */
  5454  			oSettings.nTable.parentNode.removeChild( oSettings.nTable );
  5455  			$(oSettings.nTableWrapper).remove();
  5456  			
  5457  			oSettings.aaSorting = [];
  5458  			oSettings.aaSortingFixed = [];
  5459  			_fnSortingClasses( oSettings );
  5460  			
  5461  			$(_fnGetTrNodes( oSettings )).removeClass( oSettings.asStripeClasses.join(' ') );
  5462  			
  5463  			$('th, td', oSettings.nTHead).removeClass( [
  5464  				oSettings.oClasses.sSortable,
  5465  				oSettings.oClasses.sSortableAsc,
  5466  				oSettings.oClasses.sSortableDesc,
  5467  				oSettings.oClasses.sSortableNone ].join(' ')
  5468  			);
  5469  			if ( oSettings.bJUI )
  5470  			{
  5471  				$('th span.'+oSettings.oClasses.sSortIcon
  5472  					+ ', td span.'+oSettings.oClasses.sSortIcon, oSettings.nTHead).remove();
  5473  		
  5474  				$('th, td', oSettings.nTHead).each( function () {
  5475  					var jqWrapper = $('div.'+oSettings.oClasses.sSortJUIWrapper, this);
  5476  					var kids = jqWrapper.contents();
  5477  					$(this).append( kids );
  5478  					jqWrapper.remove();
  5479  				} );
  5480  			}
  5481  			
  5482  			/* Add the TR elements back into the table in their original order */
  5483  			if ( !bRemove && oSettings.nTableReinsertBefore )
  5484  			{
  5485  				nOrig.insertBefore( oSettings.nTable, oSettings.nTableReinsertBefore );
  5486  			}
  5487  			else if ( !bRemove )
  5488  			{
  5489  				nOrig.appendChild( oSettings.nTable );
  5490  			}
  5491  		
  5492  			for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
  5493  			{
  5494  				if ( oSettings.aoData[i].nTr !== null )
  5495  				{
  5496  					nBody.appendChild( oSettings.aoData[i].nTr );
  5497  				}
  5498  			}
  5499  			
  5500  			/* Restore the width of the original table */
  5501  			if ( oSettings.oFeatures.bAutoWidth === true )
  5502  			{
  5503  			  oSettings.nTable.style.width = _fnStringToCss(oSettings.sDestroyWidth);
  5504  			}
  5505  			
  5506  			/* If the were originally stripe classes - then we add them back here. Note
  5507  			 * this is not fool proof (for example if not all rows had stripe classes - but
  5508  			 * it's a good effort without getting carried away
  5509  			 */
  5510  			iLen = oSettings.asDestroyStripes.length;
  5511  			if (iLen)
  5512  			{
  5513  				var anRows = $(nBody).children('tr');
  5514  				for ( i=0 ; i<iLen ; i++ )
  5515  				{
  5516  					anRows.filter(':nth-child(' + iLen + 'n + ' + i + ')').addClass( oSettings.asDestroyStripes[i] );
  5517  				}
  5518  			}
  5519  			
  5520  			/* Remove the settings object from the settings array */
  5521  			for ( i=0, iLen=DataTable.settings.length ; i<iLen ; i++ )
  5522  			{
  5523  				if ( DataTable.settings[i] == oSettings )
  5524  				{
  5525  					DataTable.settings.splice( i, 1 );
  5526  				}
  5527  			}
  5528  			
  5529  			/* End it all */
  5530  			oSettings = null;
  5531  			oInit = null;
  5532  		};
  5533  		
  5534  		
  5535  		/**
  5536  		 * Redraw the table
  5537  		 *  @param {bool} [bComplete=true] Re-filter and resort (if enabled) the table before the draw.
  5538  		 *  @dtopt API
  5539  		 *
  5540  		 *  @example
  5541  		 *    $(document).ready(function() {
  5542  		 *      var oTable = $('#example').dataTable();
  5543  		 *      
  5544  		 *      // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
  5545  		 *      oTable.fnDraw();
  5546  		 *    } );
  5547  		 */
  5548  		this.fnDraw = function( bComplete )
  5549  		{
  5550  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5551  			if ( bComplete === false )
  5552  			{
  5553  				_fnCalculateEnd( oSettings );
  5554  				_fnDraw( oSettings );
  5555  			}
  5556  			else
  5557  			{
  5558  				_fnReDraw( oSettings );
  5559  			}
  5560  		};
  5561  		
  5562  		
  5563  		/**
  5564  		 * Filter the input based on data
  5565  		 *  @param {string} sInput String to filter the table on
  5566  		 *  @param {int|null} [iColumn] Column to limit filtering to
  5567  		 *  @param {bool} [bRegex=false] Treat as regular expression or not
  5568  		 *  @param {bool} [bSmart=true] Perform smart filtering or not
  5569  		 *  @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
  5570  		 *  @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
  5571  		 *  @dtopt API
  5572  		 *
  5573  		 *  @example
  5574  		 *    $(document).ready(function() {
  5575  		 *      var oTable = $('#example').dataTable();
  5576  		 *      
  5577  		 *      // Sometime later - filter...
  5578  		 *      oTable.fnFilter( 'test string' );
  5579  		 *    } );
  5580  		 */
  5581  		this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
  5582  		{
  5583  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5584  			
  5585  			if ( !oSettings.oFeatures.bFilter )
  5586  			{
  5587  				return;
  5588  			}
  5589  			
  5590  			if ( bRegex === undefined || bRegex === null )
  5591  			{
  5592  				bRegex = false;
  5593  			}
  5594  			
  5595  			if ( bSmart === undefined || bSmart === null )
  5596  			{
  5597  				bSmart = true;
  5598  			}
  5599  			
  5600  			if ( bShowGlobal === undefined || bShowGlobal === null )
  5601  			{
  5602  				bShowGlobal = true;
  5603  			}
  5604  			
  5605  			if ( bCaseInsensitive === undefined || bCaseInsensitive === null )
  5606  			{
  5607  				bCaseInsensitive = true;
  5608  			}
  5609  			
  5610  			if ( iColumn === undefined || iColumn === null )
  5611  			{
  5612  				/* Global filter */
  5613  				_fnFilterComplete( oSettings, {
  5614  					"sSearch":sInput+"",
  5615  					"bRegex": bRegex,
  5616  					"bSmart": bSmart,
  5617  					"bCaseInsensitive": bCaseInsensitive
  5618  				}, 1 );
  5619  				
  5620  				if ( bShowGlobal && oSettings.aanFeatures.f )
  5621  				{
  5622  					var n = oSettings.aanFeatures.f;
  5623  					for ( var i=0, iLen=n.length ; i<iLen ; i++ )
  5624  					{
  5625  						// IE9 throws an 'unknown error' if document.activeElement is used
  5626  						// inside an iframe or frame...
  5627  						try {
  5628  							if ( n[i]._DT_Input != document.activeElement )
  5629  							{
  5630  								$(n[i]._DT_Input).val( sInput );
  5631  							}
  5632  						}
  5633  						catch ( e ) {
  5634  							$(n[i]._DT_Input).val( sInput );
  5635  						}
  5636  					}
  5637  				}
  5638  			}
  5639  			else
  5640  			{
  5641  				/* Single column filter */
  5642  				$.extend( oSettings.aoPreSearchCols[ iColumn ], {
  5643  					"sSearch": sInput+"",
  5644  					"bRegex": bRegex,
  5645  					"bSmart": bSmart,
  5646  					"bCaseInsensitive": bCaseInsensitive
  5647  				} );
  5648  				_fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
  5649  			}
  5650  		};
  5651  		
  5652  		
  5653  		/**
  5654  		 * Get the data for the whole table, an individual row or an individual cell based on the 
  5655  		 * provided parameters.
  5656  		 *  @param {int|node} [mRow] A TR row node, TD/TH cell node or an integer. If given as
  5657  		 *    a TR node then the data source for the whole row will be returned. If given as a
  5658  		 *    TD/TH cell node then iCol will be automatically calculated and the data for the
  5659  		 *    cell returned. If given as an integer, then this is treated as the aoData internal
  5660  		 *    data index for the row (see fnGetPosition) and the data for that row used.
  5661  		 *  @param {int} [iCol] Optional column index that you want the data of.
  5662  		 *  @returns {array|object|string} If mRow is undefined, then the data for all rows is
  5663  		 *    returned. If mRow is defined, just data for that row, and is iCol is
  5664  		 *    defined, only data for the designated cell is returned.
  5665  		 *  @dtopt API
  5666  		 *
  5667  		 *  @example
  5668  		 *    // Row data
  5669  		 *    $(document).ready(function() {
  5670  		 *      oTable = $('#example').dataTable();
  5671  		 *
  5672  		 *      oTable.$('tr').click( function () {
  5673  		 *        var data = oTable.fnGetData( this );
  5674  		 *        // ... do something with the array / object of data for the row
  5675  		 *      } );
  5676  		 *    } );
  5677  		 *
  5678  		 *  @example
  5679  		 *    // Individual cell data
  5680  		 *    $(document).ready(function() {
  5681  		 *      oTable = $('#example').dataTable();
  5682  		 *
  5683  		 *      oTable.$('td').click( function () {
  5684  		 *        var sData = oTable.fnGetData( this );
  5685  		 *        alert( 'The cell clicked on had the value of '+sData );
  5686  		 *      } );
  5687  		 *    } );
  5688  		 */
  5689  		this.fnGetData = function( mRow, iCol )
  5690  		{
  5691  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5692  			
  5693  			if ( mRow !== undefined )
  5694  			{
  5695  				var iRow = mRow;
  5696  				if ( typeof mRow === 'object' )
  5697  				{
  5698  					var sNode = mRow.nodeName.toLowerCase();
  5699  					if (sNode === "tr" )
  5700  					{
  5701  						iRow = _fnNodeToDataIndex(oSettings, mRow);
  5702  					}
  5703  					else if ( sNode === "td" )
  5704  					{
  5705  						iRow = _fnNodeToDataIndex(oSettings, mRow.parentNode);
  5706  						iCol = _fnNodeToColumnIndex( oSettings, iRow, mRow );
  5707  					}
  5708  				}
  5709  		
  5710  				if ( iCol !== undefined )
  5711  				{
  5712  					return _fnGetCellData( oSettings, iRow, iCol, '' );
  5713  				}
  5714  				return (oSettings.aoData[iRow]!==undefined) ?
  5715  					oSettings.aoData[iRow]._aData : null;
  5716  			}
  5717  			return _fnGetDataMaster( oSettings );
  5718  		};
  5719  		
  5720  		
  5721  		/**
  5722  		 * Get an array of the TR nodes that are used in the table's body. Note that you will 
  5723  		 * typically want to use the '$' API method in preference to this as it is more 
  5724  		 * flexible.
  5725  		 *  @param {int} [iRow] Optional row index for the TR element you want
  5726  		 *  @returns {array|node} If iRow is undefined, returns an array of all TR elements
  5727  		 *    in the table's body, or iRow is defined, just the TR element requested.
  5728  		 *  @dtopt API
  5729  		 *
  5730  		 *  @example
  5731  		 *    $(document).ready(function() {
  5732  		 *      var oTable = $('#example').dataTable();
  5733  		 *      
  5734  		 *      // Get the nodes from the table
  5735  		 *      var nNodes = oTable.fnGetNodes( );
  5736  		 *    } );
  5737  		 */
  5738  		this.fnGetNodes = function( iRow )
  5739  		{
  5740  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5741  			
  5742  			if ( iRow !== undefined ) {
  5743  				return (oSettings.aoData[iRow]!==undefined) ?
  5744  					oSettings.aoData[iRow].nTr : null;
  5745  			}
  5746  			return _fnGetTrNodes( oSettings );
  5747  		};
  5748  		
  5749  		
  5750  		/**
  5751  		 * Get the array indexes of a particular cell from it's DOM element
  5752  		 * and column index including hidden columns
  5753  		 *  @param {node} nNode this can either be a TR, TD or TH in the table's body
  5754  		 *  @returns {int} If nNode is given as a TR, then a single index is returned, or
  5755  		 *    if given as a cell, an array of [row index, column index (visible), 
  5756  		 *    column index (all)] is given.
  5757  		 *  @dtopt API
  5758  		 *
  5759  		 *  @example
  5760  		 *    $(document).ready(function() {
  5761  		 *      $('#example tbody td').click( function () {
  5762  		 *        // Get the position of the current data from the node
  5763  		 *        var aPos = oTable.fnGetPosition( this );
  5764  		 *        
  5765  		 *        // Get the data array for this row
  5766  		 *        var aData = oTable.fnGetData( aPos[0] );
  5767  		 *        
  5768  		 *        // Update the data array and return the value
  5769  		 *        aData[ aPos[1] ] = 'clicked';
  5770  		 *        this.innerHTML = 'clicked';
  5771  		 *      } );
  5772  		 *      
  5773  		 *      // Init DataTables
  5774  		 *      oTable = $('#example').dataTable();
  5775  		 *    } );
  5776  		 */
  5777  		this.fnGetPosition = function( nNode )
  5778  		{
  5779  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5780  			var sNodeName = nNode.nodeName.toUpperCase();
  5781  			
  5782  			if ( sNodeName == "TR" )
  5783  			{
  5784  				return _fnNodeToDataIndex(oSettings, nNode);
  5785  			}
  5786  			else if ( sNodeName == "TD" || sNodeName == "TH" )
  5787  			{
  5788  				var iDataIndex = _fnNodeToDataIndex( oSettings, nNode.parentNode );
  5789  				var iColumnIndex = _fnNodeToColumnIndex( oSettings, iDataIndex, nNode );
  5790  				return [ iDataIndex, _fnColumnIndexToVisible(oSettings, iColumnIndex ), iColumnIndex ];
  5791  			}
  5792  			return null;
  5793  		};
  5794  		
  5795  		
  5796  		/**
  5797  		 * Check to see if a row is 'open' or not.
  5798  		 *  @param {node} nTr the table row to check
  5799  		 *  @returns {boolean} true if the row is currently open, false otherwise
  5800  		 *  @dtopt API
  5801  		 *
  5802  		 *  @example
  5803  		 *    $(document).ready(function() {
  5804  		 *      var oTable;
  5805  		 *      
  5806  		 *      // 'open' an information row when a row is clicked on
  5807  		 *      $('#example tbody tr').click( function () {
  5808  		 *        if ( oTable.fnIsOpen(this) ) {
  5809  		 *          oTable.fnClose( this );
  5810  		 *        } else {
  5811  		 *          oTable.fnOpen( this, "Temporary row opened", "info_row" );
  5812  		 *        }
  5813  		 *      } );
  5814  		 *      
  5815  		 *      oTable = $('#example').dataTable();
  5816  		 *    } );
  5817  		 */
  5818  		this.fnIsOpen = function( nTr )
  5819  		{
  5820  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5821  			var aoOpenRows = oSettings.aoOpenRows;
  5822  			
  5823  			for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ )
  5824  			{
  5825  				if ( oSettings.aoOpenRows[i].nParent == nTr )
  5826  				{
  5827  					return true;
  5828  				}
  5829  			}
  5830  			return false;
  5831  		};
  5832  		
  5833  		
  5834  		/**
  5835  		 * This function will place a new row directly after a row which is currently 
  5836  		 * on display on the page, with the HTML contents that is passed into the 
  5837  		 * function. This can be used, for example, to ask for confirmation that a 
  5838  		 * particular record should be deleted.
  5839  		 *  @param {node} nTr The table row to 'open'
  5840  		 *  @param {string|node|jQuery} mHtml The HTML to put into the row
  5841  		 *  @param {string} sClass Class to give the new TD cell
  5842  		 *  @returns {node} The row opened. Note that if the table row passed in as the
  5843  		 *    first parameter, is not found in the table, this method will silently
  5844  		 *    return.
  5845  		 *  @dtopt API
  5846  		 *
  5847  		 *  @example
  5848  		 *    $(document).ready(function() {
  5849  		 *      var oTable;
  5850  		 *      
  5851  		 *      // 'open' an information row when a row is clicked on
  5852  		 *      $('#example tbody tr').click( function () {
  5853  		 *        if ( oTable.fnIsOpen(this) ) {
  5854  		 *          oTable.fnClose( this );
  5855  		 *        } else {
  5856  		 *          oTable.fnOpen( this, "Temporary row opened", "info_row" );
  5857  		 *        }
  5858  		 *      } );
  5859  		 *      
  5860  		 *      oTable = $('#example').dataTable();
  5861  		 *    } );
  5862  		 */
  5863  		this.fnOpen = function( nTr, mHtml, sClass )
  5864  		{
  5865  			/* Find settings from table node */
  5866  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5867  		
  5868  			/* Check that the row given is in the table */
  5869  			var nTableRows = _fnGetTrNodes( oSettings );
  5870  			if ( $.inArray(nTr, nTableRows) === -1 )
  5871  			{
  5872  				return;
  5873  			}
  5874  			
  5875  			/* the old open one if there is one */
  5876  			this.fnClose( nTr );
  5877  			
  5878  			var nNewRow = document.createElement("tr");
  5879  			var nNewCell = document.createElement("td");
  5880  			nNewRow.appendChild( nNewCell );
  5881  			nNewCell.className = sClass;
  5882  			nNewCell.colSpan = _fnVisbleColumns( oSettings );
  5883  		
  5884  			if (typeof mHtml === "string")
  5885  			{
  5886  				nNewCell.innerHTML = mHtml;
  5887  			}
  5888  			else
  5889  			{
  5890  				$(nNewCell).html( mHtml );
  5891  			}
  5892  		
  5893  			/* If the nTr isn't on the page at the moment - then we don't insert at the moment */
  5894  			var nTrs = $('tr', oSettings.nTBody);
  5895  			if ( $.inArray(nTr, nTrs) != -1  )
  5896  			{
  5897  				$(nNewRow).insertAfter(nTr);
  5898  			}
  5899  			
  5900  			oSettings.aoOpenRows.push( {
  5901  				"nTr": nNewRow,
  5902  				"nParent": nTr
  5903  			} );
  5904  			
  5905  			return nNewRow;
  5906  		};
  5907  		
  5908  		
  5909  		/**
  5910  		 * Change the pagination - provides the internal logic for pagination in a simple API 
  5911  		 * function. With this function you can have a DataTables table go to the next, 
  5912  		 * previous, first or last pages.
  5913  		 *  @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
  5914  		 *    or page number to jump to (integer), note that page 0 is the first page.
  5915  		 *  @param {bool} [bRedraw=true] Redraw the table or not
  5916  		 *  @dtopt API
  5917  		 *
  5918  		 *  @example
  5919  		 *    $(document).ready(function() {
  5920  		 *      var oTable = $('#example').dataTable();
  5921  		 *      oTable.fnPageChange( 'next' );
  5922  		 *    } );
  5923  		 */
  5924  		this.fnPageChange = function ( mAction, bRedraw )
  5925  		{
  5926  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5927  			_fnPageChange( oSettings, mAction );
  5928  			_fnCalculateEnd( oSettings );
  5929  			
  5930  			if ( bRedraw === undefined || bRedraw )
  5931  			{
  5932  				_fnDraw( oSettings );
  5933  			}
  5934  		};
  5935  		
  5936  		
  5937  		/**
  5938  		 * Show a particular column
  5939  		 *  @param {int} iCol The column whose display should be changed
  5940  		 *  @param {bool} bShow Show (true) or hide (false) the column
  5941  		 *  @param {bool} [bRedraw=true] Redraw the table or not
  5942  		 *  @dtopt API
  5943  		 *
  5944  		 *  @example
  5945  		 *    $(document).ready(function() {
  5946  		 *      var oTable = $('#example').dataTable();
  5947  		 *      
  5948  		 *      // Hide the second column after initialisation
  5949  		 *      oTable.fnSetColumnVis( 1, false );
  5950  		 *    } );
  5951  		 */
  5952  		this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
  5953  		{
  5954  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  5955  			var i, iLen;
  5956  			var aoColumns = oSettings.aoColumns;
  5957  			var aoData = oSettings.aoData;
  5958  			var nTd, bAppend, iBefore;
  5959  			
  5960  			/* No point in doing anything if we are requesting what is already true */
  5961  			if ( aoColumns[iCol].bVisible == bShow )
  5962  			{
  5963  				return;
  5964  			}
  5965  			
  5966  			/* Show the column */
  5967  			if ( bShow )
  5968  			{
  5969  				var iInsert = 0;
  5970  				for ( i=0 ; i<iCol ; i++ )
  5971  				{
  5972  					if ( aoColumns[i].bVisible )
  5973  					{
  5974  						iInsert++;
  5975  					}
  5976  				}
  5977  				
  5978  				/* Need to decide if we should use appendChild or insertBefore */
  5979  				bAppend = (iInsert >= _fnVisbleColumns( oSettings ));
  5980  		
  5981  				/* Which coloumn should we be inserting before? */
  5982  				if ( !bAppend )
  5983  				{
  5984  					for ( i=iCol ; i<aoColumns.length ; i++ )
  5985  					{
  5986  						if ( aoColumns[i].bVisible )
  5987  						{
  5988  							iBefore = i;
  5989  							break;
  5990  						}
  5991  					}
  5992  				}
  5993  		
  5994  				for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
  5995  				{
  5996  					if ( aoData[i].nTr !== null )
  5997  					{
  5998  						if ( bAppend )
  5999  						{
  6000  							aoData[i].nTr.appendChild( 
  6001  								aoData[i]._anHidden[iCol]
  6002  							);
  6003  						}
  6004  						else
  6005  						{
  6006  							aoData[i].nTr.insertBefore(
  6007  								aoData[i]._anHidden[iCol], 
  6008  								_fnGetTdNodes( oSettings, i )[iBefore] );
  6009  						}
  6010  					}
  6011  				}
  6012  			}
  6013  			else
  6014  			{
  6015  				/* Remove a column from display */
  6016  				for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
  6017  				{
  6018  					if ( aoData[i].nTr !== null )
  6019  					{
  6020  						nTd = _fnGetTdNodes( oSettings, i )[iCol];
  6021  						aoData[i]._anHidden[iCol] = nTd;
  6022  						nTd.parentNode.removeChild( nTd );
  6023  					}
  6024  				}
  6025  			}
  6026  		
  6027  			/* Clear to set the visible flag */
  6028  			aoColumns[iCol].bVisible = bShow;
  6029  		
  6030  			/* Redraw the header and footer based on the new column visibility */
  6031  			_fnDrawHead( oSettings, oSettings.aoHeader );
  6032  			if ( oSettings.nTFoot )
  6033  			{
  6034  				_fnDrawHead( oSettings, oSettings.aoFooter );
  6035  			}
  6036  			
  6037  			/* If there are any 'open' rows, then we need to alter the colspan for this col change */
  6038  			for ( i=0, iLen=oSettings.aoOpenRows.length ; i<iLen ; i++ )
  6039  			{
  6040  				oSettings.aoOpenRows[i].nTr.colSpan = _fnVisbleColumns( oSettings );
  6041  			}
  6042  			
  6043  			/* Do a redraw incase anything depending on the table columns needs it 
  6044  			 * (built-in: scrolling) 
  6045  			 */
  6046  			if ( bRedraw === undefined || bRedraw )
  6047  			{
  6048  				_fnAdjustColumnSizing( oSettings );
  6049  				_fnDraw( oSettings );
  6050  			}
  6051  			
  6052  			_fnSaveState( oSettings );
  6053  		};
  6054  		
  6055  		
  6056  		/**
  6057  		 * Get the settings for a particular table for external manipulation
  6058  		 *  @returns {object} DataTables settings object. See 
  6059  		 *    {@link DataTable.models.oSettings}
  6060  		 *  @dtopt API
  6061  		 *
  6062  		 *  @example
  6063  		 *    $(document).ready(function() {
  6064  		 *      var oTable = $('#example').dataTable();
  6065  		 *      var oSettings = oTable.fnSettings();
  6066  		 *      
  6067  		 *      // Show an example parameter from the settings
  6068  		 *      alert( oSettings._iDisplayStart );
  6069  		 *    } );
  6070  		 */
  6071  		this.fnSettings = function()
  6072  		{
  6073  			return _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  6074  		};
  6075  		
  6076  		
  6077  		/**
  6078  		 * Sort the table by a particular column
  6079  		 *  @param {int} iCol the data index to sort on. Note that this will not match the 
  6080  		 *    'display index' if you have hidden data entries
  6081  		 *  @dtopt API
  6082  		 *
  6083  		 *  @example
  6084  		 *    $(document).ready(function() {
  6085  		 *      var oTable = $('#example').dataTable();
  6086  		 *      
  6087  		 *      // Sort immediately with columns 0 and 1
  6088  		 *      oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
  6089  		 *    } );
  6090  		 */
  6091  		this.fnSort = function( aaSort )
  6092  		{
  6093  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  6094  			oSettings.aaSorting = aaSort;
  6095  			_fnSort( oSettings );
  6096  		};
  6097  		
  6098  		
  6099  		/**
  6100  		 * Attach a sort listener to an element for a given column
  6101  		 *  @param {node} nNode the element to attach the sort listener to
  6102  		 *  @param {int} iColumn the column that a click on this node will sort on
  6103  		 *  @param {function} [fnCallback] callback function when sort is run
  6104  		 *  @dtopt API
  6105  		 *
  6106  		 *  @example
  6107  		 *    $(document).ready(function() {
  6108  		 *      var oTable = $('#example').dataTable();
  6109  		 *      
  6110  		 *      // Sort on column 1, when 'sorter' is clicked on
  6111  		 *      oTable.fnSortListener( document.getElementById('sorter'), 1 );
  6112  		 *    } );
  6113  		 */
  6114  		this.fnSortListener = function( nNode, iColumn, fnCallback )
  6115  		{
  6116  			_fnSortAttachListener( _fnSettingsFromNode( this[DataTable.ext.iApiIndex] ), nNode, iColumn,
  6117  			 	fnCallback );
  6118  		};
  6119  		
  6120  		
  6121  		/**
  6122  		 * Update a table cell or row - this method will accept either a single value to
  6123  		 * update the cell with, an array of values with one element for each column or
  6124  		 * an object in the same format as the original data source. The function is
  6125  		 * self-referencing in order to make the multi column updates easier.
  6126  		 *  @param {object|array|string} mData Data to update the cell/row with
  6127  		 *  @param {node|int} mRow TR element you want to update or the aoData index
  6128  		 *  @param {int} [iColumn] The column to update (not used of mData is an array or object)
  6129  		 *  @param {bool} [bRedraw=true] Redraw the table or not
  6130  		 *  @param {bool} [bAction=true] Perform pre-draw actions or not
  6131  		 *  @returns {int} 0 on success, 1 on error
  6132  		 *  @dtopt API
  6133  		 *
  6134  		 *  @example
  6135  		 *    $(document).ready(function() {
  6136  		 *      var oTable = $('#example').dataTable();
  6137  		 *      oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
  6138  		 *      oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], 1, 0 ); // Row
  6139  		 *    } );
  6140  		 */
  6141  		this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
  6142  		{
  6143  			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
  6144  			var i, iLen, sDisplay;
  6145  			var iRow = (typeof mRow === 'object') ? 
  6146  				_fnNodeToDataIndex(oSettings, mRow) : mRow;
  6147  			
  6148  			if ( $.isArray(mData) && iColumn === undefined )
  6149  			{
  6150  				/* Array update - update the whole row */
  6151  				oSettings.aoData[iRow]._aData = mData.slice();
  6152  				
  6153  				/* Flag to the function that we are recursing */
  6154  				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
  6155  				{
  6156  					this.fnUpdate( _fnGetCellData( oSettings, iRow, i ), iRow, i, false, false );
  6157  				}
  6158  			}
  6159  			else if ( $.isPlainObject(mData) && iColumn === undefined )
  6160  			{
  6161  				/* Object update - update the whole row - assume the developer gets the object right */
  6162  				oSettings.aoData[iRow]._aData = $.extend( true, {}, mData );
  6163  		
  6164  				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
  6165  				{
  6166  					this.fnUpdate( _fnGetCellData( oSettings, iRow, i ), iRow, i, false, false );
  6167  				}
  6168  			}
  6169  			else
  6170  			{
  6171  				/* Individual cell update */
  6172  				_fnSetCellData( oSettings, iRow, iColumn, mData );
  6173  				sDisplay = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
  6174  				
  6175  				var oCol = oSettings.aoColumns[iColumn];
  6176  				if ( oCol.fnRender !== null )
  6177  				{
  6178  					sDisplay = _fnRender( oSettings, iRow, iColumn );
  6179  					if ( oCol.bUseRendered )
  6180  					{
  6181  						_fnSetCellData( oSettings, iRow, iColumn, sDisplay );
  6182  					}
  6183  				}
  6184  				
  6185  				if ( oSettings.aoData[iRow].nTr !== null )
  6186  				{
  6187  					/* Do the actual HTML update */
  6188  					_fnGetTdNodes( oSettings, iRow )[iColumn].innerHTML = sDisplay;
  6189  				}
  6190  			}
  6191  			
  6192  			/* Modify the search index for this row (strictly this is likely not needed, since fnReDraw
  6193  			 * will rebuild the search array - however, the redraw might be disabled by the user)
  6194  			 */
  6195  			var iDisplayIndex = $.inArray( iRow, oSettings.aiDisplay );
  6196  			oSettings.asDataSearch[iDisplayIndex] = _fnBuildSearchRow(
  6197  				oSettings, 
  6198  				_fnGetRowData( oSettings, iRow, 'filter', _fnGetColumns( oSettings, 'bSearchable' ) )
  6199  			);
  6200  			
  6201  			/* Perform pre-draw actions */
  6202  			if ( bAction === undefined || bAction )
  6203  			{
  6204  				_fnAdjustColumnSizing( oSettings );
  6205  			}
  6206  			
  6207  			/* Redraw the table */
  6208  			if ( bRedraw === undefined || bRedraw )
  6209  			{
  6210  				_fnReDraw( oSettings );
  6211  			}
  6212  			return 0;
  6213  		};
  6214  		
  6215  		
  6216  		/**
  6217  		 * Provide a common method for plug-ins to check the version of DataTables being used, in order
  6218  		 * to ensure compatibility.
  6219  		 *  @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
  6220  		 *    formats "X" and "X.Y" are also acceptable.
  6221  		 *  @returns {boolean} true if this version of DataTables is greater or equal to the required
  6222  		 *    version, or false if this version of DataTales is not suitable
  6223  		 *  @method
  6224  		 *  @dtopt API
  6225  		 *
  6226  		 *  @example
  6227  		 *    $(document).ready(function() {
  6228  		 *      var oTable = $('#example').dataTable();
  6229  		 *      alert( oTable.fnVersionCheck( '1.9.0' ) );
  6230  		 *    } );
  6231  		 */
  6232  		this.fnVersionCheck = DataTable.ext.fnVersionCheck;
  6233  		
  6234  		
  6235  		/*
  6236  		 * This is really a good bit rubbish this method of exposing the internal methods
  6237  		 * publicly... - To be fixed in 2.0 using methods on the prototype
  6238  		 */
  6239  		
  6240  		
  6241  		/**
  6242  		 * Create a wrapper function for exporting an internal functions to an external API.
  6243  		 *  @param {string} sFunc API function name
  6244  		 *  @returns {function} wrapped function
  6245  		 *  @memberof DataTable#oApi
  6246  		 */
  6247  		function _fnExternApiFunc (sFunc)
  6248  		{
  6249  			return function() {
  6250  				var aArgs = [_fnSettingsFromNode(this[DataTable.ext.iApiIndex])].concat( 
  6251  					Array.prototype.slice.call(arguments) );
  6252  				return DataTable.ext.oApi[sFunc].apply( this, aArgs );
  6253  			};
  6254  		}
  6255  		
  6256  		
  6257  		/**
  6258  		 * Reference to internal functions for use by plug-in developers. Note that these
  6259  		 * methods are references to internal functions and are considered to be private.
  6260  		 * If you use these methods, be aware that they are liable to change between versions
  6261  		 * (check the upgrade notes).
  6262  		 *  @namespace
  6263  		 */
  6264  		this.oApi = {
  6265  			"_fnExternApiFunc": _fnExternApiFunc,
  6266  			"_fnInitialise": _fnInitialise,
  6267  			"_fnInitComplete": _fnInitComplete,
  6268  			"_fnLanguageCompat": _fnLanguageCompat,
  6269  			"_fnAddColumn": _fnAddColumn,
  6270  			"_fnColumnOptions": _fnColumnOptions,
  6271  			"_fnAddData": _fnAddData,
  6272  			"_fnCreateTr": _fnCreateTr,
  6273  			"_fnGatherData": _fnGatherData,
  6274  			"_fnBuildHead": _fnBuildHead,
  6275  			"_fnDrawHead": _fnDrawHead,
  6276  			"_fnDraw": _fnDraw,
  6277  			"_fnReDraw": _fnReDraw,
  6278  			"_fnAjaxUpdate": _fnAjaxUpdate,
  6279  			"_fnAjaxParameters": _fnAjaxParameters,
  6280  			"_fnAjaxUpdateDraw": _fnAjaxUpdateDraw,
  6281  			"_fnServerParams": _fnServerParams,
  6282  			"_fnAddOptionsHtml": _fnAddOptionsHtml,
  6283  			"_fnFeatureHtmlTable": _fnFeatureHtmlTable,
  6284  			"_fnScrollDraw": _fnScrollDraw,
  6285  			"_fnAdjustColumnSizing": _fnAdjustColumnSizing,
  6286  			"_fnFeatureHtmlFilter": _fnFeatureHtmlFilter,
  6287  			"_fnFilterComplete": _fnFilterComplete,
  6288  			"_fnFilterCustom": _fnFilterCustom,
  6289  			"_fnFilterColumn": _fnFilterColumn,
  6290  			"_fnFilter": _fnFilter,
  6291  			"_fnBuildSearchArray": _fnBuildSearchArray,
  6292  			"_fnBuildSearchRow": _fnBuildSearchRow,
  6293  			"_fnFilterCreateSearch": _fnFilterCreateSearch,
  6294  			"_fnDataToSearch": _fnDataToSearch,
  6295  			"_fnSort": _fnSort,
  6296  			"_fnSortAttachListener": _fnSortAttachListener,
  6297  			"_fnSortingClasses": _fnSortingClasses,
  6298  			"_fnFeatureHtmlPaginate": _fnFeatureHtmlPaginate,
  6299  			"_fnPageChange": _fnPageChange,
  6300  			"_fnFeatureHtmlInfo": _fnFeatureHtmlInfo,
  6301  			"_fnUpdateInfo": _fnUpdateInfo,
  6302  			"_fnFeatureHtmlLength": _fnFeatureHtmlLength,
  6303  			"_fnFeatureHtmlProcessing": _fnFeatureHtmlProcessing,
  6304  			"_fnProcessingDisplay": _fnProcessingDisplay,
  6305  			"_fnVisibleToColumnIndex": _fnVisibleToColumnIndex,
  6306  			"_fnColumnIndexToVisible": _fnColumnIndexToVisible,
  6307  			"_fnNodeToDataIndex": _fnNodeToDataIndex,
  6308  			"_fnVisbleColumns": _fnVisbleColumns,
  6309  			"_fnCalculateEnd": _fnCalculateEnd,
  6310  			"_fnConvertToWidth": _fnConvertToWidth,
  6311  			"_fnCalculateColumnWidths": _fnCalculateColumnWidths,
  6312  			"_fnScrollingWidthAdjust": _fnScrollingWidthAdjust,
  6313  			"_fnGetWidestNode": _fnGetWidestNode,
  6314  			"_fnGetMaxLenString": _fnGetMaxLenString,
  6315  			"_fnStringToCss": _fnStringToCss,
  6316  			"_fnDetectType": _fnDetectType,
  6317  			"_fnSettingsFromNode": _fnSettingsFromNode,
  6318  			"_fnGetDataMaster": _fnGetDataMaster,
  6319  			"_fnGetTrNodes": _fnGetTrNodes,
  6320  			"_fnGetTdNodes": _fnGetTdNodes,
  6321  			"_fnEscapeRegex": _fnEscapeRegex,
  6322  			"_fnDeleteIndex": _fnDeleteIndex,
  6323  			"_fnReOrderIndex": _fnReOrderIndex,
  6324  			"_fnColumnOrdering": _fnColumnOrdering,
  6325  			"_fnLog": _fnLog,
  6326  			"_fnClearTable": _fnClearTable,
  6327  			"_fnSaveState": _fnSaveState,
  6328  			"_fnLoadState": _fnLoadState,
  6329  			"_fnCreateCookie": _fnCreateCookie,
  6330  			"_fnReadCookie": _fnReadCookie,
  6331  			"_fnDetectHeader": _fnDetectHeader,
  6332  			"_fnGetUniqueThs": _fnGetUniqueThs,
  6333  			"_fnScrollBarWidth": _fnScrollBarWidth,
  6334  			"_fnApplyToChildren": _fnApplyToChildren,
  6335  			"_fnMap": _fnMap,
  6336  			"_fnGetRowData": _fnGetRowData,
  6337  			"_fnGetCellData": _fnGetCellData,
  6338  			"_fnSetCellData": _fnSetCellData,
  6339  			"_fnGetObjectDataFn": _fnGetObjectDataFn,
  6340  			"_fnSetObjectDataFn": _fnSetObjectDataFn,
  6341  			"_fnApplyColumnDefs": _fnApplyColumnDefs,
  6342  			"_fnBindAction": _fnBindAction,
  6343  			"_fnExtend": _fnExtend,
  6344  			"_fnCallbackReg": _fnCallbackReg,
  6345  			"_fnCallbackFire": _fnCallbackFire,
  6346  			"_fnJsonString": _fnJsonString,
  6347  			"_fnRender": _fnRender,
  6348  			"_fnNodeToColumnIndex": _fnNodeToColumnIndex,
  6349  			"_fnInfoMacros": _fnInfoMacros,
  6350  			"_fnBrowserDetect": _fnBrowserDetect,
  6351  			"_fnGetColumns": _fnGetColumns
  6352  		};
  6353  		
  6354  		$.extend( DataTable.ext.oApi, this.oApi );
  6355  		
  6356  		for ( var sFunc in DataTable.ext.oApi )
  6357  		{
  6358  			if ( sFunc )
  6359  			{
  6360  				this[sFunc] = _fnExternApiFunc(sFunc);
  6361  			}
  6362  		}
  6363  		
  6364  		
  6365  		var _that = this;
  6366  		this.each(function() {
  6367  			var i=0, iLen, j, jLen, k, kLen;
  6368  			var sId = this.getAttribute( 'id' );
  6369  			var bInitHandedOff = false;
  6370  			var bUsePassedData = false;
  6371  			
  6372  			
  6373  			/* Sanity check */
  6374  			if ( this.nodeName.toLowerCase() != 'table' )
  6375  			{
  6376  				_fnLog( null, 0, "Attempted to initialise DataTables on a node which is not a "+
  6377  					"table: "+this.nodeName );
  6378  				return;
  6379  			}
  6380  			
  6381  			/* Check to see if we are re-initialising a table */
  6382  			for ( i=0, iLen=DataTable.settings.length ; i<iLen ; i++ )
  6383  			{
  6384  				/* Base check on table node */
  6385  				if ( DataTable.settings[i].nTable == this )
  6386  				{
  6387  					if ( oInit === undefined || oInit.bRetrieve )
  6388  					{
  6389  						return DataTable.settings[i].oInstance;
  6390  					}
  6391  					else if ( oInit.bDestroy )
  6392  					{
  6393  						DataTable.settings[i].oInstance.fnDestroy();
  6394  						break;
  6395  					}
  6396  					else
  6397  					{
  6398  						_fnLog( DataTable.settings[i], 0, "Cannot reinitialise DataTable.\n\n"+
  6399  							"To retrieve the DataTables object for this table, pass no arguments or see "+
  6400  							"the docs for bRetrieve and bDestroy" );
  6401  						return;
  6402  					}
  6403  				}
  6404  				
  6405  				/* If the element we are initialising has the same ID as a table which was previously
  6406  				 * initialised, but the table nodes don't match (from before) then we destroy the old
  6407  				 * instance by simply deleting it. This is under the assumption that the table has been
  6408  				 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
  6409  				 */
  6410  				if ( DataTable.settings[i].sTableId == this.id )
  6411  				{
  6412  					DataTable.settings.splice( i, 1 );
  6413  					break;
  6414  				}
  6415  			}
  6416  			
  6417  			/* Ensure the table has an ID - required for accessibility */
  6418  			if ( sId === null || sId === "" )
  6419  			{
  6420  				sId = "DataTables_Table_"+(DataTable.ext._oExternConfig.iNextUnique++);
  6421  				this.id = sId;
  6422  			}
  6423  			
  6424  			/* Create the settings object for this table and set some of the default parameters */
  6425  			var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
  6426  				"nTable":        this,
  6427  				"oApi":          _that.oApi,
  6428  				"oInit":         oInit,
  6429  				"sDestroyWidth": $(this).width(),
  6430  				"sInstance":     sId,
  6431  				"sTableId":      sId
  6432  			} );
  6433  			DataTable.settings.push( oSettings );
  6434  			
  6435  			// Need to add the instance after the instance after the settings object has been added
  6436  			// to the settings array, so we can self reference the table instance if more than one
  6437  			oSettings.oInstance = (_that.length===1) ? _that : $(this).dataTable();
  6438  			
  6439  			/* Setting up the initialisation object */
  6440  			if ( !oInit )
  6441  			{
  6442  				oInit = {};
  6443  			}
  6444  			
  6445  			// Backwards compatibility, before we apply all the defaults
  6446  			if ( oInit.oLanguage )
  6447  			{
  6448  				_fnLanguageCompat( oInit.oLanguage );
  6449  			}
  6450  			
  6451  			oInit = _fnExtend( $.extend(true, {}, DataTable.defaults), oInit );
  6452  			
  6453  			// Map the initialisation options onto the settings object
  6454  			_fnMap( oSettings.oFeatures, oInit, "bPaginate" );
  6455  			_fnMap( oSettings.oFeatures, oInit, "bLengthChange" );
  6456  			_fnMap( oSettings.oFeatures, oInit, "bFilter" );
  6457  			_fnMap( oSettings.oFeatures, oInit, "bSort" );
  6458  			_fnMap( oSettings.oFeatures, oInit, "bInfo" );
  6459  			_fnMap( oSettings.oFeatures, oInit, "bProcessing" );
  6460  			_fnMap( oSettings.oFeatures, oInit, "bAutoWidth" );
  6461  			_fnMap( oSettings.oFeatures, oInit, "bSortClasses" );
  6462  			_fnMap( oSettings.oFeatures, oInit, "bServerSide" );
  6463  			_fnMap( oSettings.oFeatures, oInit, "bDeferRender" );
  6464  			_fnMap( oSettings.oScroll, oInit, "sScrollX", "sX" );
  6465  			_fnMap( oSettings.oScroll, oInit, "sScrollXInner", "sXInner" );
  6466  			_fnMap( oSettings.oScroll, oInit, "sScrollY", "sY" );
  6467  			_fnMap( oSettings.oScroll, oInit, "bScrollCollapse", "bCollapse" );
  6468  			_fnMap( oSettings.oScroll, oInit, "bScrollInfinite", "bInfinite" );
  6469  			_fnMap( oSettings.oScroll, oInit, "iScrollLoadGap", "iLoadGap" );
  6470  			_fnMap( oSettings.oScroll, oInit, "bScrollAutoCss", "bAutoCss" );
  6471  			_fnMap( oSettings, oInit, "asStripeClasses" );
  6472  			_fnMap( oSettings, oInit, "asStripClasses", "asStripeClasses" ); // legacy
  6473  			_fnMap( oSettings, oInit, "fnServerData" );
  6474  			_fnMap( oSettings, oInit, "fnFormatNumber" );
  6475  			_fnMap( oSettings, oInit, "sServerMethod" );
  6476  			_fnMap( oSettings, oInit, "aaSorting" );
  6477  			_fnMap( oSettings, oInit, "aaSortingFixed" );
  6478  			_fnMap( oSettings, oInit, "aLengthMenu" );
  6479  			_fnMap( oSettings, oInit, "sPaginationType" );
  6480  			_fnMap( oSettings, oInit, "sAjaxSource" );
  6481  			_fnMap( oSettings, oInit, "sAjaxDataProp" );
  6482  			_fnMap( oSettings, oInit, "iCookieDuration" );
  6483  			_fnMap( oSettings, oInit, "sCookiePrefix" );
  6484  			_fnMap( oSettings, oInit, "sDom" );
  6485  			_fnMap( oSettings, oInit, "bSortCellsTop" );
  6486  			_fnMap( oSettings, oInit, "iTabIndex" );
  6487  			_fnMap( oSettings, oInit, "oSearch", "oPreviousSearch" );
  6488  			_fnMap( oSettings, oInit, "aoSearchCols", "aoPreSearchCols" );
  6489  			_fnMap( oSettings, oInit, "iDisplayLength", "_iDisplayLength" );
  6490  			_fnMap( oSettings, oInit, "bJQueryUI", "bJUI" );
  6491  			_fnMap( oSettings, oInit, "fnCookieCallback" );
  6492  			_fnMap( oSettings, oInit, "fnStateLoad" );
  6493  			_fnMap( oSettings, oInit, "fnStateSave" );
  6494  			_fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
  6495  			
  6496  			/* Callback functions which are array driven */
  6497  			_fnCallbackReg( oSettings, 'aoDrawCallback',       oInit.fnDrawCallback,      'user' );
  6498  			_fnCallbackReg( oSettings, 'aoServerParams',       oInit.fnServerParams,      'user' );
  6499  			_fnCallbackReg( oSettings, 'aoStateSaveParams',    oInit.fnStateSaveParams,   'user' );
  6500  			_fnCallbackReg( oSettings, 'aoStateLoadParams',    oInit.fnStateLoadParams,   'user' );
  6501  			_fnCallbackReg( oSettings, 'aoStateLoaded',        oInit.fnStateLoaded,       'user' );
  6502  			_fnCallbackReg( oSettings, 'aoRowCallback',        oInit.fnRowCallback,       'user' );
  6503  			_fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow,        'user' );
  6504  			_fnCallbackReg( oSettings, 'aoHeaderCallback',     oInit.fnHeaderCallback,    'user' );
  6505  			_fnCallbackReg( oSettings, 'aoFooterCallback',     oInit.fnFooterCallback,    'user' );
  6506  			_fnCallbackReg( oSettings, 'aoInitComplete',       oInit.fnInitComplete,      'user' );
  6507  			_fnCallbackReg( oSettings, 'aoPreDrawCallback',    oInit.fnPreDrawCallback,   'user' );
  6508  			
  6509  			if ( oSettings.oFeatures.bServerSide && oSettings.oFeatures.bSort &&
  6510  				   oSettings.oFeatures.bSortClasses )
  6511  			{
  6512  				/* Enable sort classes for server-side processing. Safe to do it here, since server-side
  6513  				 * processing must be enabled by the developer
  6514  				 */
  6515  				_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSortingClasses, 'server_side_sort_classes' );
  6516  			}
  6517  			else if ( oSettings.oFeatures.bDeferRender )
  6518  			{
  6519  				_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSortingClasses, 'defer_sort_classes' );
  6520  			}
  6521  			
  6522  			if ( oInit.bJQueryUI )
  6523  			{
  6524  				/* Use the JUI classes object for display. You could clone the oStdClasses object if 
  6525  				 * you want to have multiple tables with multiple independent classes 
  6526  				 */
  6527  				$.extend( oSettings.oClasses, DataTable.ext.oJUIClasses );
  6528  				
  6529  				if ( oInit.sDom === DataTable.defaults.sDom && DataTable.defaults.sDom === "lfrtip" )
  6530  				{
  6531  					/* Set the DOM to use a layout suitable for jQuery UI's theming */
  6532  					oSettings.sDom = '<"H"lfr>t<"F"ip>';
  6533  				}
  6534  			}
  6535  			else
  6536  			{
  6537  				$.extend( oSettings.oClasses, DataTable.ext.oStdClasses );
  6538  			}
  6539  			$(this).addClass( oSettings.oClasses.sTable );
  6540  			
  6541  			/* Calculate the scroll bar width and cache it for use later on */
  6542  			if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
  6543  			{
  6544  				oSettings.oScroll.iBarWidth = _fnScrollBarWidth();
  6545  			}
  6546  			
  6547  			if ( oSettings.iInitDisplayStart === undefined )
  6548  			{
  6549  				/* Display start point, taking into account the save saving */
  6550  				oSettings.iInitDisplayStart = oInit.iDisplayStart;
  6551  				oSettings._iDisplayStart = oInit.iDisplayStart;
  6552  			}
  6553  			
  6554  			/* Must be done after everything which can be overridden by a cookie! */
  6555  			if ( oInit.bStateSave )
  6556  			{
  6557  				oSettings.oFeatures.bStateSave = true;
  6558  				_fnLoadState( oSettings, oInit );
  6559  				_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
  6560  			}
  6561  			
  6562  			if ( oInit.iDeferLoading !== null )
  6563  			{
  6564  				oSettings.bDeferLoading = true;
  6565  				var tmp = $.isArray( oInit.iDeferLoading );
  6566  				oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
  6567  				oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
  6568  			}
  6569  			
  6570  			if ( oInit.aaData !== null )
  6571  			{
  6572  				bUsePassedData = true;
  6573  			}
  6574  			
  6575  			/* Language definitions */
  6576  			if ( oInit.oLanguage.sUrl !== "" )
  6577  			{
  6578  				/* Get the language definitions from a file - because this Ajax call makes the language
  6579  				 * get async to the remainder of this function we use bInitHandedOff to indicate that 
  6580  				 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
  6581  				 */
  6582  				oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl;
  6583  				$.getJSON( oSettings.oLanguage.sUrl, null, function( json ) {
  6584  					_fnLanguageCompat( json );
  6585  					$.extend( true, oSettings.oLanguage, oInit.oLanguage, json );
  6586  					_fnInitialise( oSettings );
  6587  				} );
  6588  				bInitHandedOff = true;
  6589  			}
  6590  			else
  6591  			{
  6592  				$.extend( true, oSettings.oLanguage, oInit.oLanguage );
  6593  			}
  6594  			
  6595  			
  6596  			/*
  6597  			 * Stripes
  6598  			 */
  6599  			if ( oInit.asStripeClasses === null )
  6600  			{
  6601  				oSettings.asStripeClasses =[
  6602  					oSettings.oClasses.sStripeOdd,
  6603  					oSettings.oClasses.sStripeEven
  6604  				];
  6605  			}
  6606  			
  6607  			/* Remove row stripe classes if they are already on the table row */
  6608  			iLen=oSettings.asStripeClasses.length;
  6609  			oSettings.asDestroyStripes = [];
  6610  			if (iLen)
  6611  			{
  6612  				var bStripeRemove = false;
  6613  				var anRows = $(this).children('tbody').children('tr:lt(' + iLen + ')');
  6614  				for ( i=0 ; i<iLen ; i++ )
  6615  				{
  6616  					if ( anRows.hasClass( oSettings.asStripeClasses[i] ) )
  6617  					{
  6618  						bStripeRemove = true;
  6619  						
  6620  						/* Store the classes which we are about to remove so they can be re-added on destroy */
  6621  						oSettings.asDestroyStripes.push( oSettings.asStripeClasses[i] );
  6622  					}
  6623  				}
  6624  				
  6625  				if ( bStripeRemove )
  6626  				{
  6627  					anRows.removeClass( oSettings.asStripeClasses.join(' ') );
  6628  				}
  6629  			}
  6630  			
  6631  			/*
  6632  			 * Columns
  6633  			 * See if we should load columns automatically or use defined ones
  6634  			 */
  6635  			var anThs = [];
  6636  			var aoColumnsInit;
  6637  			var nThead = this.getElementsByTagName('thead');
  6638  			if ( nThead.length !== 0 )
  6639  			{
  6640  				_fnDetectHeader( oSettings.aoHeader, nThead[0] );
  6641  				anThs = _fnGetUniqueThs( oSettings );
  6642  			}
  6643  			
  6644  			/* If not given a column array, generate one with nulls */
  6645  			if ( oInit.aoColumns === null )
  6646  			{
  6647  				aoColumnsInit = [];
  6648  				for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
  6649  				{
  6650  					aoColumnsInit.push( null );
  6651  				}
  6652  			}
  6653  			else
  6654  			{
  6655  				aoColumnsInit = oInit.aoColumns;
  6656  			}
  6657  			
  6658  			/* Add the columns */
  6659  			for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
  6660  			{
  6661  				/* Short cut - use the loop to check if we have column visibility state to restore */
  6662  				if ( oInit.saved_aoColumns !== undefined && oInit.saved_aoColumns.length == iLen )
  6663  				{
  6664  					if ( aoColumnsInit[i] === null )
  6665  					{
  6666  						aoColumnsInit[i] = {};
  6667  					}
  6668  					aoColumnsInit[i].bVisible = oInit.saved_aoColumns[i].bVisible;
  6669  				}
  6670  				
  6671  				_fnAddColumn( oSettings, anThs ? anThs[i] : null );
  6672  			}
  6673  			
  6674  			/* Apply the column definitions */
  6675  			_fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
  6676  				_fnColumnOptions( oSettings, iCol, oDef );
  6677  			} );
  6678  			
  6679  			
  6680  			/*
  6681  			 * Sorting
  6682  			 * Check the aaSorting array
  6683  			 */
  6684  			for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )
  6685  			{
  6686  				if ( oSettings.aaSorting[i][0] >= oSettings.aoColumns.length )
  6687  				{
  6688  					oSettings.aaSorting[i][0] = 0;
  6689  				}
  6690  				var oColumn = oSettings.aoColumns[ oSettings.aaSorting[i][0] ];
  6691  				
  6692  				/* Add a default sorting index */
  6693  				if ( oSettings.aaSorting[i][2] === undefined )
  6694  				{
  6695  					oSettings.aaSorting[i][2] = 0;
  6696  				}
  6697  				
  6698  				/* If aaSorting is not defined, then we use the first indicator in asSorting */
  6699  				if ( oInit.aaSorting === undefined && oSettings.saved_aaSorting === undefined )
  6700  				{
  6701  					oSettings.aaSorting[i][1] = oColumn.asSorting[0];
  6702  				}
  6703  				
  6704  				/* Set the current sorting index based on aoColumns.asSorting */
  6705  				for ( j=0, jLen=oColumn.asSorting.length ; j<jLen ; j++ )
  6706  				{
  6707  					if ( oSettings.aaSorting[i][1] == oColumn.asSorting[j] )
  6708  					{
  6709  						oSettings.aaSorting[i][2] = j;
  6710  						break;
  6711  					}
  6712  				}
  6713  			}
  6714  				
  6715  			/* Do a first pass on the sorting classes (allows any size changes to be taken into
  6716  			 * account, and also will apply sorting disabled classes if disabled
  6717  			 */
  6718  			_fnSortingClasses( oSettings );
  6719  			
  6720  			
  6721  			/*
  6722  			 * Final init
  6723  			 * Cache the header, body and footer as required, creating them if needed
  6724  			 */
  6725  			
  6726  			/* Browser support detection */
  6727  			_fnBrowserDetect( oSettings );
  6728  			
  6729  			// Work around for Webkit bug 83867 - store the caption-side before removing from doc
  6730  			var captions = $(this).children('caption').each( function () {
  6731  				this._captionSide = $(this).css('caption-side');
  6732  			} );
  6733  			
  6734  			var thead = $(this).children('thead');
  6735  			if ( thead.length === 0 )
  6736  			{
  6737  				thead = [ document.createElement( 'thead' ) ];
  6738  				this.appendChild( thead[0] );
  6739  			}
  6740  			oSettings.nTHead = thead[0];
  6741  			
  6742  			var tbody = $(this).children('tbody');
  6743  			if ( tbody.length === 0 )
  6744  			{
  6745  				tbody = [ document.createElement( 'tbody' ) ];
  6746  				this.appendChild( tbody[0] );
  6747  			}
  6748  			oSettings.nTBody = tbody[0];
  6749  			oSettings.nTBody.setAttribute( "role", "alert" );
  6750  			oSettings.nTBody.setAttribute( "aria-live", "polite" );
  6751  			oSettings.nTBody.setAttribute( "aria-relevant", "all" );
  6752  			
  6753  			var tfoot = $(this).children('tfoot');
  6754  			if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
  6755  			{
  6756  				// If we are a scrolling table, and no footer has been given, then we need to create
  6757  				// a tfoot element for the caption element to be appended to
  6758  				tfoot = [ document.createElement( 'tfoot' ) ];
  6759  				this.appendChild( tfoot[0] );
  6760  			}
  6761  			
  6762  			if ( tfoot.length > 0 )
  6763  			{
  6764  				oSettings.nTFoot = tfoot[0];
  6765  				_fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
  6766  			}
  6767  			
  6768  			/* Check if there is data passing into the constructor */
  6769  			if ( bUsePassedData )
  6770  			{
  6771  				for ( i=0 ; i<oInit.aaData.length ; i++ )
  6772  				{
  6773  					_fnAddData( oSettings, oInit.aaData[ i ] );
  6774  				}
  6775  			}
  6776  			else
  6777  			{
  6778  				/* Grab the data from the page */
  6779  				_fnGatherData( oSettings );
  6780  			}
  6781  			
  6782  			/* Copy the data index array */
  6783  			oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
  6784  			
  6785  			/* Initialisation complete - table can be drawn */
  6786  			oSettings.bInitialised = true;
  6787  			
  6788  			/* Check if we need to initialise the table (it might not have been handed off to the
  6789  			 * language processor)
  6790  			 */
  6791  			if ( bInitHandedOff === false )
  6792  			{
  6793  				_fnInitialise( oSettings );
  6794  			}
  6795  		} );
  6796  		_that = null;
  6797  		return this;
  6798  	};
  6799  
  6800  	
  6801  	
  6802  	/**
  6803  	 * Provide a common method for plug-ins to check the version of DataTables being used, in order
  6804  	 * to ensure compatibility.
  6805  	 *  @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
  6806  	 *    formats "X" and "X.Y" are also acceptable.
  6807  	 *  @returns {boolean} true if this version of DataTables is greater or equal to the required
  6808  	 *    version, or false if this version of DataTales is not suitable
  6809  	 *  @static
  6810  	 *  @dtopt API-Static
  6811  	 *
  6812  	 *  @example
  6813  	 *    alert( $.fn.dataTable.fnVersionCheck( '1.9.0' ) );
  6814  	 */
  6815  	DataTable.fnVersionCheck = function( sVersion )
  6816  	{
  6817  		/* This is cheap, but effective */
  6818  		var fnZPad = function (Zpad, count)
  6819  		{
  6820  			while(Zpad.length < count) {
  6821  				Zpad += '0';
  6822  			}
  6823  			return Zpad;
  6824  		};
  6825  		var aThis = DataTable.ext.sVersion.split('.');
  6826  		var aThat = sVersion.split('.');
  6827  		var sThis = '', sThat = '';
  6828  		
  6829  		for ( var i=0, iLen=aThat.length ; i<iLen ; i++ )
  6830  		{
  6831  			sThis += fnZPad( aThis[i], 3 );
  6832  			sThat += fnZPad( aThat[i], 3 );
  6833  		}
  6834  		
  6835  		return parseInt(sThis, 10) >= parseInt(sThat, 10);
  6836  	};
  6837  	
  6838  	
  6839  	/**
  6840  	 * Check if a TABLE node is a DataTable table already or not.
  6841  	 *  @param {node} nTable The TABLE node to check if it is a DataTable or not (note that other
  6842  	 *    node types can be passed in, but will always return false).
  6843  	 *  @returns {boolean} true the table given is a DataTable, or false otherwise
  6844  	 *  @static
  6845  	 *  @dtopt API-Static
  6846  	 *
  6847  	 *  @example
  6848  	 *    var ex = document.getElementById('example');
  6849  	 *    if ( ! $.fn.DataTable.fnIsDataTable( ex ) ) {
  6850  	 *      $(ex).dataTable();
  6851  	 *    }
  6852  	 */
  6853  	DataTable.fnIsDataTable = function ( nTable )
  6854  	{
  6855  		var o = DataTable.settings;
  6856  	
  6857  		for ( var i=0 ; i<o.length ; i++ )
  6858  		{
  6859  			if ( o[i].nTable === nTable || o[i].nScrollHead === nTable || o[i].nScrollFoot === nTable )
  6860  			{
  6861  				return true;
  6862  			}
  6863  		}
  6864  	
  6865  		return false;
  6866  	};
  6867  	
  6868  	
  6869  	/**
  6870  	 * Get all DataTable tables that have been initialised - optionally you can select to
  6871  	 * get only currently visible tables.
  6872  	 *  @param {boolean} [bVisible=false] Flag to indicate if you want all (default) or 
  6873  	 *    visible tables only.
  6874  	 *  @returns {array} Array of TABLE nodes (not DataTable instances) which are DataTables
  6875  	 *  @static
  6876  	 *  @dtopt API-Static
  6877  	 *
  6878  	 *  @example
  6879  	 *    var table = $.fn.dataTable.fnTables(true);
  6880  	 *    if ( table.length > 0 ) {
  6881  	 *      $(table).dataTable().fnAdjustColumnSizing();
  6882  	 *    }
  6883  	 */
  6884  	DataTable.fnTables = function ( bVisible )
  6885  	{
  6886  		var out = [];
  6887  	
  6888  		jQuery.each( DataTable.settings, function (i, o) {
  6889  			if ( !bVisible || (bVisible === true && $(o.nTable).is(':visible')) )
  6890  			{
  6891  				out.push( o.nTable );
  6892  			}
  6893  		} );
  6894  	
  6895  		return out;
  6896  	};
  6897  	
  6898  
  6899  	/**
  6900  	 * Version string for plug-ins to check compatibility. Allowed format is
  6901  	 * a.b.c.d.e where: a:int, b:int, c:int, d:string(dev|beta), e:int. d and
  6902  	 * e are optional
  6903  	 *  @member
  6904  	 *  @type string
  6905  	 *  @default Version number
  6906  	 */
  6907  	DataTable.version = "1.9.4";
  6908  
  6909  	/**
  6910  	 * Private data store, containing all of the settings objects that are created for the
  6911  	 * tables on a given page.
  6912  	 * 
  6913  	 * Note that the <i>DataTable.settings</i> object is aliased to <i>jQuery.fn.dataTableExt</i> 
  6914  	 * through which it may be accessed and manipulated, or <i>jQuery.fn.dataTable.settings</i>.
  6915  	 *  @member
  6916  	 *  @type array
  6917  	 *  @default []
  6918  	 *  @private
  6919  	 */
  6920  	DataTable.settings = [];
  6921  
  6922  	/**
  6923  	 * Object models container, for the various models that DataTables has available
  6924  	 * to it. These models define the objects that are used to hold the active state 
  6925  	 * and configuration of the table.
  6926  	 *  @namespace
  6927  	 */
  6928  	DataTable.models = {};
  6929  	
  6930  	
  6931  	/**
  6932  	 * DataTables extension options and plug-ins. This namespace acts as a collection "area"
  6933  	 * for plug-ins that can be used to extend the default DataTables behaviour - indeed many
  6934  	 * of the build in methods use this method to provide their own capabilities (sorting methods
  6935  	 * for example).
  6936  	 * 
  6937  	 * Note that this namespace is aliased to jQuery.fn.dataTableExt so it can be readily accessed
  6938  	 * and modified by plug-ins.
  6939  	 *  @namespace
  6940  	 */
  6941  	DataTable.models.ext = {
  6942  		/**
  6943  		 * Plug-in filtering functions - this method of filtering is complimentary to the default
  6944  		 * type based filtering, and a lot more comprehensive as it allows you complete control
  6945  		 * over the filtering logic. Each element in this array is a function (parameters
  6946  		 * described below) that is called for every row in the table, and your logic decides if
  6947  		 * it should be included in the filtered data set or not.
  6948  		 *   <ul>
  6949  		 *     <li>
  6950  		 *       Function input parameters:
  6951  		 *       <ul>
  6952  		 *         <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
  6953  		 *         <li>{array|object} Data for the row to be processed (same as the original format
  6954  		 *           that was passed in as the data source, or an array from a DOM data source</li>
  6955  		 *         <li>{int} Row index in aoData ({@link DataTable.models.oSettings.aoData}), which can
  6956  		 *           be useful to retrieve the TR element if you need DOM interaction.</li>
  6957  		 *       </ul>
  6958  		 *     </li>
  6959  		 *     <li>
  6960  		 *       Function return:
  6961  		 *       <ul>
  6962  		 *         <li>{boolean} Include the row in the filtered result set (true) or not (false)</li>
  6963  		 *       </ul>
  6964  		 *     </il>
  6965  		 *   </ul>
  6966  		 *  @type array
  6967  		 *  @default []
  6968  		 *
  6969  		 *  @example
  6970  		 *    // The following example shows custom filtering being applied to the fourth column (i.e.
  6971  		 *    // the aData[3] index) based on two input values from the end-user, matching the data in 
  6972  		 *    // a certain range.
  6973  		 *    $.fn.dataTableExt.afnFiltering.push(
  6974  		 *      function( oSettings, aData, iDataIndex ) {
  6975  		 *        var iMin = document.getElementById('min').value * 1;
  6976  		 *        var iMax = document.getElementById('max').value * 1;
  6977  		 *        var iVersion = aData[3] == "-" ? 0 : aData[3]*1;
  6978  		 *        if ( iMin == "" && iMax == "" ) {
  6979  		 *          return true;
  6980  		 *        }
  6981  		 *        else if ( iMin == "" && iVersion < iMax ) {
  6982  		 *          return true;
  6983  		 *        }
  6984  		 *        else if ( iMin < iVersion && "" == iMax ) {
  6985  		 *          return true;
  6986  		 *        }
  6987  		 *        else if ( iMin < iVersion && iVersion < iMax ) {
  6988  		 *          return true;
  6989  		 *        }
  6990  		 *        return false;
  6991  		 *      }
  6992  		 *    );
  6993  		 */
  6994  		"afnFiltering": [],
  6995  	
  6996  	
  6997  		/**
  6998  		 * Plug-in sorting functions - this method of sorting is complimentary to the default type
  6999  		 * based sorting that DataTables does automatically, allowing much greater control over the
  7000  		 * the data that is being used to sort a column. This is useful if you want to do sorting
  7001  		 * based on live data (for example the contents of an 'input' element) rather than just the
  7002  		 * static string that DataTables knows of. The way these plug-ins work is that you create
  7003  		 * an array of the values you wish to be sorted for the column in question and then return
  7004  		 * that array. Which pre-sorting function is run here depends on the sSortDataType parameter
  7005  		 * that is used for the column (if any). This is the corollary of <i>ofnSearch</i> for sort 
  7006  		 * data.
  7007  		 *   <ul>
  7008  	     *     <li>
  7009  	     *       Function input parameters:
  7010  	     *       <ul>
  7011  		 *         <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
  7012  	     *         <li>{int} Target column index</li>
  7013  	     *       </ul>
  7014  	     *     </li>
  7015  		 *     <li>
  7016  		 *       Function return:
  7017  		 *       <ul>
  7018  		 *         <li>{array} Data for the column to be sorted upon</li>
  7019  		 *       </ul>
  7020  		 *     </il>
  7021  		 *   </ul>
  7022  		 *  
  7023  		 * Note that as of v1.9, it is typically preferable to use <i>mData</i> to prepare data for
  7024  		 * the different uses that DataTables can put the data to. Specifically <i>mData</i> when
  7025  		 * used as a function will give you a 'type' (sorting, filtering etc) that you can use to 
  7026  		 * prepare the data as required for the different types. As such, this method is deprecated.
  7027  		 *  @type array
  7028  		 *  @default []
  7029  		 *  @deprecated
  7030  		 *
  7031  		 *  @example
  7032  		 *    // Updating the cached sorting information with user entered values in HTML input elements
  7033  		 *    jQuery.fn.dataTableExt.afnSortData['dom-text'] = function ( oSettings, iColumn )
  7034  		 *    {
  7035  		 *      var aData = [];
  7036  		 *      $( 'td:eq('+iColumn+') input', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
  7037  		 *        aData.push( this.value );
  7038  		 *      } );
  7039  		 *      return aData;
  7040  		 *    }
  7041  		 */
  7042  		"afnSortData": [],
  7043  	
  7044  	
  7045  		/**
  7046  		 * Feature plug-ins - This is an array of objects which describe the feature plug-ins that are
  7047  		 * available to DataTables. These feature plug-ins are accessible through the sDom initialisation
  7048  		 * option. As such, each feature plug-in must describe a function that is used to initialise
  7049  		 * itself (fnInit), a character so the feature can be enabled by sDom (cFeature) and the name
  7050  		 * of the feature (sFeature). Thus the objects attached to this method must provide:
  7051  		 *   <ul>
  7052  		 *     <li>{function} fnInit Initialisation of the plug-in
  7053  		 *       <ul>
  7054  	     *         <li>
  7055  	     *           Function input parameters:
  7056  	     *           <ul>
  7057  		 *             <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
  7058  	     *           </ul>
  7059  	     *         </li>
  7060  		 *         <li>
  7061  		 *           Function return:
  7062  		 *           <ul>
  7063  		 *             <li>{node|null} The element which contains your feature. Note that the return
  7064  		 *                may also be void if your plug-in does not require to inject any DOM elements 
  7065  		 *                into DataTables control (sDom) - for example this might be useful when 
  7066  		 *                developing a plug-in which allows table control via keyboard entry.</li>
  7067  		 *           </ul>
  7068  		 *         </il>
  7069  		 *       </ul>
  7070  		 *     </li>
  7071  		 *     <li>{character} cFeature Character that will be matched in sDom - case sensitive</li>
  7072  		 *     <li>{string} sFeature Feature name</li>
  7073  		 *   </ul>
  7074  		 *  @type array
  7075  		 *  @default []
  7076  		 * 
  7077  		 *  @example
  7078  		 *    // How TableTools initialises itself.
  7079  		 *    $.fn.dataTableExt.aoFeatures.push( {
  7080  		 *      "fnInit": function( oSettings ) {
  7081  		 *        return new TableTools( { "oDTSettings": oSettings } );
  7082  		 *      },
  7083  		 *      "cFeature": "T",
  7084  		 *      "sFeature": "TableTools"
  7085  		 *    } );
  7086  		 */
  7087  		"aoFeatures": [],
  7088  	
  7089  	
  7090  		/**
  7091  		 * Type detection plug-in functions - DataTables utilises types to define how sorting and
  7092  		 * filtering behave, and types can be either  be defined by the developer (sType for the
  7093  		 * column) or they can be automatically detected by the methods in this array. The functions
  7094  		 * defined in the array are quite simple, taking a single parameter (the data to analyse) 
  7095  		 * and returning the type if it is a known type, or null otherwise.
  7096  		 *   <ul>
  7097  	     *     <li>
  7098  	     *       Function input parameters:
  7099  	     *       <ul>
  7100  		 *         <li>{*} Data from the column cell to be analysed</li>
  7101  	     *       </ul>
  7102  	     *     </li>
  7103  		 *     <li>
  7104  		 *       Function return:
  7105  		 *       <ul>
  7106  		 *         <li>{string|null} Data type detected, or null if unknown (and thus pass it
  7107  		 *           on to the other type detection functions.</li>
  7108  		 *       </ul>
  7109  		 *     </il>
  7110  		 *   </ul>
  7111  		 *  @type array
  7112  		 *  @default []
  7113  		 *  
  7114  		 *  @example
  7115  		 *    // Currency type detection plug-in:
  7116  		 *    jQuery.fn.dataTableExt.aTypes.push(
  7117  		 *      function ( sData ) {
  7118  		 *        var sValidChars = "0123456789.-";
  7119  		 *        var Char;
  7120  		 *        
  7121  		 *        // Check the numeric part
  7122  		 *        for ( i=1 ; i<sData.length ; i++ ) {
  7123  		 *          Char = sData.charAt(i); 
  7124  		 *          if (sValidChars.indexOf(Char) == -1) {
  7125  		 *            return null;
  7126  		 *          }
  7127  		 *        }
  7128  		 *        
  7129  		 *        // Check prefixed by currency
  7130  		 *        if ( sData.charAt(0) == '$' || sData.charAt(0) == '&pound;' ) {
  7131  		 *          return 'currency';
  7132  		 *        }
  7133  		 *        return null;
  7134  		 *      }
  7135  		 *    );
  7136  		 */
  7137  		"aTypes": [],
  7138  	
  7139  	
  7140  		/**
  7141  		 * Provide a common method for plug-ins to check the version of DataTables being used, 
  7142  		 * in order to ensure compatibility.
  7143  		 *  @type function
  7144  		 *  @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note 
  7145  		 *    that the formats "X" and "X.Y" are also acceptable.
  7146  		 *  @returns {boolean} true if this version of DataTables is greater or equal to the 
  7147  		 *    required version, or false if this version of DataTales is not suitable
  7148  		 *
  7149  		 *  @example
  7150  		 *    $(document).ready(function() {
  7151  		 *      var oTable = $('#example').dataTable();
  7152  		 *      alert( oTable.fnVersionCheck( '1.9.0' ) );
  7153  		 *    } );
  7154  		 */
  7155  		"fnVersionCheck": DataTable.fnVersionCheck,
  7156  	
  7157  	
  7158  		/**
  7159  		 * Index for what 'this' index API functions should use
  7160  		 *  @type int
  7161  		 *  @default 0
  7162  		 */
  7163  		"iApiIndex": 0,
  7164  	
  7165  	
  7166  		/**
  7167  		 * Pre-processing of filtering data plug-ins - When you assign the sType for a column
  7168  		 * (or have it automatically detected for you by DataTables or a type detection plug-in), 
  7169  		 * you will typically be using this for custom sorting, but it can also be used to provide 
  7170  		 * custom filtering by allowing you to pre-processing the data and returning the data in
  7171  		 * the format that should be filtered upon. This is done by adding functions this object 
  7172  		 * with a parameter name which matches the sType for that target column. This is the
  7173  		 * corollary of <i>afnSortData</i> for filtering data.
  7174  		 *   <ul>
  7175  	     *     <li>
  7176  	     *       Function input parameters:
  7177  	     *       <ul>
  7178  		 *         <li>{*} Data from the column cell to be prepared for filtering</li>
  7179  	     *       </ul>
  7180  	     *     </li>
  7181  		 *     <li>
  7182  		 *       Function return:
  7183  		 *       <ul>
  7184  		 *         <li>{string|null} Formatted string that will be used for the filtering.</li>
  7185  		 *       </ul>
  7186  		 *     </il>
  7187  		 *   </ul>
  7188  		 * 
  7189  		 * Note that as of v1.9, it is typically preferable to use <i>mData</i> to prepare data for
  7190  		 * the different uses that DataTables can put the data to. Specifically <i>mData</i> when
  7191  		 * used as a function will give you a 'type' (sorting, filtering etc) that you can use to 
  7192  		 * prepare the data as required for the different types. As such, this method is deprecated.
  7193  		 *  @type object
  7194  		 *  @default {}
  7195  		 *  @deprecated
  7196  		 *
  7197  		 *  @example
  7198  		 *    $.fn.dataTableExt.ofnSearch['title-numeric'] = function ( sData ) {
  7199  		 *      return sData.replace(/\n/g," ").replace( /<.*?>/g, "" );
  7200  		 *    }
  7201  		 */
  7202  		"ofnSearch": {},
  7203  	
  7204  	
  7205  		/**
  7206  		 * Container for all private functions in DataTables so they can be exposed externally
  7207  		 *  @type object
  7208  		 *  @default {}
  7209  		 */
  7210  		"oApi": {},
  7211  	
  7212  	
  7213  		/**
  7214  		 * Storage for the various classes that DataTables uses
  7215  		 *  @type object
  7216  		 *  @default {}
  7217  		 */
  7218  		"oStdClasses": {},
  7219  		
  7220  	
  7221  		/**
  7222  		 * Storage for the various classes that DataTables uses - jQuery UI suitable
  7223  		 *  @type object
  7224  		 *  @default {}
  7225  		 */
  7226  		"oJUIClasses": {},
  7227  	
  7228  	
  7229  		/**
  7230  		 * Pagination plug-in methods - The style and controls of the pagination can significantly 
  7231  		 * impact on how the end user interacts with the data in your table, and DataTables allows 
  7232  		 * the addition of pagination controls by extending this object, which can then be enabled
  7233  		 * through the <i>sPaginationType</i> initialisation parameter. Each pagination type that
  7234  		 * is added is an object (the property name of which is what <i>sPaginationType</i> refers
  7235  		 * to) that has two properties, both methods that are used by DataTables to update the
  7236  		 * control's state.
  7237  		 *   <ul>
  7238  		 *     <li>
  7239  		 *       fnInit -  Initialisation of the paging controls. Called only during initialisation 
  7240  		 *         of the table. It is expected that this function will add the required DOM elements 
  7241  		 *         to the page for the paging controls to work. The element pointer 
  7242  		 *         'oSettings.aanFeatures.p' array is provided by DataTables to contain the paging 
  7243  		 *         controls (note that this is a 2D array to allow for multiple instances of each 
  7244  		 *         DataTables DOM element). It is suggested that you add the controls to this element 
  7245  		 *         as children
  7246  		 *       <ul>
  7247  	     *         <li>
  7248  	     *           Function input parameters:
  7249  	     *           <ul>
  7250  		 *             <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
  7251  		 *             <li>{node} Container into which the pagination controls must be inserted</li>
  7252  		 *             <li>{function} Draw callback function - whenever the controls cause a page
  7253  		 *               change, this method must be called to redraw the table.</li>
  7254  	     *           </ul>
  7255  	     *         </li>
  7256  		 *         <li>
  7257  		 *           Function return:
  7258  		 *           <ul>
  7259  		 *             <li>No return required</li>
  7260  		 *           </ul>
  7261  		 *         </il>
  7262  		 *       </ul>
  7263  		 *     </il>
  7264  		 *     <li>
  7265  		 *       fnInit -  This function is called whenever the paging status of the table changes and is
  7266  		 *         typically used to update classes and/or text of the paging controls to reflex the new 
  7267  		 *         status.
  7268  		 *       <ul>
  7269  	     *         <li>
  7270  	     *           Function input parameters:
  7271  	     *           <ul>
  7272  		 *             <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
  7273  		 *             <li>{function} Draw callback function - in case you need to redraw the table again
  7274  		 *               or attach new event listeners</li>
  7275  	     *           </ul>
  7276  	     *         </li>
  7277  		 *         <li>
  7278  		 *           Function return:
  7279  		 *           <ul>
  7280  		 *             <li>No return required</li>
  7281  		 *           </ul>
  7282  		 *         </il>
  7283  		 *       </ul>
  7284  		 *     </il>
  7285  		 *   </ul>
  7286  		 *  @type object
  7287  		 *  @default {}
  7288  		 *
  7289  		 *  @example
  7290  		 *    $.fn.dataTableExt.oPagination.four_button = {
  7291  		 *      "fnInit": function ( oSettings, nPaging, fnCallbackDraw ) {
  7292  		 *        nFirst = document.createElement( 'span' );
  7293  		 *        nPrevious = document.createElement( 'span' );
  7294  		 *        nNext = document.createElement( 'span' );
  7295  		 *        nLast = document.createElement( 'span' );
  7296  		 *        
  7297  		 *        nFirst.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sFirst ) );
  7298  		 *        nPrevious.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sPrevious ) );
  7299  		 *        nNext.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sNext ) );
  7300  		 *        nLast.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sLast ) );
  7301  		 *        
  7302  		 *        nFirst.className = "paginate_button first";
  7303  		 *        nPrevious.className = "paginate_button previous";
  7304  		 *        nNext.className="paginate_button next";
  7305  		 *        nLast.className = "paginate_button last";
  7306  		 *        
  7307  		 *        nPaging.appendChild( nFirst );
  7308  		 *        nPaging.appendChild( nPrevious );
  7309  		 *        nPaging.appendChild( nNext );
  7310  		 *        nPaging.appendChild( nLast );
  7311  		 *        
  7312  		 *        $(nFirst).click( function () {
  7313  		 *          oSettings.oApi._fnPageChange( oSettings, "first" );
  7314  		 *          fnCallbackDraw( oSettings );
  7315  		 *        } );
  7316  		 *        
  7317  		 *        $(nPrevious).click( function() {
  7318  		 *          oSettings.oApi._fnPageChange( oSettings, "previous" );
  7319  		 *          fnCallbackDraw( oSettings );
  7320  		 *        } );
  7321  		 *        
  7322  		 *        $(nNext).click( function() {
  7323  		 *          oSettings.oApi._fnPageChange( oSettings, "next" );
  7324  		 *          fnCallbackDraw( oSettings );
  7325  		 *        } );
  7326  		 *        
  7327  		 *        $(nLast).click( function() {
  7328  		 *          oSettings.oApi._fnPageChange( oSettings, "last" );
  7329  		 *          fnCallbackDraw( oSettings );
  7330  		 *        } );
  7331  		 *        
  7332  		 *        $(nFirst).bind( 'selectstart', function () { return false; } );
  7333  		 *        $(nPrevious).bind( 'selectstart', function () { return false; } );
  7334  		 *        $(nNext).bind( 'selectstart', function () { return false; } );
  7335  		 *        $(nLast).bind( 'selectstart', function () { return false; } );
  7336  		 *      },
  7337  		 *      
  7338  		 *      "fnUpdate": function ( oSettings, fnCallbackDraw ) {
  7339  		 *        if ( !oSettings.aanFeatures.p ) {
  7340  		 *          return;
  7341  		 *        }
  7342  		 *        
  7343  		 *        // Loop over each instance of the pager
  7344  		 *        var an = oSettings.aanFeatures.p;
  7345  		 *        for ( var i=0, iLen=an.length ; i<iLen ; i++ ) {
  7346  		 *          var buttons = an[i].getElementsByTagName('span');
  7347  		 *          if ( oSettings._iDisplayStart === 0 ) {
  7348  		 *            buttons[0].className = "paginate_disabled_previous";
  7349  		 *            buttons[1].className = "paginate_disabled_previous";
  7350  		 *          }
  7351  		 *          else {
  7352  		 *            buttons[0].className = "paginate_enabled_previous";
  7353  		 *            buttons[1].className = "paginate_enabled_previous";
  7354  		 *          }
  7355  		 *          
  7356  		 *          if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) {
  7357  		 *            buttons[2].className = "paginate_disabled_next";
  7358  		 *            buttons[3].className = "paginate_disabled_next";
  7359  		 *          }
  7360  		 *          else {
  7361  		 *            buttons[2].className = "paginate_enabled_next";
  7362  		 *            buttons[3].className = "paginate_enabled_next";
  7363  		 *          }
  7364  		 *        }
  7365  		 *      }
  7366  		 *    };
  7367  		 */
  7368  		"oPagination": {},
  7369  	
  7370  	
  7371  		/**
  7372  		 * Sorting plug-in methods - Sorting in DataTables is based on the detected type of the
  7373  		 * data column (you can add your own type detection functions, or override automatic 
  7374  		 * detection using sType). With this specific type given to the column, DataTables will 
  7375  		 * apply the required sort from the functions in the object. Each sort type must provide
  7376  		 * two mandatory methods, one each for ascending and descending sorting, and can optionally
  7377  		 * provide a pre-formatting method that will help speed up sorting by allowing DataTables
  7378  		 * to pre-format the sort data only once (rather than every time the actual sort functions
  7379  		 * are run). The two sorting functions are typical Javascript sort methods:
  7380  		 *   <ul>
  7381  	     *     <li>
  7382  	     *       Function input parameters:
  7383  	     *       <ul>
  7384  		 *         <li>{*} Data to compare to the second parameter</li>
  7385  		 *         <li>{*} Data to compare to the first parameter</li>
  7386  	     *       </ul>
  7387  	     *     </li>
  7388  		 *     <li>
  7389  		 *       Function return:
  7390  		 *       <ul>
  7391  		 *         <li>{int} Sorting match: <0 if first parameter should be sorted lower than
  7392  		 *           the second parameter, ===0 if the two parameters are equal and >0 if
  7393  		 *           the first parameter should be sorted height than the second parameter.</li>
  7394  		 *       </ul>
  7395  		 *     </il>
  7396  		 *   </ul>
  7397  		 *  @type object
  7398  		 *  @default {}
  7399  		 *
  7400  		 *  @example
  7401  		 *    // Case-sensitive string sorting, with no pre-formatting method
  7402  		 *    $.extend( $.fn.dataTableExt.oSort, {
  7403  		 *      "string-case-asc": function(x,y) {
  7404  		 *        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
  7405  		 *      },
  7406  		 *      "string-case-desc": function(x,y) {
  7407  		 *        return ((x < y) ? 1 : ((x > y) ? -1 : 0));
  7408  		 *      }
  7409  		 *    } );
  7410  		 *
  7411  		 *  @example
  7412  		 *    // Case-insensitive string sorting, with pre-formatting
  7413  		 *    $.extend( $.fn.dataTableExt.oSort, {
  7414  		 *      "string-pre": function(x) {
  7415  		 *        return x.toLowerCase();
  7416  		 *      },
  7417  		 *      "string-asc": function(x,y) {
  7418  		 *        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
  7419  		 *      },
  7420  		 *      "string-desc": function(x,y) {
  7421  		 *        return ((x < y) ? 1 : ((x > y) ? -1 : 0));
  7422  		 *      }
  7423  		 *    } );
  7424  		 */
  7425  		"oSort": {},
  7426  	
  7427  	
  7428  		/**
  7429  		 * Version string for plug-ins to check compatibility. Allowed format is
  7430  		 * a.b.c.d.e where: a:int, b:int, c:int, d:string(dev|beta), e:int. d and
  7431  		 * e are optional
  7432  		 *  @type string
  7433  		 *  @default Version number
  7434  		 */
  7435  		"sVersion": DataTable.version,
  7436  	
  7437  	
  7438  		/**
  7439  		 * How should DataTables report an error. Can take the value 'alert' or 'throw'
  7440  		 *  @type string
  7441  		 *  @default alert
  7442  		 */
  7443  		"sErrMode": "alert",
  7444  	
  7445  	
  7446  		/**
  7447  		 * Store information for DataTables to access globally about other instances
  7448  		 *  @namespace
  7449  		 *  @private
  7450  		 */
  7451  		"_oExternConfig": {
  7452  			/* int:iNextUnique - next unique number for an instance */
  7453  			"iNextUnique": 0
  7454  		}
  7455  	};
  7456  	
  7457  	
  7458  	
  7459  	
  7460  	/**
  7461  	 * Template object for the way in which DataTables holds information about
  7462  	 * search information for the global filter and individual column filters.
  7463  	 *  @namespace
  7464  	 */
  7465  	DataTable.models.oSearch = {
  7466  		/**
  7467  		 * Flag to indicate if the filtering should be case insensitive or not
  7468  		 *  @type boolean
  7469  		 *  @default true
  7470  		 */
  7471  		"bCaseInsensitive": true,
  7472  	
  7473  		/**
  7474  		 * Applied search term
  7475  		 *  @type string
  7476  		 *  @default <i>Empty string</i>
  7477  		 */
  7478  		"sSearch": "",
  7479  	
  7480  		/**
  7481  		 * Flag to indicate if the search term should be interpreted as a
  7482  		 * regular expression (true) or not (false) and therefore and special
  7483  		 * regex characters escaped.
  7484  		 *  @type boolean
  7485  		 *  @default false
  7486  		 */
  7487  		"bRegex": false,
  7488  	
  7489  		/**
  7490  		 * Flag to indicate if DataTables is to use its smart filtering or not.
  7491  		 *  @type boolean
  7492  		 *  @default true
  7493  		 */
  7494  		"bSmart": true
  7495  	};
  7496  	
  7497  	
  7498  	
  7499  	
  7500  	/**
  7501  	 * Template object for the way in which DataTables holds information about
  7502  	 * each individual row. This is the object format used for the settings 
  7503  	 * aoData array.
  7504  	 *  @namespace
  7505  	 */
  7506  	DataTable.models.oRow = {
  7507  		/**
  7508  		 * TR element for the row
  7509  		 *  @type node
  7510  		 *  @default null
  7511  		 */
  7512  		"nTr": null,
  7513  	
  7514  		/**
  7515  		 * Data object from the original data source for the row. This is either
  7516  		 * an array if using the traditional form of DataTables, or an object if
  7517  		 * using mData options. The exact type will depend on the passed in
  7518  		 * data from the data source, or will be an array if using DOM a data 
  7519  		 * source.
  7520  		 *  @type array|object
  7521  		 *  @default []
  7522  		 */
  7523  		"_aData": [],
  7524  	
  7525  		/**
  7526  		 * Sorting data cache - this array is ostensibly the same length as the
  7527  		 * number of columns (although each index is generated only as it is 
  7528  		 * needed), and holds the data that is used for sorting each column in the
  7529  		 * row. We do this cache generation at the start of the sort in order that
  7530  		 * the formatting of the sort data need be done only once for each cell
  7531  		 * per sort. This array should not be read from or written to by anything
  7532  		 * other than the master sorting methods.
  7533  		 *  @type array
  7534  		 *  @default []
  7535  		 *  @private
  7536  		 */
  7537  		"_aSortData": [],
  7538  	
  7539  		/**
  7540  		 * Array of TD elements that are cached for hidden rows, so they can be
  7541  		 * reinserted into the table if a column is made visible again (or to act
  7542  		 * as a store if a column is made hidden). Only hidden columns have a 
  7543  		 * reference in the array. For non-hidden columns the value is either
  7544  		 * undefined or null.
  7545  		 *  @type array nodes
  7546  		 *  @default []
  7547  		 *  @private
  7548  		 */
  7549  		"_anHidden": [],
  7550  	
  7551  		/**
  7552  		 * Cache of the class name that DataTables has applied to the row, so we
  7553  		 * can quickly look at this variable rather than needing to do a DOM check
  7554  		 * on className for the nTr property.
  7555  		 *  @type string
  7556  		 *  @default <i>Empty string</i>
  7557  		 *  @private
  7558  		 */
  7559  		"_sRowStripe": ""
  7560  	};
  7561  	
  7562  	
  7563  	
  7564  	/**
  7565  	 * Template object for the column information object in DataTables. This object
  7566  	 * is held in the settings aoColumns array and contains all the information that
  7567  	 * DataTables needs about each individual column.
  7568  	 * 
  7569  	 * Note that this object is related to {@link DataTable.defaults.columns} 
  7570  	 * but this one is the internal data store for DataTables's cache of columns.
  7571  	 * It should NOT be manipulated outside of DataTables. Any configuration should
  7572  	 * be done through the initialisation options.
  7573  	 *  @namespace
  7574  	 */
  7575  	DataTable.models.oColumn = {
  7576  		/**
  7577  		 * A list of the columns that sorting should occur on when this column
  7578  		 * is sorted. That this property is an array allows multi-column sorting
  7579  		 * to be defined for a column (for example first name / last name columns
  7580  		 * would benefit from this). The values are integers pointing to the
  7581  		 * columns to be sorted on (typically it will be a single integer pointing
  7582  		 * at itself, but that doesn't need to be the case).
  7583  		 *  @type array
  7584  		 */
  7585  		"aDataSort": null,
  7586  	
  7587  		/**
  7588  		 * Define the sorting directions that are applied to the column, in sequence
  7589  		 * as the column is repeatedly sorted upon - i.e. the first value is used
  7590  		 * as the sorting direction when the column if first sorted (clicked on).
  7591  		 * Sort it again (click again) and it will move on to the next index.
  7592  		 * Repeat until loop.
  7593  		 *  @type array
  7594  		 */
  7595  		"asSorting": null,
  7596  		
  7597  		/**
  7598  		 * Flag to indicate if the column is searchable, and thus should be included
  7599  		 * in the filtering or not.
  7600  		 *  @type boolean
  7601  		 */
  7602  		"bSearchable": null,
  7603  		
  7604  		/**
  7605  		 * Flag to indicate if the column is sortable or not.
  7606  		 *  @type boolean
  7607  		 */
  7608  		"bSortable": null,
  7609  		
  7610  		/**
  7611  		 * <code>Deprecated</code> When using fnRender, you have two options for what 
  7612  		 * to do with the data, and this property serves as the switch. Firstly, you 
  7613  		 * can have the sorting and filtering use the rendered value (true - default), 
  7614  		 * or you can have the sorting and filtering us the original value (false).
  7615  		 *
  7616  		 * Please note that this option has now been deprecated and will be removed
  7617  		 * in the next version of DataTables. Please use mRender / mData rather than
  7618  		 * fnRender.
  7619  		 *  @type boolean
  7620  		 *  @deprecated
  7621  		 */
  7622  		"bUseRendered": null,
  7623  		
  7624  		/**
  7625  		 * Flag to indicate if the column is currently visible in the table or not
  7626  		 *  @type boolean
  7627  		 */
  7628  		"bVisible": null,
  7629  		
  7630  		/**
  7631  		 * Flag to indicate to the type detection method if the automatic type
  7632  		 * detection should be used, or if a column type (sType) has been specified
  7633  		 *  @type boolean
  7634  		 *  @default true
  7635  		 *  @private
  7636  		 */
  7637  		"_bAutoType": true,
  7638  		
  7639  		/**
  7640  		 * Developer definable function that is called whenever a cell is created (Ajax source,
  7641  		 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
  7642  		 * allowing you to modify the DOM element (add background colour for example) when the
  7643  		 * element is available.
  7644  		 *  @type function
  7645  		 *  @param {element} nTd The TD node that has been created
  7646  		 *  @param {*} sData The Data for the cell
  7647  		 *  @param {array|object} oData The data for the whole row
  7648  		 *  @param {int} iRow The row index for the aoData data store
  7649  		 *  @default null
  7650  		 */
  7651  		"fnCreatedCell": null,
  7652  		
  7653  		/**
  7654  		 * Function to get data from a cell in a column. You should <b>never</b>
  7655  		 * access data directly through _aData internally in DataTables - always use
  7656  		 * the method attached to this property. It allows mData to function as
  7657  		 * required. This function is automatically assigned by the column 
  7658  		 * initialisation method
  7659  		 *  @type function
  7660  		 *  @param {array|object} oData The data array/object for the array 
  7661  		 *    (i.e. aoData[]._aData)
  7662  		 *  @param {string} sSpecific The specific data type you want to get - 
  7663  		 *    'display', 'type' 'filter' 'sort'
  7664  		 *  @returns {*} The data for the cell from the given row's data
  7665  		 *  @default null
  7666  		 */
  7667  		"fnGetData": null,
  7668  		
  7669  		/**
  7670  		 * <code>Deprecated</code> Custom display function that will be called for the 
  7671  		 * display of each cell in this column.
  7672  		 *
  7673  		 * Please note that this option has now been deprecated and will be removed
  7674  		 * in the next version of DataTables. Please use mRender / mData rather than
  7675  		 * fnRender.
  7676  		 *  @type function
  7677  		 *  @param {object} o Object with the following parameters:
  7678  		 *  @param {int}    o.iDataRow The row in aoData
  7679  		 *  @param {int}    o.iDataColumn The column in question
  7680  		 *  @param {array}  o.aData The data for the row in question
  7681  		 *  @param {object} o.oSettings The settings object for this DataTables instance
  7682  		 *  @returns {string} The string you which to use in the display
  7683  		 *  @default null
  7684  		 *  @deprecated
  7685  		 */
  7686  		"fnRender": null,
  7687  		
  7688  		/**
  7689  		 * Function to set data for a cell in the column. You should <b>never</b> 
  7690  		 * set the data directly to _aData internally in DataTables - always use
  7691  		 * this method. It allows mData to function as required. This function
  7692  		 * is automatically assigned by the column initialisation method
  7693  		 *  @type function
  7694  		 *  @param {array|object} oData The data array/object for the array 
  7695  		 *    (i.e. aoData[]._aData)
  7696  		 *  @param {*} sValue Value to set
  7697  		 *  @default null
  7698  		 */
  7699  		"fnSetData": null,
  7700  		
  7701  		/**
  7702  		 * Property to read the value for the cells in the column from the data 
  7703  		 * source array / object. If null, then the default content is used, if a
  7704  		 * function is given then the return from the function is used.
  7705  		 *  @type function|int|string|null
  7706  		 *  @default null
  7707  		 */
  7708  		"mData": null,
  7709  		
  7710  		/**
  7711  		 * Partner property to mData which is used (only when defined) to get
  7712  		 * the data - i.e. it is basically the same as mData, but without the
  7713  		 * 'set' option, and also the data fed to it is the result from mData.
  7714  		 * This is the rendering method to match the data method of mData.
  7715  		 *  @type function|int|string|null
  7716  		 *  @default null
  7717  		 */
  7718  		"mRender": null,
  7719  		
  7720  		/**
  7721  		 * Unique header TH/TD element for this column - this is what the sorting
  7722  		 * listener is attached to (if sorting is enabled.)
  7723  		 *  @type node
  7724  		 *  @default null
  7725  		 */
  7726  		"nTh": null,
  7727  		
  7728  		/**
  7729  		 * Unique footer TH/TD element for this column (if there is one). Not used 
  7730  		 * in DataTables as such, but can be used for plug-ins to reference the 
  7731  		 * footer for each column.
  7732  		 *  @type node
  7733  		 *  @default null
  7734  		 */
  7735  		"nTf": null,
  7736  		
  7737  		/**
  7738  		 * The class to apply to all TD elements in the table's TBODY for the column
  7739  		 *  @type string
  7740  		 *  @default null
  7741  		 */
  7742  		"sClass": null,
  7743  		
  7744  		/**
  7745  		 * When DataTables calculates the column widths to assign to each column,
  7746  		 * it finds the longest string in each column and then constructs a
  7747  		 * temporary table and reads the widths from that. The problem with this
  7748  		 * is that "mmm" is much wider then "iiii", but the latter is a longer 
  7749  		 * string - thus the calculation can go wrong (doing it properly and putting
  7750  		 * it into an DOM object and measuring that is horribly(!) slow). Thus as
  7751  		 * a "work around" we provide this option. It will append its value to the
  7752  		 * text that is found to be the longest string for the column - i.e. padding.
  7753  		 *  @type string
  7754  		 */
  7755  		"sContentPadding": null,
  7756  		
  7757  		/**
  7758  		 * Allows a default value to be given for a column's data, and will be used
  7759  		 * whenever a null data source is encountered (this can be because mData
  7760  		 * is set to null, or because the data source itself is null).
  7761  		 *  @type string
  7762  		 *  @default null
  7763  		 */
  7764  		"sDefaultContent": null,
  7765  		
  7766  		/**
  7767  		 * Name for the column, allowing reference to the column by name as well as
  7768  		 * by index (needs a lookup to work by name).
  7769  		 *  @type string
  7770  		 */
  7771  		"sName": null,
  7772  		
  7773  		/**
  7774  		 * Custom sorting data type - defines which of the available plug-ins in
  7775  		 * afnSortData the custom sorting will use - if any is defined.
  7776  		 *  @type string
  7777  		 *  @default std
  7778  		 */
  7779  		"sSortDataType": 'std',
  7780  		
  7781  		/**
  7782  		 * Class to be applied to the header element when sorting on this column
  7783  		 *  @type string
  7784  		 *  @default null
  7785  		 */
  7786  		"sSortingClass": null,
  7787  		
  7788  		/**
  7789  		 * Class to be applied to the header element when sorting on this column -
  7790  		 * when jQuery UI theming is used.
  7791  		 *  @type string
  7792  		 *  @default null
  7793  		 */
  7794  		"sSortingClassJUI": null,
  7795  		
  7796  		/**
  7797  		 * Title of the column - what is seen in the TH element (nTh).
  7798  		 *  @type string
  7799  		 */
  7800  		"sTitle": null,
  7801  		
  7802  		/**
  7803  		 * Column sorting and filtering type
  7804  		 *  @type string
  7805  		 *  @default null
  7806  		 */
  7807  		"sType": null,
  7808  		
  7809  		/**
  7810  		 * Width of the column
  7811  		 *  @type string
  7812  		 *  @default null
  7813  		 */
  7814  		"sWidth": null,
  7815  		
  7816  		/**
  7817  		 * Width of the column when it was first "encountered"
  7818  		 *  @type string
  7819  		 *  @default null
  7820  		 */
  7821  		"sWidthOrig": null
  7822  	};
  7823  	
  7824  	
  7825  	
  7826  	/**
  7827  	 * Initialisation options that can be given to DataTables at initialisation 
  7828  	 * time.
  7829  	 *  @namespace
  7830  	 */
  7831  	DataTable.defaults = {
  7832  		/**
  7833  		 * An array of data to use for the table, passed in at initialisation which 
  7834  		 * will be used in preference to any data which is already in the DOM. This is
  7835  		 * particularly useful for constructing tables purely in Javascript, for
  7836  		 * example with a custom Ajax call.
  7837  		 *  @type array
  7838  		 *  @default null
  7839  		 *  @dtopt Option
  7840  		 * 
  7841  		 *  @example
  7842  		 *    // Using a 2D array data source
  7843  		 *    $(document).ready( function () {
  7844  		 *      $('#example').dataTable( {
  7845  		 *        "aaData": [
  7846  		 *          ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
  7847  		 *          ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
  7848  		 *        ],
  7849  		 *        "aoColumns": [
  7850  		 *          { "sTitle": "Engine" },
  7851  		 *          { "sTitle": "Browser" },
  7852  		 *          { "sTitle": "Platform" },
  7853  		 *          { "sTitle": "Version" },
  7854  		 *          { "sTitle": "Grade" }
  7855  		 *        ]
  7856  		 *      } );
  7857  		 *    } );
  7858  		 *    
  7859  		 *  @example
  7860  		 *    // Using an array of objects as a data source (mData)
  7861  		 *    $(document).ready( function () {
  7862  		 *      $('#example').dataTable( {
  7863  		 *        "aaData": [
  7864  		 *          {
  7865  		 *            "engine":   "Trident",
  7866  		 *            "browser":  "Internet Explorer 4.0",
  7867  		 *            "platform": "Win 95+",
  7868  		 *            "version":  4,
  7869  		 *            "grade":    "X"
  7870  		 *          },
  7871  		 *          {
  7872  		 *            "engine":   "Trident",
  7873  		 *            "browser":  "Internet Explorer 5.0",
  7874  		 *            "platform": "Win 95+",
  7875  		 *            "version":  5,
  7876  		 *            "grade":    "C"
  7877  		 *          }
  7878  		 *        ],
  7879  		 *        "aoColumns": [
  7880  		 *          { "sTitle": "Engine",   "mData": "engine" },
  7881  		 *          { "sTitle": "Browser",  "mData": "browser" },
  7882  		 *          { "sTitle": "Platform", "mData": "platform" },
  7883  		 *          { "sTitle": "Version",  "mData": "version" },
  7884  		 *          { "sTitle": "Grade",    "mData": "grade" }
  7885  		 *        ]
  7886  		 *      } );
  7887  		 *    } );
  7888  		 */
  7889  		"aaData": null,
  7890  	
  7891  	
  7892  		/**
  7893  		 * If sorting is enabled, then DataTables will perform a first pass sort on 
  7894  		 * initialisation. You can define which column(s) the sort is performed upon, 
  7895  		 * and the sorting direction, with this variable. The aaSorting array should 
  7896  		 * contain an array for each column to be sorted initially containing the 
  7897  		 * column's index and a direction string ('asc' or 'desc').
  7898  		 *  @type array
  7899  		 *  @default [[0,'asc']]
  7900  		 *  @dtopt Option
  7901  		 * 
  7902  		 *  @example
  7903  		 *    // Sort by 3rd column first, and then 4th column
  7904  		 *    $(document).ready( function() {
  7905  		 *      $('#example').dataTable( {
  7906  		 *        "aaSorting": [[2,'asc'], [3,'desc']]
  7907  		 *      } );
  7908  		 *    } );
  7909  		 *    
  7910  		 *    // No initial sorting
  7911  		 *    $(document).ready( function() {
  7912  		 *      $('#example').dataTable( {
  7913  		 *        "aaSorting": []
  7914  		 *      } );
  7915  		 *    } );
  7916  		 */
  7917  		"aaSorting": [[0,'asc']],
  7918  	
  7919  	
  7920  		/**
  7921  		 * This parameter is basically identical to the aaSorting parameter, but 
  7922  		 * cannot be overridden by user interaction with the table. What this means 
  7923  		 * is that you could have a column (visible or hidden) which the sorting will 
  7924  		 * always be forced on first - any sorting after that (from the user) will 
  7925  		 * then be performed as required. This can be useful for grouping rows 
  7926  		 * together.
  7927  		 *  @type array
  7928  		 *  @default null
  7929  		 *  @dtopt Option
  7930  		 * 
  7931  		 *  @example
  7932  		 *    $(document).ready( function() {
  7933  		 *      $('#example').dataTable( {
  7934  		 *        "aaSortingFixed": [[0,'asc']]
  7935  		 *      } );
  7936  		 *    } )
  7937  		 */
  7938  		"aaSortingFixed": null,
  7939  	
  7940  	
  7941  		/**
  7942  		 * This parameter allows you to readily specify the entries in the length drop
  7943  		 * down menu that DataTables shows when pagination is enabled. It can be 
  7944  		 * either a 1D array of options which will be used for both the displayed 
  7945  		 * option and the value, or a 2D array which will use the array in the first 
  7946  		 * position as the value, and the array in the second position as the 
  7947  		 * displayed options (useful for language strings such as 'All').
  7948  		 *  @type array
  7949  		 *  @default [ 10, 25, 50, 100 ]
  7950  		 *  @dtopt Option
  7951  		 * 
  7952  		 *  @example
  7953  		 *    $(document).ready( function() {
  7954  		 *      $('#example').dataTable( {
  7955  		 *        "aLengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
  7956  		 *      } );
  7957  		 *    } );
  7958  		 *  
  7959  		 *  @example
  7960  		 *    // Setting the default display length as well as length menu
  7961  		 *    // This is likely to be wanted if you remove the '10' option which
  7962  		 *    // is the iDisplayLength default.
  7963  		 *    $(document).ready( function() {
  7964  		 *      $('#example').dataTable( {
  7965  		 *        "iDisplayLength": 25,
  7966  		 *        "aLengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]]
  7967  		 *      } );
  7968  		 *    } );
  7969  		 */
  7970  		"aLengthMenu": [ 10, 25, 50, 100 ],
  7971  	
  7972  	
  7973  		/**
  7974  		 * The aoColumns option in the initialisation parameter allows you to define
  7975  		 * details about the way individual columns behave. For a full list of
  7976  		 * column options that can be set, please see 
  7977  		 * {@link DataTable.defaults.columns}. Note that if you use aoColumns to
  7978  		 * define your columns, you must have an entry in the array for every single
  7979  		 * column that you have in your table (these can be null if you don't which
  7980  		 * to specify any options).
  7981  		 *  @member
  7982  		 */
  7983  		"aoColumns": null,
  7984  	
  7985  		/**
  7986  		 * Very similar to aoColumns, aoColumnDefs allows you to target a specific 
  7987  		 * column, multiple columns, or all columns, using the aTargets property of 
  7988  		 * each object in the array. This allows great flexibility when creating 
  7989  		 * tables, as the aoColumnDefs arrays can be of any length, targeting the 
  7990  		 * columns you specifically want. aoColumnDefs may use any of the column 
  7991  		 * options available: {@link DataTable.defaults.columns}, but it _must_
  7992  		 * have aTargets defined in each object in the array. Values in the aTargets
  7993  		 * array may be:
  7994  		 *   <ul>
  7995  		 *     <li>a string - class name will be matched on the TH for the column</li>
  7996  		 *     <li>0 or a positive integer - column index counting from the left</li>
  7997  		 *     <li>a negative integer - column index counting from the right</li>
  7998  		 *     <li>the string "_all" - all columns (i.e. assign a default)</li>
  7999  		 *   </ul>
  8000  		 *  @member
  8001  		 */
  8002  		"aoColumnDefs": null,
  8003  	
  8004  	
  8005  		/**
  8006  		 * Basically the same as oSearch, this parameter defines the individual column
  8007  		 * filtering state at initialisation time. The array must be of the same size 
  8008  		 * as the number of columns, and each element be an object with the parameters
  8009  		 * "sSearch" and "bEscapeRegex" (the latter is optional). 'null' is also
  8010  		 * accepted and the default will be used.
  8011  		 *  @type array
  8012  		 *  @default []
  8013  		 *  @dtopt Option
  8014  		 * 
  8015  		 *  @example
  8016  		 *    $(document).ready( function() {
  8017  		 *      $('#example').dataTable( {
  8018  		 *        "aoSearchCols": [
  8019  		 *          null,
  8020  		 *          { "sSearch": "My filter" },
  8021  		 *          null,
  8022  		 *          { "sSearch": "^[0-9]", "bEscapeRegex": false }
  8023  		 *        ]
  8024  		 *      } );
  8025  		 *    } )
  8026  		 */
  8027  		"aoSearchCols": [],
  8028  	
  8029  	
  8030  		/**
  8031  		 * An array of CSS classes that should be applied to displayed rows. This 
  8032  		 * array may be of any length, and DataTables will apply each class 
  8033  		 * sequentially, looping when required.
  8034  		 *  @type array
  8035  		 *  @default null <i>Will take the values determined by the oClasses.sStripe*
  8036  		 *    options</i>
  8037  		 *  @dtopt Option
  8038  		 * 
  8039  		 *  @example
  8040  		 *    $(document).ready( function() {
  8041  		 *      $('#example').dataTable( {
  8042  		 *        "asStripeClasses": [ 'strip1', 'strip2', 'strip3' ]
  8043  		 *      } );
  8044  		 *    } )
  8045  		 */
  8046  		"asStripeClasses": null,
  8047  	
  8048  	
  8049  		/**
  8050  		 * Enable or disable automatic column width calculation. This can be disabled
  8051  		 * as an optimisation (it takes some time to calculate the widths) if the
  8052  		 * tables widths are passed in using aoColumns.
  8053  		 *  @type boolean
  8054  		 *  @default true
  8055  		 *  @dtopt Features
  8056  		 * 
  8057  		 *  @example
  8058  		 *    $(document).ready( function () {
  8059  		 *      $('#example').dataTable( {
  8060  		 *        "bAutoWidth": false
  8061  		 *      } );
  8062  		 *    } );
  8063  		 */
  8064  		"bAutoWidth": true,
  8065  	
  8066  	
  8067  		/**
  8068  		 * Deferred rendering can provide DataTables with a huge speed boost when you
  8069  		 * are using an Ajax or JS data source for the table. This option, when set to
  8070  		 * true, will cause DataTables to defer the creation of the table elements for
  8071  		 * each row until they are needed for a draw - saving a significant amount of
  8072  		 * time.
  8073  		 *  @type boolean
  8074  		 *  @default false
  8075  		 *  @dtopt Features
  8076  		 * 
  8077  		 *  @example
  8078  		 *    $(document).ready( function() {
  8079  		 *      var oTable = $('#example').dataTable( {
  8080  		 *        "sAjaxSource": "sources/arrays.txt",
  8081  		 *        "bDeferRender": true
  8082  		 *      } );
  8083  		 *    } );
  8084  		 */
  8085  		"bDeferRender": false,
  8086  	
  8087  	
  8088  		/**
  8089  		 * Replace a DataTable which matches the given selector and replace it with 
  8090  		 * one which has the properties of the new initialisation object passed. If no
  8091  		 * table matches the selector, then the new DataTable will be constructed as
  8092  		 * per normal.
  8093  		 *  @type boolean
  8094  		 *  @default false
  8095  		 *  @dtopt Options
  8096  		 * 
  8097  		 *  @example
  8098  		 *    $(document).ready( function() {
  8099  		 *      $('#example').dataTable( {
  8100  		 *        "sScrollY": "200px",
  8101  		 *        "bPaginate": false
  8102  		 *      } );
  8103  		 *      
  8104  		 *      // Some time later....
  8105  		 *      $('#example').dataTable( {
  8106  		 *        "bFilter": false,
  8107  		 *        "bDestroy": true
  8108  		 *      } );
  8109  		 *    } );
  8110  		 */
  8111  		"bDestroy": false,
  8112  	
  8113  	
  8114  		/**
  8115  		 * Enable or disable filtering of data. Filtering in DataTables is "smart" in
  8116  		 * that it allows the end user to input multiple words (space separated) and
  8117  		 * will match a row containing those words, even if not in the order that was
  8118  		 * specified (this allow matching across multiple columns). Note that if you
  8119  		 * wish to use filtering in DataTables this must remain 'true' - to remove the
  8120  		 * default filtering input box and retain filtering abilities, please use
  8121  		 * {@link DataTable.defaults.sDom}.
  8122  		 *  @type boolean
  8123  		 *  @default true
  8124  		 *  @dtopt Features
  8125  		 * 
  8126  		 *  @example
  8127  		 *    $(document).ready( function () {
  8128  		 *      $('#example').dataTable( {
  8129  		 *        "bFilter": false
  8130  		 *      } );
  8131  		 *    } );
  8132  		 */
  8133  		"bFilter": true,
  8134  	
  8135  	
  8136  		/**
  8137  		 * Enable or disable the table information display. This shows information 
  8138  		 * about the data that is currently visible on the page, including information
  8139  		 * about filtered data if that action is being performed.
  8140  		 *  @type boolean
  8141  		 *  @default true
  8142  		 *  @dtopt Features
  8143  		 * 
  8144  		 *  @example
  8145  		 *    $(document).ready( function () {
  8146  		 *      $('#example').dataTable( {
  8147  		 *        "bInfo": false
  8148  		 *      } );
  8149  		 *    } );
  8150  		 */
  8151  		"bInfo": true,
  8152  	
  8153  	
  8154  		/**
  8155  		 * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some
  8156  		 * slightly different and additional mark-up from what DataTables has
  8157  		 * traditionally used).
  8158  		 *  @type boolean
  8159  		 *  @default false
  8160  		 *  @dtopt Features
  8161  		 * 
  8162  		 *  @example
  8163  		 *    $(document).ready( function() {
  8164  		 *      $('#example').dataTable( {
  8165  		 *        "bJQueryUI": true
  8166  		 *      } );
  8167  		 *    } );
  8168  		 */
  8169  		"bJQueryUI": false,
  8170  	
  8171  	
  8172  		/**
  8173  		 * Allows the end user to select the size of a formatted page from a select
  8174  		 * menu (sizes are 10, 25, 50 and 100). Requires pagination (bPaginate).
  8175  		 *  @type boolean
  8176  		 *  @default true
  8177  		 *  @dtopt Features
  8178  		 * 
  8179  		 *  @example
  8180  		 *    $(document).ready( function () {
  8181  		 *      $('#example').dataTable( {
  8182  		 *        "bLengthChange": false
  8183  		 *      } );
  8184  		 *    } );
  8185  		 */
  8186  		"bLengthChange": true,
  8187  	
  8188  	
  8189  		/**
  8190  		 * Enable or disable pagination.
  8191  		 *  @type boolean
  8192  		 *  @default true
  8193  		 *  @dtopt Features
  8194  		 * 
  8195  		 *  @example
  8196  		 *    $(document).ready( function () {
  8197  		 *      $('#example').dataTable( {
  8198  		 *        "bPaginate": false
  8199  		 *      } );
  8200  		 *    } );
  8201  		 */
  8202  		"bPaginate": true,
  8203  	
  8204  	
  8205  		/**
  8206  		 * Enable or disable the display of a 'processing' indicator when the table is
  8207  		 * being processed (e.g. a sort). This is particularly useful for tables with
  8208  		 * large amounts of data where it can take a noticeable amount of time to sort
  8209  		 * the entries.
  8210  		 *  @type boolean
  8211  		 *  @default false
  8212  		 *  @dtopt Features
  8213  		 * 
  8214  		 *  @example
  8215  		 *    $(document).ready( function () {
  8216  		 *      $('#example').dataTable( {
  8217  		 *        "bProcessing": true
  8218  		 *      } );
  8219  		 *    } );
  8220  		 */
  8221  		"bProcessing": false,
  8222  	
  8223  	
  8224  		/**
  8225  		 * Retrieve the DataTables object for the given selector. Note that if the
  8226  		 * table has already been initialised, this parameter will cause DataTables
  8227  		 * to simply return the object that has already been set up - it will not take
  8228  		 * account of any changes you might have made to the initialisation object
  8229  		 * passed to DataTables (setting this parameter to true is an acknowledgement
  8230  		 * that you understand this). bDestroy can be used to reinitialise a table if
  8231  		 * you need.
  8232  		 *  @type boolean
  8233  		 *  @default false
  8234  		 *  @dtopt Options
  8235  		 * 
  8236  		 *  @example
  8237  		 *    $(document).ready( function() {
  8238  		 *      initTable();
  8239  		 *      tableActions();
  8240  		 *    } );
  8241  		 *    
  8242  		 *    function initTable ()
  8243  		 *    {
  8244  		 *      return $('#example').dataTable( {
  8245  		 *        "sScrollY": "200px",
  8246  		 *        "bPaginate": false,
  8247  		 *        "bRetrieve": true
  8248  		 *      } );
  8249  		 *    }
  8250  		 *    
  8251  		 *    function tableActions ()
  8252  		 *    {
  8253  		 *      var oTable = initTable();
  8254  		 *      // perform API operations with oTable 
  8255  		 *    }
  8256  		 */
  8257  		"bRetrieve": false,
  8258  	
  8259  	
  8260  		/**
  8261  		 * Indicate if DataTables should be allowed to set the padding / margin
  8262  		 * etc for the scrolling header elements or not. Typically you will want
  8263  		 * this.
  8264  		 *  @type boolean
  8265  		 *  @default true
  8266  		 *  @dtopt Options
  8267  		 * 
  8268  		 *  @example
  8269  		 *    $(document).ready( function() {
  8270  		 *      $('#example').dataTable( {
  8271  		 *        "bScrollAutoCss": false,
  8272  		 *        "sScrollY": "200px"
  8273  		 *      } );
  8274  		 *    } );
  8275  		 */
  8276  		"bScrollAutoCss": true,
  8277  	
  8278  	
  8279  		/**
  8280  		 * When vertical (y) scrolling is enabled, DataTables will force the height of
  8281  		 * the table's viewport to the given height at all times (useful for layout).
  8282  		 * However, this can look odd when filtering data down to a small data set,
  8283  		 * and the footer is left "floating" further down. This parameter (when
  8284  		 * enabled) will cause DataTables to collapse the table's viewport down when
  8285  		 * the result set will fit within the given Y height.
  8286  		 *  @type boolean
  8287  		 *  @default false
  8288  		 *  @dtopt Options
  8289  		 * 
  8290  		 *  @example
  8291  		 *    $(document).ready( function() {
  8292  		 *      $('#example').dataTable( {
  8293  		 *        "sScrollY": "200",
  8294  		 *        "bScrollCollapse": true
  8295  		 *      } );
  8296  		 *    } );
  8297  		 */
  8298  		"bScrollCollapse": false,
  8299  	
  8300  	
  8301  		/**
  8302  		 * Enable infinite scrolling for DataTables (to be used in combination with
  8303  		 * sScrollY). Infinite scrolling means that DataTables will continually load
  8304  		 * data as a user scrolls through a table, which is very useful for large
  8305  		 * dataset. This cannot be used with pagination, which is automatically
  8306  		 * disabled. Note - the Scroller extra for DataTables is recommended in
  8307  		 * in preference to this option.
  8308  		 *  @type boolean
  8309  		 *  @default false
  8310  		 *  @dtopt Features
  8311  		 * 
  8312  		 *  @example
  8313  		 *    $(document).ready( function() {
  8314  		 *      $('#example').dataTable( {
  8315  		 *        "bScrollInfinite": true,
  8316  		 *        "bScrollCollapse": true,
  8317  		 *        "sScrollY": "200px"
  8318  		 *      } );
  8319  		 *    } );
  8320  		 */
  8321  		"bScrollInfinite": false,
  8322  	
  8323  	
  8324  		/**
  8325  		 * Configure DataTables to use server-side processing. Note that the
  8326  		 * sAjaxSource parameter must also be given in order to give DataTables a
  8327  		 * source to obtain the required data for each draw.
  8328  		 *  @type boolean
  8329  		 *  @default false
  8330  		 *  @dtopt Features
  8331  		 *  @dtopt Server-side
  8332  		 * 
  8333  		 *  @example
  8334  		 *    $(document).ready( function () {
  8335  		 *      $('#example').dataTable( {
  8336  		 *        "bServerSide": true,
  8337  		 *        "sAjaxSource": "xhr.php"
  8338  		 *      } );
  8339  		 *    } );
  8340  		 */
  8341  		"bServerSide": false,
  8342  	
  8343  	
  8344  		/**
  8345  		 * Enable or disable sorting of columns. Sorting of individual columns can be
  8346  		 * disabled by the "bSortable" option for each column.
  8347  		 *  @type boolean
  8348  		 *  @default true
  8349  		 *  @dtopt Features
  8350  		 * 
  8351  		 *  @example
  8352  		 *    $(document).ready( function () {
  8353  		 *      $('#example').dataTable( {
  8354  		 *        "bSort": false
  8355  		 *      } );
  8356  		 *    } );
  8357  		 */
  8358  		"bSort": true,
  8359  	
  8360  	
  8361  		/**
  8362  		 * Allows control over whether DataTables should use the top (true) unique
  8363  		 * cell that is found for a single column, or the bottom (false - default).
  8364  		 * This is useful when using complex headers.
  8365  		 *  @type boolean
  8366  		 *  @default false
  8367  		 *  @dtopt Options
  8368  		 * 
  8369  		 *  @example
  8370  		 *    $(document).ready( function() {
  8371  		 *      $('#example').dataTable( {
  8372  		 *        "bSortCellsTop": true
  8373  		 *      } );
  8374  		 *    } );
  8375  		 */
  8376  		"bSortCellsTop": false,
  8377  	
  8378  	
  8379  		/**
  8380  		 * Enable or disable the addition of the classes 'sorting_1', 'sorting_2' and
  8381  		 * 'sorting_3' to the columns which are currently being sorted on. This is
  8382  		 * presented as a feature switch as it can increase processing time (while
  8383  		 * classes are removed and added) so for large data sets you might want to
  8384  		 * turn this off.
  8385  		 *  @type boolean
  8386  		 *  @default true
  8387  		 *  @dtopt Features
  8388  		 * 
  8389  		 *  @example
  8390  		 *    $(document).ready( function () {
  8391  		 *      $('#example').dataTable( {
  8392  		 *        "bSortClasses": false
  8393  		 *      } );
  8394  		 *    } );
  8395  		 */
  8396  		"bSortClasses": true,
  8397  	
  8398  	
  8399  		/**
  8400  		 * Enable or disable state saving. When enabled a cookie will be used to save
  8401  		 * table display information such as pagination information, display length,
  8402  		 * filtering and sorting. As such when the end user reloads the page the
  8403  		 * display display will match what thy had previously set up.
  8404  		 *  @type boolean
  8405  		 *  @default false
  8406  		 *  @dtopt Features
  8407  		 * 
  8408  		 *  @example
  8409  		 *    $(document).ready( function () {
  8410  		 *      $('#example').dataTable( {
  8411  		 *        "bStateSave": true
  8412  		 *      } );
  8413  		 *    } );
  8414  		 */
  8415  		"bStateSave": false,
  8416  	
  8417  	
  8418  		/**
  8419  		 * Customise the cookie and / or the parameters being stored when using
  8420  		 * DataTables with state saving enabled. This function is called whenever
  8421  		 * the cookie is modified, and it expects a fully formed cookie string to be
  8422  		 * returned. Note that the data object passed in is a Javascript object which
  8423  		 * must be converted to a string (JSON.stringify for example).
  8424  		 *  @type function
  8425  		 *  @param {string} sName Name of the cookie defined by DataTables
  8426  		 *  @param {object} oData Data to be stored in the cookie
  8427  		 *  @param {string} sExpires Cookie expires string
  8428  		 *  @param {string} sPath Path of the cookie to set
  8429  		 *  @returns {string} Cookie formatted string (which should be encoded by
  8430  		 *    using encodeURIComponent())
  8431  		 *  @dtopt Callbacks
  8432  		 * 
  8433  		 *  @example
  8434  		 *    $(document).ready( function () {
  8435  		 *      $('#example').dataTable( {
  8436  		 *        "fnCookieCallback": function (sName, oData, sExpires, sPath) {
  8437  		 *          // Customise oData or sName or whatever else here
  8438  		 *          return sName + "="+JSON.stringify(oData)+"; expires=" + sExpires +"; path=" + sPath;
  8439  		 *        }
  8440  		 *      } );
  8441  		 *    } );
  8442  		 */
  8443  		"fnCookieCallback": null,
  8444  	
  8445  	
  8446  		/**
  8447  		 * This function is called when a TR element is created (and all TD child
  8448  		 * elements have been inserted), or registered if using a DOM source, allowing
  8449  		 * manipulation of the TR element (adding classes etc).
  8450  		 *  @type function
  8451  		 *  @param {node} nRow "TR" element for the current row
  8452  		 *  @param {array} aData Raw data array for this row
  8453  		 *  @param {int} iDataIndex The index of this row in aoData
  8454  		 *  @dtopt Callbacks
  8455  		 * 
  8456  		 *  @example
  8457  		 *    $(document).ready( function() {
  8458  		 *      $('#example').dataTable( {
  8459  		 *        "fnCreatedRow": function( nRow, aData, iDataIndex ) {
  8460  		 *          // Bold the grade for all 'A' grade browsers
  8461  		 *          if ( aData[4] == "A" )
  8462  		 *          {
  8463  		 *            $('td:eq(4)', nRow).html( '<b>A</b>' );
  8464  		 *          }
  8465  		 *        }
  8466  		 *      } );
  8467  		 *    } );
  8468  		 */
  8469  		"fnCreatedRow": null,
  8470  	
  8471  	
  8472  		/**
  8473  		 * This function is called on every 'draw' event, and allows you to
  8474  		 * dynamically modify any aspect you want about the created DOM.
  8475  		 *  @type function
  8476  		 *  @param {object} oSettings DataTables settings object
  8477  		 *  @dtopt Callbacks
  8478  		 * 
  8479  		 *  @example
  8480  		 *    $(document).ready( function() {
  8481  		 *      $('#example').dataTable( {
  8482  		 *        "fnDrawCallback": function( oSettings ) {
  8483  		 *          alert( 'DataTables has redrawn the table' );
  8484  		 *        }
  8485  		 *      } );
  8486  		 *    } );
  8487  		 */
  8488  		"fnDrawCallback": null,
  8489  	
  8490  	
  8491  		/**
  8492  		 * Identical to fnHeaderCallback() but for the table footer this function
  8493  		 * allows you to modify the table footer on every 'draw' even.
  8494  		 *  @type function
  8495  		 *  @param {node} nFoot "TR" element for the footer
  8496  		 *  @param {array} aData Full table data (as derived from the original HTML)
  8497  		 *  @param {int} iStart Index for the current display starting point in the 
  8498  		 *    display array
  8499  		 *  @param {int} iEnd Index for the current display ending point in the 
  8500  		 *    display array
  8501  		 *  @param {array int} aiDisplay Index array to translate the visual position
  8502  		 *    to the full data array
  8503  		 *  @dtopt Callbacks
  8504  		 * 
  8505  		 *  @example
  8506  		 *    $(document).ready( function() {
  8507  		 *      $('#example').dataTable( {
  8508  		 *        "fnFooterCallback": function( nFoot, aData, iStart, iEnd, aiDisplay ) {
  8509  		 *          nFoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+iStart;
  8510  		 *        }
  8511  		 *      } );
  8512  		 *    } )
  8513  		 */
  8514  		"fnFooterCallback": null,
  8515  	
  8516  	
  8517  		/**
  8518  		 * When rendering large numbers in the information element for the table
  8519  		 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
  8520  		 * to have a comma separator for the 'thousands' units (e.g. 1 million is
  8521  		 * rendered as "1,000,000") to help readability for the end user. This
  8522  		 * function will override the default method DataTables uses.
  8523  		 *  @type function
  8524  		 *  @member
  8525  		 *  @param {int} iIn number to be formatted
  8526  		 *  @returns {string} formatted string for DataTables to show the number
  8527  		 *  @dtopt Callbacks
  8528  		 * 
  8529  		 *  @example
  8530  		 *    $(document).ready( function() {
  8531  		 *      $('#example').dataTable( {
  8532  		 *        "fnFormatNumber": function ( iIn ) {
  8533  		 *          if ( iIn &lt; 1000 ) {
  8534  		 *            return iIn;
  8535  		 *          } else {
  8536  		 *            var 
  8537  		 *              s=(iIn+""), 
  8538  		 *              a=s.split(""), out="", 
  8539  		 *              iLen=s.length;
  8540  		 *            
  8541  		 *            for ( var i=0 ; i&lt;iLen ; i++ ) {
  8542  		 *              if ( i%3 === 0 &amp;&amp; i !== 0 ) {
  8543  		 *                out = "'"+out;
  8544  		 *              }
  8545  		 *              out = a[iLen-i-1]+out;
  8546  		 *            }
  8547  		 *          }
  8548  		 *          return out;
  8549  		 *        };
  8550  		 *      } );
  8551  		 *    } );
  8552  		 */
  8553  		"fnFormatNumber": function ( iIn ) {
  8554  			if ( iIn < 1000 )
  8555  			{
  8556  				// A small optimisation for what is likely to be the majority of use cases
  8557  				return iIn;
  8558  			}
  8559  	
  8560  			var s=(iIn+""), a=s.split(""), out="", iLen=s.length;
  8561  			
  8562  			for ( var i=0 ; i<iLen ; i++ )
  8563  			{
  8564  				if ( i%3 === 0 && i !== 0 )
  8565  				{
  8566  					out = this.oLanguage.sInfoThousands+out;
  8567  				}
  8568  				out = a[iLen-i-1]+out;
  8569  			}
  8570  			return out;
  8571  		},
  8572  	
  8573  	
  8574  		/**
  8575  		 * This function is called on every 'draw' event, and allows you to
  8576  		 * dynamically modify the header row. This can be used to calculate and
  8577  		 * display useful information about the table.
  8578  		 *  @type function
  8579  		 *  @param {node} nHead "TR" element for the header
  8580  		 *  @param {array} aData Full table data (as derived from the original HTML)
  8581  		 *  @param {int} iStart Index for the current display starting point in the
  8582  		 *    display array
  8583  		 *  @param {int} iEnd Index for the current display ending point in the
  8584  		 *    display array
  8585  		 *  @param {array int} aiDisplay Index array to translate the visual position
  8586  		 *    to the full data array
  8587  		 *  @dtopt Callbacks
  8588  		 * 
  8589  		 *  @example
  8590  		 *    $(document).ready( function() {
  8591  		 *      $('#example').dataTable( {
  8592  		 *        "fnHeaderCallback": function( nHead, aData, iStart, iEnd, aiDisplay ) {
  8593  		 *          nHead.getElementsByTagName('th')[0].innerHTML = "Displaying "+(iEnd-iStart)+" records";
  8594  		 *        }
  8595  		 *      } );
  8596  		 *    } )
  8597  		 */
  8598  		"fnHeaderCallback": null,
  8599  	
  8600  	
  8601  		/**
  8602  		 * The information element can be used to convey information about the current
  8603  		 * state of the table. Although the internationalisation options presented by
  8604  		 * DataTables are quite capable of dealing with most customisations, there may
  8605  		 * be times where you wish to customise the string further. This callback
  8606  		 * allows you to do exactly that.
  8607  		 *  @type function
  8608  		 *  @param {object} oSettings DataTables settings object
  8609  		 *  @param {int} iStart Starting position in data for the draw
  8610  		 *  @param {int} iEnd End position in data for the draw
  8611  		 *  @param {int} iMax Total number of rows in the table (regardless of
  8612  		 *    filtering)
  8613  		 *  @param {int} iTotal Total number of rows in the data set, after filtering
  8614  		 *  @param {string} sPre The string that DataTables has formatted using it's
  8615  		 *    own rules
  8616  		 *  @returns {string} The string to be displayed in the information element.
  8617  		 *  @dtopt Callbacks
  8618  		 * 
  8619  		 *  @example
  8620  		 *    $('#example').dataTable( {
  8621  		 *      "fnInfoCallback": function( oSettings, iStart, iEnd, iMax, iTotal, sPre ) {
  8622  		 *        return iStart +" to "+ iEnd;
  8623  		 *      }
  8624  		 *    } );
  8625  		 */
  8626  		"fnInfoCallback": null,
  8627  	
  8628  	
  8629  		/**
  8630  		 * Called when the table has been initialised. Normally DataTables will
  8631  		 * initialise sequentially and there will be no need for this function,
  8632  		 * however, this does not hold true when using external language information
  8633  		 * since that is obtained using an async XHR call.
  8634  		 *  @type function
  8635  		 *  @param {object} oSettings DataTables settings object
  8636  		 *  @param {object} json The JSON object request from the server - only
  8637  		 *    present if client-side Ajax sourced data is used
  8638  		 *  @dtopt Callbacks
  8639  		 * 
  8640  		 *  @example
  8641  		 *    $(document).ready( function() {
  8642  		 *      $('#example').dataTable( {
  8643  		 *        "fnInitComplete": function(oSettings, json) {
  8644  		 *          alert( 'DataTables has finished its initialisation.' );
  8645  		 *        }
  8646  		 *      } );
  8647  		 *    } )
  8648  		 */
  8649  		"fnInitComplete": null,
  8650  	
  8651  	
  8652  		/**
  8653  		 * Called at the very start of each table draw and can be used to cancel the
  8654  		 * draw by returning false, any other return (including undefined) results in
  8655  		 * the full draw occurring).
  8656  		 *  @type function
  8657  		 *  @param {object} oSettings DataTables settings object
  8658  		 *  @returns {boolean} False will cancel the draw, anything else (including no
  8659  		 *    return) will allow it to complete.
  8660  		 *  @dtopt Callbacks
  8661  		 * 
  8662  		 *  @example
  8663  		 *    $(document).ready( function() {
  8664  		 *      $('#example').dataTable( {
  8665  		 *        "fnPreDrawCallback": function( oSettings ) {
  8666  		 *          if ( $('#test').val() == 1 ) {
  8667  		 *            return false;
  8668  		 *          }
  8669  		 *        }
  8670  		 *      } );
  8671  		 *    } );
  8672  		 */
  8673  		"fnPreDrawCallback": null,
  8674  	
  8675  	
  8676  		/**
  8677  		 * This function allows you to 'post process' each row after it have been
  8678  		 * generated for each table draw, but before it is rendered on screen. This
  8679  		 * function might be used for setting the row class name etc.
  8680  		 *  @type function
  8681  		 *  @param {node} nRow "TR" element for the current row
  8682  		 *  @param {array} aData Raw data array for this row
  8683  		 *  @param {int} iDisplayIndex The display index for the current table draw
  8684  		 *  @param {int} iDisplayIndexFull The index of the data in the full list of
  8685  		 *    rows (after filtering)
  8686  		 *  @dtopt Callbacks
  8687  		 * 
  8688  		 *  @example
  8689  		 *    $(document).ready( function() {
  8690  		 *      $('#example').dataTable( {
  8691  		 *        "fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
  8692  		 *          // Bold the grade for all 'A' grade browsers
  8693  		 *          if ( aData[4] == "A" )
  8694  		 *          {
  8695  		 *            $('td:eq(4)', nRow).html( '<b>A</b>' );
  8696  		 *          }
  8697  		 *        }
  8698  		 *      } );
  8699  		 *    } );
  8700  		 */
  8701  		"fnRowCallback": null,
  8702  	
  8703  	
  8704  		/**
  8705  		 * This parameter allows you to override the default function which obtains
  8706  		 * the data from the server ($.getJSON) so something more suitable for your
  8707  		 * application. For example you could use POST data, or pull information from
  8708  		 * a Gears or AIR database.
  8709  		 *  @type function
  8710  		 *  @member
  8711  		 *  @param {string} sSource HTTP source to obtain the data from (sAjaxSource)
  8712  		 *  @param {array} aoData A key/value pair object containing the data to send
  8713  		 *    to the server
  8714  		 *  @param {function} fnCallback to be called on completion of the data get
  8715  		 *    process that will draw the data on the page.
  8716  		 *  @param {object} oSettings DataTables settings object
  8717  		 *  @dtopt Callbacks
  8718  		 *  @dtopt Server-side
  8719  		 * 
  8720  		 *  @example
  8721  		 *    // POST data to server
  8722  		 *    $(document).ready( function() {
  8723  		 *      $('#example').dataTable( {
  8724  		 *        "bProcessing": true,
  8725  		 *        "bServerSide": true,
  8726  		 *        "sAjaxSource": "xhr.php",
  8727  		 *        "fnServerData": function ( sSource, aoData, fnCallback, oSettings ) {
  8728  		 *          oSettings.jqXHR = $.ajax( {
  8729  		 *            "dataType": 'json', 
  8730  		 *            "type": "POST", 
  8731  		 *            "url": sSource, 
  8732  		 *            "data": aoData, 
  8733  		 *            "success": fnCallback
  8734  		 *          } );
  8735  		 *        }
  8736  		 *      } );
  8737  		 *    } );
  8738  		 */
  8739  		"fnServerData": function ( sUrl, aoData, fnCallback, oSettings ) {
  8740  			oSettings.jqXHR = $.ajax( {
  8741  				"url":  sUrl,
  8742  				"data": aoData,
  8743  				"success": function (json) {
  8744  					if ( json.sError ) {
  8745  						oSettings.oApi._fnLog( oSettings, 0, json.sError );
  8746  					}
  8747  					
  8748  					$(oSettings.oInstance).trigger('xhr', [oSettings, json]);
  8749  					fnCallback( json );
  8750  				},
  8751  				"dataType": "json",
  8752  				"cache": false,
  8753  				"type": oSettings.sServerMethod,
  8754  				"error": function (xhr, error, thrown) {
  8755  					if ( error == "parsererror" ) {
  8756  						oSettings.oApi._fnLog( oSettings, 0, "DataTables warning: JSON data from "+
  8757  							"server could not be parsed. This is caused by a JSON formatting error." );
  8758  					}
  8759  				}
  8760  			} );
  8761  		},
  8762  	
  8763  	
  8764  		/**
  8765  		 * It is often useful to send extra data to the server when making an Ajax
  8766  		 * request - for example custom filtering information, and this callback
  8767  		 * function makes it trivial to send extra information to the server. The
  8768  		 * passed in parameter is the data set that has been constructed by
  8769  		 * DataTables, and you can add to this or modify it as you require.
  8770  		 *  @type function
  8771  		 *  @param {array} aoData Data array (array of objects which are name/value
  8772  		 *    pairs) that has been constructed by DataTables and will be sent to the
  8773  		 *    server. In the case of Ajax sourced data with server-side processing
  8774  		 *    this will be an empty array, for server-side processing there will be a
  8775  		 *    significant number of parameters!
  8776  		 *  @returns {undefined} Ensure that you modify the aoData array passed in,
  8777  		 *    as this is passed by reference.
  8778  		 *  @dtopt Callbacks
  8779  		 *  @dtopt Server-side
  8780  		 * 
  8781  		 *  @example
  8782  		 *    $(document).ready( function() {
  8783  		 *      $('#example').dataTable( {
  8784  		 *        "bProcessing": true,
  8785  		 *        "bServerSide": true,
  8786  		 *        "sAjaxSource": "scripts/server_processing.php",
  8787  		 *        "fnServerParams": function ( aoData ) {
  8788  		 *          aoData.push( { "name": "more_data", "value": "my_value" } );
  8789  		 *        }
  8790  		 *      } );
  8791  		 *    } );
  8792  		 */
  8793  		"fnServerParams": null,
  8794  	
  8795  	
  8796  		/**
  8797  		 * Load the table state. With this function you can define from where, and how, the
  8798  		 * state of a table is loaded. By default DataTables will load from its state saving
  8799  		 * cookie, but you might wish to use local storage (HTML5) or a server-side database.
  8800  		 *  @type function
  8801  		 *  @member
  8802  		 *  @param {object} oSettings DataTables settings object
  8803  		 *  @return {object} The DataTables state object to be loaded
  8804  		 *  @dtopt Callbacks
  8805  		 * 
  8806  		 *  @example
  8807  		 *    $(document).ready( function() {
  8808  		 *      $('#example').dataTable( {
  8809  		 *        "bStateSave": true,
  8810  		 *        "fnStateLoad": function (oSettings) {
  8811  		 *          var o;
  8812  		 *          
  8813  		 *          // Send an Ajax request to the server to get the data. Note that
  8814  		 *          // this is a synchronous request.
  8815  		 *          $.ajax( {
  8816  		 *            "url": "/state_load",
  8817  		 *            "async": false,
  8818  		 *            "dataType": "json",
  8819  		 *            "success": function (json) {
  8820  		 *              o = json;
  8821  		 *            }
  8822  		 *          } );
  8823  		 *          
  8824  		 *          return o;
  8825  		 *        }
  8826  		 *      } );
  8827  		 *    } );
  8828  		 */
  8829  		"fnStateLoad": function ( oSettings ) {
  8830  			var sData = this.oApi._fnReadCookie( oSettings.sCookiePrefix+oSettings.sInstance );
  8831  			var oData;
  8832  	
  8833  			try {
  8834  				oData = (typeof $.parseJSON === 'function') ? 
  8835  					$.parseJSON(sData) : eval( '('+sData+')' );
  8836  			} catch (e) {
  8837  				oData = null;
  8838  			}
  8839  	
  8840  			return oData;
  8841  		},
  8842  	
  8843  	
  8844  		/**
  8845  		 * Callback which allows modification of the saved state prior to loading that state.
  8846  		 * This callback is called when the table is loading state from the stored data, but
  8847  		 * prior to the settings object being modified by the saved state. Note that for 
  8848  		 * plug-in authors, you should use the 'stateLoadParams' event to load parameters for 
  8849  		 * a plug-in.
  8850  		 *  @type function
  8851  		 *  @param {object} oSettings DataTables settings object
  8852  		 *  @param {object} oData The state object that is to be loaded
  8853  		 *  @dtopt Callbacks
  8854  		 * 
  8855  		 *  @example
  8856  		 *    // Remove a saved filter, so filtering is never loaded
  8857  		 *    $(document).ready( function() {
  8858  		 *      $('#example').dataTable( {
  8859  		 *        "bStateSave": true,
  8860  		 *        "fnStateLoadParams": function (oSettings, oData) {
  8861  		 *          oData.oSearch.sSearch = "";
  8862  		 *        }
  8863  		 *      } );
  8864  		 *    } );
  8865  		 * 
  8866  		 *  @example
  8867  		 *    // Disallow state loading by returning false
  8868  		 *    $(document).ready( function() {
  8869  		 *      $('#example').dataTable( {
  8870  		 *        "bStateSave": true,
  8871  		 *        "fnStateLoadParams": function (oSettings, oData) {
  8872  		 *          return false;
  8873  		 *        }
  8874  		 *      } );
  8875  		 *    } );
  8876  		 */
  8877  		"fnStateLoadParams": null,
  8878  	
  8879  	
  8880  		/**
  8881  		 * Callback that is called when the state has been loaded from the state saving method
  8882  		 * and the DataTables settings object has been modified as a result of the loaded state.
  8883  		 *  @type function
  8884  		 *  @param {object} oSettings DataTables settings object
  8885  		 *  @param {object} oData The state object that was loaded
  8886  		 *  @dtopt Callbacks
  8887  		 * 
  8888  		 *  @example
  8889  		 *    // Show an alert with the filtering value that was saved
  8890  		 *    $(document).ready( function() {
  8891  		 *      $('#example').dataTable( {
  8892  		 *        "bStateSave": true,
  8893  		 *        "fnStateLoaded": function (oSettings, oData) {
  8894  		 *          alert( 'Saved filter was: '+oData.oSearch.sSearch );
  8895  		 *        }
  8896  		 *      } );
  8897  		 *    } );
  8898  		 */
  8899  		"fnStateLoaded": null,
  8900  	
  8901  	
  8902  		/**
  8903  		 * Save the table state. This function allows you to define where and how the state
  8904  		 * information for the table is stored - by default it will use a cookie, but you
  8905  		 * might want to use local storage (HTML5) or a server-side database.
  8906  		 *  @type function
  8907  		 *  @member
  8908  		 *  @param {object} oSettings DataTables settings object
  8909  		 *  @param {object} oData The state object to be saved
  8910  		 *  @dtopt Callbacks
  8911  		 * 
  8912  		 *  @example
  8913  		 *    $(document).ready( function() {
  8914  		 *      $('#example').dataTable( {
  8915  		 *        "bStateSave": true,
  8916  		 *        "fnStateSave": function (oSettings, oData) {
  8917  		 *          // Send an Ajax request to the server with the state object
  8918  		 *          $.ajax( {
  8919  		 *            "url": "/state_save",
  8920  		 *            "data": oData,
  8921  		 *            "dataType": "json",
  8922  		 *            "method": "POST"
  8923  		 *            "success": function () {}
  8924  		 *          } );
  8925  		 *        }
  8926  		 *      } );
  8927  		 *    } );
  8928  		 */
  8929  		"fnStateSave": function ( oSettings, oData ) {
  8930  			this.oApi._fnCreateCookie( 
  8931  				oSettings.sCookiePrefix+oSettings.sInstance, 
  8932  				this.oApi._fnJsonString(oData), 
  8933  				oSettings.iCookieDuration, 
  8934  				oSettings.sCookiePrefix, 
  8935  				oSettings.fnCookieCallback
  8936  			);
  8937  		},
  8938  	
  8939  	
  8940  		/**
  8941  		 * Callback which allows modification of the state to be saved. Called when the table 
  8942  		 * has changed state a new state save is required. This method allows modification of
  8943  		 * the state saving object prior to actually doing the save, including addition or 
  8944  		 * other state properties or modification. Note that for plug-in authors, you should 
  8945  		 * use the 'stateSaveParams' event to save parameters for a plug-in.
  8946  		 *  @type function
  8947  		 *  @param {object} oSettings DataTables settings object
  8948  		 *  @param {object} oData The state object to be saved
  8949  		 *  @dtopt Callbacks
  8950  		 * 
  8951  		 *  @example
  8952  		 *    // Remove a saved filter, so filtering is never saved
  8953  		 *    $(document).ready( function() {
  8954  		 *      $('#example').dataTable( {
  8955  		 *        "bStateSave": true,
  8956  		 *        "fnStateSaveParams": function (oSettings, oData) {
  8957  		 *          oData.oSearch.sSearch = "";
  8958  		 *        }
  8959  		 *      } );
  8960  		 *    } );
  8961  		 */
  8962  		"fnStateSaveParams": null,
  8963  	
  8964  	
  8965  		/**
  8966  		 * Duration of the cookie which is used for storing session information. This
  8967  		 * value is given in seconds.
  8968  		 *  @type int
  8969  		 *  @default 7200 <i>(2 hours)</i>
  8970  		 *  @dtopt Options
  8971  		 * 
  8972  		 *  @example
  8973  		 *    $(document).ready( function() {
  8974  		 *      $('#example').dataTable( {
  8975  		 *        "iCookieDuration": 60*60*24; // 1 day
  8976  		 *      } );
  8977  		 *    } )
  8978  		 */
  8979  		"iCookieDuration": 7200,
  8980  	
  8981  	
  8982  		/**
  8983  		 * When enabled DataTables will not make a request to the server for the first
  8984  		 * page draw - rather it will use the data already on the page (no sorting etc
  8985  		 * will be applied to it), thus saving on an XHR at load time. iDeferLoading
  8986  		 * is used to indicate that deferred loading is required, but it is also used
  8987  		 * to tell DataTables how many records there are in the full table (allowing
  8988  		 * the information element and pagination to be displayed correctly). In the case
  8989  		 * where a filtering is applied to the table on initial load, this can be
  8990  		 * indicated by giving the parameter as an array, where the first element is
  8991  		 * the number of records available after filtering and the second element is the
  8992  		 * number of records without filtering (allowing the table information element
  8993  		 * to be shown correctly).
  8994  		 *  @type int | array
  8995  		 *  @default null
  8996  		 *  @dtopt Options
  8997  		 * 
  8998  		 *  @example
  8999  		 *    // 57 records available in the table, no filtering applied
  9000  		 *    $(document).ready( function() {
  9001  		 *      $('#example').dataTable( {
  9002  		 *        "bServerSide": true,
  9003  		 *        "sAjaxSource": "scripts/server_processing.php",
  9004  		 *        "iDeferLoading": 57
  9005  		 *      } );
  9006  		 *    } );
  9007  		 * 
  9008  		 *  @example
  9009  		 *    // 57 records after filtering, 100 without filtering (an initial filter applied)
  9010  		 *    $(document).ready( function() {
  9011  		 *      $('#example').dataTable( {
  9012  		 *        "bServerSide": true,
  9013  		 *        "sAjaxSource": "scripts/server_processing.php",
  9014  		 *        "iDeferLoading": [ 57, 100 ],
  9015  		 *        "oSearch": {
  9016  		 *          "sSearch": "my_filter"
  9017  		 *        }
  9018  		 *      } );
  9019  		 *    } );
  9020  		 */
  9021  		"iDeferLoading": null,
  9022  	
  9023  	
  9024  		/**
  9025  		 * Number of rows to display on a single page when using pagination. If
  9026  		 * feature enabled (bLengthChange) then the end user will be able to override
  9027  		 * this to a custom setting using a pop-up menu.
  9028  		 *  @type int
  9029  		 *  @default 10
  9030  		 *  @dtopt Options
  9031  		 * 
  9032  		 *  @example
  9033  		 *    $(document).ready( function() {
  9034  		 *      $('#example').dataTable( {
  9035  		 *        "iDisplayLength": 50
  9036  		 *      } );
  9037  		 *    } )
  9038  		 */
  9039  		"iDisplayLength": 10,
  9040  	
  9041  	
  9042  		/**
  9043  		 * Define the starting point for data display when using DataTables with
  9044  		 * pagination. Note that this parameter is the number of records, rather than
  9045  		 * the page number, so if you have 10 records per page and want to start on
  9046  		 * the third page, it should be "20".
  9047  		 *  @type int
  9048  		 *  @default 0
  9049  		 *  @dtopt Options
  9050  		 * 
  9051  		 *  @example
  9052  		 *    $(document).ready( function() {
  9053  		 *      $('#example').dataTable( {
  9054  		 *        "iDisplayStart": 20
  9055  		 *      } );
  9056  		 *    } )
  9057  		 */
  9058  		"iDisplayStart": 0,
  9059  	
  9060  	
  9061  		/**
  9062  		 * The scroll gap is the amount of scrolling that is left to go before
  9063  		 * DataTables will load the next 'page' of data automatically. You typically
  9064  		 * want a gap which is big enough that the scrolling will be smooth for the
  9065  		 * user, while not so large that it will load more data than need.
  9066  		 *  @type int
  9067  		 *  @default 100
  9068  		 *  @dtopt Options
  9069  		 * 
  9070  		 *  @example
  9071  		 *    $(document).ready( function() {
  9072  		 *      $('#example').dataTable( {
  9073  		 *        "bScrollInfinite": true,
  9074  		 *        "bScrollCollapse": true,
  9075  		 *        "sScrollY": "200px",
  9076  		 *        "iScrollLoadGap": 50
  9077  		 *      } );
  9078  		 *    } );
  9079  		 */
  9080  		"iScrollLoadGap": 100,
  9081  	
  9082  	
  9083  		/**
  9084  		 * By default DataTables allows keyboard navigation of the table (sorting, paging,
  9085  		 * and filtering) by adding a tabindex attribute to the required elements. This
  9086  		 * allows you to tab through the controls and press the enter key to activate them.
  9087  		 * The tabindex is default 0, meaning that the tab follows the flow of the document.
  9088  		 * You can overrule this using this parameter if you wish. Use a value of -1 to
  9089  		 * disable built-in keyboard navigation.
  9090  		 *  @type int
  9091  		 *  @default 0
  9092  		 *  @dtopt Options
  9093  		 * 
  9094  		 *  @example
  9095  		 *    $(document).ready( function() {
  9096  		 *      $('#example').dataTable( {
  9097  		 *        "iTabIndex": 1
  9098  		 *      } );
  9099  		 *    } );
  9100  		 */
  9101  		"iTabIndex": 0,
  9102  	
  9103  	
  9104  		/**
  9105  		 * All strings that DataTables uses in the user interface that it creates
  9106  		 * are defined in this object, allowing you to modified them individually or
  9107  		 * completely replace them all as required.
  9108  		 *  @namespace
  9109  		 */
  9110  		"oLanguage": {
  9111  			/**
  9112  			 * Strings that are used for WAI-ARIA labels and controls only (these are not
  9113  			 * actually visible on the page, but will be read by screenreaders, and thus
  9114  			 * must be internationalised as well).
  9115  			 *  @namespace
  9116  			 */
  9117  			"oAria": {
  9118  				/**
  9119  				 * ARIA label that is added to the table headers when the column may be
  9120  				 * sorted ascending by activing the column (click or return when focused).
  9121  				 * Note that the column header is prefixed to this string.
  9122  				 *  @type string
  9123  				 *  @default : activate to sort column ascending
  9124  				 *  @dtopt Language
  9125  				 * 
  9126  				 *  @example
  9127  				 *    $(document).ready( function() {
  9128  				 *      $('#example').dataTable( {
  9129  				 *        "oLanguage": {
  9130  				 *          "oAria": {
  9131  				 *            "sSortAscending": " - click/return to sort ascending"
  9132  				 *          }
  9133  				 *        }
  9134  				 *      } );
  9135  				 *    } );
  9136  				 */
  9137  				"sSortAscending": ": activate to sort column ascending",
  9138  	
  9139  				/**
  9140  				 * ARIA label that is added to the table headers when the column may be
  9141  				 * sorted descending by activing the column (click or return when focused).
  9142  				 * Note that the column header is prefixed to this string.
  9143  				 *  @type string
  9144  				 *  @default : activate to sort column ascending
  9145  				 *  @dtopt Language
  9146  				 * 
  9147  				 *  @example
  9148  				 *    $(document).ready( function() {
  9149  				 *      $('#example').dataTable( {
  9150  				 *        "oLanguage": {
  9151  				 *          "oAria": {
  9152  				 *            "sSortDescending": " - click/return to sort descending"
  9153  				 *          }
  9154  				 *        }
  9155  				 *      } );
  9156  				 *    } );
  9157  				 */
  9158  				"sSortDescending": ": activate to sort column descending"
  9159  			},
  9160  	
  9161  			/**
  9162  			 * Pagination string used by DataTables for the two built-in pagination
  9163  			 * control types ("two_button" and "full_numbers")
  9164  			 *  @namespace
  9165  			 */
  9166  			"oPaginate": {
  9167  				/**
  9168  				 * Text to use when using the 'full_numbers' type of pagination for the
  9169  				 * button to take the user to the first page.
  9170  				 *  @type string
  9171  				 *  @default First
  9172  				 *  @dtopt Language
  9173  				 * 
  9174  				 *  @example
  9175  				 *    $(document).ready( function() {
  9176  				 *      $('#example').dataTable( {
  9177  				 *        "oLanguage": {
  9178  				 *          "oPaginate": {
  9179  				 *            "sFirst": "First page"
  9180  				 *          }
  9181  				 *        }
  9182  				 *      } );
  9183  				 *    } );
  9184  				 */
  9185  				"sFirst": "First",
  9186  			
  9187  			
  9188  				/**
  9189  				 * Text to use when using the 'full_numbers' type of pagination for the
  9190  				 * button to take the user to the last page.
  9191  				 *  @type string
  9192  				 *  @default Last
  9193  				 *  @dtopt Language
  9194  				 * 
  9195  				 *  @example
  9196  				 *    $(document).ready( function() {
  9197  				 *      $('#example').dataTable( {
  9198  				 *        "oLanguage": {
  9199  				 *          "oPaginate": {
  9200  				 *            "sLast": "Last page"
  9201  				 *          }
  9202  
  9203  				 *        }
  9204  				 *      } );
  9205  				 *    } );
  9206  				 */
  9207  				"sLast": "Last",
  9208  			
  9209  			
  9210  				/**
  9211  				 * Text to use for the 'next' pagination button (to take the user to the 
  9212  				 * next page).
  9213  				 *  @type string
  9214  				 *  @default Next
  9215  				 *  @dtopt Language
  9216  				 * 
  9217  				 *  @example
  9218  				 *    $(document).ready( function() {
  9219  				 *      $('#example').dataTable( {
  9220  				 *        "oLanguage": {
  9221  				 *          "oPaginate": {
  9222  				 *            "sNext": "Next page"
  9223  				 *          }
  9224  				 *        }
  9225  				 *      } );
  9226  				 *    } );
  9227  				 */
  9228  				"sNext": "Next",
  9229  			
  9230  			
  9231  				/**
  9232  				 * Text to use for the 'previous' pagination button (to take the user to  
  9233  				 * the previous page).
  9234  				 *  @type string
  9235  				 *  @default Previous
  9236  				 *  @dtopt Language
  9237  				 * 
  9238  				 *  @example
  9239  				 *    $(document).ready( function() {
  9240  				 *      $('#example').dataTable( {
  9241  				 *        "oLanguage": {
  9242  				 *          "oPaginate": {
  9243  				 *            "sPrevious": "Previous page"
  9244  				 *          }
  9245  				 *        }
  9246  				 *      } );
  9247  				 *    } );
  9248  				 */
  9249  				"sPrevious": "Previous"
  9250  			},
  9251  		
  9252  			/**
  9253  			 * This string is shown in preference to sZeroRecords when the table is
  9254  			 * empty of data (regardless of filtering). Note that this is an optional
  9255  			 * parameter - if it is not given, the value of sZeroRecords will be used
  9256  			 * instead (either the default or given value).
  9257  			 *  @type string
  9258  			 *  @default No data available in table
  9259  			 *  @dtopt Language
  9260  			 * 
  9261  			 *  @example
  9262  			 *    $(document).ready( function() {
  9263  			 *      $('#example').dataTable( {
  9264  			 *        "oLanguage": {
  9265  			 *          "sEmptyTable": "No data available in table"
  9266  			 *        }
  9267  			 *      } );
  9268  			 *    } );
  9269  			 */
  9270  			"sEmptyTable": "No data available in table",
  9271  		
  9272  		
  9273  			/**
  9274  			 * This string gives information to the end user about the information that 
  9275  			 * is current on display on the page. The _START_, _END_ and _TOTAL_ 
  9276  			 * variables are all dynamically replaced as the table display updates, and 
  9277  			 * can be freely moved or removed as the language requirements change.
  9278  			 *  @type string
  9279  			 *  @default Showing _START_ to _END_ of _TOTAL_ entries
  9280  			 *  @dtopt Language
  9281  			 * 
  9282  			 *  @example
  9283  			 *    $(document).ready( function() {
  9284  			 *      $('#example').dataTable( {
  9285  			 *        "oLanguage": {
  9286  			 *          "sInfo": "Got a total of _TOTAL_ entries to show (_START_ to _END_)"
  9287  			 *        }
  9288  			 *      } );
  9289  			 *    } );
  9290  			 */
  9291  			"sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
  9292  		
  9293  		
  9294  			/**
  9295  			 * Display information string for when the table is empty. Typically the 
  9296  			 * format of this string should match sInfo.
  9297  			 *  @type string
  9298  			 *  @default Showing 0 to 0 of 0 entries
  9299  			 *  @dtopt Language
  9300  			 * 
  9301  			 *  @example
  9302  			 *    $(document).ready( function() {
  9303  			 *      $('#example').dataTable( {
  9304  			 *        "oLanguage": {
  9305  			 *          "sInfoEmpty": "No entries to show"
  9306  			 *        }
  9307  			 *      } );
  9308  			 *    } );
  9309  			 */
  9310  			"sInfoEmpty": "Showing 0 to 0 of 0 entries",
  9311  		
  9312  		
  9313  			/**
  9314  			 * When a user filters the information in a table, this string is appended 
  9315  			 * to the information (sInfo) to give an idea of how strong the filtering 
  9316  			 * is. The variable _MAX_ is dynamically updated.
  9317  			 *  @type string
  9318  			 *  @default (filtered from _MAX_ total entries)
  9319  			 *  @dtopt Language
  9320  			 * 
  9321  			 *  @example
  9322  			 *    $(document).ready( function() {
  9323  			 *      $('#example').dataTable( {
  9324  			 *        "oLanguage": {
  9325  			 *          "sInfoFiltered": " - filtering from _MAX_ records"
  9326  			 *        }
  9327  			 *      } );
  9328  			 *    } );
  9329  			 */
  9330  			"sInfoFiltered": "(filtered from _MAX_ total entries)",
  9331  		
  9332  		
  9333  			/**
  9334  			 * If can be useful to append extra information to the info string at times,
  9335  			 * and this variable does exactly that. This information will be appended to
  9336  			 * the sInfo (sInfoEmpty and sInfoFiltered in whatever combination they are
  9337  			 * being used) at all times.
  9338  			 *  @type string
  9339  			 *  @default <i>Empty string</i>
  9340  			 *  @dtopt Language
  9341  			 * 
  9342  			 *  @example
  9343  			 *    $(document).ready( function() {
  9344  			 *      $('#example').dataTable( {
  9345  			 *        "oLanguage": {
  9346  			 *          "sInfoPostFix": "All records shown are derived from real information."
  9347  			 *        }
  9348  			 *      } );
  9349  			 *    } );
  9350  			 */
  9351  			"sInfoPostFix": "",
  9352  		
  9353  		
  9354  			/**
  9355  			 * DataTables has a build in number formatter (fnFormatNumber) which is used
  9356  			 * to format large numbers that are used in the table information. By
  9357  			 * default a comma is used, but this can be trivially changed to any
  9358  			 * character you wish with this parameter.
  9359  			 *  @type string
  9360  			 *  @default ,
  9361  			 *  @dtopt Language
  9362  			 * 
  9363  			 *  @example
  9364  			 *    $(document).ready( function() {
  9365  			 *      $('#example').dataTable( {
  9366  			 *        "oLanguage": {
  9367  			 *          "sInfoThousands": "'"
  9368  			 *        }
  9369  			 *      } );
  9370  			 *    } );
  9371  			 */
  9372  			"sInfoThousands": ",",
  9373  		
  9374  		
  9375  			/**
  9376  			 * Detail the action that will be taken when the drop down menu for the
  9377  			 * pagination length option is changed. The '_MENU_' variable is replaced
  9378  			 * with a default select list of 10, 25, 50 and 100, and can be replaced
  9379  			 * with a custom select box if required.
  9380  			 *  @type string
  9381  			 *  @default Show _MENU_ entries
  9382  			 *  @dtopt Language
  9383  			 * 
  9384  			 *  @example
  9385  			 *    // Language change only
  9386  			 *    $(document).ready( function() {
  9387  			 *      $('#example').dataTable( {
  9388  			 *        "oLanguage": {
  9389  			 *          "sLengthMenu": "Display _MENU_ records"
  9390  			 *        }
  9391  			 *      } );
  9392  			 *    } );
  9393  			 *    
  9394  			 *  @example
  9395  			 *    // Language and options change
  9396  			 *    $(document).ready( function() {
  9397  			 *      $('#example').dataTable( {
  9398  			 *        "oLanguage": {
  9399  			 *          "sLengthMenu": 'Display <select>'+
  9400  			 *            '<option value="10">10</option>'+
  9401  			 *            '<option value="20">20</option>'+
  9402  			 *            '<option value="30">30</option>'+
  9403  			 *            '<option value="40">40</option>'+
  9404  			 *            '<option value="50">50</option>'+
  9405  			 *            '<option value="-1">All</option>'+
  9406  			 *            '</select> records'
  9407  			 *        }
  9408  			 *      } );
  9409  			 *    } );
  9410  			 */
  9411  			"sLengthMenu": "每页显示 _MENU_ 条数据",
  9412  		
  9413  		
  9414  			/**
  9415  			 * When using Ajax sourced data and during the first draw when DataTables is
  9416  			 * gathering the data, this message is shown in an empty row in the table to
  9417  			 * indicate to the end user the the data is being loaded. Note that this
  9418  			 * parameter is not used when loading data by server-side processing, just
  9419  			 * Ajax sourced data with client-side processing.
  9420  			 *  @type string
  9421  			 *  @default Loading...
  9422  			 *  @dtopt Language
  9423  			 * 
  9424  			 *  @example
  9425  			 *    $(document).ready( function() {
  9426  			 *      $('#example').dataTable( {
  9427  			 *        "oLanguage": {
  9428  			 *          "sLoadingRecords": "Please wait - loading..."
  9429  			 *        }
  9430  			 *      } );
  9431  			 *    } );
  9432  			 */
  9433  			"sLoadingRecords": "Loading...",
  9434  		
  9435  		
  9436  			/**
  9437  			 * Text which is displayed when the table is processing a user action
  9438  			 * (usually a sort command or similar).
  9439  			 *  @type string
  9440  			 *  @default Processing...
  9441  			 *  @dtopt Language
  9442  			 * 
  9443  			 *  @example
  9444  			 *    $(document).ready( function() {
  9445  			 *      $('#example').dataTable( {
  9446  			 *        "oLanguage": {
  9447  			 *          "sProcessing": "DataTables is currently busy"
  9448  			 *        }
  9449  			 *      } );
  9450  			 *    } );
  9451  			 */
  9452  			"sProcessing": "Processing...",
  9453  		
  9454  		
  9455  			/**
  9456  			 * Details the actions that will be taken when the user types into the
  9457  			 * filtering input text box. The variable "_INPUT_", if used in the string,
  9458  			 * is replaced with the HTML text box for the filtering input allowing
  9459  			 * control over where it appears in the string. If "_INPUT_" is not given
  9460  			 * then the input box is appended to the string automatically.
  9461  			 *  @type string
  9462  			 *  @default Search:
  9463  			 *  @dtopt Language
  9464  			 * 
  9465  			 *  @example
  9466  			 *    // Input text box will be appended at the end automatically
  9467  			 *    $(document).ready( function() {
  9468  			 *      $('#example').dataTable( {
  9469  			 *        "oLanguage": {
  9470  			 *          "sSearch": "Filter records:"
  9471  			 *        }
  9472  			 *      } );
  9473  			 *    } );
  9474  			 *    
  9475  			 *  @example
  9476  			 *    // Specify where the filter should appear
  9477  			 *    $(document).ready( function() {
  9478  			 *      $('#example').dataTable( {
  9479  			 *        "oLanguage": {
  9480  			 *          "sSearch": "Apply filter _INPUT_ to table"
  9481  			 *        }
  9482  			 *      } );
  9483  			 *    } );
  9484  			 */
  9485  			"sSearch": "搜索:",
  9486  		
  9487  		
  9488  			/**
  9489  			 * All of the language information can be stored in a file on the
  9490  			 * server-side, which DataTables will look up if this parameter is passed.
  9491  			 * It must store the URL of the language file, which is in a JSON format,
  9492  			 * and the object has the same properties as the oLanguage object in the
  9493  			 * initialiser object (i.e. the above parameters). Please refer to one of
  9494  			 * the example language files to see how this works in action.
  9495  			 *  @type string
  9496  			 *  @default <i>Empty string - i.e. disabled</i>
  9497  			 *  @dtopt Language
  9498  			 * 
  9499  			 *  @example
  9500  			 *    $(document).ready( function() {
  9501  			 *      $('#example').dataTable( {
  9502  			 *        "oLanguage": {
  9503  			 *          "sUrl": "http://www.sprymedia.co.uk/dataTables/lang.txt"
  9504  			 *        }
  9505  			 *      } );
  9506  			 *    } );
  9507  			 */
  9508  			"sUrl": "",
  9509  		
  9510  		
  9511  			/**
  9512  			 * Text shown inside the table records when the is no information to be
  9513  			 * displayed after filtering. sEmptyTable is shown when there is simply no
  9514  			 * information in the table at all (regardless of filtering).
  9515  			 *  @type string
  9516  			 *  @default No matching records found
  9517  			 *  @dtopt Language
  9518  			 * 
  9519  			 *  @example
  9520  			 *    $(document).ready( function() {
  9521  			 *      $('#example').dataTable( {
  9522  			 *        "oLanguage": {
  9523  			 *          "sZeroRecords": "No records to display"
  9524  			 *        }
  9525  			 *      } );
  9526  			 *    } );
  9527  			 */
  9528  			"sZeroRecords": "没有搜索到匹配的数据记录"
  9529  		},
  9530  	
  9531  	
  9532  		/**
  9533  		 * This parameter allows you to have define the global filtering state at
  9534  		 * initialisation time. As an object the "sSearch" parameter must be
  9535  		 * defined, but all other parameters are optional. When "bRegex" is true,
  9536  		 * the search string will be treated as a regular expression, when false
  9537  		 * (default) it will be treated as a straight string. When "bSmart"
  9538  		 * DataTables will use it's smart filtering methods (to word match at
  9539  		 * any point in the data), when false this will not be done.
  9540  		 *  @namespace
  9541  		 *  @extends DataTable.models.oSearch
  9542  		 *  @dtopt Options
  9543  		 * 
  9544  		 *  @example
  9545  		 *    $(document).ready( function() {
  9546  		 *      $('#example').dataTable( {
  9547  		 *        "oSearch": {"sSearch": "Initial search"}
  9548  		 *      } );
  9549  		 *    } )
  9550  		 */
  9551  		"oSearch": $.extend( {}, DataTable.models.oSearch ),
  9552  	
  9553  	
  9554  		/**
  9555  		 * By default DataTables will look for the property 'aaData' when obtaining
  9556  		 * data from an Ajax source or for server-side processing - this parameter
  9557  		 * allows that property to be changed. You can use Javascript dotted object
  9558  		 * notation to get a data source for multiple levels of nesting.
  9559  		 *  @type string
  9560  		 *  @default aaData
  9561  		 *  @dtopt Options
  9562  		 *  @dtopt Server-side
  9563  		 * 
  9564  		 *  @example
  9565  		 *    // Get data from { "data": [...] }
  9566  		 *    $(document).ready( function() {
  9567  		 *      var oTable = $('#example').dataTable( {
  9568  		 *        "sAjaxSource": "sources/data.txt",
  9569  		 *        "sAjaxDataProp": "data"
  9570  		 *      } );
  9571  		 *    } );
  9572  		 *    
  9573  		 *  @example
  9574  		 *    // Get data from { "data": { "inner": [...] } }
  9575  		 *    $(document).ready( function() {
  9576  		 *      var oTable = $('#example').dataTable( {
  9577  		 *        "sAjaxSource": "sources/data.txt",
  9578  		 *        "sAjaxDataProp": "data.inner"
  9579  		 *      } );
  9580  		 *    } );
  9581  		 */
  9582  		"sAjaxDataProp": "aaData",
  9583  	
  9584  	
  9585  		/**
  9586  		 * You can instruct DataTables to load data from an external source using this
  9587  		 * parameter (use aData if you want to pass data in you already have). Simply
  9588  		 * provide a url a JSON object can be obtained from. This object must include
  9589  		 * the parameter 'aaData' which is the data source for the table.
  9590  		 *  @type string
  9591  		 *  @default null
  9592  		 *  @dtopt Options
  9593  		 *  @dtopt Server-side
  9594  		 * 
  9595  		 *  @example
  9596  		 *    $(document).ready( function() {
  9597  		 *      $('#example').dataTable( {
  9598  		 *        "sAjaxSource": "http://www.sprymedia.co.uk/dataTables/json.php"
  9599  		 *      } );
  9600  		 *    } )
  9601  		 */
  9602  		"sAjaxSource": null,
  9603  	
  9604  	
  9605  		/**
  9606  		 * This parameter can be used to override the default prefix that DataTables
  9607  		 * assigns to a cookie when state saving is enabled.
  9608  		 *  @type string
  9609  		 *  @default SpryMedia_DataTables_
  9610  		 *  @dtopt Options
  9611  		 * 
  9612  		 *  @example
  9613  		 *    $(document).ready( function() {
  9614  		 *      $('#example').dataTable( {
  9615  		 *        "sCookiePrefix": "my_datatable_",
  9616  		 *      } );
  9617  		 *    } );
  9618  		 */
  9619  		"sCookiePrefix": "SpryMedia_DataTables_",
  9620  	
  9621  	
  9622  		/**
  9623  		 * This initialisation variable allows you to specify exactly where in the
  9624  		 * DOM you want DataTables to inject the various controls it adds to the page
  9625  		 * (for example you might want the pagination controls at the top of the
  9626  		 * table). DIV elements (with or without a custom class) can also be added to
  9627  		 * aid styling. The follow syntax is used:
  9628  		 *   <ul>
  9629  		 *     <li>The following options are allowed:	
  9630  		 *       <ul>
  9631  		 *         <li>'l' - Length changing</li
  9632  		 *         <li>'f' - Filtering input</li>
  9633  		 *         <li>'t' - The table!</li>
  9634  		 *         <li>'i' - Information</li>
  9635  		 *         <li>'p' - Pagination</li>
  9636  		 *         <li>'r' - pRocessing</li>
  9637  		 *       </ul>
  9638  		 *     </li>
  9639  		 *     <li>The following constants are allowed:
  9640  		 *       <ul>
  9641  		 *         <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
  9642  		 *         <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
  9643  		 *       </ul>
  9644  		 *     </li>
  9645  		 *     <li>The following syntax is expected:
  9646  		 *       <ul>
  9647  		 *         <li>'&lt;' and '&gt;' - div elements</li>
  9648  		 *         <li>'&lt;"class" and '&gt;' - div with a class</li>
  9649  		 *         <li>'&lt;"#id" and '&gt;' - div with an ID</li>
  9650  		 *       </ul>
  9651  		 *     </li>
  9652  		 *     <li>Examples:
  9653  		 *       <ul>
  9654  		 *         <li>'&lt;"wrapper"flipt&gt;'</li>
  9655  		 *         <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
  9656  		 *       </ul>
  9657  		 *     </li>
  9658  		 *   </ul>
  9659  		 *  @type string
  9660  		 *  @default lfrtip <i>(when bJQueryUI is false)</i> <b>or</b> 
  9661  		 *    <"H"lfr>t<"F"ip> <i>(when bJQueryUI is true)</i>
  9662  		 *  @dtopt Options
  9663  		 * 
  9664  		 *  @example
  9665  		 *    $(document).ready( function() {
  9666  		 *      $('#example').dataTable( {
  9667  		 *        "sDom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
  9668  		 *      } );
  9669  		 *    } );
  9670  		 */
  9671  		"sDom": "lfrtip",
  9672  	
  9673  	
  9674  		/**
  9675  		 * DataTables features two different built-in pagination interaction methods
  9676  		 * ('two_button' or 'full_numbers') which present different page controls to
  9677  		 * the end user. Further methods can be added using the API (see below).
  9678  		 *  @type string
  9679  		 *  @default two_button
  9680  		 *  @dtopt Options
  9681  		 * 
  9682  		 *  @example
  9683  		 *    $(document).ready( function() {
  9684  		 *      $('#example').dataTable( {
  9685  		 *        "sPaginationType": "full_numbers"
  9686  		 *      } );
  9687  		 *    } )
  9688  		 */
  9689  		"sPaginationType": "two_button",
  9690  	
  9691  	
  9692  		/**
  9693  		 * Enable horizontal scrolling. When a table is too wide to fit into a certain
  9694  		 * layout, or you have a large number of columns in the table, you can enable
  9695  		 * x-scrolling to show the table in a viewport, which can be scrolled. This
  9696  		 * property can be any CSS unit, or a number (in which case it will be treated
  9697  		 * as a pixel measurement).
  9698  		 *  @type string
  9699  		 *  @default <i>blank string - i.e. disabled</i>
  9700  		 *  @dtopt Features
  9701  		 * 
  9702  		 *  @example
  9703  		 *    $(document).ready( function() {
  9704  		 *      $('#example').dataTable( {
  9705  		 *        "sScrollX": "100%",
  9706  		 *        "bScrollCollapse": true
  9707  		 *      } );
  9708  		 *    } );
  9709  		 */
  9710  		"sScrollX": "",
  9711  	
  9712  	
  9713  		/**
  9714  		 * This property can be used to force a DataTable to use more width than it
  9715  		 * might otherwise do when x-scrolling is enabled. For example if you have a
  9716  		 * table which requires to be well spaced, this parameter is useful for
  9717  		 * "over-sizing" the table, and thus forcing scrolling. This property can by
  9718  		 * any CSS unit, or a number (in which case it will be treated as a pixel
  9719  		 * measurement).
  9720  		 *  @type string
  9721  		 *  @default <i>blank string - i.e. disabled</i>
  9722  		 *  @dtopt Options
  9723  		 * 
  9724  		 *  @example
  9725  		 *    $(document).ready( function() {
  9726  		 *      $('#example').dataTable( {
  9727  		 *        "sScrollX": "100%",
  9728  		 *        "sScrollXInner": "110%"
  9729  		 *      } );
  9730  		 *    } );
  9731  		 */
  9732  		"sScrollXInner": "",
  9733  	
  9734  	
  9735  		/**
  9736  		 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
  9737  		 * to the given height, and enable scrolling for any data which overflows the
  9738  		 * current viewport. This can be used as an alternative to paging to display
  9739  		 * a lot of data in a small area (although paging and scrolling can both be
  9740  		 * enabled at the same time). This property can be any CSS unit, or a number
  9741  		 * (in which case it will be treated as a pixel measurement).
  9742  		 *  @type string
  9743  		 *  @default <i>blank string - i.e. disabled</i>
  9744  		 *  @dtopt Features
  9745  		 * 
  9746  		 *  @example
  9747  		 *    $(document).ready( function() {
  9748  		 *      $('#example').dataTable( {
  9749  		 *        "sScrollY": "200px",
  9750  		 *        "bPaginate": false
  9751  		 *      } );
  9752  		 *    } );
  9753  		 */
  9754  		"sScrollY": "",
  9755  	
  9756  	
  9757  		/**
  9758  		 * Set the HTTP method that is used to make the Ajax call for server-side
  9759  		 * processing or Ajax sourced data.
  9760  		 *  @type string
  9761  		 *  @default GET
  9762  		 *  @dtopt Options
  9763  		 *  @dtopt Server-side
  9764  		 * 
  9765  		 *  @example
  9766  		 *    $(document).ready( function() {
  9767  		 *      $('#example').dataTable( {
  9768  		 *        "bServerSide": true,
  9769  		 *        "sAjaxSource": "scripts/post.php",
  9770  		 *        "sServerMethod": "POST"
  9771  		 *      } );
  9772  		 *    } );
  9773  		 */
  9774  		"sServerMethod": "GET"
  9775  	};
  9776  	
  9777  	
  9778  	
  9779  	/**
  9780  	 * Column options that can be given to DataTables at initialisation time.
  9781  	 *  @namespace
  9782  	 */
  9783  	DataTable.defaults.columns = {
  9784  		/**
  9785  		 * Allows a column's sorting to take multiple columns into account when 
  9786  		 * doing a sort. For example first name / last name columns make sense to 
  9787  		 * do a multi-column sort over the two columns.
  9788  		 *  @type array
  9789  		 *  @default null <i>Takes the value of the column index automatically</i>
  9790  		 *  @dtopt Columns
  9791  		 * 
  9792  		 *  @example
  9793  		 *    // Using aoColumnDefs
  9794  		 *    $(document).ready( function() {
  9795  		 *      $('#example').dataTable( {
  9796  		 *        "aoColumnDefs": [
  9797  		 *          { "aDataSort": [ 0, 1 ], "aTargets": [ 0 ] },
  9798  		 *          { "aDataSort": [ 1, 0 ], "aTargets": [ 1 ] },
  9799  		 *          { "aDataSort": [ 2, 3, 4 ], "aTargets": [ 2 ] }
  9800  		 *        ]
  9801  		 *      } );
  9802  		 *    } );
  9803  		 *    
  9804  		 *  @example
  9805  		 *    // Using aoColumns
  9806  		 *    $(document).ready( function() {
  9807  		 *      $('#example').dataTable( {
  9808  		 *        "aoColumns": [
  9809  		 *          { "aDataSort": [ 0, 1 ] },
  9810  		 *          { "aDataSort": [ 1, 0 ] },
  9811  		 *          { "aDataSort": [ 2, 3, 4 ] },
  9812  		 *          null,
  9813  		 *          null
  9814  		 *        ]
  9815  		 *      } );
  9816  		 *    } );
  9817  		 */
  9818  		"aDataSort": null,
  9819  	
  9820  	
  9821  		/**
  9822  		 * You can control the default sorting direction, and even alter the behaviour
  9823  		 * of the sort handler (i.e. only allow ascending sorting etc) using this
  9824  		 * parameter.
  9825  		 *  @type array
  9826  		 *  @default [ 'asc', 'desc' ]
  9827  		 *  @dtopt Columns
  9828  		 * 
  9829  		 *  @example
  9830  		 *    // Using aoColumnDefs
  9831  		 *    $(document).ready( function() {
  9832  		 *      $('#example').dataTable( {
  9833  		 *        "aoColumnDefs": [
  9834  		 *          { "asSorting": [ "asc" ], "aTargets": [ 1 ] },
  9835  		 *          { "asSorting": [ "desc", "asc", "asc" ], "aTargets": [ 2 ] },
  9836  		 *          { "asSorting": [ "desc" ], "aTargets": [ 3 ] }
  9837  		 *        ]
  9838  		 *      } );
  9839  		 *    } );
  9840  		 *    
  9841  		 *  @example
  9842  		 *    // Using aoColumns
  9843  		 *    $(document).ready( function() {
  9844  		 *      $('#example').dataTable( {
  9845  		 *        "aoColumns": [
  9846  		 *          null,
  9847  		 *          { "asSorting": [ "asc" ] },
  9848  		 *          { "asSorting": [ "desc", "asc", "asc" ] },
  9849  		 *          { "asSorting": [ "desc" ] },
  9850  		 *          null
  9851  		 *        ]
  9852  		 *      } );
  9853  		 *    } );
  9854  		 */
  9855  		"asSorting": [ 'asc', 'desc' ],
  9856  	
  9857  	
  9858  		/**
  9859  		 * Enable or disable filtering on the data in this column.
  9860  		 *  @type boolean
  9861  		 *  @default true
  9862  		 *  @dtopt Columns
  9863  		 * 
  9864  		 *  @example
  9865  		 *    // Using aoColumnDefs
  9866  		 *    $(document).ready( function() {
  9867  		 *      $('#example').dataTable( {
  9868  		 *        "aoColumnDefs": [ 
  9869  		 *          { "bSearchable": false, "aTargets": [ 0 ] }
  9870  		 *        ] } );
  9871  		 *    } );
  9872  		 *    
  9873  		 *  @example
  9874  		 *    // Using aoColumns
  9875  		 *    $(document).ready( function() {
  9876  		 *      $('#example').dataTable( {
  9877  		 *        "aoColumns": [ 
  9878  		 *          { "bSearchable": false },
  9879  		 *          null,
  9880  		 *          null,
  9881  		 *          null,
  9882  		 *          null
  9883  		 *        ] } );
  9884  		 *    } );
  9885  		 */
  9886  		"bSearchable": true,
  9887  	
  9888  	
  9889  		/**
  9890  		 * Enable or disable sorting on this column.
  9891  		 *  @type boolean
  9892  		 *  @default true
  9893  		 *  @dtopt Columns
  9894  		 * 
  9895  		 *  @example
  9896  		 *    // Using aoColumnDefs
  9897  		 *    $(document).ready( function() {
  9898  		 *      $('#example').dataTable( {
  9899  		 *        "aoColumnDefs": [ 
  9900  		 *          { "bSortable": false, "aTargets": [ 0 ] }
  9901  		 *        ] } );
  9902  		 *    } );
  9903  		 *    
  9904  		 *  @example
  9905  		 *    // Using aoColumns
  9906  		 *    $(document).ready( function() {
  9907  		 *      $('#example').dataTable( {
  9908  		 *        "aoColumns": [ 
  9909  		 *          { "bSortable": false },
  9910  		 *          null,
  9911  		 *          null,
  9912  		 *          null,
  9913  		 *          null
  9914  		 *        ] } );
  9915  		 *    } );
  9916  		 */
  9917  		"bSortable": true,
  9918  	
  9919  	
  9920  		/**
  9921  		 * <code>Deprecated</code> When using fnRender() for a column, you may wish 
  9922  		 * to use the original data (before rendering) for sorting and filtering 
  9923  		 * (the default is to used the rendered data that the user can see). This 
  9924  		 * may be useful for dates etc.
  9925  		 * 
  9926  		 * Please note that this option has now been deprecated and will be removed
  9927  		 * in the next version of DataTables. Please use mRender / mData rather than
  9928  		 * fnRender.
  9929  		 *  @type boolean
  9930  		 *  @default true
  9931  		 *  @dtopt Columns
  9932  		 *  @deprecated
  9933  		 */
  9934  		"bUseRendered": true,
  9935  	
  9936  	
  9937  		/**
  9938  		 * Enable or disable the display of this column.
  9939  		 *  @type boolean
  9940  		 *  @default true
  9941  		 *  @dtopt Columns
  9942  		 * 
  9943  		 *  @example
  9944  		 *    // Using aoColumnDefs
  9945  		 *    $(document).ready( function() {
  9946  		 *      $('#example').dataTable( {
  9947  		 *        "aoColumnDefs": [ 
  9948  		 *          { "bVisible": false, "aTargets": [ 0 ] }
  9949  		 *        ] } );
  9950  		 *    } );
  9951  		 *    
  9952  		 *  @example
  9953  		 *    // Using aoColumns
  9954  		 *    $(document).ready( function() {
  9955  		 *      $('#example').dataTable( {
  9956  		 *        "aoColumns": [ 
  9957  		 *          { "bVisible": false },
  9958  		 *          null,
  9959  		 *          null,
  9960  		 *          null,
  9961  		 *          null
  9962  		 *        ] } );
  9963  		 *    } );
  9964  		 */
  9965  		"bVisible": true,
  9966  		
  9967  		
  9968  		/**
  9969  		 * Developer definable function that is called whenever a cell is created (Ajax source,
  9970  		 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
  9971  		 * allowing you to modify the DOM element (add background colour for example) when the
  9972  		 * element is available.
  9973  		 *  @type function
  9974  		 *  @param {element} nTd The TD node that has been created
  9975  		 *  @param {*} sData The Data for the cell
  9976  		 *  @param {array|object} oData The data for the whole row
  9977  		 *  @param {int} iRow The row index for the aoData data store
  9978  		 *  @param {int} iCol The column index for aoColumns
  9979  		 *  @dtopt Columns
  9980  		 * 
  9981  		 *  @example
  9982  		 *    $(document).ready( function() {
  9983  		 *      $('#example').dataTable( {
  9984  		 *        "aoColumnDefs": [ {
  9985  		 *          "aTargets": [3],
  9986  		 *          "fnCreatedCell": function (nTd, sData, oData, iRow, iCol) {
  9987  		 *            if ( sData == "1.7" ) {
  9988  		 *              $(nTd).css('color', 'blue')
  9989  		 *            }
  9990  		 *          }
  9991  		 *        } ]
  9992  		 *      });
  9993  		 *    } );
  9994  		 */
  9995  		"fnCreatedCell": null,
  9996  	
  9997  	
  9998  		/**
  9999  		 * <code>Deprecated</code> Custom display function that will be called for the 
 10000  		 * display of each cell in this column.
 10001  		 *
 10002  		 * Please note that this option has now been deprecated and will be removed
 10003  		 * in the next version of DataTables. Please use mRender / mData rather than
 10004  		 * fnRender.
 10005  		 *  @type function
 10006  		 *  @param {object} o Object with the following parameters:
 10007  		 *  @param {int}    o.iDataRow The row in aoData
 10008  		 *  @param {int}    o.iDataColumn The column in question
 10009  		 *  @param {array}  o.aData The data for the row in question
 10010  		 *  @param {object} o.oSettings The settings object for this DataTables instance
 10011  		 *  @param {object} o.mDataProp The data property used for this column
 10012  		 *  @param {*}      val The current cell value
 10013  		 *  @returns {string} The string you which to use in the display
 10014  		 *  @dtopt Columns
 10015  		 *  @deprecated
 10016  		 */
 10017  		"fnRender": null,
 10018  	
 10019  	
 10020  		/**
 10021  		 * The column index (starting from 0!) that you wish a sort to be performed
 10022  		 * upon when this column is selected for sorting. This can be used for sorting
 10023  		 * on hidden columns for example.
 10024  		 *  @type int
 10025  		 *  @default -1 <i>Use automatically calculated column index</i>
 10026  		 *  @dtopt Columns
 10027  		 * 
 10028  		 *  @example
 10029  		 *    // Using aoColumnDefs
 10030  		 *    $(document).ready( function() {
 10031  		 *      $('#example').dataTable( {
 10032  		 *        "aoColumnDefs": [ 
 10033  		 *          { "iDataSort": 1, "aTargets": [ 0 ] }
 10034  		 *        ]
 10035  		 *      } );
 10036  		 *    } );
 10037  		 *    
 10038  		 *  @example
 10039  		 *    // Using aoColumns
 10040  		 *    $(document).ready( function() {
 10041  		 *      $('#example').dataTable( {
 10042  		 *        "aoColumns": [ 
 10043  		 *          { "iDataSort": 1 },
 10044  		 *          null,
 10045  		 *          null,
 10046  		 *          null,
 10047  		 *          null
 10048  		 *        ]
 10049  		 *      } );
 10050  		 *    } );
 10051  		 */
 10052  		"iDataSort": -1,
 10053  	
 10054  	
 10055  		/**
 10056  		 * This parameter has been replaced by mData in DataTables to ensure naming
 10057  		 * consistency. mDataProp can still be used, as there is backwards compatibility
 10058  		 * in DataTables for this option, but it is strongly recommended that you use
 10059  		 * mData in preference to mDataProp.
 10060  		 *  @name DataTable.defaults.columns.mDataProp
 10061  		 */
 10062  	
 10063  	
 10064  		/**
 10065  		 * This property can be used to read data from any JSON data source property,
 10066  		 * including deeply nested objects / properties. mData can be given in a
 10067  		 * number of different ways which effect its behaviour:
 10068  		 *   <ul>
 10069  		 *     <li>integer - treated as an array index for the data source. This is the
 10070  		 *       default that DataTables uses (incrementally increased for each column).</li>
 10071  		 *     <li>string - read an object property from the data source. Note that you can
 10072  		 *       use Javascript dotted notation to read deep properties / arrays from the
 10073  		 *       data source.</li>
 10074  		 *     <li>null - the sDefaultContent option will be used for the cell (null
 10075  		 *       by default, so you will need to specify the default content you want -
 10076  		 *       typically an empty string). This can be useful on generated columns such 
 10077  		 *       as edit / delete action columns.</li>
 10078  		 *     <li>function - the function given will be executed whenever DataTables 
 10079  		 *       needs to set or get the data for a cell in the column. The function 
 10080  		 *       takes three parameters:
 10081  		 *       <ul>
 10082  		 *         <li>{array|object} The data source for the row</li>
 10083  		 *         <li>{string} The type call data requested - this will be 'set' when
 10084  		 *           setting data or 'filter', 'display', 'type', 'sort' or undefined when 
 10085  		 *           gathering data. Note that when <i>undefined</i> is given for the type
 10086  		 *           DataTables expects to get the raw data for the object back</li>
 10087  		 *         <li>{*} Data to set when the second parameter is 'set'.</li>
 10088  		 *       </ul>
 10089  		 *       The return value from the function is not required when 'set' is the type
 10090  		 *       of call, but otherwise the return is what will be used for the data
 10091  		 *       requested.</li>
 10092  		 *    </ul>
 10093  		 *
 10094  		 * Note that prior to DataTables 1.9.2 mData was called mDataProp. The name change
 10095  		 * reflects the flexibility of this property and is consistent with the naming of
 10096  		 * mRender. If 'mDataProp' is given, then it will still be used by DataTables, as
 10097  		 * it automatically maps the old name to the new if required.
 10098  		 *  @type string|int|function|null
 10099  		 *  @default null <i>Use automatically calculated column index</i>
 10100  		 *  @dtopt Columns
 10101  		 * 
 10102  		 *  @example
 10103  		 *    // Read table data from objects
 10104  		 *    $(document).ready( function() {
 10105  		 *      var oTable = $('#example').dataTable( {
 10106  		 *        "sAjaxSource": "sources/deep.txt",
 10107  		 *        "aoColumns": [
 10108  		 *          { "mData": "engine" },
 10109  		 *          { "mData": "browser" },
 10110  		 *          { "mData": "platform.inner" },
 10111  		 *          { "mData": "platform.details.0" },
 10112  		 *          { "mData": "platform.details.1" }
 10113  		 *        ]
 10114  		 *      } );
 10115  		 *    } );
 10116  		 * 
 10117  		 *  @example
 10118  		 *    // Using mData as a function to provide different information for
 10119  		 *    // sorting, filtering and display. In this case, currency (price)
 10120  		 *    $(document).ready( function() {
 10121  		 *      var oTable = $('#example').dataTable( {
 10122  		 *        "aoColumnDefs": [ {
 10123  		 *          "aTargets": [ 0 ],
 10124  		 *          "mData": function ( source, type, val ) {
 10125  		 *            if (type === 'set') {
 10126  		 *              source.price = val;
 10127  		 *              // Store the computed dislay and filter values for efficiency
 10128  		 *              source.price_display = val=="" ? "" : "$"+numberFormat(val);
 10129  		 *              source.price_filter  = val=="" ? "" : "$"+numberFormat(val)+" "+val;
 10130  		 *              return;
 10131  		 *            }
 10132  		 *            else if (type === 'display') {
 10133  		 *              return source.price_display;
 10134  		 *            }
 10135  		 *            else if (type === 'filter') {
 10136  		 *              return source.price_filter;
 10137  		 *            }
 10138  		 *            // 'sort', 'type' and undefined all just use the integer
 10139  		 *            return source.price;
 10140  		 *          }
 10141  		 *        } ]
 10142  		 *      } );
 10143  		 *    } );
 10144  		 */
 10145  		"mData": null,
 10146  	
 10147  	
 10148  		/**
 10149  		 * This property is the rendering partner to mData and it is suggested that
 10150  		 * when you want to manipulate data for display (including filtering, sorting etc)
 10151  		 * but not altering the underlying data for the table, use this property. mData
 10152  		 * can actually do everything this property can and more, but this parameter is
 10153  		 * easier to use since there is no 'set' option. Like mData is can be given
 10154  		 * in a number of different ways to effect its behaviour, with the addition of 
 10155  		 * supporting array syntax for easy outputting of arrays (including arrays of
 10156  		 * objects):
 10157  		 *   <ul>
 10158  		 *     <li>integer - treated as an array index for the data source. This is the
 10159  		 *       default that DataTables uses (incrementally increased for each column).</li>
 10160  		 *     <li>string - read an object property from the data source. Note that you can
 10161  		 *       use Javascript dotted notation to read deep properties / arrays from the
 10162  		 *       data source and also array brackets to indicate that the data reader should
 10163  		 *       loop over the data source array. When characters are given between the array
 10164  		 *       brackets, these characters are used to join the data source array together.
 10165  		 *       For example: "accounts[, ].name" would result in a comma separated list with
 10166  		 *       the 'name' value from the 'accounts' array of objects.</li>
 10167  		 *     <li>function - the function given will be executed whenever DataTables 
 10168  		 *       needs to set or get the data for a cell in the column. The function 
 10169  		 *       takes three parameters:
 10170  		 *       <ul>
 10171  		 *         <li>{array|object} The data source for the row (based on mData)</li>
 10172  		 *         <li>{string} The type call data requested - this will be 'filter', 'display', 
 10173  		 *           'type' or 'sort'.</li>
 10174  		 *         <li>{array|object} The full data source for the row (not based on mData)</li>
 10175  		 *       </ul>
 10176  		 *       The return value from the function is what will be used for the data
 10177  		 *       requested.</li>
 10178  		 *    </ul>
 10179  		 *  @type string|int|function|null
 10180  		 *  @default null <i>Use mData</i>
 10181  		 *  @dtopt Columns
 10182  		 * 
 10183  		 *  @example
 10184  		 *    // Create a comma separated list from an array of objects
 10185  		 *    $(document).ready( function() {
 10186  		 *      var oTable = $('#example').dataTable( {
 10187  		 *        "sAjaxSource": "sources/deep.txt",
 10188  		 *        "aoColumns": [
 10189  		 *          { "mData": "engine" },
 10190  		 *          { "mData": "browser" },
 10191  		 *          {
 10192  		 *            "mData": "platform",
 10193  		 *            "mRender": "[, ].name"
 10194  		 *          }
 10195  		 *        ]
 10196  		 *      } );
 10197  		 *    } );
 10198  		 * 
 10199  		 *  @example
 10200  		 *    // Use as a function to create a link from the data source
 10201  		 *    $(document).ready( function() {
 10202  		 *      var oTable = $('#example').dataTable( {
 10203  		 *        "aoColumnDefs": [
 10204  		 *        {
 10205  		 *          "aTargets": [ 0 ],
 10206  		 *          "mData": "download_link",
 10207  		 *          "mRender": function ( data, type, full ) {
 10208  		 *            return '<a href="'+data+'">Download</a>';
 10209  		 *          }
 10210  		 *        ]
 10211  		 *      } );
 10212  		 *    } );
 10213  		 */
 10214  		"mRender": null,
 10215  	
 10216  	
 10217  		/**
 10218  		 * Change the cell type created for the column - either TD cells or TH cells. This
 10219  		 * can be useful as TH cells have semantic meaning in the table body, allowing them
 10220  		 * to act as a header for a row (you may wish to add scope='row' to the TH elements).
 10221  		 *  @type string
 10222  		 *  @default td
 10223  		 *  @dtopt Columns
 10224  		 * 
 10225  		 *  @example
 10226  		 *    // Make the first column use TH cells
 10227  		 *    $(document).ready( function() {
 10228  		 *      var oTable = $('#example').dataTable( {
 10229  		 *        "aoColumnDefs": [ {
 10230  		 *          "aTargets": [ 0 ],
 10231  		 *          "sCellType": "th"
 10232  		 *        } ]
 10233  		 *      } );
 10234  		 *    } );
 10235  		 */
 10236  		"sCellType": "td",
 10237  	
 10238  	
 10239  		/**
 10240  		 * Class to give to each cell in this column.
 10241  		 *  @type string
 10242  		 *  @default <i>Empty string</i>
 10243  		 *  @dtopt Columns
 10244  		 * 
 10245  		 *  @example
 10246  		 *    // Using aoColumnDefs
 10247  		 *    $(document).ready( function() {
 10248  		 *      $('#example').dataTable( {
 10249  		 *        "aoColumnDefs": [ 
 10250  		 *          { "sClass": "my_class", "aTargets": [ 0 ] }
 10251  		 *        ]
 10252  		 *      } );
 10253  		 *    } );
 10254  		 *    
 10255  		 *  @example
 10256  		 *    // Using aoColumns
 10257  		 *    $(document).ready( function() {
 10258  		 *      $('#example').dataTable( {
 10259  		 *        "aoColumns": [ 
 10260  		 *          { "sClass": "my_class" },
 10261  		 *          null,
 10262  		 *          null,
 10263  		 *          null,
 10264  		 *          null
 10265  		 *        ]
 10266  		 *      } );
 10267  		 *    } );
 10268  		 */
 10269  		"sClass": "",
 10270  		
 10271  		/**
 10272  		 * When DataTables calculates the column widths to assign to each column,
 10273  		 * it finds the longest string in each column and then constructs a
 10274  		 * temporary table and reads the widths from that. The problem with this
 10275  		 * is that "mmm" is much wider then "iiii", but the latter is a longer 
 10276  		 * string - thus the calculation can go wrong (doing it properly and putting
 10277  		 * it into an DOM object and measuring that is horribly(!) slow). Thus as
 10278  		 * a "work around" we provide this option. It will append its value to the
 10279  		 * text that is found to be the longest string for the column - i.e. padding.
 10280  		 * Generally you shouldn't need this, and it is not documented on the 
 10281  		 * general DataTables.net documentation
 10282  		 *  @type string
 10283  		 *  @default <i>Empty string<i>
 10284  		 *  @dtopt Columns
 10285  		 *    
 10286  		 *  @example
 10287  		 *    // Using aoColumns
 10288  		 *    $(document).ready( function() {
 10289  		 *      $('#example').dataTable( {
 10290  		 *        "aoColumns": [ 
 10291  		 *          null,
 10292  		 *          null,
 10293  		 *          null,
 10294  		 *          {
 10295  		 *            "sContentPadding": "mmm"
 10296  		 *          }
 10297  		 *        ]
 10298  		 *      } );
 10299  		 *    } );
 10300  		 */
 10301  		"sContentPadding": "",
 10302  	
 10303  	
 10304  		/**
 10305  		 * Allows a default value to be given for a column's data, and will be used
 10306  		 * whenever a null data source is encountered (this can be because mData
 10307  		 * is set to null, or because the data source itself is null).
 10308  		 *  @type string
 10309  		 *  @default null
 10310  		 *  @dtopt Columns
 10311  		 * 
 10312  		 *  @example
 10313  		 *    // Using aoColumnDefs
 10314  		 *    $(document).ready( function() {
 10315  		 *      $('#example').dataTable( {
 10316  		 *        "aoColumnDefs": [ 
 10317  		 *          {
 10318  		 *            "mData": null,
 10319  		 *            "sDefaultContent": "Edit",
 10320  		 *            "aTargets": [ -1 ]
 10321  		 *          }
 10322  		 *        ]
 10323  		 *      } );
 10324  		 *    } );
 10325  		 *    
 10326  		 *  @example
 10327  		 *    // Using aoColumns
 10328  		 *    $(document).ready( function() {
 10329  		 *      $('#example').dataTable( {
 10330  		 *        "aoColumns": [ 
 10331  		 *          null,
 10332  		 *          null,
 10333  		 *          null,
 10334  		 *          {
 10335  		 *            "mData": null,
 10336  		 *            "sDefaultContent": "Edit"
 10337  		 *          }
 10338  		 *        ]
 10339  		 *      } );
 10340  		 *    } );
 10341  		 */
 10342  		"sDefaultContent": null,
 10343  	
 10344  	
 10345  		/**
 10346  		 * This parameter is only used in DataTables' server-side processing. It can
 10347  		 * be exceptionally useful to know what columns are being displayed on the
 10348  		 * client side, and to map these to database fields. When defined, the names
 10349  		 * also allow DataTables to reorder information from the server if it comes
 10350  		 * back in an unexpected order (i.e. if you switch your columns around on the
 10351  		 * client-side, your server-side code does not also need updating).
 10352  		 *  @type string
 10353  		 *  @default <i>Empty string</i>
 10354  		 *  @dtopt Columns
 10355  		 * 
 10356  		 *  @example
 10357  		 *    // Using aoColumnDefs
 10358  		 *    $(document).ready( function() {
 10359  		 *      $('#example').dataTable( {
 10360  		 *        "aoColumnDefs": [ 
 10361  		 *          { "sName": "engine", "aTargets": [ 0 ] },
 10362  		 *          { "sName": "browser", "aTargets": [ 1 ] },
 10363  		 *          { "sName": "platform", "aTargets": [ 2 ] },
 10364  		 *          { "sName": "version", "aTargets": [ 3 ] },
 10365  		 *          { "sName": "grade", "aTargets": [ 4 ] }
 10366  		 *        ]
 10367  		 *      } );
 10368  		 *    } );
 10369  		 *    
 10370  		 *  @example
 10371  		 *    // Using aoColumns
 10372  		 *    $(document).ready( function() {
 10373  		 *      $('#example').dataTable( {
 10374  		 *        "aoColumns": [ 
 10375  		 *          { "sName": "engine" },
 10376  		 *          { "sName": "browser" },
 10377  		 *          { "sName": "platform" },
 10378  		 *          { "sName": "version" },
 10379  		 *          { "sName": "grade" }
 10380  		 *        ]
 10381  		 *      } );
 10382  		 *    } );
 10383  		 */
 10384  		"sName": "",
 10385  	
 10386  	
 10387  		/**
 10388  		 * Defines a data source type for the sorting which can be used to read
 10389  		 * real-time information from the table (updating the internally cached
 10390  		 * version) prior to sorting. This allows sorting to occur on user editable
 10391  		 * elements such as form inputs.
 10392  		 *  @type string
 10393  		 *  @default std
 10394  		 *  @dtopt Columns
 10395  		 * 
 10396  		 *  @example
 10397  		 *    // Using aoColumnDefs
 10398  		 *    $(document).ready( function() {
 10399  		 *      $('#example').dataTable( {
 10400  		 *        "aoColumnDefs": [
 10401  		 *          { "sSortDataType": "dom-text", "aTargets": [ 2, 3 ] },
 10402  		 *          { "sType": "numeric", "aTargets": [ 3 ] },
 10403  		 *          { "sSortDataType": "dom-select", "aTargets": [ 4 ] },
 10404  		 *          { "sSortDataType": "dom-checkbox", "aTargets": [ 5 ] }
 10405  		 *        ]
 10406  		 *      } );
 10407  		 *    } );
 10408  		 *    
 10409  		 *  @example
 10410  		 *    // Using aoColumns
 10411  		 *    $(document).ready( function() {
 10412  		 *      $('#example').dataTable( {
 10413  		 *        "aoColumns": [
 10414  		 *          null,
 10415  		 *          null,
 10416  		 *          { "sSortDataType": "dom-text" },
 10417  		 *          { "sSortDataType": "dom-text", "sType": "numeric" },
 10418  		 *          { "sSortDataType": "dom-select" },
 10419  		 *          { "sSortDataType": "dom-checkbox" }
 10420  		 *        ]
 10421  		 *      } );
 10422  		 *    } );
 10423  		 */
 10424  		"sSortDataType": "std",
 10425  	
 10426  	
 10427  		/**
 10428  		 * The title of this column.
 10429  		 *  @type string
 10430  		 *  @default null <i>Derived from the 'TH' value for this column in the 
 10431  		 *    original HTML table.</i>
 10432  		 *  @dtopt Columns
 10433  		 * 
 10434  		 *  @example
 10435  		 *    // Using aoColumnDefs
 10436  		 *    $(document).ready( function() {
 10437  		 *      $('#example').dataTable( {
 10438  		 *        "aoColumnDefs": [ 
 10439  		 *          { "sTitle": "My column title", "aTargets": [ 0 ] }
 10440  		 *        ]
 10441  		 *      } );
 10442  		 *    } );
 10443  		 *    
 10444  		 *  @example
 10445  		 *    // Using aoColumns
 10446  		 *    $(document).ready( function() {
 10447  		 *      $('#example').dataTable( {
 10448  		 *        "aoColumns": [ 
 10449  		 *          { "sTitle": "My column title" },
 10450  		 *          null,
 10451  		 *          null,
 10452  		 *          null,
 10453  		 *          null
 10454  		 *        ]
 10455  		 *      } );
 10456  		 *    } );
 10457  		 */
 10458  		"sTitle": null,
 10459  	
 10460  	
 10461  		/**
 10462  		 * The type allows you to specify how the data for this column will be sorted.
 10463  		 * Four types (string, numeric, date and html (which will strip HTML tags
 10464  		 * before sorting)) are currently available. Note that only date formats
 10465  		 * understood by Javascript's Date() object will be accepted as type date. For
 10466  		 * example: "Mar 26, 2008 5:03 PM". May take the values: 'string', 'numeric',
 10467  		 * 'date' or 'html' (by default). Further types can be adding through
 10468  		 * plug-ins.
 10469  		 *  @type string
 10470  		 *  @default null <i>Auto-detected from raw data</i>
 10471  		 *  @dtopt Columns
 10472  		 * 
 10473  		 *  @example
 10474  		 *    // Using aoColumnDefs
 10475  		 *    $(document).ready( function() {
 10476  		 *      $('#example').dataTable( {
 10477  		 *        "aoColumnDefs": [ 
 10478  		 *          { "sType": "html", "aTargets": [ 0 ] }
 10479  		 *        ]
 10480  		 *      } );
 10481  		 *    } );
 10482  		 *    
 10483  		 *  @example
 10484  		 *    // Using aoColumns
 10485  		 *    $(document).ready( function() {
 10486  		 *      $('#example').dataTable( {
 10487  		 *        "aoColumns": [ 
 10488  		 *          { "sType": "html" },
 10489  		 *          null,
 10490  		 *          null,
 10491  		 *          null,
 10492  		 *          null
 10493  		 *        ]
 10494  		 *      } );
 10495  		 *    } );
 10496  		 */
 10497  		"sType": null,
 10498  	
 10499  	
 10500  		/**
 10501  		 * Defining the width of the column, this parameter may take any CSS value
 10502  		 * (3em, 20px etc). DataTables apples 'smart' widths to columns which have not
 10503  		 * been given a specific width through this interface ensuring that the table
 10504  		 * remains readable.
 10505  		 *  @type string
 10506  		 *  @default null <i>Automatic</i>
 10507  		 *  @dtopt Columns
 10508  		 * 
 10509  		 *  @example
 10510  		 *    // Using aoColumnDefs
 10511  		 *    $(document).ready( function() {
 10512  		 *      $('#example').dataTable( {
 10513  		 *        "aoColumnDefs": [ 
 10514  		 *          { "sWidth": "20%", "aTargets": [ 0 ] }
 10515  		 *        ]
 10516  		 *      } );
 10517  		 *    } );
 10518  		 *    
 10519  		 *  @example
 10520  		 *    // Using aoColumns
 10521  		 *    $(document).ready( function() {
 10522  		 *      $('#example').dataTable( {
 10523  		 *        "aoColumns": [ 
 10524  		 *          { "sWidth": "20%" },
 10525  		 *          null,
 10526  		 *          null,
 10527  		 *          null,
 10528  		 *          null
 10529  		 *        ]
 10530  		 *      } );
 10531  		 *    } );
 10532  		 */
 10533  		"sWidth": null
 10534  	};
 10535  	
 10536  	
 10537  	
 10538  	/**
 10539  	 * DataTables settings object - this holds all the information needed for a
 10540  	 * given table, including configuration, data and current application of the
 10541  	 * table options. DataTables does not have a single instance for each DataTable
 10542  	 * with the settings attached to that instance, but rather instances of the
 10543  	 * DataTable "class" are created on-the-fly as needed (typically by a 
 10544  	 * $().dataTable() call) and the settings object is then applied to that
 10545  	 * instance.
 10546  	 * 
 10547  	 * Note that this object is related to {@link DataTable.defaults} but this 
 10548  	 * one is the internal data store for DataTables's cache of columns. It should
 10549  	 * NOT be manipulated outside of DataTables. Any configuration should be done
 10550  	 * through the initialisation options.
 10551  	 *  @namespace
 10552  	 *  @todo Really should attach the settings object to individual instances so we
 10553  	 *    don't need to create new instances on each $().dataTable() call (if the
 10554  	 *    table already exists). It would also save passing oSettings around and
 10555  	 *    into every single function. However, this is a very significant 
 10556  	 *    architecture change for DataTables and will almost certainly break
 10557  	 *    backwards compatibility with older installations. This is something that
 10558  	 *    will be done in 2.0.
 10559  	 */
 10560  	DataTable.models.oSettings = {
 10561  		/**
 10562  		 * Primary features of DataTables and their enablement state.
 10563  		 *  @namespace
 10564  		 */
 10565  		"oFeatures": {
 10566  			
 10567  			/**
 10568  			 * Flag to say if DataTables should automatically try to calculate the
 10569  			 * optimum table and columns widths (true) or not (false).
 10570  			 * Note that this parameter will be set by the initialisation routine. To
 10571  			 * set a default use {@link DataTable.defaults}.
 10572  			 *  @type boolean
 10573  			 */
 10574  			"bAutoWidth": null,
 10575  	
 10576  			/**
 10577  			 * Delay the creation of TR and TD elements until they are actually
 10578  			 * needed by a driven page draw. This can give a significant speed
 10579  			 * increase for Ajax source and Javascript source data, but makes no
 10580  			 * difference at all fro DOM and server-side processing tables.
 10581  			 * Note that this parameter will be set by the initialisation routine. To
 10582  			 * set a default use {@link DataTable.defaults}.
 10583  			 *  @type boolean
 10584  			 */
 10585  			"bDeferRender": null,
 10586  			
 10587  			/**
 10588  			 * Enable filtering on the table or not. Note that if this is disabled
 10589  			 * then there is no filtering at all on the table, including fnFilter.
 10590  			 * To just remove the filtering input use sDom and remove the 'f' option.
 10591  			 * Note that this parameter will be set by the initialisation routine. To
 10592  			 * set a default use {@link DataTable.defaults}.
 10593  			 *  @type boolean
 10594  			 */
 10595  			"bFilter": null,
 10596  			
 10597  			/**
 10598  			 * Table information element (the 'Showing x of y records' div) enable
 10599  			 * flag.
 10600  			 * Note that this parameter will be set by the initialisation routine. To
 10601  			 * set a default use {@link DataTable.defaults}.
 10602  			 *  @type boolean
 10603  			 */
 10604  			"bInfo": null,
 10605  			
 10606  			/**
 10607  			 * Present a user control allowing the end user to change the page size
 10608  			 * when pagination is enabled.
 10609  			 * Note that this parameter will be set by the initialisation routine. To
 10610  			 * set a default use {@link DataTable.defaults}.
 10611  			 *  @type boolean
 10612  			 */
 10613  			"bLengthChange": null,
 10614  	
 10615  			/**
 10616  			 * Pagination enabled or not. Note that if this is disabled then length
 10617  			 * changing must also be disabled.
 10618  			 * Note that this parameter will be set by the initialisation routine. To
 10619  			 * set a default use {@link DataTable.defaults}.
 10620  			 *  @type boolean
 10621  			 */
 10622  			"bPaginate": null,
 10623  			
 10624  			/**
 10625  			 * Processing indicator enable flag whenever DataTables is enacting a
 10626  			 * user request - typically an Ajax request for server-side processing.
 10627  			 * Note that this parameter will be set by the initialisation routine. To
 10628  			 * set a default use {@link DataTable.defaults}.
 10629  			 *  @type boolean
 10630  			 */
 10631  			"bProcessing": null,
 10632  			
 10633  			/**
 10634  			 * Server-side processing enabled flag - when enabled DataTables will
 10635  			 * get all data from the server for every draw - there is no filtering,
 10636  			 * sorting or paging done on the client-side.
 10637  			 * Note that this parameter will be set by the initialisation routine. To
 10638  			 * set a default use {@link DataTable.defaults}.
 10639  			 *  @type boolean
 10640  			 */
 10641  			"bServerSide": null,
 10642  			
 10643  			/**
 10644  			 * Sorting enablement flag.
 10645  			 * Note that this parameter will be set by the initialisation routine. To
 10646  			 * set a default use {@link DataTable.defaults}.
 10647  			 *  @type boolean
 10648  			 */
 10649  			"bSort": null,
 10650  			
 10651  			/**
 10652  			 * Apply a class to the columns which are being sorted to provide a
 10653  			 * visual highlight or not. This can slow things down when enabled since
 10654  			 * there is a lot of DOM interaction.
 10655  			 * Note that this parameter will be set by the initialisation routine. To
 10656  			 * set a default use {@link DataTable.defaults}.
 10657  			 *  @type boolean
 10658  			 */
 10659  			"bSortClasses": null,
 10660  			
 10661  			/**
 10662  			 * State saving enablement flag.
 10663  			 * Note that this parameter will be set by the initialisation routine. To
 10664  			 * set a default use {@link DataTable.defaults}.
 10665  			 *  @type boolean
 10666  			 */
 10667  			"bStateSave": null
 10668  		},
 10669  		
 10670  	
 10671  		/**
 10672  		 * Scrolling settings for a table.
 10673  		 *  @namespace
 10674  		 */
 10675  		"oScroll": {
 10676  			/**
 10677  			 * Indicate if DataTables should be allowed to set the padding / margin
 10678  			 * etc for the scrolling header elements or not. Typically you will want
 10679  			 * this.
 10680  			 * Note that this parameter will be set by the initialisation routine. To
 10681  			 * set a default use {@link DataTable.defaults}.
 10682  			 *  @type boolean
 10683  			 */
 10684  			"bAutoCss": null,
 10685  			
 10686  			/**
 10687  			 * When the table is shorter in height than sScrollY, collapse the
 10688  			 * table container down to the height of the table (when true).
 10689  			 * Note that this parameter will be set by the initialisation routine. To
 10690  			 * set a default use {@link DataTable.defaults}.
 10691  			 *  @type boolean
 10692  			 */
 10693  			"bCollapse": null,
 10694  			
 10695  			/**
 10696  			 * Infinite scrolling enablement flag. Now deprecated in favour of
 10697  			 * using the Scroller plug-in.
 10698  			 * Note that this parameter will be set by the initialisation routine. To
 10699  			 * set a default use {@link DataTable.defaults}.
 10700  			 *  @type boolean
 10701  			 */
 10702  			"bInfinite": null,
 10703  			
 10704  			/**
 10705  			 * Width of the scrollbar for the web-browser's platform. Calculated
 10706  			 * during table initialisation.
 10707  			 *  @type int
 10708  			 *  @default 0
 10709  			 */
 10710  			"iBarWidth": 0,
 10711  			
 10712  			/**
 10713  			 * Space (in pixels) between the bottom of the scrolling container and 
 10714  			 * the bottom of the scrolling viewport before the next page is loaded
 10715  			 * when using infinite scrolling.
 10716  			 * Note that this parameter will be set by the initialisation routine. To
 10717  			 * set a default use {@link DataTable.defaults}.
 10718  			 *  @type int
 10719  			 */
 10720  			"iLoadGap": null,
 10721  			
 10722  			/**
 10723  			 * Viewport width for horizontal scrolling. Horizontal scrolling is 
 10724  			 * disabled if an empty string.
 10725  			 * Note that this parameter will be set by the initialisation routine. To
 10726  			 * set a default use {@link DataTable.defaults}.
 10727  			 *  @type string
 10728  			 */
 10729  			"sX": null,
 10730  			
 10731  			/**
 10732  			 * Width to expand the table to when using x-scrolling. Typically you
 10733  			 * should not need to use this.
 10734  			 * Note that this parameter will be set by the initialisation routine. To
 10735  			 * set a default use {@link DataTable.defaults}.
 10736  			 *  @type string
 10737  			 *  @deprecated
 10738  			 */
 10739  			"sXInner": null,
 10740  			
 10741  			/**
 10742  			 * Viewport height for vertical scrolling. Vertical scrolling is disabled
 10743  			 * if an empty string.
 10744  			 * Note that this parameter will be set by the initialisation routine. To
 10745  			 * set a default use {@link DataTable.defaults}.
 10746  			 *  @type string
 10747  			 */
 10748  			"sY": null
 10749  		},
 10750  		
 10751  		/**
 10752  		 * Language information for the table.
 10753  		 *  @namespace
 10754  		 *  @extends DataTable.defaults.oLanguage
 10755  		 */
 10756  		"oLanguage": {
 10757  			/**
 10758  			 * Information callback function. See 
 10759  			 * {@link DataTable.defaults.fnInfoCallback}
 10760  			 *  @type function
 10761  			 *  @default null
 10762  			 */
 10763  			"fnInfoCallback": null
 10764  		},
 10765  		
 10766  		/**
 10767  		 * Browser support parameters
 10768  		 *  @namespace
 10769  		 */
 10770  		"oBrowser": {
 10771  			/**
 10772  			 * Indicate if the browser incorrectly calculates width:100% inside a
 10773  			 * scrolling element (IE6/7)
 10774  			 *  @type boolean
 10775  			 *  @default false
 10776  			 */
 10777  			"bScrollOversize": false
 10778  		},
 10779  		
 10780  		/**
 10781  		 * Array referencing the nodes which are used for the features. The 
 10782  		 * parameters of this object match what is allowed by sDom - i.e.
 10783  		 *   <ul>
 10784  		 *     <li>'l' - Length changing</li>
 10785  		 *     <li>'f' - Filtering input</li>
 10786  		 *     <li>'t' - The table!</li>
 10787  		 *     <li>'i' - Information</li>
 10788  		 *     <li>'p' - Pagination</li>
 10789  		 *     <li>'r' - pRocessing</li>
 10790  		 *   </ul>
 10791  		 *  @type array
 10792  		 *  @default []
 10793  		 */
 10794  		"aanFeatures": [],
 10795  		
 10796  		/**
 10797  		 * Store data information - see {@link DataTable.models.oRow} for detailed
 10798  		 * information.
 10799  		 *  @type array
 10800  		 *  @default []
 10801  		 */
 10802  		"aoData": [],
 10803  		
 10804  		/**
 10805  		 * Array of indexes which are in the current display (after filtering etc)
 10806  		 *  @type array
 10807  		 *  @default []
 10808  		 */
 10809  		"aiDisplay": [],
 10810  		
 10811  		/**
 10812  		 * Array of indexes for display - no filtering
 10813  		 *  @type array
 10814  		 *  @default []
 10815  		 */
 10816  		"aiDisplayMaster": [],
 10817  		
 10818  		/**
 10819  		 * Store information about each column that is in use
 10820  		 *  @type array
 10821  		 *  @default []
 10822  		 */
 10823  		"aoColumns": [],
 10824  		
 10825  		/**
 10826  		 * Store information about the table's header
 10827  		 *  @type array
 10828  		 *  @default []
 10829  		 */
 10830  		"aoHeader": [],
 10831  		
 10832  		/**
 10833  		 * Store information about the table's footer
 10834  		 *  @type array
 10835  		 *  @default []
 10836  		 */
 10837  		"aoFooter": [],
 10838  		
 10839  		/**
 10840  		 * Search data array for regular expression searching
 10841  		 *  @type array
 10842  		 *  @default []
 10843  		 */
 10844  		"asDataSearch": [],
 10845  		
 10846  		/**
 10847  		 * Store the applied global search information in case we want to force a 
 10848  		 * research or compare the old search to a new one.
 10849  		 * Note that this parameter will be set by the initialisation routine. To
 10850  		 * set a default use {@link DataTable.defaults}.
 10851  		 *  @namespace
 10852  		 *  @extends DataTable.models.oSearch
 10853  		 */
 10854  		"oPreviousSearch": {},
 10855  		
 10856  		/**
 10857  		 * Store the applied search for each column - see 
 10858  		 * {@link DataTable.models.oSearch} for the format that is used for the
 10859  		 * filtering information for each column.
 10860  		 *  @type array
 10861  		 *  @default []
 10862  		 */
 10863  		"aoPreSearchCols": [],
 10864  		
 10865  		/**
 10866  		 * Sorting that is applied to the table. Note that the inner arrays are
 10867  		 * used in the following manner:
 10868  		 * <ul>
 10869  		 *   <li>Index 0 - column number</li>
 10870  		 *   <li>Index 1 - current sorting direction</li>
 10871  		 *   <li>Index 2 - index of asSorting for this column</li>
 10872  		 * </ul>
 10873  		 * Note that this parameter will be set by the initialisation routine. To
 10874  		 * set a default use {@link DataTable.defaults}.
 10875  		 *  @type array
 10876  		 *  @todo These inner arrays should really be objects
 10877  		 */
 10878  		"aaSorting": null,
 10879  		
 10880  		/**
 10881  		 * Sorting that is always applied to the table (i.e. prefixed in front of
 10882  		 * aaSorting).
 10883  		 * Note that this parameter will be set by the initialisation routine. To
 10884  		 * set a default use {@link DataTable.defaults}.
 10885  		 *  @type array|null
 10886  		 *  @default null
 10887  		 */
 10888  		"aaSortingFixed": null,
 10889  		
 10890  		/**
 10891  		 * Classes to use for the striping of a table.
 10892  		 * Note that this parameter will be set by the initialisation routine. To
 10893  		 * set a default use {@link DataTable.defaults}.
 10894  		 *  @type array
 10895  		 *  @default []
 10896  		 */
 10897  		"asStripeClasses": null,
 10898  		
 10899  		/**
 10900  		 * If restoring a table - we should restore its striping classes as well
 10901  		 *  @type array
 10902  		 *  @default []
 10903  		 */
 10904  		"asDestroyStripes": [],
 10905  		
 10906  		/**
 10907  		 * If restoring a table - we should restore its width 
 10908  		 *  @type int
 10909  		 *  @default 0
 10910  		 */
 10911  		"sDestroyWidth": 0,
 10912  		
 10913  		/**
 10914  		 * Callback functions array for every time a row is inserted (i.e. on a draw).
 10915  		 *  @type array
 10916  		 *  @default []
 10917  		 */
 10918  		"aoRowCallback": [],
 10919  		
 10920  		/**
 10921  		 * Callback functions for the header on each draw.
 10922  		 *  @type array
 10923  		 *  @default []
 10924  		 */
 10925  		"aoHeaderCallback": [],
 10926  		
 10927  		/**
 10928  		 * Callback function for the footer on each draw.
 10929  		 *  @type array
 10930  		 *  @default []
 10931  		 */
 10932  		"aoFooterCallback": [],
 10933  		
 10934  		/**
 10935  		 * Array of callback functions for draw callback functions
 10936  		 *  @type array
 10937  		 *  @default []
 10938  		 */
 10939  		"aoDrawCallback": [],
 10940  		
 10941  		/**
 10942  		 * Array of callback functions for row created function
 10943  		 *  @type array
 10944  		 *  @default []
 10945  		 */
 10946  		"aoRowCreatedCallback": [],
 10947  		
 10948  		/**
 10949  		 * Callback functions for just before the table is redrawn. A return of 
 10950  		 * false will be used to cancel the draw.
 10951  		 *  @type array
 10952  		 *  @default []
 10953  		 */
 10954  		"aoPreDrawCallback": [],
 10955  		
 10956  		/**
 10957  		 * Callback functions for when the table has been initialised.
 10958  		 *  @type array
 10959  		 *  @default []
 10960  		 */
 10961  		"aoInitComplete": [],
 10962  	
 10963  		
 10964  		/**
 10965  		 * Callbacks for modifying the settings to be stored for state saving, prior to
 10966  		 * saving state.
 10967  		 *  @type array
 10968  		 *  @default []
 10969  		 */
 10970  		"aoStateSaveParams": [],
 10971  		
 10972  		/**
 10973  		 * Callbacks for modifying the settings that have been stored for state saving
 10974  		 * prior to using the stored values to restore the state.
 10975  		 *  @type array
 10976  		 *  @default []
 10977  		 */
 10978  		"aoStateLoadParams": [],
 10979  		
 10980  		/**
 10981  		 * Callbacks for operating on the settings object once the saved state has been
 10982  		 * loaded
 10983  		 *  @type array
 10984  		 *  @default []
 10985  		 */
 10986  		"aoStateLoaded": [],
 10987  		
 10988  		/**
 10989  		 * Cache the table ID for quick access
 10990  		 *  @type string
 10991  		 *  @default <i>Empty string</i>
 10992  		 */
 10993  		"sTableId": "",
 10994  		
 10995  		/**
 10996  		 * The TABLE node for the main table
 10997  		 *  @type node
 10998  		 *  @default null
 10999  		 */
 11000  		"nTable": null,
 11001  		
 11002  		/**
 11003  		 * Permanent ref to the thead element
 11004  		 *  @type node
 11005  		 *  @default null
 11006  		 */
 11007  		"nTHead": null,
 11008  		
 11009  		/**
 11010  		 * Permanent ref to the tfoot element - if it exists
 11011  		 *  @type node
 11012  		 *  @default null
 11013  		 */
 11014  		"nTFoot": null,
 11015  		
 11016  		/**
 11017  		 * Permanent ref to the tbody element
 11018  		 *  @type node
 11019  		 *  @default null
 11020  		 */
 11021  		"nTBody": null,
 11022  		
 11023  		/**
 11024  		 * Cache the wrapper node (contains all DataTables controlled elements)
 11025  		 *  @type node
 11026  		 *  @default null
 11027  		 */
 11028  		"nTableWrapper": null,
 11029  		
 11030  		/**
 11031  		 * Indicate if when using server-side processing the loading of data 
 11032  		 * should be deferred until the second draw.
 11033  		 * Note that this parameter will be set by the initialisation routine. To
 11034  		 * set a default use {@link DataTable.defaults}.
 11035  		 *  @type boolean
 11036  		 *  @default false
 11037  		 */
 11038  		"bDeferLoading": false,
 11039  		
 11040  		/**
 11041  		 * Indicate if all required information has been read in
 11042  		 *  @type boolean
 11043  		 *  @default false
 11044  		 */
 11045  		"bInitialised": false,
 11046  		
 11047  		/**
 11048  		 * Information about open rows. Each object in the array has the parameters
 11049  		 * 'nTr' and 'nParent'
 11050  		 *  @type array
 11051  		 *  @default []
 11052  		 */
 11053  		"aoOpenRows": [],
 11054  		
 11055  		/**
 11056  		 * Dictate the positioning of DataTables' control elements - see
 11057  		 * {@link DataTable.model.oInit.sDom}.
 11058  		 * Note that this parameter will be set by the initialisation routine. To
 11059  		 * set a default use {@link DataTable.defaults}.
 11060  		 *  @type string
 11061  		 *  @default null
 11062  		 */
 11063  		"sDom": null,
 11064  		
 11065  		/**
 11066  		 * Which type of pagination should be used.
 11067  		 * Note that this parameter will be set by the initialisation routine. To
 11068  		 * set a default use {@link DataTable.defaults}.
 11069  		 *  @type string 
 11070  		 *  @default two_button
 11071  		 */
 11072  		"sPaginationType": "two_button",
 11073  		
 11074  		/**
 11075  		 * The cookie duration (for bStateSave) in seconds.
 11076  		 * Note that this parameter will be set by the initialisation routine. To
 11077  		 * set a default use {@link DataTable.defaults}.
 11078  		 *  @type int
 11079  		 *  @default 0
 11080  		 */
 11081  		"iCookieDuration": 0,
 11082  		
 11083  		/**
 11084  		 * The cookie name prefix.
 11085  		 * Note that this parameter will be set by the initialisation routine. To
 11086  		 * set a default use {@link DataTable.defaults}.
 11087  		 *  @type string
 11088  		 *  @default <i>Empty string</i>
 11089  		 */
 11090  		"sCookiePrefix": "",
 11091  		
 11092  		/**
 11093  		 * Callback function for cookie creation.
 11094  		 * Note that this parameter will be set by the initialisation routine. To
 11095  		 * set a default use {@link DataTable.defaults}.
 11096  		 *  @type function
 11097  		 *  @default null
 11098  		 */
 11099  		"fnCookieCallback": null,
 11100  		
 11101  		/**
 11102  		 * Array of callback functions for state saving. Each array element is an 
 11103  		 * object with the following parameters:
 11104  		 *   <ul>
 11105  		 *     <li>function:fn - function to call. Takes two parameters, oSettings
 11106  		 *       and the JSON string to save that has been thus far created. Returns
 11107  		 *       a JSON string to be inserted into a json object 
 11108  		 *       (i.e. '"param": [ 0, 1, 2]')</li>
 11109  		 *     <li>string:sName - name of callback</li>
 11110  		 *   </ul>
 11111  		 *  @type array
 11112  		 *  @default []
 11113  		 */
 11114  		"aoStateSave": [],
 11115  		
 11116  		/**
 11117  		 * Array of callback functions for state loading. Each array element is an 
 11118  		 * object with the following parameters:
 11119  		 *   <ul>
 11120  		 *     <li>function:fn - function to call. Takes two parameters, oSettings 
 11121  		 *       and the object stored. May return false to cancel state loading</li>
 11122  		 *     <li>string:sName - name of callback</li>
 11123  		 *   </ul>
 11124  		 *  @type array
 11125  		 *  @default []
 11126  		 */
 11127  		"aoStateLoad": [],
 11128  		
 11129  		/**
 11130  		 * State that was loaded from the cookie. Useful for back reference
 11131  		 *  @type object
 11132  		 *  @default null
 11133  		 */
 11134  		"oLoadedState": null,
 11135  		
 11136  		/**
 11137  		 * Source url for AJAX data for the table.
 11138  		 * Note that this parameter will be set by the initialisation routine. To
 11139  		 * set a default use {@link DataTable.defaults}.
 11140  		 *  @type string
 11141  		 *  @default null
 11142  		 */
 11143  		"sAjaxSource": null,
 11144  		
 11145  		/**
 11146  		 * Property from a given object from which to read the table data from. This
 11147  		 * can be an empty string (when not server-side processing), in which case 
 11148  		 * it is  assumed an an array is given directly.
 11149  		 * Note that this parameter will be set by the initialisation routine. To
 11150  		 * set a default use {@link DataTable.defaults}.
 11151  		 *  @type string
 11152  		 */
 11153  		"sAjaxDataProp": null,
 11154  		
 11155  		/**
 11156  		 * Note if draw should be blocked while getting data
 11157  		 *  @type boolean
 11158  		 *  @default true
 11159  		 */
 11160  		"bAjaxDataGet": true,
 11161  		
 11162  		/**
 11163  		 * The last jQuery XHR object that was used for server-side data gathering. 
 11164  		 * This can be used for working with the XHR information in one of the 
 11165  		 * callbacks
 11166  		 *  @type object
 11167  		 *  @default null
 11168  		 */
 11169  		"jqXHR": null,
 11170  		
 11171  		/**
 11172  		 * Function to get the server-side data.
 11173  		 * Note that this parameter will be set by the initialisation routine. To
 11174  		 * set a default use {@link DataTable.defaults}.
 11175  		 *  @type function
 11176  		 */
 11177  		"fnServerData": null,
 11178  		
 11179  		/**
 11180  		 * Functions which are called prior to sending an Ajax request so extra 
 11181  		 * parameters can easily be sent to the server
 11182  		 *  @type array
 11183  		 *  @default []
 11184  		 */
 11185  		"aoServerParams": [],
 11186  		
 11187  		/**
 11188  		 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if 
 11189  		 * required).
 11190  		 * Note that this parameter will be set by the initialisation routine. To
 11191  		 * set a default use {@link DataTable.defaults}.
 11192  		 *  @type string
 11193  		 */
 11194  		"sServerMethod": null,
 11195  		
 11196  		/**
 11197  		 * Format numbers for display.
 11198  		 * Note that this parameter will be set by the initialisation routine. To
 11199  		 * set a default use {@link DataTable.defaults}.
 11200  		 *  @type function
 11201  		 */
 11202  		"fnFormatNumber": null,
 11203  		
 11204  		/**
 11205  		 * List of options that can be used for the user selectable length menu.
 11206  		 * Note that this parameter will be set by the initialisation routine. To
 11207  		 * set a default use {@link DataTable.defaults}.
 11208  		 *  @type array
 11209  		 *  @default []
 11210  		 */
 11211  		"aLengthMenu": null,
 11212  		
 11213  		/**
 11214  		 * Counter for the draws that the table does. Also used as a tracker for
 11215  		 * server-side processing
 11216  		 *  @type int
 11217  		 *  @default 0
 11218  		 */
 11219  		"iDraw": 0,
 11220  		
 11221  		/**
 11222  		 * Indicate if a redraw is being done - useful for Ajax
 11223  		 *  @type boolean
 11224  		 *  @default false
 11225  		 */
 11226  		"bDrawing": false,
 11227  		
 11228  		/**
 11229  		 * Draw index (iDraw) of the last error when parsing the returned data
 11230  		 *  @type int
 11231  		 *  @default -1
 11232  		 */
 11233  		"iDrawError": -1,
 11234  		
 11235  		/**
 11236  		 * Paging display length
 11237  		 *  @type int
 11238  		 *  @default 10
 11239  		 */
 11240  		"_iDisplayLength": 10,
 11241  	
 11242  		/**
 11243  		 * Paging start point - aiDisplay index
 11244  		 *  @type int
 11245  		 *  @default 0
 11246  		 */
 11247  		"_iDisplayStart": 0,
 11248  	
 11249  		/**
 11250  		 * Paging end point - aiDisplay index. Use fnDisplayEnd rather than
 11251  		 * this property to get the end point
 11252  		 *  @type int
 11253  		 *  @default 10
 11254  		 *  @private
 11255  		 */
 11256  		"_iDisplayEnd": 10,
 11257  		
 11258  		/**
 11259  		 * Server-side processing - number of records in the result set
 11260  		 * (i.e. before filtering), Use fnRecordsTotal rather than
 11261  		 * this property to get the value of the number of records, regardless of
 11262  		 * the server-side processing setting.
 11263  		 *  @type int
 11264  		 *  @default 0
 11265  		 *  @private
 11266  		 */
 11267  		"_iRecordsTotal": 0,
 11268  	
 11269  		/**
 11270  		 * Server-side processing - number of records in the current display set
 11271  		 * (i.e. after filtering). Use fnRecordsDisplay rather than
 11272  		 * this property to get the value of the number of records, regardless of
 11273  		 * the server-side processing setting.
 11274  		 *  @type boolean
 11275  		 *  @default 0
 11276  		 *  @private
 11277  		 */
 11278  		"_iRecordsDisplay": 0,
 11279  		
 11280  		/**
 11281  		 * Flag to indicate if jQuery UI marking and classes should be used.
 11282  		 * Note that this parameter will be set by the initialisation routine. To
 11283  		 * set a default use {@link DataTable.defaults}.
 11284  		 *  @type boolean
 11285  		 */
 11286  		"bJUI": null,
 11287  		
 11288  		/**
 11289  		 * The classes to use for the table
 11290  		 *  @type object
 11291  		 *  @default {}
 11292  		 */
 11293  		"oClasses": {},
 11294  		
 11295  		/**
 11296  		 * Flag attached to the settings object so you can check in the draw 
 11297  		 * callback if filtering has been done in the draw. Deprecated in favour of
 11298  		 * events.
 11299  		 *  @type boolean
 11300  		 *  @default false
 11301  		 *  @deprecated
 11302  		 */
 11303  		"bFiltered": false,
 11304  		
 11305  		/**
 11306  		 * Flag attached to the settings object so you can check in the draw 
 11307  		 * callback if sorting has been done in the draw. Deprecated in favour of
 11308  		 * events.
 11309  		 *  @type boolean
 11310  		 *  @default false
 11311  		 *  @deprecated
 11312  		 */
 11313  		"bSorted": false,
 11314  		
 11315  		/**
 11316  		 * Indicate that if multiple rows are in the header and there is more than 
 11317  		 * one unique cell per column, if the top one (true) or bottom one (false) 
 11318  		 * should be used for sorting / title by DataTables.
 11319  		 * Note that this parameter will be set by the initialisation routine. To
 11320  		 * set a default use {@link DataTable.defaults}.
 11321  		 *  @type boolean
 11322  		 */
 11323  		"bSortCellsTop": null,
 11324  		
 11325  		/**
 11326  		 * Initialisation object that is used for the table
 11327  		 *  @type object
 11328  		 *  @default null
 11329  		 */
 11330  		"oInit": null,
 11331  		
 11332  		/**
 11333  		 * Destroy callback functions - for plug-ins to attach themselves to the
 11334  		 * destroy so they can clean up markup and events.
 11335  		 *  @type array
 11336  		 *  @default []
 11337  		 */
 11338  		"aoDestroyCallback": [],
 11339  	
 11340  		
 11341  		/**
 11342  		 * Get the number of records in the current record set, before filtering
 11343  		 *  @type function
 11344  		 */
 11345  		"fnRecordsTotal": function ()
 11346  		{
 11347  			if ( this.oFeatures.bServerSide ) {
 11348  				return parseInt(this._iRecordsTotal, 10);
 11349  			} else {
 11350  				return this.aiDisplayMaster.length;
 11351  			}
 11352  		},
 11353  		
 11354  		/**
 11355  		 * Get the number of records in the current record set, after filtering
 11356  		 *  @type function
 11357  		 */
 11358  		"fnRecordsDisplay": function ()
 11359  		{
 11360  			if ( this.oFeatures.bServerSide ) {
 11361  				return parseInt(this._iRecordsDisplay, 10);
 11362  			} else {
 11363  				return this.aiDisplay.length;
 11364  			}
 11365  		},
 11366  		
 11367  		/**
 11368  		 * Set the display end point - aiDisplay index
 11369  		 *  @type function
 11370  		 *  @todo Should do away with _iDisplayEnd and calculate it on-the-fly here
 11371  		 */
 11372  		"fnDisplayEnd": function ()
 11373  		{
 11374  			if ( this.oFeatures.bServerSide ) {
 11375  				if ( this.oFeatures.bPaginate === false || this._iDisplayLength == -1 ) {
 11376  					return this._iDisplayStart+this.aiDisplay.length;
 11377  				} else {
 11378  					return Math.min( this._iDisplayStart+this._iDisplayLength, 
 11379  						this._iRecordsDisplay );
 11380  				}
 11381  			} else {
 11382  				return this._iDisplayEnd;
 11383  			}
 11384  		},
 11385  		
 11386  		/**
 11387  		 * The DataTables object for this table
 11388  		 *  @type object
 11389  		 *  @default null
 11390  		 */
 11391  		"oInstance": null,
 11392  		
 11393  		/**
 11394  		 * Unique identifier for each instance of the DataTables object. If there
 11395  		 * is an ID on the table node, then it takes that value, otherwise an
 11396  		 * incrementing internal counter is used.
 11397  		 *  @type string
 11398  		 *  @default null
 11399  		 */
 11400  		"sInstance": null,
 11401  	
 11402  		/**
 11403  		 * tabindex attribute value that is added to DataTables control elements, allowing
 11404  		 * keyboard navigation of the table and its controls.
 11405  		 */
 11406  		"iTabIndex": 0,
 11407  	
 11408  		/**
 11409  		 * DIV container for the footer scrolling table if scrolling
 11410  		 */
 11411  		"nScrollHead": null,
 11412  	
 11413  		/**
 11414  		 * DIV container for the footer scrolling table if scrolling
 11415  		 */
 11416  		"nScrollFoot": null
 11417  	};
 11418  
 11419  	/**
 11420  	 * Extension object for DataTables that is used to provide all extension options.
 11421  	 * 
 11422  	 * Note that the <i>DataTable.ext</i> object is available through
 11423  	 * <i>jQuery.fn.dataTable.ext</i> where it may be accessed and manipulated. It is
 11424  	 * also aliased to <i>jQuery.fn.dataTableExt</i> for historic reasons.
 11425  	 *  @namespace
 11426  	 *  @extends DataTable.models.ext
 11427  	 */
 11428  	DataTable.ext = $.extend( true, {}, DataTable.models.ext );
 11429  	
 11430  	$.extend( DataTable.ext.oStdClasses, {
 11431  		"sTable": "dataTable",
 11432  	
 11433  		/* Two buttons buttons */
 11434  		"sPagePrevEnabled": "paginate_enabled_previous",
 11435  		"sPagePrevDisabled": "paginate_disabled_previous",
 11436  		"sPageNextEnabled": "paginate_enabled_next",
 11437  		"sPageNextDisabled": "paginate_disabled_next",
 11438  		"sPageJUINext": "",
 11439  		"sPageJUIPrev": "",
 11440  		
 11441  		/* Full numbers paging buttons */
 11442  		"sPageButton": "paginate_button",
 11443  		"sPageButtonActive": "paginate_active",
 11444  		"sPageButtonStaticDisabled": "paginate_button paginate_button_disabled",
 11445  		"sPageFirst": "first",
 11446  		"sPagePrevious": "previous",
 11447  		"sPageNext": "next",
 11448  		"sPageLast": "last",
 11449  		
 11450  		/* Striping classes */
 11451  		"sStripeOdd": "odd",
 11452  		"sStripeEven": "even",
 11453  		
 11454  		/* Empty row */
 11455  		"sRowEmpty": "dataTables_empty",
 11456  		
 11457  		/* Features */
 11458  		"sWrapper": "dataTables_wrapper",
 11459  		"sFilter": "dataTables_filter",
 11460  		"sInfo": "dataTables_info",
 11461  		"sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
 11462  		"sLength": "dataTables_length",
 11463  		"sProcessing": "dataTables_processing",
 11464  		
 11465  		/* Sorting */
 11466  		"sSortAsc": "sorting_asc",
 11467  		"sSortDesc": "sorting_desc",
 11468  		"sSortable": "sorting", /* Sortable in both directions */
 11469  		"sSortableAsc": "sorting_asc_disabled",
 11470  		"sSortableDesc": "sorting_desc_disabled",
 11471  		"sSortableNone": "sorting_disabled",
 11472  		"sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
 11473  		"sSortJUIAsc": "",
 11474  		"sSortJUIDesc": "",
 11475  		"sSortJUI": "",
 11476  		"sSortJUIAscAllowed": "",
 11477  		"sSortJUIDescAllowed": "",
 11478  		"sSortJUIWrapper": "",
 11479  		"sSortIcon": "",
 11480  		
 11481  		/* Scrolling */
 11482  		"sScrollWrapper": "dataTables_scroll",
 11483  		"sScrollHead": "dataTables_scrollHead",
 11484  		"sScrollHeadInner": "dataTables_scrollHeadInner",
 11485  		"sScrollBody": "dataTables_scrollBody",
 11486  		"sScrollFoot": "dataTables_scrollFoot",
 11487  		"sScrollFootInner": "dataTables_scrollFootInner",
 11488  		
 11489  		/* Misc */
 11490  		"sFooterTH": "",
 11491  		"sJUIHeader": "",
 11492  		"sJUIFooter": ""
 11493  	} );
 11494  	
 11495  	
 11496  	$.extend( DataTable.ext.oJUIClasses, DataTable.ext.oStdClasses, {
 11497  		/* Two buttons buttons */
 11498  		"sPagePrevEnabled": "fg-button ui-button ui-state-default ui-corner-left",
 11499  		"sPagePrevDisabled": "fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",
 11500  		"sPageNextEnabled": "fg-button ui-button ui-state-default ui-corner-right",
 11501  		"sPageNextDisabled": "fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",
 11502  		"sPageJUINext": "ui-icon ui-icon-circle-arrow-e",
 11503  		"sPageJUIPrev": "ui-icon ui-icon-circle-arrow-w",
 11504  		
 11505  		/* Full numbers paging buttons */
 11506  		"sPageButton": "fg-button ui-button ui-state-default",
 11507  		"sPageButtonActive": "fg-button ui-button ui-state-default ui-state-disabled",
 11508  		"sPageButtonStaticDisabled": "fg-button ui-button ui-state-default ui-state-disabled",
 11509  		"sPageFirst": "first ui-corner-tl ui-corner-bl",
 11510  		"sPageLast": "last ui-corner-tr ui-corner-br",
 11511  		
 11512  		/* Features */
 11513  		"sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
 11514  			"ui-buttonset-multi paging_", /* Note that the type is postfixed */
 11515  		
 11516  		/* Sorting */
 11517  		"sSortAsc": "ui-state-default",
 11518  		"sSortDesc": "ui-state-default",
 11519  		"sSortable": "ui-state-default",
 11520  		"sSortableAsc": "ui-state-default",
 11521  		"sSortableDesc": "ui-state-default",
 11522  		"sSortableNone": "ui-state-default",
 11523  		"sSortJUIAsc": "css_right ui-icon ui-icon-triangle-1-n",
 11524  		"sSortJUIDesc": "css_right ui-icon ui-icon-triangle-1-s",
 11525  		"sSortJUI": "css_right ui-icon ui-icon-carat-2-n-s",
 11526  		"sSortJUIAscAllowed": "css_right ui-icon ui-icon-carat-1-n",
 11527  		"sSortJUIDescAllowed": "css_right ui-icon ui-icon-carat-1-s",
 11528  		"sSortJUIWrapper": "DataTables_sort_wrapper",
 11529  		"sSortIcon": "DataTables_sort_icon",
 11530  		
 11531  		/* Scrolling */
 11532  		"sScrollHead": "dataTables_scrollHead ui-state-default",
 11533  		"sScrollFoot": "dataTables_scrollFoot ui-state-default",
 11534  		
 11535  		/* Misc */
 11536  		"sFooterTH": "ui-state-default",
 11537  		"sJUIHeader": "fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix",
 11538  		"sJUIFooter": "fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix"
 11539  	} );
 11540  	
 11541  	/*
 11542  	 * Variable: oPagination
 11543  	 * Purpose:  
 11544  	 * Scope:    jQuery.fn.dataTableExt
 11545  	 */
 11546  	$.extend( DataTable.ext.oPagination, {
 11547  		/*
 11548  		 * Variable: two_button
 11549  		 * Purpose:  Standard two button (forward/back) pagination
 11550  		 * Scope:    jQuery.fn.dataTableExt.oPagination
 11551  		 */
 11552  		"two_button": {
 11553  			/*
 11554  			 * Function: oPagination.two_button.fnInit
 11555  			 * Purpose:  Initialise dom elements required for pagination with forward/back buttons only
 11556  			 * Returns:  -
 11557  			 * Inputs:   object:oSettings - dataTables settings object
 11558  			 *           node:nPaging - the DIV which contains this pagination control
 11559  			 *           function:fnCallbackDraw - draw function which must be called on update
 11560  			 */
 11561  			"fnInit": function ( oSettings, nPaging, fnCallbackDraw )
 11562  			{
 11563  				var oLang = oSettings.oLanguage.oPaginate;
 11564  				var oClasses = oSettings.oClasses;
 11565  				var fnClickHandler = function ( e ) {
 11566  					if ( oSettings.oApi._fnPageChange( oSettings, e.data.action ) )
 11567  					{
 11568  						fnCallbackDraw( oSettings );
 11569  					}
 11570  				};
 11571  	
 11572  				var sAppend = (!oSettings.bJUI) ?
 11573  					'<a class="'+oSettings.oClasses.sPagePrevDisabled+'" tabindex="'+oSettings.iTabIndex+'" role="button">'+oLang.sPrevious+'</a>'+
 11574  					'<a class="'+oSettings.oClasses.sPageNextDisabled+'" tabindex="'+oSettings.iTabIndex+'" role="button">'+oLang.sNext+'</a>'
 11575  					:
 11576  					'<a class="'+oSettings.oClasses.sPagePrevDisabled+'" tabindex="'+oSettings.iTabIndex+'" role="button"><span class="'+oSettings.oClasses.sPageJUIPrev+'"></span></a>'+
 11577  					'<a class="'+oSettings.oClasses.sPageNextDisabled+'" tabindex="'+oSettings.iTabIndex+'" role="button"><span class="'+oSettings.oClasses.sPageJUINext+'"></span></a>';
 11578  				$(nPaging).append( sAppend );
 11579  				
 11580  				var els = $('a', nPaging);
 11581  				var nPrevious = els[0],
 11582  					nNext = els[1];
 11583  				
 11584  				oSettings.oApi._fnBindAction( nPrevious, {action: "previous"}, fnClickHandler );
 11585  				oSettings.oApi._fnBindAction( nNext,     {action: "next"},     fnClickHandler );
 11586  				
 11587  				/* ID the first elements only */
 11588  				if ( !oSettings.aanFeatures.p )
 11589  				{
 11590  					nPaging.id = oSettings.sTableId+'_paginate';
 11591  					nPrevious.id = oSettings.sTableId+'_previous';
 11592  					nNext.id = oSettings.sTableId+'_next';
 11593  	
 11594  					nPrevious.setAttribute('aria-controls', oSettings.sTableId);
 11595  					nNext.setAttribute('aria-controls', oSettings.sTableId);
 11596  				}
 11597  			},
 11598  			
 11599  			/*
 11600  			 * Function: oPagination.two_button.fnUpdate
 11601  			 * Purpose:  Update the two button pagination at the end of the draw
 11602  			 * Returns:  -
 11603  			 * Inputs:   object:oSettings - dataTables settings object
 11604  			 *           function:fnCallbackDraw - draw function to call on page change
 11605  			 */
 11606  			"fnUpdate": function ( oSettings, fnCallbackDraw )
 11607  			{
 11608  				if ( !oSettings.aanFeatures.p )
 11609  				{
 11610  					return;
 11611  				}
 11612  				
 11613  				var oClasses = oSettings.oClasses;
 11614  				var an = oSettings.aanFeatures.p;
 11615  				var nNode;
 11616  	
 11617  				/* Loop over each instance of the pager */
 11618  				for ( var i=0, iLen=an.length ; i<iLen ; i++ )
 11619  				{
 11620  					nNode = an[i].firstChild;
 11621  					if ( nNode )
 11622  					{
 11623  						/* Previous page */
 11624  						nNode.className = ( oSettings._iDisplayStart === 0 ) ?
 11625  						    oClasses.sPagePrevDisabled : oClasses.sPagePrevEnabled;
 11626  						    
 11627  						/* Next page */
 11628  						nNode = nNode.nextSibling;
 11629  						nNode.className = ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) ?
 11630  						    oClasses.sPageNextDisabled : oClasses.sPageNextEnabled;
 11631  					}
 11632  				}
 11633  			}
 11634  		},
 11635  		
 11636  		
 11637  		/*
 11638  		 * Variable: iFullNumbersShowPages
 11639  		 * Purpose:  Change the number of pages which can be seen
 11640  		 * Scope:    jQuery.fn.dataTableExt.oPagination
 11641  		 */
 11642  		"iFullNumbersShowPages": 5,
 11643  		
 11644  		/*
 11645  		 * Variable: full_numbers
 11646  		 * Purpose:  Full numbers pagination
 11647  		 * Scope:    jQuery.fn.dataTableExt.oPagination
 11648  		 */
 11649  		"full_numbers": {
 11650  			/*
 11651  			 * Function: oPagination.full_numbers.fnInit
 11652  			 * Purpose:  Initialise dom elements required for pagination with a list of the pages
 11653  			 * Returns:  -
 11654  			 * Inputs:   object:oSettings - dataTables settings object
 11655  			 *           node:nPaging - the DIV which contains this pagination control
 11656  			 *           function:fnCallbackDraw - draw function which must be called on update
 11657  			 */
 11658  			"fnInit": function ( oSettings, nPaging, fnCallbackDraw )
 11659  			{
 11660  				var oLang = oSettings.oLanguage.oPaginate;
 11661  				var oClasses = oSettings.oClasses;
 11662  				var fnClickHandler = function ( e ) {
 11663  					if ( oSettings.oApi._fnPageChange( oSettings, e.data.action ) )
 11664  					{
 11665  						fnCallbackDraw( oSettings );
 11666  					}
 11667  				};
 11668  	
 11669  				$(nPaging).append(
 11670  					'<a  tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+" "+oClasses.sPageFirst+'">'+oLang.sFirst+'</a>'+
 11671  					'<a  tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+" "+oClasses.sPagePrevious+'">'+oLang.sPrevious+'</a>'+
 11672  					'<span></span>'+
 11673  					'<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+" "+oClasses.sPageNext+'">'+oLang.sNext+'</a>'+
 11674  					'<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+" "+oClasses.sPageLast+'">'+oLang.sLast+'</a>'
 11675  				);
 11676  				var els = $('a', nPaging);
 11677  				var nFirst = els[0],
 11678  					nPrev = els[1],
 11679  					nNext = els[2],
 11680  					nLast = els[3];
 11681  				
 11682  				oSettings.oApi._fnBindAction( nFirst, {action: "first"},    fnClickHandler );
 11683  				oSettings.oApi._fnBindAction( nPrev,  {action: "previous"}, fnClickHandler );
 11684  				oSettings.oApi._fnBindAction( nNext,  {action: "next"},     fnClickHandler );
 11685  				oSettings.oApi._fnBindAction( nLast,  {action: "last"},     fnClickHandler );
 11686  				
 11687  				/* ID the first elements only */
 11688  				if ( !oSettings.aanFeatures.p )
 11689  				{
 11690  					nPaging.id = oSettings.sTableId+'_paginate';
 11691  					nFirst.id =oSettings.sTableId+'_first';
 11692  					nPrev.id =oSettings.sTableId+'_previous';
 11693  					nNext.id =oSettings.sTableId+'_next';
 11694  					nLast.id =oSettings.sTableId+'_last';
 11695  				}
 11696  			},
 11697  			
 11698  			/*
 11699  			 * Function: oPagination.full_numbers.fnUpdate
 11700  			 * Purpose:  Update the list of page buttons shows
 11701  			 * Returns:  -
 11702  			 * Inputs:   object:oSettings - dataTables settings object
 11703  			 *           function:fnCallbackDraw - draw function to call on page change
 11704  			 */
 11705  			"fnUpdate": function ( oSettings, fnCallbackDraw )
 11706  			{
 11707  				if ( !oSettings.aanFeatures.p )
 11708  				{
 11709  					return;
 11710  				}
 11711  				
 11712  				var iPageCount = DataTable.ext.oPagination.iFullNumbersShowPages;
 11713  				var iPageCountHalf = Math.floor(iPageCount / 2);
 11714  				var iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength);
 11715  				var iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1;
 11716  				var sList = "";
 11717  				var iStartButton, iEndButton, i, iLen;
 11718  				var oClasses = oSettings.oClasses;
 11719  				var anButtons, anStatic, nPaginateList, nNode;
 11720  				var an = oSettings.aanFeatures.p;
 11721  				var fnBind = function (j) {
 11722  					oSettings.oApi._fnBindAction( this, {"page": j+iStartButton-1}, function(e) {
 11723  						/* Use the information in the element to jump to the required page */
 11724  						oSettings.oApi._fnPageChange( oSettings, e.data.page );
 11725  						fnCallbackDraw( oSettings );
 11726  						e.preventDefault();
 11727  					} );
 11728  				};
 11729  				
 11730  				/* Pages calculation */
 11731  				if ( oSettings._iDisplayLength === -1 )
 11732  				{
 11733  					iStartButton = 1;
 11734  					iEndButton = 1;
 11735  					iCurrentPage = 1;
 11736  				}
 11737  				else if (iPages < iPageCount)
 11738  				{
 11739  					iStartButton = 1;
 11740  					iEndButton = iPages;
 11741  				}
 11742  				else if (iCurrentPage <= iPageCountHalf)
 11743  				{
 11744  					iStartButton = 1;
 11745  					iEndButton = iPageCount;
 11746  				}
 11747  				else if (iCurrentPage >= (iPages - iPageCountHalf))
 11748  				{
 11749  					iStartButton = iPages - iPageCount + 1;
 11750  					iEndButton = iPages;
 11751  				}
 11752  				else
 11753  				{
 11754  					iStartButton = iCurrentPage - Math.ceil(iPageCount / 2) + 1;
 11755  					iEndButton = iStartButton + iPageCount - 1;
 11756  				}
 11757  	
 11758  				
 11759  				/* Build the dynamic list */
 11760  				for ( i=iStartButton ; i<=iEndButton ; i++ )
 11761  				{
 11762  					sList += (iCurrentPage !== i) ?
 11763  						'<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+'">'+oSettings.fnFormatNumber(i)+'</a>' :
 11764  						'<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButtonActive+'">'+oSettings.fnFormatNumber(i)+'</a>';
 11765  				}
 11766  				
 11767  				/* Loop over each instance of the pager */
 11768  				for ( i=0, iLen=an.length ; i<iLen ; i++ )
 11769  				{
 11770  					nNode = an[i];
 11771  					if ( !nNode.hasChildNodes() )
 11772  					{
 11773  						continue;
 11774  					}
 11775  					
 11776  					/* Build up the dynamic list first - html and listeners */
 11777  					$('span:eq(0)', nNode)
 11778  						.html( sList )
 11779  						.children('a').each( fnBind );
 11780  					
 11781  					/* Update the permanent button's classes */
 11782  					anButtons = nNode.getElementsByTagName('a');
 11783  					anStatic = [
 11784  						anButtons[0], anButtons[1], 
 11785  						anButtons[anButtons.length-2], anButtons[anButtons.length-1]
 11786  					];
 11787  	
 11788  					$(anStatic).removeClass( oClasses.sPageButton+" "+oClasses.sPageButtonActive+" "+oClasses.sPageButtonStaticDisabled );
 11789  					$([anStatic[0], anStatic[1]]).addClass( 
 11790  						(iCurrentPage==1) ?
 11791  							oClasses.sPageButtonStaticDisabled :
 11792  							oClasses.sPageButton
 11793  					);
 11794  					$([anStatic[2], anStatic[3]]).addClass(
 11795  						(iPages===0 || iCurrentPage===iPages || oSettings._iDisplayLength===-1) ?
 11796  							oClasses.sPageButtonStaticDisabled :
 11797  							oClasses.sPageButton
 11798  					);
 11799  				}
 11800  			}
 11801  		}
 11802  	} );
 11803  	
 11804  	$.extend( DataTable.ext.oSort, {
 11805  		/*
 11806  		 * text sorting
 11807  		 */
 11808  		"string-pre": function ( a )
 11809  		{
 11810  			if ( typeof a != 'string' ) {
 11811  				a = (a !== null && a.toString) ? a.toString() : '';
 11812  			}
 11813  			return a.toLowerCase();
 11814  		},
 11815  	
 11816  		"string-asc": function ( x, y )
 11817  		{
 11818  			return ((x < y) ? -1 : ((x > y) ? 1 : 0));
 11819  		},
 11820  		
 11821  		"string-desc": function ( x, y )
 11822  		{
 11823  			return ((x < y) ? 1 : ((x > y) ? -1 : 0));
 11824  		},
 11825  		
 11826  		
 11827  		/*
 11828  		 * html sorting (ignore html tags)
 11829  		 */
 11830  		"html-pre": function ( a )
 11831  		{
 11832  			return a.replace( /<.*?>/g, "" ).toLowerCase();
 11833  		},
 11834  		
 11835  		"html-asc": function ( x, y )
 11836  		{
 11837  			return ((x < y) ? -1 : ((x > y) ? 1 : 0));
 11838  		},
 11839  		
 11840  		"html-desc": function ( x, y )
 11841  		{
 11842  			return ((x < y) ? 1 : ((x > y) ? -1 : 0));
 11843  		},
 11844  		
 11845  		
 11846  		/*
 11847  		 * date sorting
 11848  		 */
 11849  		"date-pre": function ( a )
 11850  		{
 11851  			var x = Date.parse( a );
 11852  			
 11853  			if ( isNaN(x) || x==="" )
 11854  			{
 11855  				x = Date.parse( "01/01/1970 00:00:00" );
 11856  			}
 11857  			return x;
 11858  		},
 11859  	
 11860  		"date-asc": function ( x, y )
 11861  		{
 11862  			return x - y;
 11863  		},
 11864  		
 11865  		"date-desc": function ( x, y )
 11866  		{
 11867  			return y - x;
 11868  		},
 11869  		
 11870  		
 11871  		/*
 11872  		 * numerical sorting
 11873  		 */
 11874  		"numeric-pre": function ( a )
 11875  		{
 11876  			return (a=="-" || a==="") ? 0 : a*1;
 11877  		},
 11878  	
 11879  		"numeric-asc": function ( x, y )
 11880  		{
 11881  			return x - y;
 11882  		},
 11883  		
 11884  		"numeric-desc": function ( x, y )
 11885  		{
 11886  			return y - x;
 11887  		}
 11888  	} );
 11889  	
 11890  	
 11891  	$.extend( DataTable.ext.aTypes, [
 11892  		/*
 11893  		 * Function: -
 11894  		 * Purpose:  Check to see if a string is numeric
 11895  		 * Returns:  string:'numeric' or null
 11896  		 * Inputs:   mixed:sText - string to check
 11897  		 */
 11898  		function ( sData )
 11899  		{
 11900  			/* Allow zero length strings as a number */
 11901  			if ( typeof sData === 'number' )
 11902  			{
 11903  				return 'numeric';
 11904  			}
 11905  			else if ( typeof sData !== 'string' )
 11906  			{
 11907  				return null;
 11908  			}
 11909  			
 11910  			var sValidFirstChars = "0123456789-";
 11911  			var sValidChars = "0123456789.";
 11912  			var Char;
 11913  			var bDecimal = false;
 11914  			
 11915  			/* Check for a valid first char (no period and allow negatives) */
 11916  			Char = sData.charAt(0); 
 11917  			if (sValidFirstChars.indexOf(Char) == -1) 
 11918  			{
 11919  				return null;
 11920  			}
 11921  			
 11922  			/* Check all the other characters are valid */
 11923  			for ( var i=1 ; i<sData.length ; i++ ) 
 11924  			{
 11925  				Char = sData.charAt(i); 
 11926  				if (sValidChars.indexOf(Char) == -1) 
 11927  				{
 11928  					return null;
 11929  				}
 11930  				
 11931  				/* Only allowed one decimal place... */
 11932  				if ( Char == "." )
 11933  				{
 11934  					if ( bDecimal )
 11935  					{
 11936  						return null;
 11937  					}
 11938  					bDecimal = true;
 11939  				}
 11940  			}
 11941  			
 11942  			return 'numeric';
 11943  		},
 11944  		
 11945  		/*
 11946  		 * Function: -
 11947  		 * Purpose:  Check to see if a string is actually a formatted date
 11948  		 * Returns:  string:'date' or null
 11949  		 * Inputs:   string:sText - string to check
 11950  		 */
 11951  		function ( sData )
 11952  		{
 11953  			var iParse = Date.parse(sData);
 11954  			if ( (iParse !== null && !isNaN(iParse)) || (typeof sData === 'string' && sData.length === 0) )
 11955  			{
 11956  				return 'date';
 11957  			}
 11958  			return null;
 11959  		},
 11960  		
 11961  		/*
 11962  		 * Function: -
 11963  		 * Purpose:  Check to see if a string should be treated as an HTML string
 11964  		 * Returns:  string:'html' or null
 11965  		 * Inputs:   string:sText - string to check
 11966  		 */
 11967  		function ( sData )
 11968  		{
 11969  			if ( typeof sData === 'string' && sData.indexOf('<') != -1 && sData.indexOf('>') != -1 )
 11970  			{
 11971  				return 'html';
 11972  			}
 11973  			return null;
 11974  		}
 11975  	] );
 11976  	
 11977  
 11978  	// jQuery aliases
 11979  	$.fn.DataTable = DataTable;
 11980  	$.fn.dataTable = DataTable;
 11981  	$.fn.dataTableSettings = DataTable.settings;
 11982  	$.fn.dataTableExt = DataTable.ext;
 11983  
 11984  
 11985  	// Information about events fired by DataTables - for documentation.
 11986  	/**
 11987  	 * Draw event, fired whenever the table is redrawn on the page, at the same point as
 11988  	 * fnDrawCallback. This may be useful for binding events or performing calculations when
 11989  	 * the table is altered at all.
 11990  	 *  @name DataTable#draw
 11991  	 *  @event
 11992  	 *  @param {event} e jQuery event object
 11993  	 *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
 11994  	 */
 11995  
 11996  	/**
 11997  	 * Filter event, fired when the filtering applied to the table (using the build in global
 11998  	 * global filter, or column filters) is altered.
 11999  	 *  @name DataTable#filter
 12000  	 *  @event
 12001  	 *  @param {event} e jQuery event object
 12002  	 *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
 12003  	 */
 12004  
 12005  	/**
 12006  	 * Page change event, fired when the paging of the table is altered.
 12007  	 *  @name DataTable#page
 12008  	 *  @event
 12009  	 *  @param {event} e jQuery event object
 12010  	 *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
 12011  	 */
 12012  
 12013  	/**
 12014  	 * Sort event, fired when the sorting applied to the table is altered.
 12015  	 *  @name DataTable#sort
 12016  	 *  @event
 12017  	 *  @param {event} e jQuery event object
 12018  	 *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
 12019  	 */
 12020  
 12021  	/**
 12022  	 * DataTables initialisation complete event, fired when the table is fully drawn,
 12023  	 * including Ajax data loaded, if Ajax data is required.
 12024  	 *  @name DataTable#init
 12025  	 *  @event
 12026  	 *  @param {event} e jQuery event object
 12027  	 *  @param {object} oSettings DataTables settings object
 12028  	 *  @param {object} json The JSON object request from the server - only
 12029  	 *    present if client-side Ajax sourced data is used</li></ol>
 12030  	 */
 12031  
 12032  	/**
 12033  	 * State save event, fired when the table has changed state a new state save is required.
 12034  	 * This method allows modification of the state saving object prior to actually doing the
 12035  	 * save, including addition or other state properties (for plug-ins) or modification
 12036  	 * of a DataTables core property.
 12037  	 *  @name DataTable#stateSaveParams
 12038  	 *  @event
 12039  	 *  @param {event} e jQuery event object
 12040  	 *  @param {object} oSettings DataTables settings object
 12041  	 *  @param {object} json The state information to be saved
 12042  	 */
 12043  
 12044  	/**
 12045  	 * State load event, fired when the table is loading state from the stored data, but
 12046  	 * prior to the settings object being modified by the saved state - allowing modification
 12047  	 * of the saved state is required or loading of state for a plug-in.
 12048  	 *  @name DataTable#stateLoadParams
 12049  	 *  @event
 12050  	 *  @param {event} e jQuery event object
 12051  	 *  @param {object} oSettings DataTables settings object
 12052  	 *  @param {object} json The saved state information
 12053  	 */
 12054  
 12055  	/**
 12056  	 * State loaded event, fired when state has been loaded from stored data and the settings
 12057  	 * object has been modified by the loaded data.
 12058  	 *  @name DataTable#stateLoaded
 12059  	 *  @event
 12060  	 *  @param {event} e jQuery event object
 12061  	 *  @param {object} oSettings DataTables settings object
 12062  	 *  @param {object} json The saved state information
 12063  	 */
 12064  
 12065  	/**
 12066  	 * Processing event, fired when DataTables is doing some kind of processing (be it,
 12067  	 * sort, filter or anything else). Can be used to indicate to the end user that
 12068  	 * there is something happening, or that something has finished.
 12069  	 *  @name DataTable#processing
 12070  	 *  @event
 12071  	 *  @param {event} e jQuery event object
 12072  	 *  @param {object} oSettings DataTables settings object
 12073  	 *  @param {boolean} bShow Flag for if DataTables is doing processing or not
 12074  	 */
 12075  
 12076  	/**
 12077  	 * Ajax (XHR) event, fired whenever an Ajax request is completed from a request to 
 12078  	 * made to the server for new data (note that this trigger is called in fnServerData,
 12079  	 * if you override fnServerData and which to use this event, you need to trigger it in
 12080  	 * you success function).
 12081  	 *  @name DataTable#xhr
 12082  	 *  @event
 12083  	 *  @param {event} e jQuery event object
 12084  	 *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
 12085  	 *  @param {object} json JSON returned from the server
 12086  	 */
 12087  
 12088  	/**
 12089  	 * Destroy event, fired when the DataTable is destroyed by calling fnDestroy or passing
 12090  	 * the bDestroy:true parameter in the initialisation object. This can be used to remove
 12091  	 * bound events, added DOM nodes, etc.
 12092  	 *  @name DataTable#destroy
 12093  	 *  @event
 12094  	 *  @param {event} e jQuery event object
 12095  	 *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
 12096  	 */
 12097  }));
 12098  
 12099  }(window, document));
 12100