PageSourceSearch

https://www.lesedc.org/global/javascript/jquery.dataTables.js

js lesedc.org collected 2026-09-24 11:24:21 UTC 377,728 bytes, 12,105 lines download raw bytes

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 
887is 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, ao
1897Data,
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" />' : ' <input type="text" placeholder="'+sSearchStr+'" />';
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 === "Aucune donnée disponible" )
2715			{
2716				_fnMap( oLanguage, oLanguage, 'sZeroRecords', 'sEmptyTable' );
2717			}
2718		
2719			/* Likewise with loading records */
2720			if ( !oLanguage.sLoadingRecords && oLanguage.sZeroRecords &&
2721				oDefaults.sLoadingRecords === "Chargement..." )
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						if(nThs[i-iCorrector].style) {
3655							nThs[i-iCorrector].style.width = "";
3656						}
3657					}
3658					else
3659					{
3660						iCorrector++;
3661					}
3662				}
3663		
3664				/* Find the biggest td for each column and put it into the table */
3665				for ( i=0 ; i<iColums ; i++ )
3666				{
3667					if ( oSettings.aoColumns[i].bVisible )
3668					{
3669						var nTd = _fnGetWidestNode( oSettings, i );
3670						if ( nTd !== null )
3671						{
3672							nTd = nTd.cloneNode(true);
3673							if ( oSettings.aoColumns[i].sContentPadding !== "" )
3674							{
3675								nTd.innerHTML += oSettings.aoColumns[i].sContentPadding;
3676							}
3677							nTr.appendChild( nTd );
3678						}
3679					}
3680				}
3681				
3682				/* Build the table and 'display' it */
3683				nWrapper.appendChild( nCalcTmp );
3684				
3685				/* When scrolling (X or Y) we want to set the width of the table as appropriate. However,
3686				 * when not scrolling leave the table width as it is. This results in slightly different,
3687				 * but I think correct behaviour
3688				 */
3689				if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" )
3690				{
3691					nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner);
3692				}
3693				else if ( oSettings.oScroll.sX !== "" )
3694				{
3695					nCalcTmp.style.width = "";
3696					if ( $(nCalcTmp).width() < nWrapper.offsetWidth )
3697					{
3698						nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
3699					}
3700				}
3701				else if ( oSettings.oScroll.sY !== "" )
3702				{
3703					nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
3704				}
3705				else if ( widthAttr )
3706				{
3707					nCalcTmp.style.width = _fnStringToCss( widthAttr );
3708				}
3709				nCalcTmp.style.visibility = "hidden";
3710				
3711				/* Scrolling considerations */
3712				_fnScrollingWidthAdjust( oSettings, nCalcTmp );
3713				
3714				/* Read the width's calculated by the browser and store them for use by the caller. We
3715				 * first of all try to use the elements in the body, but it is possible that there are
3716				 * no elements there, under which circumstances we use the header elements
3717				 */
3718				var oNodes = $("tbody tr:eq(0)", nCalcTmp).children();
3719				if ( oNodes.length === 0 )
3720				{
3721					oNodes = _fnGetUniqueThs( oSettings, $('thead', nCalcTmp)[0] );
3722				}
3723		
3724				/* Browsers need a bit of a hand when a width is assigned to any columns when 
3725				 * x-scrolling as they tend to collapse the table to the min-width, even if
3726				 * we sent the column widths. So we need to keep track of what the table width
3727				 * should be by summing the user given values, and the automatic values
3728				 */
3729				if ( oSettings.oScroll.sX !== "" )
3730				{
3731					var iTotal = 0;
3732					iCorrector = 0;
3733					for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
3734					{
3735						if ( oSettings.aoColumns[i].bVisible )
3736						{
3737							if ( oSettings.aoColumns[i].sWidthOrig === null )
3738							{
3739								iTotal += $(oNodes[iCorrector]).outerWidth();
3740							}
3741							else
3742							{
3743								iTotal += parseInt(oSettings.aoColumns[i].sWidth.replace('px',''), 10) +
3744									($(oNodes[iCorrector]).outerWidth() - $(oNodes[iCorrector]).width());
3745							}
3746							iCorrector++;
3747						}
3748					}
3749					
3750					nCalcTmp.style.width = _fnStringToCss( iTotal );
3751					oSettings.nTable.style.width = _fnStringToCss( iTotal );
3752				}
3753		
3754				iCorrector = 0;
3755				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
3756				{
3757					if ( oSettings.aoColumns[i].bVisible )
3758					{
3759						iWidth = $(oNodes[iCorrector]).width();
3760						if ( iWidth !== null && iWidth > 0 )
3761						{
3762							oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth );
3763						}
3764						iCorrector++;
3765					}
3766				}
3767		
3768				var cssWidth = $(nCalcTmp).css('width');
3769				oSettings.nTable.style.width = (cssWidth.indexOf('%') !== -1) ?
3770				    cssWidth : _fnStringToCss( $(nCalcTmp).outerWidth() );
3771				nCalcTmp.parentNode.removeChild( nCalcTmp );
3772			}
3773		
3774			if ( widthAttr )
3775			{
3776				oSettings.nTable.style.width = _fnStringToCss( widthAttr );
3777			}
3778		}
3779		
3780		
3781		/**
3782		 * Adjust a table's width to take account of scrolling
3783		 *  @param {object} oSettings dataTables settings object
3784		 *  @param {node} n table node
3785		 *  @memberof DataTable#oApi
3786		 */
3787		function _fnScrollingWidthAdjust ( oSettings, n )
3788		{
3789			if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" )
3790			{
3791				/* When y-scrolling only, we want to remove the width of the scroll bar so the table
3792				 * + scroll bar will fit into the area avaialble.
3793				 */
3794				var iOrigWidth = $(n).width();
3795				n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth );
3796			}
3797			else if ( oSettings.oScroll.sX !== "" )
3798			{
3799				/* When x-scrolling both ways, fix the table at it's current size, without adjusting */
3800				n.style.width = _fnStringToCss( $(n).outerWidth() );
3801			}
3802		}
3803		
3804		
3805		/**
3806		 * Get the widest node
3807		 *  @param {object} oSettings dataTables settings object
3808		 *  @param {int} iCol column of interest
3809		 *  @returns {node} widest table node
3810		 *  @memberof DataTable#oApi
3811		 */
3812		function _fnGetWidestNode( oSettings, iCol )
3813		{
3814			var iMaxIndex = _fnGetMaxLenString( oSettings, iCol );
3815			if ( iMaxIndex < 0 )
3816			{
3817				return null;
3818			}
3819		
3820			if ( oSettings.aoData[iMaxIndex].nTr === null )
3821			{
3822				var n = document.createElement('td');
3823				n.innerHTML = _fnGetCellData( oSettings, iMaxIndex, iCol, '' );
3824				return n;
3825			}
3826			return _fnGetTdNodes(oSettings, iMaxIndex)[iCol];
3827		}
3828		
3829		
3830		/**
3831		 * Get the maximum strlen for each data column
3832		 *  @param {object} oSettings dataTables settings object
3833		 *  @param {int} iCol column of interest
3834		 *  @returns {string} max string length for each column
3835		 *  @memberof DataTable#oApi
3836		 */
3837		function _fnGetMaxLenString( oSettings, iCol )
3838		{
3839			var iMax = -1;
3840			var iMaxIndex = -1;
3841			
3842			for ( var i=0 ; i<oSettings.aoData.length ; i++ )
3843			{
3844				var s = _fnGetCellData( oSettings, i, iCol, 'display' )+"";
3845				s = s.replace( /<.*?>/g, "" );
3846				if ( s.length > iMax )
3847				{
3848					iMax = s.length;
3849					iMaxIndex = i;
3850				}
3851			}
3852			
3853			return iMaxIndex;
3854		}
3855		
3856		
3857		/**
3858		 * Append a CSS unit (only if required) to a string
3859		 *  @param {array} aArray1 first array
3860		 *  @param {array} aArray2 second array
3861		 *  @returns {int} 0 if match, 1 if length is different, 2 if no match
3862		 *  @memberof DataTable#oApi
3863		 */
3864		function _fnStringToCss( s )
3865		{
3866			if ( s === null )
3867			{
3868				return "0px";
3869			}
3870			
3871			if ( typeof s == 'number' )
3872			{
3873				if ( s < 0 )
3874				{
3875					return "0px";
3876				}
3877				return s+"px";
3878			}
3879			
3880			/* Check if the last character is not 0-9 */
3881			var c = s.charCodeAt( s.length-1 );
3882			if (c < 0x30 || c > 0x39)
3883			{
3884				return s;
3885			}
3886			return s+"px";
3887		}
3888		
3889		
3890		/**
3891		 * Get the width of a scroll bar in this browser being used
3892		 *  @returns {int} width in pixels
3893		 *  @memberof DataTable#oApi
3894		 */
3895		function _fnScrollBarWidth ()
3896		{  
3897			var inner = document.createElement('p');
3898			var style = inner.style;
3899			style.width = "100%";
3900			style.height = "200px";
3901			style.padding = "0px";
3902			
3903			var outer = document.createElement('div');
3904			style = outer.style;
3905			style.position = "absolute";
3906			style.top = "0px";
3907			style.left = "0px";
3908			style.visibility = "hidden";
3909			style.width = "200px";
3910			style.height = "150px";
3911			style.padding = "0px";
3912			style.overflow = "hidden";
3913			outer.appendChild(inner);
3914			
3915			document.body.appendChild(outer);
3916			var w1 = inner.offsetWidth;
3917			outer.style.overflow = 'scroll';
3918			var w2 = inner.offsetWidth;
3919			if ( w1 == w2 )
3920			{
3921				w2 = outer.clientWidth;
3922			}
3923			
3924			document.body.removeChild(outer);
3925			return (w1 - w2);  
3926		}
3927		
3928		/**
3929		 * Change the order of the table
3930		 *  @param {object} oSettings dataTables settings object
3931		 *  @param {bool} bApplyClasses optional - should we apply classes or not
3932		 *  @memberof DataTable#oApi
3933		 */
3934		function _fnSort ( oSettings, bApplyClasses )
3935		{
3936			var
3937				i, iLen, j, jLen, k, kLen,
3938				sDataType, nTh,
3939				aaSort = [],
3940			 	aiOrig = [],
3941				oSort = DataTable.ext.oSort,
3942				aoData = oSettings.aoData,
3943				aoColumns = oSettings.aoColumns,
3944				oAria = oSettings.oLanguage.oAria;
3945			
3946			/* No sorting required if server-side or no sorting array */
3947			if ( !oSettings.oFeatures.bServerSide && 
3948				(oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) )
3949			{
3950				aaSort = ( oSettings.aaSortingFixed !== null ) ?
3951					oSettings.aaSortingFixed.concat( oSettings.aaSorting ) :
3952					oSettings.aaSorting.slice();
3953				
3954				/* If there is a sorting data type, and a function belonging to it, then we need to
3955				 * get the data from the developer's function and apply it for this column
3956				 */
3957				for ( i=0 ; i<aaSort.length ; i++ )
3958				{
3959					var iColumn = aaSort[i][0];
3960					var iVisColumn = _fnColumnIndexToVisible( oSettings, iColumn );
3961					sDataType = oSettings.aoColumns[ iColumn ].sSortDataType;
3962					if ( DataTable.ext.afnSortData[sDataType] )
3963					{
3964						var aData = DataTable.ext.afnSortData[sDataType].call( 
3965							oSettings.oInstance, oSettings, iColumn, iVisColumn
3966						);
3967						if ( aData.length === aoData.length )
3968						{
3969							for ( j=0, jLen=aoData.length ; j<jLen ; j++ )
3970							{
3971								_fnSetCellData( oSettings, j, iColumn, aData[j] );
3972							}
3973						}
3974						else
3975						{
3976							_fnLog( oSettings, 0, "Returned data sort array (col "+iColumn+") is the wrong length" );
3977						}
3978					}
3979				}
3980				
3981				/* Create a value - key array of the current row positions such that we can use their
3982				 * current position during the sort, if values match, in order to perform stable sorting
3983				 */
3984				for ( i=0, iLen=oSettings.aiDisplayMaster.length ; i<iLen ; i++ )
3985				{
3986					aiOrig[ oSettings.aiDisplayMaster[i] ] = i;
3987				}
3988		
3989				/* Build an internal data array which is specific to the sort, so we can get and prep
3990				 * the data to be sorted only once, rather than needing to do it every time the sorting
3991				 * function runs. This make the sorting function a very simple comparison
3992				 */
3993				var iSortLen = aaSort.length;
3994				var fnSortFormat, aDataSort;
3995				for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
3996				{
3997					for ( j=0 ; j<iSortLen ; j++ )
3998					{
3999						aDataSort = aoColumns[ aaSort[j][0] ].aDataSort;
4000		
4001						for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
4002						{
4003							sDataType = aoColumns[ aDataSort[k] ].sType;
4004							fnSortFormat = oSort[ (sDataType ? sDataType : 'string')+"-pre" ];
4005							
4006							aoData[i]._aSortData[ aDataSort[k] ] = fnSortFormat ?
4007								fnSortFormat( _fnGetCellData( oSettings, i, aDataSort[k], 'sort' ) ) :
4008								_fnGetCellData( oSettings, i, aDataSort[k], 'sort' );
4009						}
4010					}
4011				}
4012				
4013				/* Do the sort - here we want multi-column sorting based on a given data source (column)
4014				 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
4015				 * follow on it's own, but this is what we want (example two column sorting):
4016				 *  fnLocalSorting = function(a,b){
4017				 *  	var iTest;
4018				 *  	iTest = oSort['string-asc']('data11', 'data12');
4019				 *  	if (iTest !== 0)
4020				 *  		return iTest;
4021				 *    iTest = oSort['numeric-desc']('data21', 'data22');
4022				 *    if (iTest !== 0)
4023				 *  		return iTest;
4024				 *  	return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
4025				 *  }
4026				 * Basically we have a test for each sorting column, if the data in that column is equal,
4027				 * test the next column. If all columns match, then we use a numeric sort on the row 
4028				 * positions in the original data array to provide a stable sort.
4029				 */
4030				oSettings.aiDisplayMaster.sort( function ( a, b ) {
4031					var k, l, lLen, iTest, aDataSort, sDataType;
4032					for ( k=0 ; k<iSortLen ; k++ )
4033					{
4034						aDataSort = aoColumns[ aaSort[k][0] ].aDataSort;
4035		
4036						for ( l=0, lLen=aDataSort.length ; l<lLen ; l++ )
4037						{
4038							sDataType = aoColumns[ aDataSort[l] ].sType;
4039							
4040							iTest = oSort[ (sDataType ? sDataType : 'string')+"-"+aaSort[k][1] ](
4041								aoData[a]._aSortData[ aDataSort[l] ],
4042								aoData[b]._aSortData[ aDataSort[l] ]
4043							);
4044						
4045							if ( iTest !== 0 )
4046							{
4047								return iTest;
4048							}
4049						}
4050					}
4051					
4052					return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
4053				} );
4054			}
4055			
4056			/* Alter the sorting classes to take account of the changes */
4057			if ( (bApplyClasses === undefined || bApplyClasses) && !oSettings.oFeatures.bDeferRender )
4058			{
4059				_fnSortingClasses( oSettings );
4060			}
4061		
4062			for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
4063			{
4064				var sTitle = aoColumns[i].sTitle.replace( /<.*?>/g, "" );
4065				nTh = aoColumns[i].nTh;
4066				nTh.removeAttribute('aria-sort');
4067				nTh.removeAttribute('aria-label');
4068				
4069				/* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
4070				if ( aoColumns[i].bSortable )
4071				{
4072					if ( aaSort.length > 0 && aaSort[0][0] == i )
4073					{
4074						nTh.setAttribute('aria-sort', aaSort[0][1]=="asc" ? "ascending" : "descending" );
4075						
4076						var nextSort = (aoColumns[i].asSorting[ aaSort[0][2]+1 ]) ? 
4077							aoColumns[i].asSorting[ aaSort[0][2]+1 ] : aoColumns[i].asSorting[0];
4078						nTh.setAttribute('aria-label', sTitle+
4079							(nextSort=="asc" ? oAria.sSortAscending : oAria.sSortDescending) );
4080					}
4081					else
4082					{
4083						nTh.setAttribute('aria-label', sTitle+
4084							(aoColumns[i].asSorting[0]=="asc" ? oAria.sSortAscending : oAria.sSortDescending) );
4085					}
4086				}
4087				else
4088				{
4089					nTh.setAttribute('aria-label', sTitle);
4090				}
4091			}
4092			
4093			/* Tell the draw function that we have sorted the data */
4094			oSettings.bSorted = true;
4095			$(oSettings.oInstance).trigger('sort', oSettings);
4096			
4097			/* Copy the master data into the draw array and re-draw */
4098			if ( oSettings.oFeatures.bFilter )
4099			{
4100				/* _fnFilter() will redraw the table for us */
4101				_fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
4102			}
4103			else
4104			{
4105				oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
4106				oSettings._iDisplayStart = 0; /* reset display back to page 0 */
4107				_fnCalculateEnd( oSettings );
4108				_fnDraw( oSettings );
4109			}
4110		}
4111		
4112		
4113		/**
4114		 * Attach a sort handler (click) to a node
4115		 *  @param {object} oSettings dataTables settings object
4116		 *  @param {node} nNode node to attach the handler to
4117		 *  @param {int} iDataIndex column sorting index
4118		 *  @param {function} [fnCallback] callback function
4119		 *  @memberof DataTable#oApi
4120		 */
4121		function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback )
4122		{
4123			_fnBindAction( nNode, {}, function (e) {
4124				/* If the column is not sortable - don't to anything */
4125				if ( oSettings.aoColumns[iDataIndex].bSortable === false )
4126				{
4127					return;
4128				}
4129				
4130				/*
4131				 * This is a little bit odd I admit... I declare a temporary function inside the scope of
4132				 * _fnBuildHead and the click handler in order that the code presented here can be used 
4133				 * twice - once for when bProcessing is enabled, and another time for when it is 
4134				 * disabled, as we need to perform slightly different actions.
4135				 *   Basically the issue here is that the Javascript engine in modern browsers don't 
4136				 * appear to allow the rendering engine to update the display while it is still executing
4137				 * it's thread (well - it does but only after long intervals). This means that the 
4138				 * 'processing' display doesn't appear for a table sort. To break the js thread up a bit
4139				 * I force an execution break by using setTimeout - but this breaks the expected 
4140				 * thread continuation for the end-developer's point of view (their code would execute
4141				 * too early), so we only do it when we absolutely have to.
4142				 */
4143				var fnInnerSorting = function () {
4144					var iColumn, iNextSort;
4145					
4146					/* If the shift key is pressed then we are multiple column sorting */
4147					if ( e.shiftKey )
4148					{
4149						/* Are we already doing some kind of sort on this column? */
4150						var bFound = false;
4151						for ( var i=0 ; i<oSettings.aaSorting.length ; i++ )
4152						{
4153							if ( oSettings.aaSorting[i][0] == iDataIndex )
4154							{
4155								bFound = true;
4156								iColumn = oSettings.aaSorting[i][0];
4157								iNextSort = oSettings.aaSorting[i][2]+1;
4158								
4159								if ( !oSettings.aoColumns[iColumn].asSorting[iNextSort] )
4160								{
4161									/* Reached the end of the sorting options, remove from multi-col sort */
4162									oSettings.aaSorting.splice( i, 1 );
4163								}
4164								else
4165								{
4166									/* Move onto next sorting direction */
4167									oSettings.aaSorting[i][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
4168									oSettings.aaSorting[i][2] = iNextSort;
4169								}
4170								break;
4171							}
4172						}
4173						
4174						/* No sort yet - add it in */
4175						if ( bFound === false )
4176						{
4177							oSettings.aaSorting.push( [ iDataIndex, 
4178								oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
4179						}
4180					}
4181					else
4182					{
4183						/* If no shift key then single column sort */
4184						if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex )
4185						{
4186							iColumn = oSettings.aaSorting[0][0];
4187							iNextSort = oSettings.aaSorting[0][2]+1;
4188							if ( !oSettings.aoColumns[iColumn].asSorting[iNextSort] )
4189							{
4190								iNextSort = 0;
4191							}
4192							oSettings.aaSorting[0][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
4193							oSettings.aaSorting[0][2] = iNextSort;
4194						}
4195						else
4196						{
4197							oSettings.aaSorting.splice( 0, oSettings.aaSorting.length );
4198							oSettings.aaSorting.push( [ iDataIndex, 
4199								oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
4200						}
4201					}
4202					
4203					/* Run the sort */
4204					_fnSort( oSettings );
4205				}; /* /fnInnerSorting */
4206				
4207				if ( !oSettings.oFeatures.bProcessing )
4208				{
4209					fnInnerSorting();
4210				}
4211				else
4212				{
4213					_fnProcessingDisplay( oSettings, true );
4214					setTimeout( function() {
4215						fnInnerSorting();
4216						if ( !oSettings.oFeatures.bServerSide )
4217						{
4218							_fnProcessingDisplay( oSettings, false );
4219						}
4220					}, 0 );
4221				}
4222				
4223				/* Call the user specified callback function - used for async user interaction */
4224				if ( typeof fnCallback == 'function' )
4225				{
4226					fnCallback( oSettings );
4227				}
4228			} );
4229		}
4230		
4231		
4232		/**
4233		 * Set the sorting classes on the header, Note: it is safe to call this function 
4234		 * when bSort and bSortClasses are false
4235		 *  @param {object} oSettings dataTables settings object
4236		 *  @memberof DataTable#oApi
4237		 */
4238		function _fnSortingClasses( oSettings )
4239		{
4240			var i, iLen, j, jLen, iFound;
4241			var aaSort, sClass;
4242			var iColumns = oSettings.aoColumns.length;
4243			var oClasses = oSettings.oClasses;
4244			
4245			for ( i=0 ; i<iColumns ; i++ )
4246			{
4247				if ( oSettings.aoColumns[i].bSortable )
4248				{
4249					$(oSettings.aoColumns[i].nTh).removeClass( oClasses.sSortAsc +" "+ oClasses.sSortDesc +
4250						" "+ oSettings.aoColumns[i].sSortingClass );
4251				}
4252			}
4253			
4254			if ( oSettings.aaSortingFixed !== null )
4255			{
4256				aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
4257			}
4258			else
4259			{
4260				aaSort = oSettings.aaSorting.slice();
4261			}
4262			
4263			/* Apply the required classes to the header */
4264			for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
4265			{
4266				if ( oSettings.aoColumns[i].bSortable )
4267				{
4268					sClass = oSettings.aoColumns[i].sSortingClass;
4269					iFound = -1;
4270					for ( j=0 ; j<aaSort.length ; j++ )
4271					{
4272						if ( aaSort[j][0] == i )
4273						{
4274							sClass = ( aaSort[j][1] == "asc" ) ?
4275								oClasses.sSortAsc : oClasses.sSortDesc;
4276							iFound = j;
4277							break;
4278						}
4279					}
4280					$(oSettings.aoColumns[i].nTh).addClass( sClass );
4281					
4282					if ( oSettings.bJUI )
4283					{
4284						/* jQuery UI uses extra markup */
4285						var jqSpan = $("span."+oClasses.sSortIcon,  oSettings.aoColumns[i].nTh);
4286						jqSpan.removeClass(oClasses.sSortJUIAsc +" "+ oClasses.sSortJUIDesc +" "+ 
4287							oClasses.sSortJUI +" "+ oClasses.sSortJUIAscAllowed +" "+ oClasses.sSortJUIDescAllowed );
4288						
4289						var sSpanClass;
4290						if ( iFound == -1 )
4291						{
4292						 	sSpanClass = oSettings.aoColumns[i].sSortingClassJUI;
4293						}
4294						else if ( aaSort[iFound][1] == "asc" )
4295						{
4296							sSpanClass = oClasses.sSortJUIAsc;
4297						}
4298						else
4299						{
4300							sSpanClass = oClasses.sSortJUIDesc;
4301						}
4302						
4303						jqSpan.addClass( sSpanClass );
4304					}
4305				}
4306				else
4307				{
4308					/* No sorting on this column, so add the base class. This will have been assigned by
4309					 * _fnAddColumn
4310					 */
4311					$(oSettings.aoColumns[i].nTh).addClass( oSettings.aoColumns[i].sSortingClass );
4312				}
4313			}
4314			
4315			/* 
4316			 * Apply the required classes to the table body
4317			 * Note that this is given as a feature switch since it can significantly slow down a sort
4318			 * on large data sets (adding and removing of classes is always slow at the best of times..)
4319			 * Further to this, note that this code is admittedly fairly ugly. It could be made a lot 
4320			 * simpler using jQuery selectors and add/removeClass, but that is significantly slower
4321			 * (on the order of 5 times slower) - hence the direct DOM manipulation here.
4322			 * Note that for deferred drawing we do use jQuery - the reason being that taking the first
4323			 * row found to see if the whole column needs processed can miss classes since the first
4324			 * column might be new.
4325			 */
4326			sClass = oClasses.sSortColumn;
4327			
4328			if ( oSettings.oFeatures.bSort && oSettings.oFeatures.bSortClasses )
4329			{
4330				var nTds = _fnGetTdNodes( oSettings );
4331				
4332				/* Determine what the sorting class for each column should be */
4333				var iClass, iTargetCol;
4334				var asClasses = [];
4335				for (i = 0; i < iColumns; i++)
4336				{
4337					asClasses.push("");
4338				}
4339				for (i = 0, iClass = 1; i < aaSort.length; i++)
4340				{
4341					iTargetCol = parseInt( aaSort[i][0], 10 );
4342					asClasses[iTargetCol] = sClass + iClass;
4343					
4344					if ( iClass < 3 )
4345					{
4346						iClass++;
4347					}
4348				}
4349				
4350				/* Make changes to the classes for each cell as needed */
4351				var reClass = new RegExp(sClass + "[123]");
4352				var sTmpClass, sCurrentClass, sNewClass;
4353				for ( i=0, iLen=nTds.length; i<iLen; i++ )
4354				{
4355					/* Determine which column we're looking at */
4356					iTargetCol = i % iColumns;
4357					
4358					/* What is the full list of classes now */
4359					sCurrentClass = nTds[i].className;
4360					/* What sorting class should be applied? */
4361					sNewClass = asClasses[iTargetCol];
4362					/* What would the new full list be if we did a replacement? */
4363					sTmpClass = sCurrentClass.replace(reClass, sNewClass);
4364					
4365					if ( sTmpClass != sCurrentClass )
4366					{
4367						/* We changed something */
4368						nTds[i].className = $.trim( sTmpClass );
4369					}
4370					else if ( sNewClass.length > 0 && sCurrentClass.indexOf(sNewClass) == -1 )
4371					{
4372						/* We need to add a class */
4373						nTds[i].className = sCurrentClass + " " + sNewClass;
4374					}
4375				}
4376			}
4377		}
4378		
4379		
4380		
4381		/**
4382		 * Save the state of a table in a cookie such that the page can be reloaded
4383		 *  @param {object} oSettings dataTables settings object
4384		 *  @memberof DataTable#oApi
4385		 */
4386		function _fnSaveState ( oSettings )
4387		{
4388			if ( !oSettings.oFeatures.bStateSave || oSettings.bDestroying )
4389			{
4390				return;
4391			}
4392		
4393			/* Store the interesting variables */
4394			var i, iLen, bInfinite=oSettings.oScroll.bInfinite;
4395			var oState = {
4396				"iCreate":      new Date().getTime(),
4397				"iStart":       (bInfinite ? 0 : oSettings._iDisplayStart),
4398				"iEnd":         (bInfinite ? oSettings._iDisplayLength : oSettings._iDisplayEnd),
4399				"iLength":      oSettings._iDisplayLength,
4400				"aaSorting":    $.extend( true, [], oSettings.aaSorting ),
4401				"oSearch":      $.extend( true, {}, oSettings.oPreviousSearch ),
4402				"aoSearchCols": $.extend( true, [], oSettings.aoPreSearchCols ),
4403				"abVisCols":    []
4404			};
4405		
4406			for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
4407			{
4408				oState.abVisCols.push( oSettings.aoColumns[i].bVisible );
4409			}
4410		
4411			_fnCallbackFire( oSettings, "aoStateSaveParams", 'stateSaveParams', [oSettings, oState] );
4412			
4413			oSettings.fnStateSave.call( oSettings.oInstance, oSettings, oState );
4414		}
4415		
4416		
4417		/**
4418		 * Attempt to load a saved table state from a cookie
4419		 *  @param {object} oSettings dataTables settings object
4420		 *  @param {object} oInit DataTables init object so we can override settings
4421		 *  @memberof DataTable#oApi
4422		 */
4423		function _fnLoadState ( oSettings, oInit )
4424		{
4425			if ( !oSettings.oFeatures.bStateSave )
4426			{
4427				return;
4428			}
4429		
4430			var oData = oSettings.fnStateLoad.call( oSettings.oInstance, oSettings );
4431			if ( !oData )
4432			{
4433				return;
4434			}
4435			
4436			/* Allow custom and plug-in manipulation functions to alter the saved data set and
4437			 * cancelling of loading by returning false
4438			 */
4439			var abStateLoad = _fnCallbackFire( oSettings, 'aoStateLoadParams', 'stateLoadParams', [oSettings, oData] );
4440			if ( $.inArray( false, abStateLoad ) !== -1 )
4441			{
4442				return;
4443			}
4444			
4445			/* Store the saved state so it might be accessed at any time */
4446			oSettings.oLoadedState = $.extend( true, {}, oData );
4447			
4448			/* Restore key features */
4449			oSettings._iDisplayStart    = oData.iStart;
4450			oSettings.iInitDisplayStart = oData.iStart;
4451			oSettings._iDisplayEnd      = oData.iEnd;
4452			oSettings._iDisplayLength   = oData.iLength;
4453			oSettings.aaSorting         = oData.aaSorting.slice();
4454			oSettings.saved_aaSorting   = oData.aaSorting.slice();
4455			
4456			/* Search filtering  */
4457			$.extend( oSettings.oPreviousSearch, oData.oSearch );
4458			$.extend( true, oSettings.aoPreSearchCols, oData.aoSearchCols );
4459			
4460			/* Column visibility state
4461			 * Pass back visibility settings to the init handler, but to do not here override
4462			 * the init object that the user might have passed in
4463			 */
4464			oInit.saved_aoColumns = [];
4465			for ( var i=0 ; i<oData.abVisCols.length ; i++ )
4466			{
4467				oInit.saved_aoColumns[i] = {};
4468				oInit.saved_aoColumns[i].bVisible = oData.abVisCols[i];
4469			}
4470		
4471			_fnCallbackFire( oSettings, 'aoStateLoaded', 'stateLoaded', [oSettings, oData] );
4472		}
4473		
4474		
4475		/**
4476		 * Create a new cookie with a value to store the state of a table
4477		 *  @param {string} sName name of the cookie to create
4478		 *  @param {string} sValue the value the cookie should take
4479		 *  @param {int} iSecs duration of the cookie
4480		 *  @param {string} sBaseName sName is made up of the base + file name - this is the base
4481		 *  @param {function} fnCallback User definable function to modify the cookie
4482		 *  @memberof DataTable#oApi
4483		 */
4484		function _fnCreateCookie ( sName, sValue, iSecs, sBaseName, fnCallback )
4485		{
4486			var date = new Date();
4487			date.setTime( date.getTime()+(iSecs*1000) );
4488			
4489			/* 
4490			 * Shocking but true - it would appear IE has major issues with having the path not having
4491			 * a trailing slash on it. We need the cookie to be available based on the path, so we
4492			 * have to append the file name to the cookie name. Appalling. Thanks to vex for adding the
4493			 * patch to use at least some of the path
4494			 */
4495			var aParts = window.location.pathname.split('/');
4496			var sNameFile = sName + '_' + aParts.pop().replace(/[\/:]/g,"").toLowerCase();
4497			var sFullCookie, oData;
4498			
4499			if ( fnCallback !== null )
4500			{
4501				oData = (typeof $.parseJSON === 'function') ? 
4502					$.parseJSON( sValue ) : eval( '('+sValue+')' );
4503				sFullCookie = fnCallback( sNameFile, oData, date.toGMTString(),
4504					aParts.join('/')+"/" );
4505			}
4506			else
4507			{
4508				sFullCookie = sNameFile + "=" + encodeURIComponent(sValue) +
4509					"; expires=" + date.toGMTString() +"; path=" + aParts.join('/')+"/";
4510			}
4511			
4512			/* Are we going to go over the cookie limit of 4KiB? If so, try to delete a cookies
4513			 * belonging to DataTables.
4514			 */
4515			var
4516				aCookies =document.cookie.split(';'),
4517				iNewCookieLen = sFullCookie.split(';')[0].length,
4518				aOldCookies = [];
4519			
4520			if ( iNewCookieLen+document.cookie.length+10 > 4096 ) /* Magic 10 for padding */
4521			{
4522				for ( var i=0, iLen=aCookies.length ; i<iLen ; i++ )
4523				{
4524					if ( aCookies[i].indexOf( sBaseName ) != -1 )
4525					{
4526						/* It's a DataTables cookie, so eval it and check the time stamp */
4527						var aSplitCookie = aCookies[i].split('=');
4528						try {
4529							oData = eval( '('+decodeURIComponent(aSplitCookie[1])+')' );
4530		
4531							if ( oData && oData.iCreate )
4532							{
4533								aOldCookies.push( {
4534									"name": aSplitCookie[0],
4535									"time": oData.iCreate
4536								} );
4537							}
4538						}
4539						catch( e ) {}
4540					}
4541				}
4542		
4543				// Make sure we delete the oldest ones first
4544				aOldCookies.sort( function (a, b) {
4545					return b.time - a.time;
4546				} );
4547		
4548				// Eliminate as many old DataTables cookies as we need to
4549				while ( iNewCookieLen + document.cookie.length + 10 > 4096 ) {
4550					if ( aOldCookies.length === 0 ) {
4551						// Deleted all DT cookies and still not enough space. Can't state save
4552						return;
4553					}
4554					
4555					var old = aOldCookies.pop();
4556					document.cookie = old.name+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+
4557						aParts.join('/') + "/";
4558				}
4559			}
4560			
4561			document.cookie = sFullCookie;
4562		}
4563		
4564		
4565		/**
4566		 * Read an old cookie to get a cookie with an old table state
4567		 *  @param {string} sName name of the cookie to read
4568		 *  @returns {string} contents of the cookie - or null if no cookie with that name found
4569		 *  @memberof DataTable#oApi
4570		 */
4571		function _fnReadCookie ( sName )
4572		{
4573			var
4574				aParts = window.location.pathname.split('/'),
4575				sNameEQ = sName + '_' + aParts[aParts.length-1].replace(/[\/:]/g,"").toLowerCase() + '=',
4576			 	sCookieContents = document.cookie.split(';');
4577			
4578			for( var i=0 ; i<sCookieContents.length ; i++ )
4579			{
4580				var c = sCookieContents[i];
4581				
4582				while (c.charAt(0)==' ')
4583				{
4584					c = c.substring(1,c.length);
4585				}
4586				
4587				if (c.indexOf(sNameEQ) === 0)
4588				{
4589					return decodeURIComponent( c.substring(sNameEQ.length,c.length) );
4590				}
4591			}
4592			return null;
4593		}
4594		
4595		
4596		/**
4597		 * Return the settings object for a particular table
4598		 *  @param {node} nTable table we are using as a dataTable
4599		 *  @returns {object} Settings object - or null if not found
4600		 *  @memberof DataTable#oApi
4601		 */
4602		function _fnSettingsFromNode ( nTable )
4603		{
4604			for ( var i=0 ; i<DataTable.settings.length ; i++ )
4605			{
4606				if ( DataTable.settings[i].nTable === nTable )
4607				{
4608					return DataTable.settings[i];
4609				}
4610			}
4611			
4612			return null;
4613		}
4614		
4615		
4616		/**
4617		 * Return an array with the TR nodes for the table
4618		 *  @param {object} oSettings dataTables settings object
4619		 *  @returns {array} TR array
4620		 *  @memberof DataTable#oApi
4621		 */
4622		function _fnGetTrNodes ( oSettings )
4623		{
4624			var aNodes = [];
4625			var aoData = oSettings.aoData;
4626			for ( var i=0, iLen=aoData.length ; i<iLen ; i++ )
4627			{
4628				if ( aoData[i].nTr !== null )
4629				{
4630					aNodes.push( aoData[i].nTr );
4631				}
4632			}
4633			return aNodes;
4634		}
4635		
4636		
4637		/**
4638		 * Return an flat array with all TD nodes for the table, or row
4639		 *  @param {object} oSettings dataTables settings object
4640		 *  @param {int} [iIndividualRow] aoData index to get the nodes for - optional 
4641		 *    if not given then the return array will contain all nodes for the table
4642		 *  @returns {array} TD array
4643		 *  @memberof DataTable#oApi
4644		 */
4645		function _fnGetTdNodes ( oSettings, iIndividualRow )
4646		{
4647			var anReturn = [];
4648			var iCorrector;
4649			var anTds, nTd;
4650			var iRow, iRows=oSettings.aoData.length,
4651				iColumn, iColumns, oData, sNodeName, iStart=0, iEnd=iRows;
4652			
4653			/* Allow the collection to be limited to just one row */
4654			if ( iIndividualRow !== undefined )
4655			{
4656				iStart = iIndividualRow;
4657				iEnd = iIndividualRow+1;
4658			}
4659		
4660			for ( iRow=iStart ; iRow<iEnd ; iRow++ )
4661			{
4662				oData = oSettings.aoData[iRow];
4663				if ( oData.nTr !== null )
4664				{
4665					/* get the TD child nodes - taking into account text etc nodes */
4666					anTds = [];
4667					nTd = oData.nTr.firstChild;
4668					while ( nTd )
4669					{
4670						sNodeName = nTd.nodeName.toLowerCase();
4671						if ( sNodeName == 'td' || sNodeName == 'th' )
4672						{
4673							anTds.push( nTd );
4674						}
4675						nTd = nTd.nextSibling;
4676					}
4677		
4678					iCorrector = 0;
4679					for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
4680					{
4681						if ( oSettings.aoColumns[iColumn].bVisible )
4682						{
4683							anReturn.push( anTds[iColumn-iCorrector] );
4684						}
4685						else
4686						{
4687							anReturn.push( oData._anHidden[iColumn] );
4688							iCorrector++;
4689						}
4690					}
4691				}
4692			}
4693		
4694			return anReturn;
4695		}
4696		
4697		
4698		/**
4699		 * Log an error message
4700		 *  @param {object} oSettings dataTables settings object
4701		 *  @param {int} iLevel log error messages, or display them to the user
4702		 *  @param {string} sMesg error message
4703		 *  @memberof DataTable#oApi
4704		 */
4705		function _fnLog( oSettings, iLevel, sMesg )
4706		{
4707			var sAlert = (oSettings===null) ?
4708				"DataTables warning: "+sMesg :
4709				"DataTables warning (table id = '"+oSettings.sTableId+"'): "+sMesg;
4710			
4711			if ( iLevel === 0 )
4712			{
4713				if ( DataTable.ext.sErrMode == 'alert' )
4714				{
4715					alert( sAlert );
4716				}
4717				else
4718				{
4719					throw new Error(sAlert);
4720				}
4721				return;
4722			}
4723			else if ( window.console && console.log )
4724			{
4725				console.log( sAlert );
4726			}
4727		}
4728		
4729		
4730		/**
4731		 * See if a property is defined on one object, if so assign it to the other object
4732		 *  @param {object} oRet target object
4733		 *  @param {object} oSrc source object
4734		 *  @param {string} sName property
4735		 *  @param {string} [sMappedName] name to map too - optional, sName used if not given
4736		 *  @memberof DataTable#oApi
4737		 */
4738		function _fnMap( oRet, oSrc, sName, sMappedName )
4739		{
4740			if ( sMappedName === undefined )
4741			{
4742				sMappedName = sName;
4743			}
4744			if ( oSrc[sName] !== undefined )
4745			{
4746				oRet[sMappedName] = oSrc[sName];
4747			}
4748		}
4749		
4750		
4751		/**
4752		 * Extend objects - very similar to jQuery.extend, but deep copy objects, and shallow
4753		 * copy arrays. The reason we need to do this, is that we don't want to deep copy array
4754		 * init values (such as aaSorting) since the dev wouldn't be able to override them, but
4755		 * we do want to deep copy arrays.
4756		 *  @param {object} oOut Object to extend
4757		 *  @param {object} oExtender Object from which the properties will be applied to oOut
4758		 *  @returns {object} oOut Reference, just for convenience - oOut === the return.
4759		 *  @memberof DataTable#oApi
4760		 *  @todo This doesn't take account of arrays inside the deep copied objects.
4761		 */
4762		function _fnExtend( oOut, oExtender )
4763		{
4764			var val;
4765			
4766			for ( var prop in oExtender )
4767			{
4768				if ( oExtender.hasOwnProperty(prop) )
4769				{
4770					val = oExtender[prop];
4771		
4772					if ( typeof oInit[prop] === 'object' && val !== null && $.isArray(val) === false )
4773					{
4774						$.extend( true, oOut[prop], val );
4775					}
4776					else
4777					{
4778						oOut[prop] = val;
4779					}
4780				}
4781			}
4782		
4783			return oOut;
4784		}
4785		
4786		
4787		/**
4788		 * Bind an event handers to allow a click or return key to activate the callback.
4789		 * This is good for accessibility since a return on the keyboard will have the
4790		 * same effect as a click, if the element has focus.
4791		 *  @param {element} n Element to bind the action to
4792		 *  @param {object} oData Data object to pass to the triggered function
4793		 *  @param {function} fn Callback function for when the event is triggered
4794		 *  @memberof DataTable#oApi
4795		 */
4796		function _fnBindAction( n, oData, fn )
4797		{
4798			$(n)
4799				.bind( 'click.DT', oData, function (e) {
4800						n.blur(); // Remove focus outline for mouse users
4801						fn(e);
4802					} )
4803				.bind( 'keypress.DT', oData, function (e){
4804					if ( e.which === 13 ) {
4805						fn(e);
4806					} } )
4807				.bind( 'selectstart.DT', function () {
4808					/* Take the brutal approach to cancelling text selection */
4809					return false;
4810					} );
4811		}
4812		
4813		
4814		/**
4815		 * Register a callback function. Easily allows a callback function to be added to
4816		 * an array store of callback functions that can then all be called together.
4817		 *  @param {object} oSettings dataTables settings object
4818		 *  @param {string} sStore Name of the array storage for the callbacks in oSettings
4819		 *  @param {function} fn Function to be called back
4820		 *  @param {string} sName Identifying name for the callback (i.e. a label)
4821		 *  @memberof DataTable#oApi
4822		 */
4823		function _fnCallbackReg( oSettings, sStore, fn, sName )
4824		{
4825			if ( fn )
4826			{
4827				oSettings[sStore].push( {
4828					"fn": fn,
4829					"sName": sName
4830				} );
4831			}
4832		}
4833		
4834		
4835		/**
4836		 * Fire callback functions and trigger events. Note that the loop over the callback
4837		 * array store is done backwards! Further note that you do not want to fire off triggers
4838		 * in time sensitive applications (for example cell creation) as its slow.
4839		 *  @param {object} oSettings dataTables settings object
4840		 *  @param {string} sStore Name of the array storage for the callbacks in oSettings
4841		 *  @param {string} sTrigger Name of the jQuery custom event to trigger. If null no trigger
4842		 *    is fired
4843		 *  @param {array} aArgs Array of arguments to pass to the callback function / trigger
4844		 *  @memberof DataTable#oApi
4845		 */
4846		function _fnCallbackFire( oSettings, sStore, sTrigger, aArgs )
4847		{
4848			var aoStore = oSettings[sStore];
4849			var aRet =[];
4850		
4851			for ( var i=aoStore.length-1 ; i>=0 ; i-- )
4852			{
4853				aRet.push( aoStore[i].fn.apply( oSettings.oInstance, aArgs ) );
4854			}
4855		
4856			if ( sTrigger !== null )
4857			{
4858				$(oSettings.oInstance).trigger(sTrigger, aArgs);
4859			}
4860		
4861			return aRet;
4862		}
4863		
4864		
4865		/**
4866		 * JSON stringify. If JSON.stringify it provided by the browser, json2.js or any other
4867		 * library, then we use that as it is fast, safe and accurate. If the function isn't 
4868		 * available then we need to built it ourselves - the inspiration for this function comes
4869		 * from Craig Buckler ( http://www.sitepoint.com/javascript-json-serialization/ ). It is
4870		 * not perfect and absolutely should not be used as a replacement to json2.js - but it does
4871		 * do what we need, without requiring a dependency for DataTables.
4872		 *  @param {object} o JSON object to be converted
4873		 *  @returns {string} JSON string
4874		 *  @memberof DataTable#oApi
4875		 */
4876		var _fnJsonString = (window.JSON) ? JSON.stringify : function( o )
4877		{
4878			/* Not an object or array */
4879			var sType = typeof o;
4880			if (sType !== "object" || o === null)
4881			{
4882				// simple data type
4883				if (sType === "string")
4884				{
4885					o = '"'+o+'"';
4886				}
4887				return o+"";
4888			}
4889		
4890			/* If object or array, need to recurse over it */
4891			var
4892				sProp, mValue,
4893				json = [],
4894				bArr = $.isArray(o);
4895			
4896			for (sProp in o)
4897			{
4898				mValue = o[sProp];
4899				sType = typeof mValue;
4900		
4901				if (sType === "string")
4902				{
4903					mValue = '"'+mValue+'"';
4904				}
4905				else if (sType === "object" && mValue !== null)
4906				{
4907					mValue = _fnJsonString(mValue);
4908				}
4909		
4910				json.push((bArr ? "" : '"'+sProp+'":') + mValue);
4911			}
4912		
4913			return (bArr ? "[" : "{") + json + (bArr ? "]" : "}");
4914		};
4915		
4916		
4917		/**
4918		 * From some browsers (specifically IE6/7) we need special handling to work around browser
4919		 * bugs - this function is used to detect when these workarounds are needed.
4920		 *  @param {object} oSettings dataTables settings object
4921		 *  @memberof DataTable#oApi
4922		 */
4923		function _fnBrowserDetect( oSettings )
4924		{
4925			/* IE6/7 will oversize a width 100% element inside a scrolling element, to include the
4926			 * width of the scrollbar, while other browsers ensure the inner element is contained
4927			 * without forcing scrolling
4928			 */
4929			var n = $(
4930				'<div style="position:absolute; top:0; left:0; height:1px; width:1px; overflow:hidden">'+
4931					'<div style="position:absolute; top:1px; left:1px; width:100px; overflow:scroll;">'+
4932						'<div id="DT_BrowserTest" style="width:100%; height:10px;"></div>'+
4933					'</div>'+
4934				'</div>')[0];
4935		
4936			document.body.appendChild( n );
4937			oSettings.oBrowser.bScrollOversize = $('#DT_BrowserTest', n)[0].offsetWidth === 100 ? true : false;
4938			document.body.removeChild( n );
4939		}
4940		
4941
4942		/**
4943		 * Perform a jQuery selector action on the table's TR elements (from the tbody) and
4944		 * return the resulting jQuery object.
4945		 *  @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
4946		 *  @param {object} [oOpts] Optional parameters for modifying the rows to be included
4947		 *  @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
4948		 *    criterion ("applied") or all TR elements (i.e. no filter).
4949		 *  @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
4950		 *    Can be either 'current', whereby the current sorting of the table is used, or
4951		 *    'original' whereby the original order the data was read into the table is used.
4952		 *  @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
4953		 *    ("current") or not ("all"). If 'current' is given, then order is assumed to be 
4954		 *    'current' and filter is 'applied', regardless of what they might be given as.
4955		 *  @returns {object} jQuery object, filtered by the given selector.
4956		 *  @dtopt API
4957		 *
4958		 *  @example
4959		 *    $(document).ready(function() {
4960		 *      var oTable = $('#example').dataTable();
4961		 *
4962		 *      // Highlight every second row
4963		 *      oTable.$('tr:odd').css('backgroundColor', 'blue');
4964		 *    } );
4965		 *
4966		 *  @example
4967		 *    $(document).ready(function() {
4968		 *      var oTable = $('#example').dataTable();
4969		 *
4970		 *      // Filter to rows with 'Webkit' in them, add a background colour and then
4971		 *      // remove the filter, thus highlighting the 'Webkit' rows only.
4972		 *      oTable.fnFilter('Webkit');
4973		 *      oTable.$('tr', {"filter": "applied"}).css('backgroundColor', 'blue');
4974		 *      oTable.fnFilter('');
4975		 *    } );
4976		 */
4977		this.$ = function ( sSelector, oOpts )
4978		{
4979			var i, iLen, a = [], tr;
4980			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
4981			var aoData = oSettings.aoData;
4982			var aiDisplay = oSettings.aiDisplay;
4983			var aiDisplayMaster = oSettings.aiDisplayMaster;
4984		
4985			if ( !oOpts )
4986			{
4987				oOpts = {};
4988			}
4989		
4990			oOpts = $.extend( {}, {
4991				"filter": "none", // applied
4992				"order": "current", // "original"
4993				"page": "all" // current
4994			}, oOpts );
4995		
4996			// Current page implies that order=current and fitler=applied, since it is fairly
4997			// senseless otherwise
4998			if ( oOpts.page == 'current' )
4999			{
5000				for ( i=oSettings._iDisplayStart, iLen=oSettings.fnDisplayEnd() ; i<iLen ; i++ )
5001				{
5002					tr = aoData[ aiDisplay[i] ].nTr;
5003					if ( tr )
5004					{
5005						a.push( tr );
5006					}
5007				}
5008			}
5009			else if ( oOpts.order == "current" && oOpts.filter == "none" )
5010			{
5011				for ( i=0, iLen=aiDisplayMaster.length ; i<iLen ; i++ )
5012				{
5013					tr = aoData[ aiDisplayMaster[i] ].nTr;
5014					if ( tr )
5015					{
5016						a.push( tr );
5017					}
5018				}
5019			}
5020			else if ( oOpts.order == "current" && oOpts.filter == "applied" )
5021			{
5022				for ( i=0, iLen=aiDisplay.length ; i<iLen ; i++ )
5023				{
5024					tr = aoData[ aiDisplay[i] ].nTr;
5025					if ( tr )
5026					{
5027						a.push( tr );
5028					}
5029				}
5030			}
5031			else if ( oOpts.order == "original" && oOpts.filter == "none" )
5032			{
5033				for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
5034				{
5035					tr = aoData[ i ].nTr ;
5036					if ( tr )
5037					{
5038						a.push( tr );
5039					}
5040				}
5041			}
5042			else if ( oOpts.order == "original" && oOpts.filter == "applied" )
5043			{
5044				for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
5045				{
5046					tr = aoData[ i ].nTr;
5047					if ( $.inArray( i, aiDisplay ) !== -1 && tr )
5048					{
5049						a.push( tr );
5050					}
5051				}
5052			}
5053			else
5054			{
5055				_fnLog( oSettings, 1, "Unknown selection options" );
5056			}
5057		
5058			/* We need to filter on the TR elements and also 'find' in their descendants
5059			 * to make the selector act like it would in a full table - so we need
5060			 * to build both results and then combine them together
5061			 */
5062			var jqA = $(a);
5063			var jqTRs = jqA.filter( sSelector );
5064			var jqDescendants = jqA.find( sSelector );
5065		
5066			return $( [].concat($.makeArray(jqTRs), $.makeArray(jqDescendants)) );
5067		};
5068		
5069		
5070		/**
5071		 * Almost identical to $ in operation, but in this case returns the data for the matched
5072		 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
5073		 * rather than any descendants, so the data can be obtained for the row/cell. If matching
5074		 * rows are found, the data returned is the original data array/object that was used to  
5075		 * create the row (or a generated array if from a DOM source).
5076		 *
5077		 * This method is often useful in-combination with $ where both functions are given the
5078		 * same parameters and the array indexes will match identically.
5079		 *  @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
5080		 *  @param {object} [oOpts] Optional parameters for modifying the rows to be included
5081		 *  @param {string} [oOpts.filter=none] Select elements that meet the current filter
5082		 *    criterion ("applied") or all elements (i.e. no filter).
5083		 *  @param {string} [oOpts.order=current] Order of the data in the processed array.
5084		 *    Can be either 'current', whereby the current sorting of the table is used, or
5085		 *    'original' whereby the original order the data was read into the table is used.
5086		 *  @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
5087		 *    ("current") or not ("all"). If 'current' is given, then order is assumed to be 
5088		 *    'current' and filter is 'applied', regardless of what they might be given as.
5089		 *  @returns {array} Data for the matched elements. If any elements, as a result of the
5090		 *    selector, were not TR, TD or TH elements in the DataTable, they will have a null 
5091		 *    entry in the array.
5092		 *  @dtopt API
5093		 *
5094		 *  @example
5095		 *    $(document).ready(function() {
5096		 *      var oTable = $('#example').dataTable();
5097		 *
5098		 *      // Get the data from the first row in the table
5099		 *      var data = oTable._('tr:first');
5100		 *
5101		 *      // Do something useful with the data
5102		 *      alert( "First cell is: "+data[0] );
5103		 *    } );
5104		 *
5105		 *  @example
5106		 *    $(document).ready(function() {
5107		 *      var oTable = $('#example').dataTable();
5108		 *
5109		 *      // Filter to 'Webkit' and get all data for 
5110		 *      oTable.fnFilter('Webkit');
5111		 *      var data = oTable._('tr', {"filter": "applied"});
5112		 *      
5113		 *      // Do something with the data
5114		 *      alert( data.length+" rows matched the filter" );
5115		 *    } );
5116		 */
5117		this._ = function ( sSelector, oOpts )
5118		{
5119			var aOut = [];
5120			var i, iLen, iIndex;
5121			var aTrs = this.$( sSelector, oOpts );
5122		
5123			for ( i=0, iLen=aTrs.length ; i<iLen ; i++ )
5124			{
5125				aOut.push( this.fnGetData(aTrs[i]) );
5126			}
5127		
5128			return aOut;
5129		};
5130		
5131		
5132		/**
5133		 * Add a single new row or multiple rows of data to the table. Please note
5134		 * that this is suitable for client-side processing only - if you are using 
5135		 * server-side processing (i.e. "bServerSide": true), then to add data, you
5136		 * must add it to the data source, i.e. the server-side, through an Ajax call.
5137		 *  @param {array|object} mData The data to be added to the table. This can be:
5138		 *    <ul>
5139		 *      <li>1D array of data - add a single row with the data provided</li>
5140		 *      <li>2D array of arrays - add multiple rows in a single call</li>
5141		 *      <li>object - data object when using <i>mData</i></li>
5142		 *      <li>array of objects - multiple data objects when using <i>mData</i></li>
5143		 *    </ul>
5144		 *  @param {bool} [bRedraw=true] redraw the table or not
5145		 *  @returns {array} An array of integers, representing the list of indexes in 
5146		 *    <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to 
5147		 *    the table.
5148		 *  @dtopt API
5149		 *
5150		 *  @example
5151		 *    // Global var for counter
5152		 *    var giCount = 2;
5153		 *    
5154		 *    $(document).ready(function() {
5155		 *      $('#example').dataTable();
5156		 *    } );
5157		 *    
5158		 *    function fnClickAddRow() {
5159		 *      $('#example').dataTable().fnAddData( [
5160		 *        giCount+".1",
5161		 *        giCount+".2",
5162		 *        giCount+".3",
5163		 *        giCount+".4" ]
5164		 *      );
5165		 *        
5166		 *      giCount++;
5167		 *    }
5168		 */
5169		this.fnAddData = function( mData, bRedraw )
5170		{
5171			if ( mData.length === 0 )
5172			{
5173				return [];
5174			}
5175			
5176			var aiReturn = [];
5177			var iTest;
5178			
5179			/* Find settings from table node */
5180			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5181			
5182			/* Check if we want to add multiple rows or not */
5183			if ( typeof mData[0] === "object" && mData[0] !== null )
5184			{
5185				for ( var i=0 ; i<mData.length ; i++ )
5186				{
5187					iTest = _fnAddData( oSettings, mData[i] );
5188					if ( iTest == -1 )
5189					{
5190						return aiReturn;
5191					}
5192					aiReturn.push( iTest );
5193				}
5194			}
5195			else
5196			{
5197				iTest = _fnAddData( oSettings, mData );
5198				if ( iTest == -1 )
5199				{
5200					return aiReturn;
5201				}
5202				aiReturn.push( iTest );
5203			}
5204			
5205			oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
5206			
5207			if ( bRedraw === undefined || bRedraw )
5208			{
5209				_fnReDraw( oSettings );
5210			}
5211			return aiReturn;
5212		};
5213		
5214		
5215		/**
5216		 * This function will make DataTables recalculate the column sizes, based on the data 
5217		 * contained in the table and the sizes applied to the columns (in the DOM, CSS or 
5218		 * through the sWidth parameter). This can be useful when the width of the table's 
5219		 * parent element changes (for example a window resize).
5220		 *  @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
5221		 *  @dtopt API
5222		 *
5223		 *  @example
5224		 *    $(document).ready(function() {
5225		 *      var oTable = $('#example').dataTable( {
5226		 *        "sScrollY": "200px",
5227		 *        "bPaginate": false
5228		 *      } );
5229		 *      
5230		 *      $(window).bind('resize', function () {
5231		 *        oTable.fnAdjustColumnSizing();
5232		 *      } );
5233		 *    } );
5234		 */
5235		this.fnAdjustColumnSizing = function ( bRedraw )
5236		{
5237			var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
5238			_fnAdjustColumnSizing( oSettings );
5239			
5240			if ( bRedraw === undefined || bRedraw )
5241			{
5242				this.fnDraw( false );
5243			}
5244			else if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
5245			{
5246				/* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
5247				this.oApi._fnScrollDraw(oSettings);
5248			}
5249		};
5250		
5251		
5252		/**
5253		 * Quickly and simply clear a table
5254		 *  @param {bool} [bRedraw=true] redraw the table or not
5255		 *  @dtopt API
5256		 *
5257		 *  @example
5258		 *    $(document).ready(function() {
5259		 *      var oTable = $('#example').dataTable();
5260		 *      
5261		 *      // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
5262		 *      oTable.fnClearTable();
5263		 *    } );
5264		 */
5265		this.fnClearTable = function( bRedraw )
5266		{
5267			/* Find settings from table node */
5268			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5269			_fnClearTable( oSettings );
5270			
5271			if ( bRedraw === undefined || bRedraw )
5272			{
5273				_fnDraw( oSettings );
5274			}
5275		};
5276		
5277		
5278		/**
5279		 * The exact opposite of 'opening' a row, this function will close any rows which 
5280		 * are currently 'open'.
5281		 *  @param {node} nTr the table row to 'close'
5282		 *  @returns {int} 0 on success, or 1 if failed (can't find the row)
5283		 *  @dtopt API
5284		 *
5285		 *  @example
5286		 *    $(document).ready(function() {
5287		 *      var oTable;
5288		 *      
5289		 *      // 'open' an information row when a row is clicked on
5290		 *      $('#example tbody tr').click( function () {
5291		 *        if ( oTable.fnIsOpen(this) ) {
5292		 *          oTable.fnClose( this );
5293		 *        } else {
5294		 *          oTable.fnOpen( this, "Temporary row opened", "info_row" );
5295		 *        }
5296		 *      } );
5297		 *      
5298		 *      oTable = $('#example').dataTable();
5299		 *    } );
5300		 */
5301		this.fnClose = function( nTr )
5302		{
5303			/* Find settings from table node */
5304			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5305			
5306			for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ )
5307			{
5308				if ( oSettings.aoOpenRows[i].nParent == nTr )
5309				{
5310					var nTrParent = oSettings.aoOpenRows[i].nTr.parentNode;
5311					if ( nTrParent )
5312					{
5313						/* Remove it if it is currently on display */
5314						nTrParent.removeChild( oSettings.aoOpenRows[i].nTr );
5315					}
5316					oSettings.aoOpenRows.splice( i, 1 );
5317					return 0;
5318				}
5319			}
5320			return 1;
5321		};
5322		
5323		
5324		/**
5325		 * Remove a row for the table
5326		 *  @param {mixed} mTarget The index of the row from aoData to be deleted, or
5327		 *    the TR element you want to delete
5328		 *  @param {function|null} [fnCallBack] Callback function
5329		 *  @param {bool} [bRedraw=true] Redraw the table or not
5330		 *  @returns {array} The row that was deleted
5331		 *  @dtopt API
5332		 *
5333		 *  @example
5334		 *    $(document).ready(function() {
5335		 *      var oTable = $('#example').dataTable();
5336		 *      
5337		 *      // Immediately remove the first row
5338		 *      oTable.fnDeleteRow( 0 );
5339		 *    } );
5340		 */
5341		this.fnDeleteRow = function( mTarget, fnCallBack, bRedraw )
5342		{
5343			/* Find settings from table node */
5344			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5345			var i, iLen, iAODataIndex;
5346			
5347			iAODataIndex = (typeof mTarget === 'object') ? 
5348				_fnNodeToDataIndex(oSettings, mTarget) : mTarget;
5349			
5350			/* Return the data array from this row */
5351			var oData = oSettings.aoData.splice( iAODataIndex, 1 );
5352		
5353			/* Update the _DT_RowIndex parameter */
5354			for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
5355			{
5356				if ( oSettings.aoData[i].nTr !== null )
5357				{
5358					oSettings.aoData[i].nTr._DT_RowIndex = i;
5359				}
5360			}
5361			
5362			/* Remove the target row from the search array */
5363			var iDisplayIndex = $.inArray( iAODataIndex, oSettings.aiDisplay );
5364			oSettings.asDataSearch.splice( iDisplayIndex, 1 );
5365			
5366			/* Delete from the display arrays */
5367			_fnDeleteIndex( oSettings.aiDisplayMaster, iAODataIndex );
5368			_fnDeleteIndex( oSettings.aiDisplay, iAODataIndex );
5369			
5370			/* If there is a user callback function - call it */
5371			if ( typeof fnCallBack === "function" )
5372			{
5373				fnCallBack.call( this, oSettings, oData );
5374			}
5375			
5376			/* Check for an 'overflow' they case for displaying the table */
5377			if ( oSettings._iDisplayStart >= oSettings.fnRecordsDisplay() )
5378			{
5379				oSettings._iDisplayStart -= oSettings._iDisplayLength;
5380				if ( oSettings._iDisplayStart < 0 )
5381				{
5382					oSettings._iDisplayStart = 0;
5383				}
5384			}
5385			
5386			if ( bRedraw === undefined || bRedraw )
5387			{
5388				_fnCalculateEnd( oSettings );
5389				_fnDraw( oSettings );
5390			}
5391			
5392			return oData;
5393		};
5394		
5395		
5396		/**
5397		 * Restore the table to it's original state in the DOM by removing all of DataTables 
5398		 * enhancements, alterations to the DOM structure of the table and event listeners.
5399		 *  @param {boolean} [bRemove=false] Completely remove the table from the DOM
5400		 *  @dtopt API
5401		 *
5402		 *  @example
5403		 *    $(document).ready(function() {
5404		 *      // This example is fairly pointless in reality, but shows how fnDestroy can be used
5405		 *      var oTable = $('#example').dataTable();
5406		 *      oTable.fnDestroy();
5407		 *    } );
5408		 */
5409		this.fnDestroy = function ( bRemove )
5410		{
5411			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5412			var nOrig = oSettings.nTableWrapper.parentNode;
5413			var nBody = oSettings.nTBody;
5414			var i, iLen;
5415		
5416			bRemove = (bRemove===undefined) ? false : bRemove;
5417			
5418			/* Flag to note that the table is currently being destroyed - no action should be taken */
5419			oSettings.bDestroying = true;
5420			
5421			/* Fire off the destroy callbacks for plug-ins etc */
5422			_fnCallbackFire( oSettings, "aoDestroyCallback", "destroy", [oSettings] );
5423		
5424			/* If the table is not being removed, restore the hidden columns */
5425			if ( !bRemove )
5426			{
5427				for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
5428				{
5429					if ( oSettings.aoColumns[i].bVisible === false )
5430					{
5431						this.fnSetColumnVis( i, true );
5432					}
5433				}
5434			}
5435			
5436			/* Blitz all DT events */
5437			$(oSettings.nTableWrapper).find('*').andSelf().unbind('.DT');
5438			
5439			/* If there is an 'empty' indicator row, remove it */
5440			$('tbody>tr>td.'+oSettings.oClasses.sRowEmpty, oSettings.nTable).parent().remove();
5441			
5442			/* When scrolling we had to break the table up - restore it */
5443			if ( oSettings.nTable != oSettings.nTHead.parentNode )
5444			{
5445				$(oSettings.nTable).children('thead').remove();
5446				oSettings.nTable.appendChild( oSettings.nTHead );
5447			}
5448			
5449			if ( oSettings.nTFoot && oSettings.nTable != oSettings.nTFoot.parentNode )
5450			{
5451				$(oSettings.nTable).children('tfoot').remove();
5452				oSettings.nTable.appendChild( oSettings.nTFoot );
5453			}
5454			
5455			/* Remove the DataTables generated nodes, events and classes */
5456			oSettings.nTable.parentNode.removeChild( oSettings.nTable );
5457			$(oSettings.nTableWrapper).remove();
5458			
5459			oSettings.aaSorting = [];
5460			oSettings.aaSortingFixed = [];
5461			_fnSortingClasses( oSettings );
5462			
5463			$(_fnGetTrNodes( oSettings )).removeClass( oSettings.asStripeClasses.join(' ') );
5464			
5465			$('th, td', oSettings.nTHead).removeClass( [
5466				oSettings.oClasses.sSortable,
5467				oSettings.oClasses.sSortableAsc,
5468				oSettings.oClasses.sSortableDesc,
5469				oSettings.oClasses.sSortableNone ].join(' ')
5470			);
5471			if ( oSettings.bJUI )
5472			{
5473				$('th span.'+oSettings.oClasses.sSortIcon
5474					+ ', td span.'+oSettings.oClasses.sSortIcon, oSettings.nTHead).remove();
5475		
5476				$('th, td', oSettings.nTHead).each( function () {
5477					var jqWrapper = $('div.'+oSettings.oClasses.sSortJUIWrapper, this);
5478					var kids = jqWrapper.contents();
5479					$(this).append( kids );
5480					jqWrapper.remove();
5481				} );
5482			}
5483			
5484			/* Add the TR elements back into the table in their original order */
5485			if ( !bRemove && oSettings.nTableReinsertBefore )
5486			{
5487				nOrig.insertBefore( oSettings.nTable, oSettings.nTableReinsertBefore );
5488			}
5489			else if ( !bRemove )
5490			{
5491				nOrig.appendChild( oSettings.nTable );
5492			}
5493		
5494			for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
5495			{
5496				if ( oSettings.aoData[i].nTr !== null )
5497				{
5498					nBody.appendChild( oSettings.aoData[i].nTr );
5499				}
5500			}
5501			
5502			/* Restore the width of the original table */
5503			if ( oSettings.oFeatures.bAutoWidth === true )
5504			{
5505			  oSettings.nTable.style.width = _fnStringToCss(oSettings.sDestroyWidth);
5506			}
5507			
5508			/* If the were originally stripe classes - then we add them back here. Note
5509			 * this is not fool proof (for example if not all rows had stripe classes - but
5510			 * it's a good effort without getting carried away
5511			 */
5512			iLen = oSettings.asDestroyStripes.length;
5513			if (iLen)
5514			{
5515				var anRows = $(nBody).children('tr');
5516				for ( i=0 ; i<iLen ; i++ )
5517				{
5518					anRows.filter(':nth-child(' + iLen + 'n + ' + i + ')').addClass( oSettings.asDestroyStripes[i] );
5519				}
5520			}
5521			
5522			/* Remove the settings object from the settings array */
5523			for ( i=0, iLen=DataTable.settings.length ; i<iLen ; i++ )
5524			{
5525				if ( DataTable.settings[i] == oSettings )
5526				{
5527					DataTable.settings.splice( i, 1 );
5528				}
5529			}
5530			
5531			/* End it all */
5532			oSettings = null;
5533			oInit = null;
5534		};
5535		
5536		
5537		/**
5538		 * Redraw the table
5539		 *  @param {bool} [bComplete=true] Re-filter and resort (if enabled) the table before the draw.
5540		 *  @dtopt API
5541		 *
5542		 *  @example
5543		 *    $(document).ready(function() {
5544		 *      var oTable = $('#example').dataTable();
5545		 *      
5546		 *      // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
5547		 *      oTable.fnDraw();
5548		 *    } );
5549		 */
5550		this.fnDraw = function( bComplete )
5551		{
5552			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5553			if ( bComplete === false )
5554			{
5555				_fnCalculateEnd( oSettings );
5556				_fnDraw( oSettings );
5557			}
5558			else
5559			{
5560				_fnReDraw( oSettings );
5561			}
5562		};
5563		
5564		
5565		/**
5566		 * Filter the input based on data
5567		 *  @param {string} sInput String to filter the table on
5568		 *  @param {int|null} [iColumn] Column to limit filtering to
5569		 *  @param {bool} [bRegex=false] Treat as regular expression or not
5570		 *  @param {bool} [bSmart=true] Perform smart filtering or not
5571		 *  @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
5572		 *  @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
5573		 *  @dtopt API
5574		 *
5575		 *  @example
5576		 *    $(document).ready(function() {
5577		 *      var oTable = $('#example').dataTable();
5578		 *      
5579		 *      // Sometime later - filter...
5580		 *      oTable.fnFilter( 'test string' );
5581		 *    } );
5582		 */
5583		this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
5584		{
5585			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5586			
5587			if ( !oSettings.oFeatures.bFilter )
5588			{
5589				return;
5590			}
5591			
5592			if ( bRegex === undefined || bRegex === null )
5593			{
5594				bRegex = false;
5595			}
5596			
5597			if ( bSmart === undefined || bSmart === null )
5598			{
5599				bSmart = true;
5600			}
5601			
5602			if ( bShowGlobal === undefined || bShowGlobal === null )
5603			{
5604				bShowGlobal = true;
5605			}
5606			
5607			if ( bCaseInsensitive === undefined || bCaseInsensitive === null )
5608			{
5609				bCaseInsensitive = true;
5610			}
5611			
5612			if ( iColumn === undefined || iColumn === null )
5613			{
5614				/* Global filter */
5615				_fnFilterComplete( oSettings, {
5616					"sSearch":sInput+"",
5617					"bRegex": bRegex,
5618					"bSmart": bSmart,
5619					"bCaseInsensitive": bCaseInsensitive
5620				}, 1 );
5621				
5622				if ( bShowGlobal && oSettings.aanFeatures.f )
5623				{
5624					var n = oSettings.aanFeatures.f;
5625					for ( var i=0, iLen=n.length ; i<iLen ; i++ )
5626					{
5627						// IE9 throws an 'unknown error' if document.activeElement is used
5628						// inside an iframe or frame...
5629						try {
5630							if ( n[i]._DT_Input != document.activeElement )
5631							{
5632								$(n[i]._DT_Input).val( sInput );
5633							}
5634						}
5635						catch ( e ) {
5636							$(n[i]._DT_Input).val( sInput );
5637						}
5638					}
5639				}
5640			}
5641			else
5642			{
5643				/* Single column filter */
5644				$.extend( oSettings.aoPreSearchCols[ iColumn ], {
5645					"sSearch": sInput+"",
5646					"bRegex": bRegex,
5647					"bSmart": bSmart,
5648					"bCaseInsensitive": bCaseInsensitive
5649				} );
5650				_fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
5651			}
5652		};
5653		
5654		
5655		/**
5656		 * Get the data for the whole table, an individual row or an individual cell based on the 
5657		 * provided parameters.
5658		 *  @param {int|node} [mRow] A TR row node, TD/TH cell node or an integer. If given as
5659		 *    a TR node then the data source for the whole row will be returned. If given as a
5660		 *    TD/TH cell node then iCol will be automatically calculated and the data for the
5661		 *    cell returned. If given as an integer, then this is treated as the aoData internal
5662		 *    data index for the row (see fnGetPosition) and the data for that row used.
5663		 *  @param {int} [iCol] Optional column index that you want the data of.
5664		 *  @returns {array|object|string} If mRow is undefined, then the data for all rows is
5665		 *    returned. If mRow is defined, just data for that row, and is iCol is
5666		 *    defined, only data for the designated cell is returned.
5667		 *  @dtopt API
5668		 *
5669		 *  @example
5670		 *    // Row data
5671		 *    $(document).ready(function() {
5672		 *      oTable = $('#example').dataTable();
5673		 *
5674		 *      oTable.$('tr').click( function () {
5675		 *        var data = oTable.fnGetData( this );
5676		 *        // ... do something with the array / object of data for the row
5677		 *      } );
5678		 *    } );
5679		 *
5680		 *  @example
5681		 *    // Individual cell data
5682		 *    $(document).ready(function() {
5683		 *      oTable = $('#example').dataTable();
5684		 *
5685		 *      oTable.$('td').click( function () {
5686		 *        var sData = oTable.fnGetData( this );
5687		 *        alert( 'The cell clicked on had the value of '+sData );
5688		 *      } );
5689		 *    } );
5690		 */
5691		this.fnGetData = function( mRow, iCol )
5692		{
5693			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5694			
5695			if ( mRow !== undefined )
5696			{
5697				var iRow = mRow;
5698				if ( typeof mRow === 'object' )
5699				{
5700					var sNode = mRow.nodeName.toLowerCase();
5701					if (sNode === "tr" )
5702					{
5703						iRow = _fnNodeToDataIndex(oSettings, mRow);
5704					}
5705					else if ( sNode === "td" )
5706					{
5707						iRow = _fnNodeToDataIndex(oSettings, mRow.parentNode);
5708						iCol = _fnNodeToColumnIndex( oSettings, iRow, mRow );
5709					}
5710				}
5711		
5712				if ( iCol !== undefined )
5713				{
5714					return _fnGetCellData( oSettings, iRow, iCol, '' );
5715				}
5716				return (oSettings.aoData[iRow]!==undefined) ?
5717					oSettings.aoData[iRow]._aData : null;
5718			}
5719			return _fnGetDataMaster( oSettings );
5720		};
5721		
5722		
5723		/**
5724		 * Get an array of the TR nodes that are used in the table's body. Note that you will 
5725		 * typically want to use the '$' API method in preference to this as it is more 
5726		 * flexible.
5727		 *  @param {int} [iRow] Optional row index for the TR element you want
5728		 *  @returns {array|node} If iRow is undefined, returns an array of all TR elements
5729		 *    in the table's body, or iRow is defined, just the TR element requested.
5730		 *  @dtopt API
5731		 *
5732		 *  @example
5733		 *    $(document).ready(function() {
5734		 *      var oTable = $('#example').dataTable();
5735		 *      
5736		 *      // Get the nodes from the table
5737		 *      var nNodes = oTable.fnGetNodes( );
5738		 *    } );
5739		 */
5740		this.fnGetNodes = function( iRow )
5741		{
5742			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5743			
5744			if ( iRow !== undefined ) {
5745				return (oSettings.aoData[iRow]!==undefined) ?
5746					oSettings.aoData[iRow].nTr : null;
5747			}
5748			return _fnGetTrNodes( oSettings );
5749		};
5750		
5751		
5752		/**
5753		 * Get the array indexes of a particular cell from it's DOM element
5754		 * and column index including hidden columns
5755		 *  @param {node} nNode this can either be a TR, TD or TH in the table's body
5756		 *  @returns {int} If nNode is given as a TR, then a single index is returned, or
5757		 *    if given as a cell, an array of [row index, column index (visible), 
5758		 *    column index (all)] is given.
5759		 *  @dtopt API
5760		 *
5761		 *  @example
5762		 *    $(document).ready(function() {
5763		 *      $('#example tbody td').click( function () {
5764		 *        // Get the position of the current data from the node
5765		 *        var aPos = oTable.fnGetPosition( this );
5766		 *        
5767		 *        // Get the data array for this row
5768		 *        var aData = oTable.fnGetData( aPos[0] );
5769		 *        
5770		 *        // Update the data array and return the value
5771		 *        aData[ aPos[1] ] = 'clicked';
5772		 *        this.innerHTML = 'clicked';
5773		 *      } );
5774		 *      
5775		 *      // Init DataTables
5776		 *      oTable = $('#example').dataTable();
5777		 *    } );
5778		 */
5779		this.fnGetPosition = function( nNode )
5780		{
5781			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5782			var sNodeName = nNode.nodeName.toUpperCase();
5783			
5784			if ( sNodeName == "TR" )
5785			{
5786				return _fnNodeToDataIndex(oSettings, nNode);
5787			}
5788			else if ( sNodeName == "TD" || sNodeName == "TH" )
5789			{
5790				var iDataIndex = _fnNodeToDataIndex( oSettings, nNode.parentNode );
5791				var iColumnIndex = _fnNodeToColumnIndex( oSettings, iDataIndex, nNode );
5792				return [ iDataIndex, _fnColumnIndexToVisible(oSettings, iColumnIndex ), iColumnIndex ];
5793			}
5794			return null;
5795		};
5796		
5797		
5798		/**
5799		 * Check to see if a row is 'open' or not.
5800		 *  @param {node} nTr the table row to check
5801		 *  @returns {boolean} true if the row is currently open, false otherwise
5802		 *  @dtopt API
5803		 *
5804		 *  @example
5805		 *    $(document).ready(function() {
5806		 *      var oTable;
5807		 *      
5808		 *      // 'open' an information row when a row is clicked on
5809		 *      $('#example tbody tr').click( function () {
5810		 *        if ( oTable.fnIsOpen(this) ) {
5811		 *          oTable.fnClose( this );
5812		 *        } else {
5813		 *          oTable.fnOpen( this, "Temporary row opened", "info_row" );
5814		 *        }
5815		 *      } );
5816		 *      
5817		 *      oTable = $('#example').dataTable();
5818		 *    } );
5819		 */
5820		this.fnIsOpen = function( nTr )
5821		{
5822			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5823			var aoOpenRows = oSettings.aoOpenRows;
5824			
5825			for ( var i=0 ; i<oSettings.aoOpenRows.length ; i++ )
5826			{
5827				if ( oSettings.aoOpenRows[i].nParent == nTr )
5828				{
5829					return true;
5830				}
5831			}
5832			return false;
5833		};
5834		
5835		
5836		/**
5837		 * This function will place a new row directly after a row which is currently 
5838		 * on display on the page, with the HTML contents that is passed into the 
5839		 * function. This can be used, for example, to ask for confirmation that a 
5840		 * particular record should be deleted.
5841		 *  @param {node} nTr The table row to 'open'
5842		 *  @param {string|node|jQuery} mHtml The HTML to put into the row
5843		 *  @param {string} sClass Class to give the new TD cell
5844		 *  @returns {node} The row opened. Note that if the table row passed in as the
5845		 *    first parameter, is not found in the table, this method will silently
5846		 *    return.
5847		 *  @dtopt API
5848		 *
5849		 *  @example
5850		 *    $(document).ready(function() {
5851		 *      var oTable;
5852		 *      
5853		 *      // 'open' an information row when a row is clicked on
5854		 *      $('#example tbody tr').click( function () {
5855		 *        if ( oTable.fnIsOpen(this) ) {
5856		 *          oTable.fnClose( this );
5857		 *        } else {
5858		 *          oTable.fnOpen( this, "Temporary row opened", "info_row" );
5859		 *        }
5860		 *      } );
5861		 *      
5862		 *      oTable = $('#example').dataTable();
5863		 *    } );
5864		 */
5865		this.fnOpen = function( nTr, mHtml, sClass )
5866		{
5867			/* Find settings from table node */
5868			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5869		
5870			/* Check that the row given is in the table */
5871			var nTableRows = _fnGetTrNodes( oSettings );
5872			if ( $.inArray(nTr, nTableRows) === -1 )
5873			{
5874				return;
5875			}
5876			
5877			/* the old open one if there is one */
5878			this.fnClose( nTr );
5879			
5880			var nNewRow = document.createElement("tr");
5881			var nNewCell = document.createElement("td");
5882			nNewRow.appendChild( nNewCell );
5883			nNewCell.className = sClass;
5884			nNewCell.colSpan = _fnVisbleColumns( oSettings );
5885		
5886			if (typeof mHtml === "string")
5887			{
5888				nNewCell.innerHTML = mHtml;
5889			}
5890			else
5891			{
5892				$(nNewCell).html( mHtml );
5893			}
5894		
5895			/* If the nTr isn't on the page at the moment - then we don't insert at the moment */
5896			var nTrs = $('tr', oSettings.nTBody);
5897			if ( $.inArray(nTr, nTrs) != -1  )
5898			{
5899				$(nNewRow).insertAfter(nTr);
5900			}
5901			
5902			oSettings.aoOpenRows.push( {
5903				"nTr": nNewRow,
5904				"nParent": nTr
5905			} );
5906			
5907			return nNewRow;
5908		};
5909		
5910		
5911		/**
5912		 * Change the pagination - provides the internal logic for pagination in a simple API 
5913		 * function. With this function you can have a DataTables table go to the next, 
5914		 * previous, first or last pages.
5915		 *  @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
5916		 *    or page number to jump to (integer), note that page 0 is the first page.
5917		 *  @param {bool} [bRedraw=true] Redraw the table or not
5918		 *  @dtopt API
5919		 *
5920		 *  @example
5921		 *    $(document).ready(function() {
5922		 *      var oTable = $('#example').dataTable();
5923		 *      oTable.fnPageChange( 'next' );
5924		 *    } );
5925		 */
5926		this.fnPageChange = function ( mAction, bRedraw )
5927		{
5928			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5929			_fnPageChange( oSettings, mAction );
5930			_fnCalculateEnd( oSettings );
5931			
5932			if ( bRedraw === undefined || bRedraw )
5933			{
5934				_fnDraw( oSettings );
5935			}
5936		};
5937		
5938		
5939		/**
5940		 * Show a particular column
5941		 *  @param {int} iCol The column whose display should be changed
5942		 *  @param {bool} bShow Show (true) or hide (false) the column
5943		 *  @param {bool} [bRedraw=true] Redraw the table or not
5944		 *  @dtopt API
5945		 *
5946		 *  @example
5947		 *    $(document).ready(function() {
5948		 *      var oTable = $('#example').dataTable();
5949		 *      
5950		 *      // Hide the second column after initialisation
5951		 *      oTable.fnSetColumnVis( 1, false );
5952		 *    } );
5953		 */
5954		this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
5955		{
5956			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
5957			var i, iLen;
5958			var aoColumns = oSettings.aoColumns;
5959			var aoData = oSettings.aoData;
5960			var nTd, bAppend, iBefore;
5961			
5962			/* No point in doing anything if we are requesting what is already true */
5963			if ( aoColumns[iCol].bVisible == bShow )
5964			{
5965				return;
5966			}
5967			
5968			/* Show the column */
5969			if ( bShow )
5970			{
5971				var iInsert = 0;
5972				for ( i=0 ; i<iCol ; i++ )
5973				{
5974					if ( aoColumns[i].bVisible )
5975					{
5976						iInsert++;
5977					}
5978				}
5979				
5980				/* Need to decide if we should use appendChild or insertBefore */
5981				bAppend = (iInsert >= _fnVisbleColumns( oSettings ));
5982		
5983				/* Which coloumn should we be inserting before? */
5984				if ( !bAppend )
5985				{
5986					for ( i=iCol ; i<aoColumns.length ; i++ )
5987					{
5988						if ( aoColumns[i].bVisible )
5989						{
5990							iBefore = i;
5991							break;
5992						}
5993					}
5994				}
5995		
5996				for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
5997				{
5998					if ( aoData[i].nTr !== null )
5999					{
6000						if ( bAppend )
6001						{
6002							aoData[i].nTr.appendChild( 
6003								aoData[i]._anHidden[iCol]
6004							);
6005						}
6006						else
6007						{
6008							aoData[i].nTr.insertBefore(
6009								aoData[i]._anHidden[iCol], 
6010								_fnGetTdNodes( oSettings, i )[iBefore] );
6011						}
6012					}
6013				}
6014			}
6015			else
6016			{
6017				/* Remove a column from display */
6018				for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
6019				{
6020					if ( aoData[i].nTr !== null )
6021					{
6022						nTd = _fnGetTdNodes( oSettings, i )[iCol];
6023						aoData[i]._anHidden[iCol] = nTd;
6024						nTd.parentNode.removeChild( nTd );
6025					}
6026				}
6027			}
6028		
6029			/* Clear to set the visible flag */
6030			aoColumns[iCol].bVisible = bShow;
6031		
6032			/* Redraw the header and footer based on the new column visibility */
6033			_fnDrawHead( oSettings, oSettings.aoHeader );
6034			if ( oSettings.nTFoot )
6035			{
6036				_fnDrawHead( oSettings, oSettings.aoFooter );
6037			}
6038			
6039			/* If there are any 'open' rows, then we need to alter the colspan for this col change */
6040			for ( i=0, iLen=oSettings.aoOpenRows.length ; i<iLen ; i++ )
6041			{
6042				oSettings.aoOpenRows[i].nTr.colSpan = _fnVisbleColumns( oSettings );
6043			}
6044			
6045			/* Do a redraw incase anything depending on the table columns needs it 
6046			 * (built-in: scrolling) 
6047			 */
6048			if ( bRedraw === undefined || bRedraw )
6049			{
6050				_fnAdjustColumnSizing( oSettings );
6051				_fnDraw( oSettings );
6052			}
6053			
6054			_fnSaveState( oSettings );
6055		};
6056		
6057		
6058		/**
6059		 * Get the settings for a particular table for external manipulation
6060		 *  @returns {object} DataTables settings object. See 
6061		 *    {@link DataTable.models.oSettings}
6062		 *  @dtopt API
6063		 *
6064		 *  @example
6065		 *    $(document).ready(function() {
6066		 *      var oTable = $('#example').dataTable();
6067		 *      var oSettings = oTable.fnSettings();
6068		 *      
6069		 *      // Show an example parameter from the settings
6070		 *      alert( oSettings._iDisplayStart );
6071		 *    } );
6072		 */
6073		this.fnSettings = function()
6074		{
6075			return _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
6076		};
6077		
6078		
6079		/**
6080		 * Sort the table by a particular column
6081		 *  @param {int} iCol the data index to sort on. Note that this will not match the 
6082		 *    'display index' if you have hidden data entries
6083		 *  @dtopt API
6084		 *
6085		 *  @example
6086		 *    $(document).ready(function() {
6087		 *      var oTable = $('#example').dataTable();
6088		 *      
6089		 *      // Sort immediately with columns 0 and 1
6090		 *      oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
6091		 *    } );
6092		 */
6093		this.fnSort = function( aaSort )
6094		{
6095			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
6096			oSettings.aaSorting = aaSort;
6097			_fnSort( oSettings );
6098		};
6099		
6100		
6101		/**
6102		 * Attach a sort listener to an element for a given column
6103		 *  @param {node} nNode the element to attach the sort listener to
6104		 *  @param {int} iColumn the column that a click on this node will sort on
6105		 *  @param {function} [fnCallback] callback function when sort is run
6106		 *  @dtopt API
6107		 *
6108		 *  @example
6109		 *    $(document).ready(function() {
6110		 *      var oTable = $('#example').dataTable();
6111		 *      
6112		 *      // Sort on column 1, when 'sorter' is clicked on
6113		 *      oTable.fnSortListener( document.getElementById('sorter'), 1 );
6114		 *    } );
6115		 */
6116		this.fnSortListener = function( nNode, iColumn, fnCallback )
6117		{
6118			_fnSortAttachListener( _fnSettingsFromNode( this[DataTable.ext.iApiIndex] ), nNode, iColumn,
6119			 	fnCallback );
6120		};
6121		
6122		
6123		/**
6124		 * Update a table cell or row - this method will accept either a single value to
6125		 * update the cell with, an array of values with one element for each column or
6126		 * an object in the same format as the original data source. The function is
6127		 * self-referencing in order to make the multi column updates easier.
6128		 *  @param {object|array|string} mData Data to update the cell/row with
6129		 *  @param {node|int} mRow TR element you want to update or the aoData index
6130		 *  @param {int} [iColumn] The column to update (not used of mData is an array or object)
6131		 *  @param {bool} [bRedraw=true] Redraw the table or not
6132		 *  @param {bool} [bAction=true] Perform pre-draw actions or not
6133		 *  @returns {int} 0 on success, 1 on error
6134		 *  @dtopt API
6135		 *
6136		 *  @example
6137		 *    $(document).ready(function() {
6138		 *      var oTable = $('#example').dataTable();
6139		 *      oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
6140		 *      oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], 1, 0 ); // Row
6141		 *    } );
6142		 */
6143		this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
6144		{
6145			var oSettings = _fnSettingsFromNode( this[DataTable.ext.iApiIndex] );
6146			var i, iLen, sDisplay;
6147			var iRow = (typeof mRow === 'object') ? 
6148				_fnNodeToDataIndex(oSettings, mRow) : mRow;
6149			
6150			if ( $.isArray(mData) && iColumn === undefined )
6151			{
6152				/* Array update - update the whole row */
6153				oSettings.aoData[iRow]._aData = mData.slice();
6154				
6155				/* Flag to the function that we are recursing */
6156				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
6157				{
6158					this.fnUpdate( _fnGetCellData( oSettings, iRow, i ), iRow, i, false, false );
6159				}
6160			}
6161			else if ( $.isPlainObject(mData) && iColumn === undefined )
6162			{
6163				/* Object update - update the whole row - assume the developer gets the object right */
6164				oSettings.aoData[iRow]._aData = $.extend( true, {}, mData );
6165		
6166				for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
6167				{
6168					this.fnUpdate( _fnGetCellData( oSettings, iRow, i ), iRow, i, false, false );
6169				}
6170			}
6171			else
6172			{
6173				/* Individual cell update */
6174				_fnSetCellData( oSettings, iRow, iColumn, mData );
6175				sDisplay = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
6176				
6177				var oCol = oSettings.aoColumns[iColumn];
6178				if ( oCol.fnRender !== null )
6179				{
6180					sDisplay = _fnRender( oSettings, iRow, iColumn );
6181					if ( oCol.bUseRendered )
6182					{
6183						_fnSetCellData( oSettings, iRow, iColumn, sDisplay );
6184					}
6185				}
6186				
6187				if ( oSettings.aoData[iRow].nTr !== null )
6188				{
6189					/* Do the actual HTML update */
6190					_fnGetTdNodes( oSettings, iRow )[iColumn].innerHTML = sDisplay;
6191				}
6192			}
6193			
6194			/* Modify the search index for this row (strictly this is likely not needed, since fnReDraw
6195			 * will rebuild the search array - however, the redraw might be disabled by the user)
6196			 */
6197			var iDisplayIndex = $.inArray( iRow, oSettings.aiDisplay );
6198			oSettings.asDataSearch[iDisplayIndex] = _fnBuildSearchRow(
6199				oSettings, 
6200				_fnGetRowData( oSettings, iRow, 'filter', _fnGetColumns( oSettings, 'bSearchable' ) )
6201			);
6202			
6203			/* Perform pre-draw actions */
6204			if ( bAction === undefined || bAction )
6205			{
6206				_fnAdjustColumnSizing( oSettings );
6207			}
6208			
6209			/* Redraw the table */
6210			if ( bRedraw === undefined || bRedraw )
6211			{
6212				_fnReDraw( oSettings );
6213			}
6214			return 0;
6215		};
6216		
6217		
6218		/**
6219		 * Provide a common method for plug-ins to check the version of DataTables being used, in order
6220		 * to ensure compatibility.
6221		 *  @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
6222		 *    formats "X" and "X.Y" are also acceptable.
6223		 *  @returns {boolean} true if this version of DataTables is greater or equal to the required
6224		 *    version, or false if this version of DataTales is not suitable
6225		 *  @method
6226		 *  @dtopt API
6227		 *
6228		 *  @example
6229		 *    $(document).ready(function() {
6230		 *      var oTable = $('#example').dataTable();
6231		 *      alert( oTable.fnVersionCheck( '1.9.0' ) );
6232		 *    } );
6233		 */
6234		this.fnVersionCheck = DataTable.ext.fnVersionCheck;
6235		
6236		
6237		/*
6238		 * This is really a good bit rubbish this method of exposing the internal methods
6239		 * publicly... - To be fixed in 2.0 using methods on the prototype
6240		 */
6241		
6242		
6243		/**
6244		 * Create a wrapper function for exporting an internal functions to an external API.
6245		 *  @param {string} sFunc API function name
6246		 *  @returns {function} wrapped function
6247		 *  @memberof DataTable#oApi
6248		 */
6249		function _fnExternApiFunc (sFunc)
6250		{
6251			return function() {
6252				var aArgs = [_fnSettingsFromNode(this[DataTable.ext.iApiIndex])].concat( 
6253					Array.prototype.slice.call(arguments) );
6254				return DataTable.ext.oApi[sFunc].apply( this, aArgs );
6255			};
6256		}
6257		
6258		
6259		/**
6260		 * Reference to internal functions for use by plug-in developers. Note that these
6261		 * methods are references to internal functions and are considered to be private.
6262		 * If you use these methods, be aware that they are liable to change between versions
6263		 * (check the upgrade notes).
6264		 *  @namespace
6265		 */
6266		this.oApi = {
6267			"_fnExternApiFunc": _fnExternApiFunc,
6268			"_fnInitialise": _fnInitialise,
6269			"_fnInitComplete": _fnInitComplete,
6270			"_fnLanguageCompat": _fnLanguageCompat,
6271			"_fnAddColumn": _fnAddColumn,
6272			"_fnColumnOptions": _fnColumnOptions,
6273			"_fnAddData": _fnAddData,
6274			"_fnCreateTr": _fnCreateTr,
6275			"_fnGatherData": _fnGatherData,
6276			"_fnBuildHead": _fnBuildHead,
6277			"_fnDrawHead": _fnDrawHead,
6278			"_fnDraw": _fnDraw,
6279			"_fnReDraw": _fnReDraw,
6280			"_fnAjaxUpdate": _fnAjaxUpdate,
6281			"_fnAjaxParameters": _fnAjaxParameters,
6282			"_fnAjaxUpdateDraw": _fnAjaxUpdateDraw,
6283			"_fnServerParams": _fnServerParams,
6284			"_fnAddOptionsHtml": _fnAddOptionsHtml,
6285			"_fnFeatureHtmlTable": _fnFeatureHtmlTable,
6286			"_fnScrollDraw": _fnScrollDraw,
6287			"_fnAdjustColumnSizing": _fnAdjustColumnSizing,
6288			"_fnFeatureHtmlFilter": _fnFeatureHtmlFilter,
6289			"_fnFilterComplete": _fnFilterComplete,
6290			"_fnFilterCustom": _fnFilterCustom,
6291			"_fnFilterColumn": _fnFilterColumn,
6292			"_fnFilter": _fnFilter,
6293			"_fnBuildSearchArray": _fnBuildSearchArray,
6294			"_fnBuildSearchRow": _fnBuildSearchRow,
6295			"_fnFilterCreateSearch": _fnFilterCreateSearch,
6296			"_fnDataToSearch": _fnDataToSearch,
6297			"_fnSort": _fnSort,
6298			"_fnSortAttachListener": _fnSortAttachListener,
6299			"_fnSortingClasses": _fnSortingClasses,
6300			"_fnFeatureHtmlPaginate": _fnFeatureHtmlPaginate,
6301			"_fnPageChange": _fnPageChange,
6302			"_fnFeatureHtmlInfo": _fnFeatureHtmlInfo,
6303			"_fnUpdateInfo": _fnUpdateInfo,
6304			"_fnFeatureHtmlLength": _fnFeatureHtmlLength,
6305			"_fnFeatureHtmlProcessing": _fnFeatureHtmlProcessing,
6306			"_fnProcessingDisplay": _fnProcessingDisplay,
6307			"_fnVisibleToColumnIndex": _fnVisibleToColumnIndex,
6308			"_fnColumnIndexToVisible": _fnColumnIndexToVisible,
6309			"_fnNodeToDataIndex": _fnNodeToDataIndex,
6310			"_fnVisbleColumns": _fnVisbleColumns,
6311			"_fnCalculateEnd": _fnCalculateEnd,
6312			"_fnConvertToWidth": _fnConvertToWidth,
6313			"_fnCalculateColumnWidths": _fnCalculateColumnWidths,
6314			"_fnScrollingWidthAdjust": _fnScrollingWidthAdjust,
6315			"_fnGetWidestNode": _fnGetWidestNode,
6316			"_fnGetMaxLenString": _fnGetMaxLenString,
6317			"_fnStringToCss": _fnStringToCss,
6318			"_fnDetectType": _fnDetectType,
6319			"_fnSettingsFromNode": _fnSettingsFromNode,
6320			"_fnGetDataMaster": _fnGetDataMaster,
6321			"_fnGetTrNodes": _fnGetTrNodes,
6322			"_fnGetTdNodes": _fnGetTdNodes,
6323			"_fnEscapeRegex": _fnEscapeRegex,
6324			"_fnDeleteIndex": _fnDeleteIndex,
6325			"_fnReOrderIndex": _fnReOrderIndex,
6326			"_fnColumnOrdering": _fnColumnOrdering,
6327			"_fnLog": _fnLog,
6328			"_fnClearTable": _fnClearTable,
6329			"_fnSaveState": _fnSaveState,
6330			"_fnLoadState": _fnLoadState,
6331			"_fnCreateCookie": _fnCreateCookie,
6332			"_fnReadCookie": _fnReadCookie,
6333			"_fnDetectHeader": _fnDetectHeader,
6334			"_fnGetUniqueThs": _fnGetUniqueThs,
6335			"_fnScrollBarWidth": _fnScrollBarWidth,
6336			"_fnApplyToChildren": _fnApplyToChildren,
6337			"_fnMap": _fnMap,
6338			"_fnGetRowData": _fnGetRowData,
6339			"_fnGetCellData": _fnGetCellData,
6340			"_fnSetCellData": _fnSetCellData,
6341			"_fnGetObjectDataFn": _fnGetObjectDataFn,
6342			"_fnSetObjectDataFn": _fnSetObjectDataFn,
6343			"_fnApplyColumnDefs": _fnApplyColumnDefs,
6344			"_fnBindAction": _fnBindAction,
6345			"_fnExtend": _fnExtend,
6346			"_fnCallbackReg": _fnCallbackReg,
6347			"_fnCallbackFire": _fnCallbackFire,
6348			"_fnJsonString": _fnJsonString,
6349			"_fnRender": _fnRender,
6350			"_fnNodeToColumnIndex": _fnNodeToColumnIndex,
6351			"_fnInfoMacros": _fnInfoMacros,
6352			"_fnBrowserDetect": _fnBrowserDetect,
6353			"_fnGetColumns": _fnGetColumns
6354		};
6355		
6356		$.extend( DataTable.ext.oApi, this.oApi );
6357		
6358		for ( var sFunc in DataTable.ext.oApi )
6359		{
6360			if ( sFunc )
6361			{
6362				this[sFunc] = _fnExternApiFunc(sFunc);
6363			}
6364		}
6365		
6366		
6367		var _that = this;
6368		this.each(function() {
6369			var i=0, iLen, j, jLen, k, kLen;
6370			var sId = this.getAttribute( 'id' );
6371			var bInitHandedOff = false;
6372			var bUsePassedData = false;
6373			
6374			
6375			/* Sanity check */
6376			if ( this.nodeName.toLowerCase() != 'table' )
6377			{
6378				_fnLog( null, 0, "Attempted to initialise DataTables on a node which is not a "+
6379					"table: "+this.nodeName );
6380				return;
6381			}
6382			
6383			/* Check to see if we are re-initialising a table */
6384			for ( i=0, iLen=DataTable.settings.length ; i<iLen ; i++ )
6385			{
6386				/* Base check on table node */
6387				if ( DataTable.settings[i].nTable == this )
6388				{
6389					if ( oInit === undefined || oInit.bRetrieve )
6390					{
6391						return DataTable.settings[i].oInstance;
6392					}
6393					else if ( oInit.bDestroy )
6394					{
6395						DataTable.settings[i].oInstance.fnDestroy();
6396						break;
6397					}
6398					else
6399					{
6400						_fnLog( DataTable.settings[i], 0, "Cannot reinitialise DataTable.\n\n"+
6401							"To retrieve the DataTables object for this table, pass no arguments or see "+
6402							"the docs for bRetrieve and bDestroy" );
6403						return;
6404					}
6405				}
6406				
6407				/* If the element we are initialising has the same ID as a table which was previously
6408				 * initialised, but the table nodes don't match (from before) then we destroy the old
6409				 * instance by simply deleting it. This is under the assumption that the table has been
6410				 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
6411				 */
6412				if ( DataTable.settings[i].sTableId == this.id )
6413				{
6414					DataTable.settings.splice( i, 1 );
6415					break;
6416				}
6417			}
6418			
6419			/* Ensure the table has an ID - required for accessibility */
6420			if ( sId === null || sId === "" )
6421			{
6422				sId = "DataTables_Table_"+(DataTable.ext._oExternConfig.iNextUnique++);
6423				this.id = sId;
6424			}
6425			
6426			/* Create the settings object for this table and set some of the default parameters */
6427			var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
6428				"nTable":        this,
6429				"oApi":          _that.oApi,
6430				"oInit":         oInit,
6431				"sDestroyWidth": $(this).width(),
6432				"sInstance":     sId,
6433				"sTableId":      sId
6434			} );
6435			DataTable.settings.push( oSettings );
6436			
6437			// Need to add the instance after the instance after the settings object has been added
6438			// to the settings array, so we can self reference the table instance if more than one
6439			oSettings.oInstance = (_that.length===1) ? _that : $(this).dataTable();
6440			
6441			/* Setting up the initialisation object */
6442			if ( !oInit )
6443			{
6444				oInit = {};
6445			}
6446			
6447			// Backwards compatibility, before we apply all the defaults
6448			if ( oInit.oLanguage )
6449			{
6450				_fnLanguageCompat( oInit.oLanguage );
6451			}
6452			
6453			oInit = _fnExtend( $.extend(true, {}, DataTable.defaults), oInit );
6454			
6455			// Map the initialisation options onto the settings object
6456			_fnMap( oSettings.oFeatures, oInit, "bPaginate" );
6457			_fnMap( oSettings.oFeatures, oInit, "bLengthChange" );
6458			_fnMap( oSettings.oFeatures, oInit, "bFilter" );
6459			_fnMap( oSettings.oFeatures, oInit, "bSort" );
6460			_fnMap( oSettings.oFeatures, oInit, "bInfo" );
6461			_fnMap( oSettings.oFeatures, oInit, "bProcessing" );
6462			_fnMap( oSettings.oFeatures, oInit, "bAutoWidth" );
6463			_fnMap( oSettings.oFeatures, oInit, "bSortClasses" );
6464			_fnMap( oSettings.oFeatures, oInit, "bServerSide" );
6465			_fnMap( oSettings.oFeatures, oInit, "bDeferRender" );
6466			_fnMap( oSettings.oScroll, oInit, "sScrollX", "sX" );
6467			_fnMap( oSettings.oScroll, oInit, "sScrollXInner", "sXInner" );
6468			_fnMap( oSettings.oScroll, oInit, "sScrollY", "sY" );
6469			_fnMap( oSettings.oScroll, oInit, "bScrollCollapse", "bCollapse" );
6470			_fnMap( oSettings.oScroll, oInit, "bScrollInfinite", "bInfinite" );
6471			_fnMap( oSettings.oScroll, oInit, "iScrollLoadGap", "iLoadGap" );
6472			_fnMap( oSettings.oScroll, oInit, "bScrollAutoCss", "bAutoCss" );
6473			_fnMap( oSettings, oInit, "asStripeClasses" );
6474			_fnMap( oSettings, oInit, "asStripClasses", "asStripeClasses" ); // legacy
6475			_fnMap( oSettings, oInit, "fnServerData" );
6476			_fnMap( oSettings, oInit, "fnFormatNumber" );
6477			_fnMap( oSettings, oInit, "sServerMethod" );
6478			_fnMap( oSettings, oInit, "aaSorting" );
6479			_fnMap( oSettings, oInit, "aaSortingFixed" );
6480			_fnMap( oSettings, oInit, "aLengthMenu" );
6481			_fnMap( oSettings, oInit, "sPaginationType" );
6482			_fnMap( oSettings, oInit, "sAjaxSource" );
6483			_fnMap( oSettings, oInit, "sAjaxDataProp" );
6484			_fnMap( oSettings, oInit, "iCookieDuration" );
6485			_fnMap( oSettings, oInit, "sCookiePrefix" );
6486			_fnMap( oSettings, oInit, "sDom" );
6487			_fnMap( oSettings, oInit, "bSortCellsTop" );
6488			_fnMap( oSettings, oInit, "iTabIndex" );
6489			_fnMap( oSettings, oInit, "oSearch", "oPreviousSearch" );
6490			_fnMap( oSettings, oInit, "aoSearchCols", "aoPreSearchCols" );
6491			_fnMap( oSettings, oInit, "iDisplayLength", "_iDisplayLength" );
6492			_fnMap( oSettings, oInit, "bJQueryUI", "bJUI" );
6493			_fnMap( oSettings, oInit, "fnCookieCallback" );
6494			_fnMap( oSettings, oInit, "fnStateLoad" );
6495			_fnMap( oSettings, oInit, "fnStateSave" );
6496			_fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
6497			
6498			/* Callback functions which are array driven */
6499			_fnCallbackReg( oSettings, 'aoDrawCallback',       oInit.fnDrawCallback,      'user' );
6500			_fnCallbackReg( oSettings, 'aoServerParams',       oInit.fnServerParams,      'user' );
6501			_fnCallbackReg( oSettings, 'aoStateSaveParams',    oInit.fnStateSaveParams,   'user' );
6502			_fnCallbackReg( oSettings, 'aoStateLoadParams',    oInit.fnStateLoadParams,   'user' );
6503			_fnCallbackReg( oSettings, 'aoStateLoaded',        oInit.fnStateLoaded,       'user' );
6504			_fnCallbackReg( oSettings, 'aoRowCallback',        oInit.fnRowCallback,       'user' );
6505			_fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow,        'user' );
6506			_fnCallbackReg( oSettings, 'aoHeaderCallback',     oInit.fnHeaderCallback,    'user' );
6507			_fnCallbackReg( oSettings, 'aoFooterCallback',     oInit.fnFooterCallback,    'user' );
6508			_fnCallbackReg( oSettings, 'aoInitComplete',       oInit.fnInitComplete,      'user' );
6509			_fnCallbackReg( oSettings, 'aoPreDrawCallback',    oInit.fnPreDrawCallback,   'user' );
6510			
6511			if ( oSettings.oFeatures.bServerSide && oSettings.oFeatures.bSort &&
6512				   oSettings.oFeatures.bSortClasses )
6513			{
6514				/* Enable sort classes for server-side processing. Safe to do it here, since server-side
6515				 * processing must be enabled by the developer
6516				 */
6517				_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSortingClasses, 'server_side_sort_classes' );
6518			}
6519			else if ( oSettings.oFeatures.bDeferRender )
6520			{
6521				_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSortingClasses, 'defer_sort_classes' );
6522			}
6523			
6524			if ( oInit.bJQueryUI )
6525			{
6526				/* Use the JUI classes object for display. You could clone the oStdClasses object if 
6527				 * you want to have multiple tables with multiple independent classes 
6528				 */
6529				$.extend( oSettings.oClasses, DataTable.ext.oJUIClasses );
6530				
6531				if ( oInit.sDom === DataTable.defaults.sDom && DataTable.defaults.sDom === "lfrtip" )
6532				{
6533					/* Set the DOM to use a layout suitable for jQuery UI's theming */
6534					oSettings.sDom = '<"H"lfr>t<"F"ip>';
6535				}
6536			}
6537			else
6538			{
6539				$.extend( oSettings.oClasses, DataTable.ext.oStdClasses );
6540			}
6541			$(this).addClass( oSettings.oClasses.sTable );
6542			
6543			/* Calculate the scroll bar width and cache it for use later on */
6544			if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
6545			{
6546				oSettings.oScroll.iBarWidth = _fnScrollBarWidth();
6547			}
6548			
6549			if ( oSettings.iInitDisplayStart === undefined )
6550			{
6551				/* Display start point, taking into account the save saving */
6552				oSettings.iInitDisplayStart = oInit.iDisplayStart;
6553				oSettings._iDisplayStart = oInit.iDisplayStart;
6554			}
6555			
6556			/* Must be done after everything which can be overridden by a cookie! */
6557			if ( oInit.bStateSave )
6558			{
6559				oSettings.oFeatures.bStateSave = true;
6560				_fnLoadState( oSettings, oInit );
6561				_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
6562			}
6563			
6564			if ( oInit.iDeferLoading !== null )
6565			{
6566				oSettings.bDeferLoading = true;
6567				var tmp = $.isArray( oInit.iDeferLoading );
6568				oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
6569				oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
6570			}
6571			
6572			if ( oInit.aaData !== null )
6573			{
6574				bUsePassedData = true;
6575			}
6576			
6577			/* Language definitions */
6578			if ( oInit.oLanguage.sUrl !== "" )
6579			{
6580				/* Get the language definitions from a file - because this Ajax call makes the language
6581				 * get async to the remainder of this function we use bInitHandedOff to indicate that 
6582				 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
6583				 */
6584				oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl;
6585				$.getJSON( oSettings.oLanguage.sUrl, null, function( json ) {
6586					_fnLanguageCompat( json );
6587					$.extend( true, oSettings.oLanguage, oInit.oLanguage, json );
6588					_fnInitialise( oSettings );
6589				} );
6590				bInitHandedOff = true;
6591			}
6592			else
6593			{
6594				$.extend( true, oSettings.oLanguage, oInit.oLanguage );
6595			}
6596			
6597			
6598			/*
6599			 * Stripes
6600			 */
6601			if ( oInit.asStripeClasses === null )
6602			{
6603				oSettings.asStripeClasses =[
6604					oSettings.oClasses.sStripeOdd,
6605					oSettings.oClasses.sStripeEven
6606				];
6607			}
6608			
6609			/* Remove row stripe classes if they are already on the table row */
6610			iLen=oSettings.asStripeClasses.length;
6611			oSettings.asDestroyStripes = [];
6612			if (iLen)
6613			{
6614				var bStripeRemove = false;
6615				var anRows = $(this).children('tbody').children('tr:lt(' + iLen + ')');
6616				for ( i=0 ; i<iLen ; i++ )
6617				{
6618					if ( anRows.hasClass( oSettings.asStripeClasses[i] ) )
6619					{
6620						bStripeRemove = true;
6621						
6622						/* Store the classes which we are about to remove so they can be re-added on destroy */
6623						oSettings.asDestroyStripes.push( oSettings.asStripeClasses[i] );
6624					}
6625				}
6626				
6627				if ( bStripeRemove )
6628				{
6629					anRows.removeClass( oSettings.asStripeClasses.join(' ') );
6630				}
6631			}
6632			
6633			/*
6634			 * Columns
6635			 * See if we should load columns automatically or use defined ones
6636			 */
6637			var anThs = [];
6638			var aoColumnsInit;
6639			var nThead = this.getElementsByTagName('thead');
6640			if ( nThead.length !== 0 )
6641			{
6642				_fnDetectHeader( oSettings.aoHeader, nThead[0] );
6643				anThs = _fnGetUniqueThs( oSettings );
6644			}
6645			
6646			/* If not given a column array, generate one with nulls */
6647			if ( oInit.aoColumns === null )
6648			{
6649				aoColumnsInit = [];
6650				for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
6651				{
6652					aoColumnsInit.push( null );
6653				}
6654			}
6655			else
6656			{
6657				aoColumnsInit = oInit.aoColumns;
6658			}
6659			
6660			/* Add the columns */
6661			for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
6662			{
6663				/* Short cut - use the loop to check if we have column visibility state to restore */
6664				if ( oInit.saved_aoColumns !== undefined && oInit.saved_aoColumns.length == iLen )
6665				{
6666					if ( aoColumnsInit[i] === null )
6667					{
6668						aoColumnsInit[i] = {};
6669					}
6670					aoColumnsInit[i].bVisible = oInit.saved_aoColumns[i].bVisible;
6671				}
6672				
6673				_fnAddColumn( oSettings, anThs ? anThs[i] : null );
6674			}
6675			
6676			/* Apply the column definitions */
6677			_fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
6678				_fnColumnOptions( oSettings, iCol, oDef );
6679			} );
6680			
6681			
6682			/*
6683			 * Sorting
6684			 * Check the aaSorting array
6685			 */
6686			for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )
6687			{
6688				if ( oSettings.aaSorting[i][0] >= oSettings.aoColumns.length )
6689				{
6690					oSettings.aaSorting[i][0] = 0;
6691				}
6692				var oColumn = oSettings.aoColumns[ oSettings.aaSorting[i][0] ];
6693				
6694				/* Add a default sorting index */
6695				if ( oSettings.aaSorting[i][2] === undefined )
6696				{
6697					oSettings.aaSorting[i][2] = 0;
6698				}
6699				
6700				/* If aaSorting is not defined, then we use the first indicator in asSorting */
6701				if ( oInit.aaSorting === undefined && oSettings.saved_aaSorting === undefined )
6702				{
6703					oSettings.aaSorting[i][1] = oColumn.asSorting[0];
6704				}
6705				
6706				/* Set the current sorting index based on aoColumns.asSorting */
6707				for ( j=0, jLen=oColumn.asSorting.length ; j<jLen ; j++ )
6708				{
6709					if ( oSettings.aaSorting[i][1] == oColumn.asSorting[j] )
6710					{
6711						oSettings.aaSorting[i][2] = j;
6712						break;
6713					}
6714				}
6715			}
6716				
6717			/* Do a first pass on the sorting classes (allows any size changes to be taken into
6718			 * account, and also will apply sorting disabled classes if disabled
6719			 */
6720			_fnSortingClasses( oSettings );
6721			
6722			
6723			/*
6724			 * Final init
6725			 * Cache the header, body and footer as required, creating them if needed
6726			 */
6727			
6728			/* Browser support detection */
6729			_fnBrowserDetect( oSettings );
6730			
6731			// Work around for Webkit bug 83867 - store the caption-side before removing from doc
6732			var captions = $(this).children('caption').each( function () {
6733				this._captionSide = $(this).css('caption-side');
6734			} );
6735			
6736			var thead = $(this).children('thead');
6737			if ( thead.length === 0 )
6738			{
6739				thead = [ document.createElement( 'thead' ) ];
6740				this.appendChild( thead[0] );
6741			}
6742			oSettings.nTHead = thead[0];
6743			
6744			var tbody = $(this).children('tbody');
6745			if ( tbody.length === 0 )
6746			{
6747				tbody = [ document.createElement( 'tbody' ) ];
6748				this.appendChild( tbody[0] );
6749			}
6750			oSettings.nTBody = tbody[0];
6751			oSettings.nTBody.setAttribute( "role", "alert" );
6752			oSettings.nTBody.setAttribute( "aria-live", "polite" );
6753			oSettings.nTBody.setAttribute( "aria-relevant", "all" );
6754			
6755			var tfoot = $(this).children('tfoot');
6756			if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
6757			{
6758				// If we are a scrolling table, and no footer has been given, then we need to create
6759				// a tfoot element for the caption element to be appended to
6760				tfoot = [ document.createElement( 'tfoot' ) ];
6761				this.appendChild( tfoot[0] );
6762			}
6763			
6764			if ( tfoot.length > 0 )
6765			{
6766				oSettings.nTFoot = tfoot[0];
6767				_fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
6768			}
6769			
6770			/* Check if there is data passing into the constructor */
6771			if ( bUsePassedData )
6772			{
6773				for ( i=0 ; i<oInit.aaData.length ; i++ )
6774				{
6775					_fnAddData( oSettings, oInit.aaData[ i ] );
6776				}
6777			}
6778			else
6779			{
6780				/* Grab the data from the page */
6781				_fnGatherData( oSettings );
6782			}
6783			
6784			/* Copy the data index array */
6785			oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
6786			
6787			/* Initialisation complete - table can be drawn */
6788			oSettings.bInitialised = true;
6789			
6790			/* Check if we need to initialise the table (it might not have been handed off to the
6791			 * language processor)
6792			 */
6793			if ( bInitHandedOff === false )
6794			{
6795				_fnInitialise( oSettings );
6796			}
6797		} );
6798		_that = null;
6799		return this;
6800	};
6801
6802	
6803	
6804	/**
6805	 * Provide a common method for plug-ins to check the version of DataTables being used, in order
6806	 * to ensure compatibility.
6807	 *  @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
6808	 *    formats "X" and "X.Y" are also acceptable.
6809	 *  @returns {boolean} true if this version of DataTables is greater or equal to the required
6810	 *    version, or false if this version of DataTales is not suitable
6811	 *  @static
6812	 *  @dtopt API-Static
6813	 *
6814	 *  @example
6815	 *    alert( $.fn.dataTable.fnVersionCheck( '1.9.0' ) );
6816	 */
6817	DataTable.fnVersionCheck = function( sVersion )
6818	{
6819		/* This is cheap, but effective */
6820		var fnZPad = function (Zpad, count)
6821		{
6822			while(Zpad.length < count) {
6823				Zpad += '0';
6824			}
6825			return Zpad;
6826		};
6827		var aThis = DataTable.ext.sVersion.split('.');
6828		var aThat = sVersion.split('.');
6829		var sThis = '', sThat = '';
6830		
6831		for ( var i=0, iLen=aThat.length ; i<iLen ; i++ )
6832		{
6833			sThis += fnZPad( aThis[i], 3 );
6834			sThat += fnZPad( aThat[i], 3 );
6835		}
6836		
6837		return parseInt(sThis, 10) >= parseInt(sThat, 10);
6838	};
6839	
6840	
6841	/**
6842	 * Check if a TABLE node is a DataTable table already or not.
6843	 *  @param {node} nTable The TABLE node to check if it is a DataTable or not (note that other
6844	 *    node types can be passed in, but will always return false).
6845	 *  @returns {boolean} true the table given is a DataTable, or false otherwise
6846	 *  @static
6847	 *  @dtopt API-Static
6848	 *
6849	 *  @example
6850	 *    var ex = document.getElementById('example');
6851	 *    if ( ! $.fn.DataTable.fnIsDataTable( ex ) ) {
6852	 *      $(ex).dataTable();
6853	 *    }
6854	 */
6855	DataTable.fnIsDataTable = function ( nTable )
6856	{
6857		var o = DataTable.settings;
6858	
6859		for ( var i=0 ; i<o.length ; i++ )
6860		{
6861			if ( o[i].nTable === nTable || o[i].nScrollHead === nTable || o[i].nScrollFoot === nTable )
6862			{
6863				return true;
6864			}
6865		}
6866	
6867		return false;
6868	};
6869	
6870	
6871	/**
6872	 * Get all DataTable tables that have been initialised - optionally you can select to
6873	 * get only currently visible tables.
6874	 *  @param {boolean} [bVisible=false] Flag to indicate if you want all (default) or 
6875	 *    visible tables only.
6876	 *  @returns {array} Array of TABLE nodes (not DataTable instances) which are DataTables
6877	 *  @static
6878	 *  @dtopt API-Static
6879	 *
6880	 *  @example
6881	 *    var table = $.fn.dataTable.fnTables(true);
6882	 *    if ( table.length > 0 ) {
6883	 *      $(table).dataTable().fnAdjustColumnSizing();
6884	 *    }
6885	 */
6886	DataTable.fnTables = function ( bVisible )
6887	{
6888		var out = [];
6889	
6890		jQuery.each( DataTable.settings, function (i, o) {
6891			if ( !bVisible || (bVisible === true && $(o.nTable).is(':visible')) )
6892			{
6893				out.push( o.nTable );
6894			}
6895		} );
6896	
6897		return out;
6898	};
6899	
6900
6901	/**
6902	 * Version string for plug-ins to check compatibility. Allowed format is
6903	 * a.b.c.d.e where: a:int, b:int, c:int, d:string(dev|beta), e:int. d and
6904	 * e are optional
6905	 *  @member
6906	 *  @type string
6907	 *  @default Version number
6908	 */
6909	DataTable.version = "1.9.4";
6910
6911	/**
6912	 * Private data store, containing all of the settings objects that are created for the
6913	 * tables on a given page.
6914	 * 
6915	 * Note that the <i>DataTable.settings</i> object is aliased to <i>jQuery.fn.dataTableExt</i> 
6916	 * through which it may be accessed and manipulated, or <i>jQuery.fn.dataTable.settings</i>.
6917	 *  @member
6918	 *  @type array
6919	 *  @default []
6920	 *  @private
6921	 */
6922	DataTable.settings = [];
6923
6924	/**
6925	 * Object models container, for the various models that DataTables has available
6926	 * to it. These models define the objects that are used to hold the active state 
6927	 * and configuration of the table.
6928	 *  @namespace
6929	 */
6930	DataTable.models = {};
6931	
6932	
6933	/**
6934	 * DataTables extension options and plug-ins. This namespace acts as a collection "area"
6935	 * for plug-ins that can be used to extend the default DataTables behaviour - indeed many
6936	 * of the build in methods use this method to provide their own capabilities (sorting methods
6937	 * for example).
6938	 * 
6939	 * Note that this namespace is aliased to jQuery.fn.dataTableExt so it can be readily accessed
6940	 * and modified by plug-ins.
6941	 *  @namespace
6942	 */
6943	DataTable.models.ext = {
6944		/**
6945		 * Plug-in filtering functions - this method of filtering is complimentary to the default
6946		 * type based filtering, and a lot more comprehensive as it allows you complete control
6947		 * over the filtering logic. Each element in this array is a function (parameters
6948		 * described below) that is called for every row in the table, and your logic decides if
6949		 * it should be included in the filtered data set or not.
6950		 *   <ul>
6951		 *     <li>
6952		 *       Function input parameters:
6953		 *       <ul>
6954		 *         <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
6955		 *         <li>{array|object} Data for the row to be processed (same as the original format
6956		 *           that was passed in as the data source, or an array from a DOM data source</li>
6957		 *         <li>{int} Row index in aoData ({@link DataTable.models.oSettings.aoData}), which can
6958		 *           be useful to retrieve the TR element if you need DOM interaction.</li>
6959		 *       </ul>
6960		 *     </li>
6961		 *     <li>
6962		 *       Function return:
6963		 *       <ul>
6964		 *         <li>{boolean} Include the row in the filtered result set (true) or not (false)</li>
6965		 *       </ul>
6966		 *     </il>
6967		 *   </ul>
6968		 *  @type array
6969		 *  @default []
6970		 *
6971		 *  @example
6972		 *    // The following example shows custom filtering being applied to the fourth column (i.e.
6973		 *    // the aData[3] index) based on two input values from the end-user, matching the data in 
6974		 *    // a certain range.
6975		 *    $.fn.dataTableExt.afnFiltering.push(
6976		 *      function( oSettings, aData, iDataIndex ) {
6977		 *        var iMin = document.getElementById('min').value * 1;
6978		 *        var iMax = document.getElementById('max').value * 1;
6979		 *        var iVersion = aData[3] == "-" ? 0 : aData[3]*1;
6980		 *        if ( iMin == "" && iMax == "" ) {
6981		 *          return true;
6982		 *        }
6983		 *        else if ( iMin == "" && iVersion < iMax ) {
6984		 *          return true;
6985		 *        }
6986		 *        else if ( iMin < iVersion && "" == iMax ) {
6987		 *          return true;
6988		 *        }
6989		 *        else if ( iMin < iVersion && iVersion < iMax ) {
6990		 *          return true;
6991		 *        }
6992		 *        return false;
6993		 *      }
6994		 *    );
6995		 */
6996		"afnFiltering": [],
6997	
6998	
6999		/**
7000		 * Plug-in sorting functions - this method of sorting is complimentary to the default type
7001		 * based sorting that DataTables does automatically, allowing much greater control over the
7002		 * the data that is being used to sort a column. This is useful if you want to do sorting
7003		 * based on live data (for example the contents of an 'input' element) rather than just the
7004		 * static string that DataTables knows of. The way these plug-ins work is that you create
7005		 * an array of the values you wish to be sorted for the column in question and then return
7006		 * that array. Which pre-sorting function is run here depends on the sSortDataType parameter
7007		 * that is used for the column (if any). This is the corollary of <i>ofnSearch</i> for sort 
7008		 * data.
7009		 *   <ul>
7010	     *     <li>
7011	     *       Function input parameters:
7012	     *       <ul>
7013		 *         <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
7014	     *         <li>{int} Target column index</li>
7015	     *       </ul>
7016	     *     </li>
7017		 *     <li>
7018		 *       Function return:
7019		 *       <ul>
7020		 *         <li>{array} Data for the column to be sorted upon</li>
7021		 *       </ul>
7022		 *     </il>
7023		 *   </ul>
7024		 *  
7025		 * Note that as of v1.9, it is typically preferable to use <i>mData</i> to prepare data for
7026		 * the different uses that DataTables can put the data to. Specifically <i>mData</i> when
7027		 * used as a function will give you a 'type' (sorting, filtering etc) that you can use to 
7028		 * prepare the data as required for the different types. As such, this method is deprecated.
7029		 *  @type array
7030		 *  @default []
7031		 *  @deprecated
7032		 *
7033		 *  @example
7034		 *    // Updating the cached sorting information with user entered values in HTML input elements
7035		 *    jQuery.fn.dataTableExt.afnSortData['dom-text'] = function ( oSettings, iColumn )
7036		 *    {
7037		 *      var aData = [];
7038		 *      $( 'td:eq('+iColumn+') input', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
7039		 *        aData.push( this.value );
7040		 *      } );
7041		 *      return aData;
7042		 *    }
7043		 */
7044		"afnSortData": [],
7045	
7046	
7047		/**
7048		 * Feature plug-ins - This is an array of objects which describe the feature plug-ins that are
7049		 * available to DataTables. These feature plug-ins are accessible through the sDom initialisation
7050		 * option. As such, each feature plug-in must describe a function that is used to initialise
7051		 * itself (fnInit), a character so the feature can be enabled by sDom (cFeature) and the name
7052		 * of the feature (sFeature). Thus the objects attached to this method must provide:
7053		 *   <ul>
7054		 *     <li>{function} fnInit Initialisation of the plug-in
7055		 *       <ul>
7056	     *         <li>
7057	     *           Function input parameters:
7058	     *           <ul>
7059		 *             <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
7060	     *           </ul>
7061	     *         </li>
7062		 *         <li>
7063		 *           Function return:
7064		 *           <ul>
7065		 *             <li>{node|null} The element which contains your feature. Note that the return
7066		 *                may also be void if your plug-in does not require to inject any DOM elements 
7067		 *                into DataTables control (sDom) - for example this might be useful when 
7068		 *                developing a plug-in which allows table control via keyboard entry.</li>
7069		 *           </ul>
7070		 *         </il>
7071		 *       </ul>
7072		 *     </li>
7073		 *     <li>{character} cFeature Character that will be matched in sDom - case sensitive</li>
7074		 *     <li>{string} sFeature Feature name</li>
7075		 *   </ul>
7076		 *  @type array
7077		 *  @default []
7078		 * 
7079		 *  @example
7080		 *    // How TableTools initialises itself.
7081		 *    $.fn.dataTableExt.aoFeatures.push( {
7082		 *      "fnInit": function( oSettings ) {
7083		 *        return new TableTools( { "oDTSettings": oSettings } );
7084		 *      },
7085		 *      "cFeature": "T",
7086		 *      "sFeature": "TableTools"
7087		 *    } );
7088		 */
7089		"aoFeatures": [],
7090	
7091	
7092		/**
7093		 * Type detection plug-in functions - DataTables utilises types to define how sorting and
7094		 * filtering behave, and types can be either  be defined by the developer (sType for the
7095		 * column) or they can be automatically detected by the methods in this array. The functions
7096		 * defined in the array are quite simple, taking a single parameter (the data to analyse) 
7097		 * and returning the type if it is a known type, or null otherwise.
7098		 *   <ul>
7099	     *     <li>
7100	     *       Function input parameters:
7101	     *       <ul>
7102		 *         <li>{*} Data from the column cell to be analysed</li>
7103	     *       </ul>
7104	     *     </li>
7105		 *     <li>
7106		 *       Function return:
7107		 *       <ul>
7108		 *         <li>{string|null} Data type detected, or null if unknown (and thus pass it
7109		 *           on to the other type detection functions.</li>
7110		 *       </ul>
7111		 *     </il>
7112		 *   </ul>
7113		 *  @type array
7114		 *  @default []
7115		 *  
7116		 *  @example
7117		 *    // Currency type detection plug-in:
7118		 *    jQuery.fn.dataTableExt.aTypes.push(
7119		 *      function ( sData ) {
7120		 *        var sValidChars = "0123456789.-";
7121		 *        var Char;
7122		 *        
7123		 *        // Check the numeric part
7124		 *        for ( i=1 ; i<sData.length ; i++ ) {
7125		 *          Char = sData.charAt(i); 
7126		 *          if (sValidChars.indexOf(Char) == -1) {
7127		 *            return null;
7128		 *          }
7129		 *        }
7130		 *        
7131		 *        // Check prefixed by currency
7132		 *        if ( sData.charAt(0) == '$' || sData.charAt(0) == '&pound;' ) {
7133		 *          return 'currency';
7134		 *        }
7135		 *        return null;
7136		 *      }
7137		 *    );
7138		 */
7139		"aTypes": [],
7140	
7141	
7142		/**
7143		 * Provide a common method for plug-ins to check the version of DataTables being used, 
7144		 * in order to ensure compatibility.
7145		 *  @type function
7146		 *  @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note 
7147		 *    that the formats "X" and "X.Y" are also acceptable.
7148		 *  @returns {boolean} true if this version of DataTables is greater or equal to the 
7149		 *    required version, or false if this version of DataTales is not suitable
7150		 *
7151		 *  @example
7152		 *    $(document).ready(function() {
7153		 *      var oTable = $('#example').dataTable();
7154		 *      alert( oTable.fnVersionCheck( '1.9.0' ) );
7155		 *    } );
7156		 */
7157		"fnVersionCheck": DataTable.fnVersionCheck,
7158	
7159	
7160		/**
7161		 * Index for what 'this' index API functions should use
7162		 *  @type int
7163		 *  @default 0
7164		 */
7165		"iApiIndex": 0,
7166	
7167	
7168		/**
7169		 * Pre-processing of filtering data plug-ins - When you assign the sType for a column
7170		 * (or have it automatically detected for you by DataTables or a type detection plug-in), 
7171		 * you will typically be using this for custom sorting, but it can also be used to provide 
7172		 * custom filtering by allowing you to pre-processing the data and returning the data in
7173		 * the format that should be filtered upon. This is done by adding functions this object 
7174		 * with a parameter name which matches the sType for that target column. This is the
7175		 * corollary of <i>afnSortData</i> for filtering data.
7176		 *   <ul>
7177	     *     <li>
7178	     *       Function input parameters:
7179	     *       <ul>
7180		 *         <li>{*} Data from the column cell to be prepared for filtering</li>
7181	     *       </ul>
7182	     *     </li>
7183		 *     <li>
7184		 *       Function return:
7185		 *       <ul>
7186		 *         <li>{string|null} Formatted string that will be used for the filtering.</li>
7187		 *       </ul>
7188		 *     </il>
7189		 *   </ul>
7190		 * 
7191		 * Note that as of v1.9, it is typically preferable to use <i>mData</i> to prepare data for
7192		 * the different uses that DataTables can put the data to. Specifically <i>mData</i> when
7193		 * used as a function will give you a 'type' (sorting, filtering etc) that you can use to 
7194		 * prepare the data as required for the different types. As such, this method is deprecated.
7195		 *  @type object
7196		 *  @default {}
7197		 *  @deprecated
7198		 *
7199		 *  @example
7200		 *    $.fn.dataTableExt.ofnSearch['title-numeric'] = function ( sData ) {
7201		 *      return sData.replace(/\n/g," ").replace( /<.*?>/g, "" );
7202		 *    }
7203		 */
7204		"ofnSearch": {},
7205	
7206	
7207		/**
7208		 * Container for all private functions in DataTables so they can be exposed externally
7209		 *  @type object
7210		 *  @default {}
7211		 */
7212		"oApi": {},
7213	
7214	
7215		/**
7216		 * Storage for the various classes that DataTables uses
7217		 *  @type object
7218		 *  @default {}
7219		 */
7220		"oStdClasses": {},
7221		
7222	
7223		/**
7224		 * Storage for the various classes that DataTables uses - jQuery UI suitable
7225		 *  @type object
7226		 *  @default {}
7227		 */
7228		"oJUIClasses": {},
7229	
7230	
7231		/**
7232		 * Pagination plug-in methods - The style and controls of the pagination can significantly 
7233		 * impact on how the end user interacts with the data in your table, and DataTables allows 
7234		 * the addition of pagination controls by extending this object, which can then be enabled
7235		 * through the <i>sPaginationType</i> initialisation parameter. Each pagination type that
7236		 * is added is an object (the property name of which is what <i>sPaginationType</i> refers
7237		 * to) that has two properties, both methods that are used by DataTables to update the
7238		 * control's state.
7239		 *   <ul>
7240		 *     <li>
7241		 *       fnInit -  Initialisation of the paging controls. Called only during initialisation 
7242		 *         of the table. It is expected that this function will add the required DOM elements 
7243		 *         to the page for the paging controls to work. The element pointer 
7244		 *         'oSettings.aanFeatures.p' array is provided by DataTables to contain the paging 
7245		 *         controls (note that this is a 2D array to allow for multiple instances of each 
7246		 *         DataTables DOM element). It is suggested that you add the controls to this element 
7247		 *         as children
7248		 *       <ul>
7249	     *         <li>
7250	     *           Function input parameters:
7251	     *           <ul>
7252		 *             <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
7253		 *             <li>{node} Container into which the pagination controls must be inserted</li>
7254		 *             <li>{function} Draw callback function - whenever the controls cause a page
7255		 *               change, this method must be called to redraw the table.</li>
7256	     *           </ul>
7257	     *         </li>
7258		 *         <li>
7259		 *           Function return:
7260		 *           <ul>
7261		 *             <li>No return required</li>
7262		 *           </ul>
7263		 *         </il>
7264		 *       </ul>
7265		 *     </il>
7266		 *     <li>
7267		 *       fnInit -  This function is called whenever the paging status of the table changes and is
7268		 *         typically used to update classes and/or text of the paging controls to reflex the new 
7269		 *         status.
7270		 *       <ul>
7271	     *         <li>
7272	     *           Function input parameters:
7273	     *           <ul>
7274		 *             <li>{object} DataTables settings object: see {@link DataTable.models.oSettings}.</li>
7275		 *             <li>{function} Draw callback function - in case you need to redraw the table again
7276		 *               or attach new event listeners</li>
7277	     *           </ul>
7278	     *         </li>
7279		 *         <li>
7280		 *           Function return:
7281		 *           <ul>
7282		 *             <li>No return required</li>
7283		 *           </ul>
7284		 *         </il>
7285		 *       </ul>
7286		 *     </il>
7287		 *   </ul>
7288		 *  @type object
7289		 *  @default {}
7290		 *
7291		 *  @example
7292		 *    $.fn.dataTableExt.oPagination.four_button = {
7293		 *      "fnInit": function ( oSettings, nPaging, fnCallbackDraw ) {
7294		 *        nFirst = document.createElement( 'span' );
7295		 *        nPrevious = document.createElement( 'span' );
7296		 *        nNext = document.createElement( 'span' );
7297		 *        nLast = document.createElement( 'span' );
7298		 *        
7299		 *        nFirst.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sFirst ) );
7300		 *        nPrevious.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sPrevious ) );
7301		 *        nNext.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sNext ) );
7302		 *        nLast.appendChild( document.createTextNode( oSettings.oLanguage.oPaginate.sLast ) );
7303		 *        
7304		 *        nFirst.className = "paginate_button first";
7305		 *        nPrevious.className = "paginate_button previous";
7306		 *        nNext.className="paginate_button next";
7307		 *        nLast.className = "paginate_button last";
7308		 *        
7309		 *        nPaging.appendChild( nFirst );
7310		 *        nPaging.appendChild( nPrevious );
7311		 *        nPaging.appendChild( nNext );
7312		 *        nPaging.appendChild( nLast );
7313		 *        
7314		 *        $(nFirst).click( function () {
7315		 *          oSettings.oApi._fnPageChange( oSettings, "first" );
7316		 *          fnCallbackDraw( oSettings );
7317		 *        } );
7318		 *        
7319		 *        $(nPrevious).click( function() {
7320		 *          oSettings.oApi._fnPageChange( oSettings, "previous" );
7321		 *          fnCallbackDraw( oSettings );
7322		 *        } );
7323		 *        
7324		 *        $(nNext).click( function() {
7325		 *          oSettings.oApi._fnPageChange( oSettings, "next" );
7326		 *          fnCallbackDraw( oSettings );
7327		 *        } );
7328		 *        
7329		 *        $(nLast).click( function() {
7330		 *          oSettings.oApi._fnPageChange( oSettings, "last" );
7331		 *          fnCallbackDraw( oSettings );
7332		 *        } );
7333		 *        
7334		 *        $(nFirst).bind( 'selectstart', function () { return false; } );
7335		 *        $(nPrevious).bind( 'selectstart', function () { return false; } );
7336		 *        $(nNext).bind( 'selectstart', function () { return false; } );
7337		 *        $(nLast).bind( 'selectstart', function () { return false; } );
7338		 *      },
7339		 *      
7340		 *      "fnUpdate": function ( oSettings, fnCallbackDraw ) {
7341		 *        if ( !oSettings.aanFeatures.p ) {
7342		 *          return;
7343		 *        }
7344		 *        
7345		 *        // Loop over each instance of the pager
7346		 *        var an = oSettings.aanFeatures.p;
7347		 *        for ( var i=0, iLen=an.length ; i<iLen ; i++ ) {
7348		 *          var buttons = an[i].getElementsByTagName('span');
7349		 *          if ( oSettings._iDisplayStart === 0 ) {
7350		 *            buttons[0].className = "paginate_disabled_previous";
7351		 *            buttons[1].className = "paginate_disabled_previous";
7352		 *          }
7353		 *          else {
7354		 *            buttons[0].className = "paginate_enabled_previous";
7355		 *            buttons[1].className = "paginate_enabled_previous";
7356		 *          }
7357		 *          
7358		 *          if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) {
7359		 *            buttons[2].className = "paginate_disabled_next";
7360		 *            buttons[3].className = "paginate_disabled_next";
7361		 *          }
7362		 *          else {
7363		 *            buttons[2].className = "paginate_enabled_next";
7364		 *            buttons[3].className = "paginate_enabled_next";
7365		 *          }
7366		 *        }
7367		 *      }
7368		 *    };
7369		 */
7370		"oPagination": {},
7371	
7372	
7373		/**
7374		 * Sorting plug-in methods - Sorting in DataTables is based on the detected type of the
7375		 * data column (you can add your own type detection functions, or override automatic 
7376		 * detection using sType). With this specific type given to the column, DataTables will 
7377		 * apply the required sort from the functions in the object. Each sort type must provide
7378		 * two mandatory methods, one each for ascending and descending sorting, and can optionally
7379		 * provide a pre-formatting method that will help speed up sorting by allowing DataTables
7380		 * to pre-format the sort data only once (rather than every time the actual sort functions
7381		 * are run). The two sorting functions are typical Javascript sort methods:
7382		 *   <ul>
7383	     *     <li>
7384	     *       Function input parameters:
7385	     *       <ul>
7386		 *         <li>{*} Data to compare to the second parameter</li>
7387		 *         <li>{*} Data to compare to the first parameter</li>
7388	     *       </ul>
7389	     *     </li>
7390		 *     <li>
7391		 *       Function return:
7392		 *       <ul>
7393		 *         <li>{int} Sorting match: <0 if first parameter should be sorted lower than
7394		 *           the second parameter, ===0 if the two parameters are equal and >0 if
7395		 *           the first parameter should be sorted height than the second parameter.</li>
7396		 *       </ul>
7397		 *     </il>
7398		 *   </ul>
7399		 *  @type object
7400		 *  @default {}
7401		 *
7402		 *  @example
7403		 *    // Case-sensitive string sorting, with no pre-formatting method
7404		 *    $.extend( $.fn.dataTableExt.oSort, {
7405		 *      "string-case-asc": function(x,y) {
7406		 *        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
7407		 *      },
7408		 *      "string-case-desc": function(x,y) {
7409		 *        return ((x < y) ? 1 : ((x > y) ? -1 : 0));
7410		 *      }
7411		 *    } );
7412		 *
7413		 *  @example
7414		 *    // Case-insensitive string sorting, with pre-formatting
7415		 *    $.extend( $.fn.dataTableExt.oSort, {
7416		 *      "string-pre": function(x) {
7417		 *        return x.toLowerCase();
7418		 *      },
7419		 *      "string-asc": function(x,y) {
7420		 *        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
7421		 *      },
7422		 *      "string-desc": function(x,y) {
7423		 *        return ((x < y) ? 1 : ((x > y) ? -1 : 0));
7424		 *      }
7425		 *    } );
7426		 */
7427		"oSort": {},
7428	
7429	
7430		/**
7431		 * Version string for plug-ins to check compatibility. Allowed format is
7432		 * a.b.c.d.e where: a:int, b:int, c:int, d:string(dev|beta), e:int. d and
7433		 * e are optional
7434		 *  @type string
7435		 *  @default Version number
7436		 */
7437		"sVersion": DataTable.version,
7438	
7439	
7440		/**
7441		 * How should DataTables report an error. Can take the value 'alert' or 'throw'
7442		 *  @type string
7443		 *  @default alert
7444		 */
7445		"sErrMode": "alert",
7446	
7447	
7448		/**
7449		 * Store information for DataTables to access globally about other instances
7450		 *  @namespace
7451		 *  @private
7452		 */
7453		"_oExternConfig": {
7454			/* int:iNextUnique - next unique number for an instance */
7455			"iNextUnique": 0
7456		}
7457	};
7458	
7459	
7460	
7461	
7462	/**
7463	 * Template object for the way in which DataTables holds information about
7464	 * search information for the global filter and individual column filters.
7465	 *  @namespace
7466	 */
7467	DataTable.models.oSearch = {
7468		/**
7469		 * Flag to indicate if the filtering should be case insensitive or not
7470		 *  @type boolean
7471		 *  @default true
7472		 */
7473		"bCaseInsensitive": true,
7474	
7475		/**
7476		 * Applied search term
7477		 *  @type string
7478		 *  @default <i>Empty string</i>
7479		 */
7480		"sSearch": "",
7481	
7482		/**
7483		 * Flag to indicate if the search term should be interpreted as a
7484		 * regular expression (true) or not (false) and therefore and special
7485		 * regex characters escaped.
7486		 *  @type boolean
7487		 *  @default false
7488		 */
7489		"bRegex": false,
7490	
7491		/**
7492		 * Flag to indicate if DataTables is to use its smart filtering or not.
7493		 *  @type boolean
7494		 *  @default true
7495		 */
7496		"bSmart": true
7497	};
7498	
7499	
7500	
7501	
7502	/**
7503	 * Template object for the way in which DataTables holds information about
7504	 * each individual row. This is the object format used for the settings 
7505	 * aoData array.
7506	 *  @namespace
7507	 */
7508	DataTable.models.oRow = {
7509		/**
7510		 * TR element for the row
7511		 *  @type node
7512		 *  @default null
7513		 */
7514		"nTr": null,
7515	
7516		/**
7517		 * Data object from the original data source for the row. This is either
7518		 * an array if using the traditional form of DataTables, or an object if
7519		 * using mData options. The exact type will depend on the passed in
7520		 * data from the data source, or will be an array if using DOM a data 
7521		 * source.
7522		 *  @type array|object
7523		 *  @default []
7524		 */
7525		"_aData": [],
7526	
7527		/**
7528		 * Sorting data cache - this array is ostensibly the same length as the
7529		 * number of columns (although each index is generated only as it is 
7530		 * needed), and holds the data that is used for sorting each column in the
7531		 * row. We do this cache generation at the start of the sort in order that
7532		 * the formatting of the sort data need be done only once for each cell
7533		 * per sort. This array should not be read from or written to by anything
7534		 * other than the master sorting methods.
7535		 *  @type array
7536		 *  @default []
7537		 *  @private
7538		 */
7539		"_aSortData": [],
7540	
7541		/**
7542		 * Array of TD elements that are cached for hidden rows, so they can be
7543		 * reinserted into the table if a column is made visible again (or to act
7544		 * as a store if a column is made hidden). Only hidden columns have a 
7545		 * reference in the array. For non-hidden columns the value is either
7546		 * undefined or null.
7547		 *  @type array nodes
7548		 *  @default []
7549		 *  @private
7550		 */
7551		"_anHidden": [],
7552	
7553		/**
7554		 * Cache of the class name that DataTables has applied to the row, so we
7555		 * can quickly look at this variable rather than needing to do a DOM check
7556		 * on className for the nTr property.
7557		 *  @type string
7558		 *  @default <i>Empty string</i>
7559		 *  @private
7560		 */
7561		"_sRowStripe": ""
7562	};
7563	
7564	
7565	
7566	/**
7567	 * Template object for the column information object in DataTables. This object
7568	 * is held in the settings aoColumns array and contains all the information that
7569	 * DataTables needs about each individual column.
7570	 * 
7571	 * Note that this object is related to {@link DataTable.defaults.columns} 
7572	 * but this one is the internal data store for DataTables's cache of columns.
7573	 * It should NOT be manipulated outside of DataTables. Any configuration should
7574	 * be done through the initialisation options.
7575	 *  @namespace
7576	 */
7577	DataTable.models.oColumn = {
7578		/**
7579		 * A list of the columns that sorting should occur on when this column
7580		 * is sorted. That this property is an array allows multi-column sorting
7581		 * to be defined for a column (for example first name / last name columns
7582		 * would benefit from this). The values are integers pointing to the
7583		 * columns to be sorted on (typically it will be a single integer pointing
7584		 * at itself, but that doesn't need to be the case).
7585		 *  @type array
7586		 */
7587		"aDataSort": null,
7588	
7589		/**
7590		 * Define the sorting directions that are applied to the column, in sequence
7591		 * as the column is repeatedly sorted upon - i.e. the first value is used
7592		 * as the sorting direction when the column if first sorted (clicked on).
7593		 * Sort it again (click again) and it will move on to the next index.
7594		 * Repeat until loop.
7595		 *  @type array
7596		 */
7597		"asSorting": null,
7598		
7599		/**
7600		 * Flag to indicate if the column is searchable, and thus should be included
7601		 * in the filtering or not.
7602		 *  @type boolean
7603		 */
7604		"bSearchable": null,
7605		
7606		/**
7607		 * Flag to indicate if the column is sortable or not.
7608		 *  @type boolean
7609		 */
7610		"bSortable": null,
7611		
7612		/**
7613		 * <code>Deprecated</code> When using fnRender, you have two options for what 
7614		 * to do with the data, and this property serves as the switch. Firstly, you 
7615		 * can have the sorting and filtering use the rendered value (true - default), 
7616		 * or you can have the sorting and filtering us the original value (false).
7617		 *
7618		 * Please note that this option has now been deprecated and will be removed
7619		 * in the next version of DataTables. Please use mRender / mData rather than
7620		 * fnRender.
7621		 *  @type boolean
7622		 *  @deprecated
7623		 */
7624		"bUseRendered": null,
7625		
7626		/**
7627		 * Flag to indicate if the column is currently visible in the table or not
7628		 *  @type boolean
7629		 */
7630		"bVisible": null,
7631		
7632		/**
7633		 * Flag to indicate to the type detection method if the automatic type
7634		 * detection should be used, or if a column type (sType) has been specified
7635		 *  @type boolean
7636		 *  @default true
7637		 *  @private
7638		 */
7639		"_bAutoType": true,
7640		
7641		/**
7642		 * Developer definable function that is called whenever a cell is created (Ajax source,
7643		 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
7644		 * allowing you to modify the DOM element (add background colour for example) when the
7645		 * element is available.
7646		 *  @type function
7647		 *  @param {element} nTd The TD node that has been created
7648		 *  @param {*} sData The Data for the cell
7649		 *  @param {array|object} oData The data for the whole row
7650		 *  @param {int} iRow The row index for the aoData data store
7651		 *  @default null
7652		 */
7653		"fnCreatedCell": null,
7654		
7655		/**
7656		 * Function to get data from a cell in a column. You should <b>never</b>
7657		 * access data directly through _aData internally in DataTables - always use
7658		 * the method attached to this property. It allows mData to function as
7659		 * required. This function is automatically assigned by the column 
7660		 * initialisation method
7661		 *  @type function
7662		 *  @param {array|object} oData The data array/object for the array 
7663		 *    (i.e. aoData[]._aData)
7664		 *  @param {string} sSpecific The specific data type you want to get - 
7665		 *    'display', 'type' 'filter' 'sort'
7666		 *  @returns {*} The data for the cell from the given row's data
7667		 *  @default null
7668		 */
7669		"fnGetData": null,
7670		
7671		/**
7672		 * <code>Deprecated</code> Custom display function that will be called for the 
7673		 * display of each cell in this column.
7674		 *
7675		 * Please note that this option has now been deprecated and will be removed
7676		 * in the next version of DataTables. Please use mRender / mData rather than
7677		 * fnRender.
7678		 *  @type function
7679		 *  @param {object} o Object with the following parameters:
7680		 *  @param {int}    o.iDataRow The row in aoData
7681		 *  @param {int}    o.iDataColumn The column in question
7682		 *  @param {array}  o.aData The data for the row in question
7683		 *  @param {object} o.oSettings The settings object for this DataTables instance
7684		 *  @returns {string} The string you which to use in the display
7685		 *  @default null
7686		 *  @deprecated
7687		 */
7688		"fnRender": null,
7689		
7690		/**
7691		 * Function to set data for a cell in the column. You should <b>never</b> 
7692		 * set the data directly to _aData internally in DataTables - always use
7693		 * this method. It allows mData to function as required. This function
7694		 * is automatically assigned by the column initialisation method
7695		 *  @type function
7696		 *  @param {array|object} oData The data array/object for the array 
7697		 *    (i.e. aoData[]._aData)
7698		 *  @param {*} sValue Value to set
7699		 *  @default null
7700		 */
7701		"fnSetData": null,
7702		
7703		/**
7704		 * Property to read the value for the cells in the column from the data 
7705		 * source array / object. If null, then the default content is used, if a
7706		 * function is given then the return from the function is used.
7707		 *  @type function|int|string|null
7708		 *  @default null
7709		 */
7710		"mData": null,
7711		
7712		/**
7713		 * Partner property to mData which is used (only when defined) to get
7714		 * the data - i.e. it is basically the same as mData, but without the
7715		 * 'set' option, and also the data fed to it is the result from mData.
7716		 * This is the rendering method to match the data method of mData.
7717		 *  @type function|int|string|null
7718		 *  @default null
7719		 */
7720		"mRender": null,
7721		
7722		/**
7723		 * Unique header TH/TD element for this column - this is what the sorting
7724		 * listener is attached to (if sorting is enabled.)
7725		 *  @type node
7726		 *  @default null
7727		 */
7728		"nTh": null,
7729		
7730		/**
7731		 * Unique footer TH/TD element for this column (if there is one). Not used 
7732		 * in DataTables as such, but can be used for plug-ins to reference the 
7733		 * footer for each column.
7734		 *  @type node
7735		 *  @default null
7736		 */
7737		"nTf": null,
7738		
7739		/**
7740		 * The class to apply to all TD elements in the table's TBODY for the column
7741		 *  @type string
7742		 *  @default null
7743		 */
7744		"sClass": null,
7745		
7746		/**
7747		 * When DataTables calculates the column widths to assign to each column,
7748		 * it finds the longest string in each column and then constructs a
7749		 * temporary table and reads the widths from that. The problem with this
7750		 * is that "mmm" is much wider then "iiii", but the latter is a longer 
7751		 * string - thus the calculation can go wrong (doing it properly and putting
7752		 * it into an DOM object and measuring that is horribly(!) slow). Thus as
7753		 * a "work around" we provide this option. It will append its value to the
7754		 * text that is found to be the longest string for the column - i.e. padding.
7755		 *  @type string
7756		 */
7757		"sContentPadding": null,
7758		
7759		/**
7760		 * Allows a default value to be given for a column's data, and will be used
7761		 * whenever a null data source is encountered (this can be because mData
7762		 * is set to null, or because the data source itself is null).
7763		 *  @type string
7764		 *  @default null
7765		 */
7766		"sDefaultContent": null,
7767		
7768		/**
7769		 * Name for the column, allowing reference to the column by name as well as
7770		 * by index (needs a lookup to work by name).
7771		 *  @type string
7772		 */
7773		"sName": null,
7774		
7775		/**
7776		 * Custom sorting data type - defines which of the available plug-ins in
7777		 * afnSortData the custom sorting will use - if any is defined.
7778		 *  @type string
7779		 *  @default std
7780		 */
7781		"sSortDataType": 'std',
7782		
7783		/**
7784		 * Class to be applied to the header element when sorting on this column
7785		 *  @type string
7786		 *  @default null
7787		 */
7788		"sSortingClass": null,
7789		
7790		/**
7791		 * Class to be applied to the header element when sorting on this column -
7792		 * when jQuery UI theming is used.
7793		 *  @type string
7794		 *  @default null
7795		 */
7796		"sSortingClassJUI": null,
7797		
7798		/**
7799		 * Title of the column - what is seen in the TH element (nTh).
7800		 *  @type string
7801		 */
7802		"sTitle": null,
7803		
7804		/**
7805		 * Column sorting and filtering type
7806		 *  @type string
7807		 *  @default null
7808		 */
7809		"sType": null,
7810		
7811		/**
7812		 * Width of the column
7813		 *  @type string
7814		 *  @default null
7815		 */
7816		"sWidth": null,
7817		
7818		/**
7819		 * Width of the column when it was first "encountered"
7820		 *  @type string
7821		 *  @default null
7822		 */
7823		"sWidthOrig": null
7824	};
7825	
7826	
7827	
7828	/**
7829	 * Initialisation options that can be given to DataTables at initialisation 
7830	 * time.
7831	 *  @namespace
7832	 */
7833	DataTable.defaults = {
7834		/**
7835		 * An array of data to use for the table, passed in at initialisation which 
7836		 * will be used in preference to any data which is already in the DOM. This is
7837		 * particularly useful for constructing tables purely in Javascript, for
7838		 * example with a custom Ajax call.
7839		 *  @type array
7840		 *  @default null
7841		 *  @dtopt Option
7842		 * 
7843		 *  @example
7844		 *    // Using a 2D array data source
7845		 *    $(document).ready( function () {
7846		 *      $('#example').dataTable( {
7847		 *        "aaData": [
7848		 *          ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
7849		 *          ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
7850		 *        ],
7851		 *        "aoColumns": [
7852		 *          { "sTitle": "Engine" },
7853		 *          { "sTitle": "Browser" },
7854		 *          { "sTitle": "Platform" },
7855		 *          { "sTitle": "Version" },
7856		 *          { "sTitle": "Grade" }
7857		 *        ]
7858		 *      } );
7859		 *    } );
7860		 *    
7861		 *  @example
7862		 *    // Using an array of objects as a data source (mData)
7863		 *    $(document).ready( function () {
7864		 *      $('#example').dataTable( {
7865		 *        "aaData": [
7866		 *          {
7867		 *            "engine":   "Trident",
7868		 *            "browser":  "Internet Explorer 4.0",
7869		 *            "platform": "Win 95+",
7870		 *            "version":  4,
7871		 *            "grade":    "X"
7872		 *          },
7873		 *          {
7874		 *            "engine":   "Trident",
7875		 *            "browser":  "Internet Explorer 5.0",
7876		 *            "platform": "Win 95+",
7877		 *            "version":  5,
7878		 *            "grade":    "C"
7879		 *          }
7880		 *        ],
7881		 *        "aoColumns": [
7882		 *          { "sTitle": "Engine",   "mData": "engine" },
7883		 *          { "sTitle": "Browser",  "mData": "browser" },
7884		 *          { "sTitle": "Platform", "mData": "platform" },
7885		 *          { "sTitle": "Version",  "mData": "version" },
7886		 *          { "sTitle": "Grade",    "mData": "grade" }
7887		 *        ]
7888		 *      } );
7889		 *    } );
7890		 */
7891		"aaData": null,
7892	
7893	
7894		/**
7895		 * If sorting is enabled, then DataTables will perform a first pass sort on 
7896		 * initialisation. You can define which column(s) the sort is performed upon, 
7897		 * and the sorting direction, with this variable. The aaSorting array should 
7898		 * contain an array for each column to be sorted initially containing the 
7899		 * column's index and a direction string ('asc' or 'desc').
7900		 *  @type array
7901		 *  @default [[0,'asc']]
7902		 *  @dtopt Option
7903		 * 
7904		 *  @example
7905		 *    // Sort by 3rd column first, and then 4th column
7906		 *    $(document).ready( function() {
7907		 *      $('#example').dataTable( {
7908		 *        "aaSorting": [[2,'asc'], [3,'desc']]
7909		 *      } );
7910		 *    } );
7911		 *    
7912		 *    // No initial sorting
7913		 *    $(document).ready( function() {
7914		 *      $('#example').dataTable( {
7915		 *        "aaSorting": []
7916		 *      } );
7917		 *    } );
7918		 */
7919		"aaSorting": [[0,'asc']],
7920	
7921	
7922		/**
7923		 * This parameter is basically identical to the aaSorting parameter, but 
7924		 * cannot be overridden by user interaction with the table. What this means 
7925		 * is that you could have a column (visible or hidden) which the sorting will 
7926		 * always be forced on first - any sorting after that (from the user) will 
7927		 * then be performed as required. This can be useful for grouping rows 
7928		 * together.
7929		 *  @type array
7930		 *  @default null
7931		 *  @dtopt Option
7932		 * 
7933		 *  @example
7934		 *    $(document).ready( function() {
7935		 *      $('#example').dataTable( {
7936		 *        "aaSortingFixed": [[0,'asc']]
7937		 *      } );
7938		 *    } )
7939		 */
7940		"aaSortingFixed": null,
7941	
7942	
7943		/**
7944		 * This parameter allows you to readily specify the entries in the length drop
7945		 * down menu that DataTables shows when pagination is enabled. It can be 
7946		 * either a 1D array of options which will be used for both the displayed 
7947		 * option and the value, or a 2D array which will use the array in the first 
7948		 * position as the value, and the array in the second position as the 
7949		 * displayed options (useful for language strings such as 'All').
7950		 *  @type array
7951		 *  @default [ 10, 25, 50, 100 ]
7952		 *  @dtopt Option
7953		 * 
7954		 *  @example
7955		 *    $(document).ready( function() {
7956		 *      $('#example').dataTable( {
7957		 *        "aLengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
7958		 *      } );
7959		 *    } );
7960		 *  
7961		 *  @example
7962		 *    // Setting the default display length as well as length menu
7963		 *    // This is likely to be wanted if you remove the '10' option which
7964		 *    // is the iDisplayLength default.
7965		 *    $(document).ready( function() {
7966		 *      $('#example').dataTable( {
7967		 *        "iDisplayLength": 25,
7968		 *        "aLengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]]
7969		 *      } );
7970		 *    } );
7971		 */
7972		"aLengthMenu": [ 10, 25, 50, 100 ],
7973	
7974	
7975		/**
7976		 * The aoColumns option in the initialisation parameter allows you to define
7977		 * details about the way individual columns behave. For a full list of
7978		 * column options that can be set, please see 
7979		 * {@link DataTable.defaults.columns}. Note that if you use aoColumns to
7980		 * define your columns, you must have an entry in the array for every single
7981		 * column that you have in your table (these can be null if you don't which
7982		 * to specify any options).
7983		 *  @member
7984		 */
7985		"aoColumns": null,
7986	
7987		/**
7988		 * Very similar to aoColumns, aoColumnDefs allows you to target a specific 
7989		 * column, multiple columns, or all columns, using the aTargets property of 
7990		 * each object in the array. This allows great flexibility when creating 
7991		 * tables, as the aoColumnDefs arrays can be of any length, targeting the 
7992		 * columns you specifically want. aoColumnDefs may use any of the column 
7993		 * options available: {@link DataTable.defaults.columns}, but it _must_
7994		 * have aTargets defined in each object in the array. Values in the aTargets
7995		 * array may be:
7996		 *   <ul>
7997		 *     <li>
7997a string - class name will be matched on the TH for the column</li>
7998		 *     <li>0 or a positive integer - column index counting from the left</li>
7999		 *     <li>a negative integer - column index counting from the right</li>
8000		 *     <li>the string "_all" - all columns (i.e. assign a default)</li>
8001		 *   </ul>
8002		 *  @member
8003		 */
8004		"aoColumnDefs": null,
8005	
8006	
8007		/**
8008		 * Basically the same as oSearch, this parameter defines the individual column
8009		 * filtering state at initialisation time. The array must be of the same size 
8010		 * as the number of columns, and each element be an object with the parameters
8011		 * "sSearch" and "bEscapeRegex" (the latter is optional). 'null' is also
8012		 * accepted and the default will be used.
8013		 *  @type array
8014		 *  @default []
8015		 *  @dtopt Option
8016		 * 
8017		 *  @example
8018		 *    $(document).ready( function() {
8019		 *      $('#example').dataTable( {
8020		 *        "aoSearchCols": [
8021		 *          null,
8022		 *          { "sSearch": "My filter" },
8023		 *          null,
8024		 *          { "sSearch": "^[0-9]", "bEscapeRegex": false }
8025		 *        ]
8026		 *      } );
8027		 *    } )
8028		 */
8029		"aoSearchCols": [],
8030	
8031	
8032		/**
8033		 * An array of CSS classes that should be applied to displayed rows. This 
8034		 * array may be of any length, and DataTables will apply each class 
8035		 * sequentially, looping when required.
8036		 *  @type array
8037		 *  @default null <i>Will take the values determined by the oClasses.sStripe*
8038		 *    options</i>
8039		 *  @dtopt Option
8040		 * 
8041		 *  @example
8042		 *    $(document).ready( function() {
8043		 *      $('#example').dataTable( {
8044		 *        "asStripeClasses": [ 'strip1', 'strip2', 'strip3' ]
8045		 *      } );
8046		 *    } )
8047		 */
8048		"asStripeClasses": null,
8049	
8050	
8051		/**
8052		 * Enable or disable automatic column width calculation. This can be disabled
8053		 * as an optimisation (it takes some time to calculate the widths) if the
8054		 * tables widths are passed in using aoColumns.
8055		 *  @type boolean
8056		 *  @default true
8057		 *  @dtopt Features
8058		 * 
8059		 *  @example
8060		 *    $(document).ready( function () {
8061		 *      $('#example').dataTable( {
8062		 *        "bAutoWidth": false
8063		 *      } );
8064		 *    } );
8065		 */
8066		"bAutoWidth": true,
8067	
8068	
8069		/**
8070		 * Deferred rendering can provide DataTables with a huge speed boost when you
8071		 * are using an Ajax or JS data source for the table. This option, when set to
8072		 * true, will cause DataTables to defer the creation of the table elements for
8073		 * each row until they are needed for a draw - saving a significant amount of
8074		 * time.
8075		 *  @type boolean
8076		 *  @default false
8077		 *  @dtopt Features
8078		 * 
8079		 *  @example
8080		 *    $(document).ready( function() {
8081		 *      var oTable = $('#example').dataTable( {
8082		 *        "sAjaxSource": "sources/arrays.txt",
8083		 *        "bDeferRender": true
8084		 *      } );
8085		 *    } );
8086		 */
8087		"bDeferRender": false,
8088	
8089	
8090		/**
8091		 * Replace a DataTable which matches the given selector and replace it with 
8092		 * one which has the properties of the new initialisation object passed. If no
8093		 * table matches the selector, then the new DataTable will be constructed as
8094		 * per normal.
8095		 *  @type boolean
8096		 *  @default false
8097		 *  @dtopt Options
8098		 * 
8099		 *  @example
8100		 *    $(document).ready( function() {
8101		 *      $('#example').dataTable( {
8102		 *        "sScrollY": "200px",
8103		 *        "bPaginate": false
8104		 *      } );
8105		 *      
8106		 *      // Some time later....
8107		 *      $('#example').dataTable( {
8108		 *        "bFilter": false,
8109		 *        "bDestroy": true
8110		 *      } );
8111		 *    } );
8112		 */
8113		"bDestroy": false,
8114	
8115	
8116		/**
8117		 * Enable or disable filtering of data. Filtering in DataTables is "smart" in
8118		 * that it allows the end user to input multiple words (space separated) and
8119		 * will match a row containing those words, even if not in the order that was
8120		 * specified (this allow matching across multiple columns). Note that if you
8121		 * wish to use filtering in DataTables this must remain 'true' - to remove the
8122		 * default filtering input box and retain filtering abilities, please use
8123		 * {@link DataTable.defaults.sDom}.
8124		 *  @type boolean
8125		 *  @default true
8126		 *  @dtopt Features
8127		 * 
8128		 *  @example
8129		 *    $(document).ready( function () {
8130		 *      $('#example').dataTable( {
8131		 *        "bFilter": false
8132		 *      } );
8133		 *    } );
8134		 */
8135		"bFilter": true,
8136	
8137	
8138		/**
8139		 * Enable or disable the table information display. This shows information 
8140		 * about the data that is currently visible on the page, including information
8141		 * about filtered data if that action is being performed.
8142		 *  @type boolean
8143		 *  @default true
8144		 *  @dtopt Features
8145		 * 
8146		 *  @example
8147		 *    $(document).ready( function () {
8148		 *      $('#example').dataTable( {
8149		 *        "bInfo": false
8150		 *      } );
8151		 *    } );
8152		 */
8153		"bInfo": true,
8154	
8155	
8156		/**
8157		 * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some
8158		 * slightly different and additional mark-up from what DataTables has
8159		 * traditionally used).
8160		 *  @type boolean
8161		 *  @default false
8162		 *  @dtopt Features
8163		 * 
8164		 *  @example
8165		 *    $(document).ready( function() {
8166		 *      $('#example').dataTable( {
8167		 *        "bJQueryUI": true
8168		 *      } );
8169		 *    } );
8170		 */
8171		"bJQueryUI": false,
8172	
8173	
8174		/**
8175		 * Allows the end user to select the size of a formatted page from a select
8176		 * menu (sizes are 10, 25, 50 and 100). Requires pagination (bPaginate).
8177		 *  @type boolean
8178		 *  @default true
8179		 *  @dtopt Features
8180		 * 
8181		 *  @example
8182		 *    $(document).ready( function () {
8183		 *      $('#example').dataTable( {
8184		 *        "bLengthChange": false
8185		 *      } );
8186		 *    } );
8187		 */
8188		"bLengthChange": true,
8189	
8190	
8191		/**
8192		 * Enable or disable pagination.
8193		 *  @type boolean
8194		 *  @default true
8195		 *  @dtopt Features
8196		 * 
8197		 *  @example
8198		 *    $(document).ready( function () {
8199		 *      $('#example').dataTable( {
8200		 *        "bPaginate": false
8201		 *      } );
8202		 *    } );
8203		 */
8204		"bPaginate": true,
8205	
8206	
8207		/**
8208		 * Enable or disable the display of a 'processing' indicator when the table is
8209		 * being processed (e.g. a sort). This is particularly useful for tables with
8210		 * large amounts of data where it can take a noticeable amount of time to sort
8211		 * the entries.
8212		 *  @type boolean
8213		 *  @default false
8214		 *  @dtopt Features
8215		 * 
8216		 *  @example
8217		 *    $(document).ready( function () {
8218		 *      $('#example').dataTable( {
8219		 *        "bProcessing": true
8220		 *      } );
8221		 *    } );
8222		 */
8223		"bProcessing": false,
8224	
8225	
8226		/**
8227		 * Retrieve the DataTables object for the given selector. Note that if the
8228		 * table has already been initialised, this parameter will cause DataTables
8229		 * to simply return the object that has already been set up - it will not take
8230		 * account of any changes you might have made to the initialisation object
8231		 * passed to DataTables (setting this parameter to true is an acknowledgement
8232		 * that you understand this). bDestroy can be used to reinitialise a table if
8233		 * you need.
8234		 *  @type boolean
8235		 *  @default false
8236		 *  @dtopt Options
8237		 * 
8238		 *  @example
8239		 *    $(document).ready( function() {
8240		 *      initTable();
8241		 *      tableActions();
8242		 *    } );
8243		 *    
8244		 *    function initTable ()
8245		 *    {
8246		 *      return $('#example').dataTable( {
8247		 *        "sScrollY": "200px",
8248		 *        "bPaginate": false,
8249		 *        "bRetrieve": true
8250		 *      } );
8251		 *    }
8252		 *    
8253		 *    function tableActions ()
8254		 *    {
8255		 *      var oTable = initTable();
8256		 *      // perform API operations with oTable 
8257		 *    }
8258		 */
8259		"bRetrieve": false,
8260	
8261	
8262		/**
8263		 * Indicate if DataTables should be allowed to set the padding / margin
8264		 * etc for the scrolling header elements or not. Typically you will want
8265		 * this.
8266		 *  @type boolean
8267		 *  @default true
8268		 *  @dtopt Options
8269		 * 
8270		 *  @example
8271		 *    $(document).ready( function() {
8272		 *      $('#example').dataTable( {
8273		 *        "bScrollAutoCss": false,
8274		 *        "sScrollY": "200px"
8275		 *      } );
8276		 *    } );
8277		 */
8278		"bScrollAutoCss": true,
8279	
8280	
8281		/**
8282		 * When vertical (y) scrolling is enabled, DataTables will force the height of
8283		 * the table's viewport to the given height at all times (useful for layout).
8284		 * However, this can look odd when filtering data down to a small data set,
8285		 * and the footer is left "floating" further down. This parameter (when
8286		 * enabled) will cause DataTables to collapse the table's viewport down when
8287		 * the result set will fit within the given Y height.
8288		 *  @type boolean
8289		 *  @default false
8290		 *  @dtopt Options
8291		 * 
8292		 *  @example
8293		 *    $(document).ready( function() {
8294		 *      $('#example').dataTable( {
8295		 *        "sScrollY": "200",
8296		 *        "bScrollCollapse": true
8297		 *      } );
8298		 *    } );
8299		 */
8300		"bScrollCollapse": false,
8301	
8302	
8303		/**
8304		 * Enable infinite scrolling for DataTables (to be used in combination with
8305		 * sScrollY). Infinite scrolling means that DataTables will continually load
8306		 * data as a user scrolls through a table, which is very useful for large
8307		 * dataset. This cannot be used with pagination, which is automatically
8308		 * disabled. Note - the Scroller extra for DataTables is recommended in
8309		 * in preference to this option.
8310		 *  @type boolean
8311		 *  @default false
8312		 *  @dtopt Features
8313		 * 
8314		 *  @example
8315		 *    $(document).ready( function() {
8316		 *      $('#example').dataTable( {
8317		 *        "bScrollInfinite": true,
8318		 *        "bScrollCollapse": true,
8319		 *        "sScrollY": "200px"
8320		 *      } );
8321		 *    } );
8322		 */
8323		"bScrollInfinite": false,
8324	
8325	
8326		/**
8327		 * Configure DataTables to use server-side processing. Note that the
8328		 * sAjaxSource parameter must also be given in order to give DataTables a
8329		 * source to obtain the required data for each draw.
8330		 *  @type boolean
8331		 *  @default false
8332		 *  @dtopt Features
8333		 *  @dtopt Server-side
8334		 * 
8335		 *  @example
8336		 *    $(document).ready( function () {
8337		 *      $('#example').dataTable( {
8338		 *        "bServerSide": true,
8339		 *        "sAjaxSource": "xhr.php"
8340		 *      } );
8341		 *    } );
8342		 */
8343		"bServerSide": false,
8344	
8345	
8346		/**
8347		 * Enable or disable sorting of columns. Sorting of individual columns can be
8348		 * disabled by the "bSortable" option for each column.
8349		 *  @type boolean
8350		 *  @default true
8351		 *  @dtopt Features
8352		 * 
8353		 *  @example
8354		 *    $(document).ready( function () {
8355		 *      $('#example').dataTable( {
8356		 *        "bSort": false
8357		 *      } );
8358		 *    } );
8359		 */
8360		"bSort": true,
8361	
8362	
8363		/**
8364		 * Allows control over whether DataTables should use the top (true) unique
8365		 * cell that is found for a single column, or the bottom (false - default).
8366		 * This is useful when using complex headers.
8367		 *  @type boolean
8368		 *  @default false
8369		 *  @dtopt Options
8370		 * 
8371		 *  @example
8372		 *    $(document).ready( function() {
8373		 *      $('#example').dataTable( {
8374		 *        "bSortCellsTop": true
8375		 *      } );
8376		 *    } );
8377		 */
8378		"bSortCellsTop": false,
8379	
8380	
8381		/**
8382		 * Enable or disable the addition of the classes 'sorting_1', 'sorting_2' and
8383		 * 'sorting_3' to the columns which are currently being sorted on. This is
8384		 * presented as a feature switch as it can increase processing time (while
8385		 * classes are removed and added) so for large data sets you might want to
8386		 * turn this off.
8387		 *  @type boolean
8388		 *  @default true
8389		 *  @dtopt Features
8390		 * 
8391		 *  @example
8392		 *    $(document).ready( function () {
8393		 *      $('#example').dataTable( {
8394		 *        "bSortClasses": false
8395		 *      } );
8396		 *    } );
8397		 */
8398		"bSortClasses": true,
8399	
8400	
8401		/**
8402		 * Enable or disable state saving. When enabled a cookie will be used to save
8403		 * table display information such as pagination information, display length,
8404		 * filtering and sorting. As such when the end user reloads the page the
8405		 * display display will match what thy had previously set up.
8406		 *  @type boolean
8407		 *  @default false
8408		 *  @dtopt Features
8409		 * 
8410		 *  @example
8411		 *    $(document).ready( function () {
8412		 *      $('#example').dataTable( {
8413		 *        "bStateSave": true
8414		 *      } );
8415		 *    } );
8416		 */
8417		"bStateSave": false,
8418	
8419	
8420		/**
8421		 * Customise the cookie and / or the parameters being stored when using
8422		 * DataTables with state saving enabled. This function is called whenever
8423		 * the cookie is modified, and it expects a fully formed cookie string to be
8424		 * returned. Note that the data object passed in is a Javascript object which
8425		 * must be converted to a string (JSON.stringify for example).
8426		 *  @type function
8427		 *  @param {string} sName Name of the cookie defined by DataTables
8428		 *  @param {object} oData Data to be stored in the cookie
8429		 *  @param {string} sExpires Cookie expires string
8430		 *  @param {string} sPath Path of the cookie to set
8431		 *  @returns {string} Cookie formatted string (which should be encoded by
8432		 *    using encodeURIComponent())
8433		 *  @dtopt Callbacks
8434		 * 
8435		 *  @example
8436		 *    $(document).ready( function () {
8437		 *      $('#example').dataTable( {
8438		 *        "fnCookieCallback": function (sName, oData, sExpires, sPath) {
8439		 *          // Customise oData or sName or whatever else here
8440		 *          return sName + "="+JSON.stringify(oData)+"; expires=" + sExpires +"; path=" + sPath;
8441		 *        }
8442		 *      } );
8443		 *    } );
8444		 */
8445		"fnCookieCallback": null,
8446	
8447	
8448		/**
8449		 * This function is called when a TR element is created (and all TD child
8450		 * elements have been inserted), or registered if using a DOM source, allowing
8451		 * manipulation of the TR element (adding classes etc).
8452		 *  @type function
8453		 *  @param {node} nRow "TR" element for the current row
8454		 *  @param {array} aData Raw data array for this row
8455		 *  @param {int} iDataIndex The index of this row in aoData
8456		 *  @dtopt Callbacks
8457		 * 
8458		 *  @example
8459		 *    $(document).ready( function() {
8460		 *      $('#example').dataTable( {
8461		 *        "fnCreatedRow": function( nRow, aData, iDataIndex ) {
8462		 *          // Bold the grade for all 'A' grade browsers
8463		 *          if ( aData[4] == "A" )
8464		 *          {
8465		 *            $('td:eq(4)', nRow).html( '<b>A</b>' );
8466		 *          }
8467		 *        }
8468		 *      } );
8469		 *    } );
8470		 */
8471		"fnCreatedRow": null,
8472	
8473	
8474		/**
8475		 * This function is called on every 'draw' event, and allows you to
8476		 * dynamically modify any aspect you want about the created DOM.
8477		 *  @type function
8478		 *  @param {object} oSettings DataTables settings object
8479		 *  @dtopt Callbacks
8480		 * 
8481		 *  @example
8482		 *    $(document).ready( function() {
8483		 *      $('#example').dataTable( {
8484		 *        "fnDrawCallback": function( oSettings ) {
8485		 *          alert( 'DataTables has redrawn the table' );
8486		 *        }
8487		 *      } );
8488		 *    } );
8489		 */
8490		"fnDrawCallback": null,
8491	
8492	
8493		/**
8494		 * Identical to fnHeaderCallback() but for the table footer this function
8495		 * allows you to modify the table footer on every 'draw' even.
8496		 *  @type function
8497		 *  @param {node} nFoot "TR" element for the footer
8498		 *  @param {array} aData Full table data (as derived from the original HTML)
8499		 *  @param {int} iStart Index for the current display starting point in the 
8500		 *    display array
8501		 *  @param {int} iEnd Index for the current display ending point in the 
8502		 *    display array
8503		 *  @param {array int} aiDisplay Index array to translate the visual position
8504		 *    to the full data array
8505		 *  @dtopt Callbacks
8506		 * 
8507		 *  @example
8508		 *    $(document).ready( function() {
8509		 *      $('#example').dataTable( {
8510		 *        "fnFooterCallback": function( nFoot, aData, iStart, iEnd, aiDisplay ) {
8511		 *          nFoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+iStart;
8512		 *        }
8513		 *      } );
8514		 *    } )
8515		 */
8516		"fnFooterCallback": null,
8517	
8518	
8519		/**
8520		 * When rendering large numbers in the information element for the table
8521		 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
8522		 * to have a comma separator for the 'thousands' units (e.g. 1 million is
8523		 * rendered as "1,000,000") to help readability for the end user. This
8524		 * function will override the default method DataTables uses.
8525		 *  @type function
8526		 *  @member
8527		 *  @param {int} iIn number to be formatted
8528		 *  @returns {string} formatted string for DataTables to show the number
8529		 *  @dtopt Callbacks
8530		 * 
8531		 *  @example
8532		 *    $(document).ready( function() {
8533		 *      $('#example').dataTable( {
8534		 *        "fnFormatNumber": function ( iIn ) {
8535		 *          if ( iIn &lt; 1000 ) {
8536		 *            return iIn;
8537		 *          } else {
8538		 *            var 
8539		 *              s=(iIn+""), 
8540		 *              a=s.split(""), out="", 
8541		 *              iLen=s.length;
8542		 *            
8543		 *            for ( var i=0 ; i&lt;iLen ; i++ ) {
8544		 *              if ( i%3 === 0 &amp;&amp; i !== 0 ) {
8545		 *                out = "'"+out;
8546		 *              }
8547		 *              out = a[iLen-i-1]+out;
8548		 *            }
8549		 *          }
8550		 *          return out;
8551		 *        };
8552		 *      } );
8553		 *    } );
8554		 */
8555		"fnFormatNumber": function ( iIn ) {
8556			if ( iIn < 1000 )
8557			{
8558				// A small optimisation for what is likely to be the majority of use cases
8559				return iIn;
8560			}
8561	
8562			var s=(iIn+""), a=s.split(""), out="", iLen=s.length;
8563			
8564			for ( var i=0 ; i<iLen ; i++ )
8565			{
8566				if ( i%3 === 0 && i !== 0 )
8567				{
8568					out = this.oLanguage.sInfoThousands+out;
8569				}
8570				out = a[iLen-i-1]+out;
8571			}
8572			return out;
8573		},
8574	
8575	
8576		/**
8577		 * This function is called on every 'draw' event, and allows you to
8578		 * dynamically modify the header row. This can be used to calculate and
8579		 * display useful information about the table.
8580		 *  @type function
8581		 *  @param {node} nHead "TR" element for the header
8582		 *  @param {array} aData Full table data (as derived from the original HTML)
8583		 *  @param {int} iStart Index for the current display starting point in the
8584		 *    display array
8585		 *  @param {int} iEnd Index for the current display ending point in the
8586		 *    display array
8587		 *  @param {array int} aiDisplay Index array to translate the visual position
8588		 *    to the full data array
8589		 *  @dtopt Callbacks
8590		 * 
8591		 *  @example
8592		 *    $(document).ready( function() {
8593		 *      $('#example').dataTable( {
8594		 *        "fnHeaderCallback": function( nHead, aData, iStart, iEnd, aiDisplay ) {
8595		 *          nHead.getElementsByTagName('th')[0].innerHTML = "Dis
8595playing "+(iEnd-iStart)+" records";
8596		 *        }
8597		 *      } );
8598		 *    } )
8599		 */
8600		"fnHeaderCallback": null,
8601	
8602	
8603		/**
8604		 * The information element can be used to convey information about the current
8605		 * state of the table. Although the internationalisation options presented by
8606		 * DataTables are quite capable of dealing with most customisations, there may
8607		 * be times where you wish to customise the string further. This callback
8608		 * allows you to do exactly that.
8609		 *  @type function
8610		 *  @param {object} oSettings DataTables settings object
8611		 *  @param {int} iStart Starting position in data for the draw
8612		 *  @param {int} iEnd End position in data for the draw
8613		 *  @param {int} iMax Total number of rows in the table (regardless of
8614		 *    filtering)
8615		 *  @param {int} iTotal Total number of rows in the data set, after filtering
8616		 *  @param {string} sPre The string that DataTables has formatted using it's
8617		 *    own rules
8618		 *  @returns {string} The string to be displayed in the information element.
8619		 *  @dtopt Callbacks
8620		 * 
8621		 *  @example
8622		 *    $('#example').dataTable( {
8623		 *      "fnInfoCallback": function( oSettings, iStart, iEnd, iMax, iTotal, sPre ) {
8624		 *        return iStart +" to "+ iEnd;
8625		 *      }
8626		 *    } );
8627		 */
8628		"fnInfoCallback": null,
8629	
8630	
8631		/**
8632		 * Called when the table has been initialised. Normally DataTables will
8633		 * initialise sequentially and there will be no need for this function,
8634		 * however, this does not hold true when using external language information
8635		 * since that is obtained using an async XHR call.
8636		 *  @type function
8637		 *  @param {object} oSettings DataTables settings object
8638		 *  @param {object} json The JSON object request from the server - only
8639		 *    present if client-side Ajax sourced data is used
8640		 *  @dtopt Callbacks
8641		 * 
8642		 *  @example
8643		 *    $(document).ready( function() {
8644		 *      $('#example').dataTable( {
8645		 *        "fnInitComplete": function(oSettings, json) {
8646		 *          alert( 'DataTables has finished its initialisation.' );
8647		 *        }
8648		 *      } );
8649		 *    } )
8650		 */
8651		"fnInitComplete": null,
8652	
8653	
8654		/**
8655		 * Called at the very start of each table draw and can be used to cancel the
8656		 * draw by returning false, any other return (including undefined) results in
8657		 * the full draw occurring).
8658		 *  @type function
8659		 *  @param {object} oSettings DataTables settings object
8660		 *  @returns {boolean} False will cancel the draw, anything else (including no
8661		 *    return) will allow it to complete.
8662		 *  @dtopt Callbacks
8663		 * 
8664		 *  @example
8665		 *    $(document).ready( function() {
8666		 *      $('#example').dataTable( {
8667		 *        "fnPreDrawCallback": function( oSettings ) {
8668		 *          if ( $('#test').val() == 1 ) {
8669		 *            return false;
8670		 *          }
8671		 *        }
8672		 *      } );
8673		 *    } );
8674		 */
8675		"fnPreDrawCallback": null,
8676	
8677	
8678		/**
8679		 * This function allows you to 'post process' each row after it have been
8680		 * generated for each table draw, but before it is rendered on screen. This
8681		 * function might be used for setting the row class name etc.
8682		 *  @type function
8683		 *  @param {node} nRow "TR" element for the current row
8684		 *  @param {array} aData Raw data array for this row
8685		 *  @param {int} iDisplayIndex The display index for the current table draw
8686		 *  @param {int} iDisplayIndexFull The index of the data in the full list of
8687		 *    rows (after filtering)
8688		 *  @dtopt Callbacks
8689		 * 
8690		 *  @example
8691		 *    $(document).ready( function() {
8692		 *      $('#example').dataTable( {
8693		 *        "fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
8694		 *          // Bold the grade for all 'A' grade browsers
8695		 *          if ( aData[4] == "A" )
8696		 *          {
8697		 *            $('td:eq(4)', nRow).html( '<b>A</b>' );
8698		 *          }
8699		 *        }
8700		 *      } );
8701		 *    } );
8702		 */
8703		"fnRowCallback": null,
8704	
8705	
8706		/**
8707		 * This parameter allows you to override the default function which obtains
8708		 * the data from the server ($.getJSON) so something more suitable for your
8709		 * application. For example you could use POST data, or pull information from
8710		 * a Gears or AIR database.
8711		 *  @type function
8712		 *  @member
8713		 *  @param {string} sSource HTTP source to obtain the data from (sAjaxSource)
8714		 *  @param {array}
8714 aoData A key/value pair object containing the data to send
8715		 *    to the server
8716		 *  @param {function} fnCallback to be called on completion of the data get
8717		 *    process that will draw the data on the page.
8718		 *  @param {object} oSettings DataTables settings object
8719		 *  @dtopt Callbacks
8720		 *  @dtopt Server-side
8721		 * 
8722		 *  @example
8723		 *    // POST data to server
8724		 *    $(document).ready( function() {
8725		 *      $('#example').dataTable( {
8726		 *        "bProcessing": true,
8727		 *        "bServerSide": true,
8728		 *        "sAjaxSource": "xhr.php",
8729		 *        "fnServerData": function ( sSource, aoData, fnCallback, oSettings ) {
8730		 *          oSettings.jqXHR = $.ajax( {
8731		 *            "dataType": 'json', 
8732		 *            "type": "POST", 
8733		 *            "url": sSource, 
8734		 *            "data": aoData, 
8735		 *            "success": fnCallback
8736		 *          } );
8737		 *        }
8738		 *      } );
8739		 *    } );
8740		 */
8741		"fnServerData": function ( sUrl, aoData, fnCallback, oSettings ) {
8742			oSettings.jqXHR = $.ajax( {
8743				"url":  sUrl,
8744				"data": aoData,
8745				"success": function (json) {
8746					if ( json.sError ) {
8747						oSettings.oApi._fnLog( oSettings, 0, json.sError );
8748					}
8749					
8750					$(oSettings.oInstance).trigger('xhr', [oSettings, json]);
8751					fnCallback( json );
8752				},
8753				"dataType": "json",
8754				"cache": false,
8755				"type": oSettings.sServerMethod,
8756				"error": function (xhr, error, thrown) {
8757					if ( error == "parsererror" ) {
8758						oSettings.oApi._fnLog( oSettings, 0, "DataTables warning: JSON data from "+
8759							"server could not be parsed. This is caused by a JSON formatting error." );
8760					}
8761				}
8762			} );
8763		},
8764	
8765	
8766		/**
8767		 * It is often useful to send extra data to the server when making an Ajax
8768		 * request - for example custom filtering information, and this callback
8769		 * function makes it trivial to send extra information to the server. The
8770		 * passed in parameter is the data set that has been constructed by
8771		 * DataTables, and you can add to this or modify it as you require.
8772		 *  @type function
8773		 *  @param {array} aoData Data array (array of objects which are name/value
8774		 *    pairs) that has been constructed by DataTables and will be sent to the
8775		 *    server. In the case of Ajax sourced data with server-side processing
8776		 *    this will be an empty array, for server-side processing there will be a
8777		 *    significant number of parameters!
8778		 *  @returns {undefined} Ensure that you modify the aoData array passed in,
8779		 *    as this is passed by reference.
8780		 *  @dtopt Callbacks
8781		 *  @dtopt Server-side
8782		 * 
8783		 *  @example
8784		 *    $(document).ready( function() {
8785		 *      $('#example').dataTable( {
8786		 *        "bProcessing": true,
8787		 *        "bServerSide": true,
8788		 *        "sAjaxSource": "scripts/server_processing.php",
8789		 *        "fnServerParams": function ( aoData ) {
8790		 *          aoData.push( { "name": "more_data", "value": "my_value" } );
8791		 *        }
8792		 *      } );
8793		 *    } );
8794		 */
8795		"fnServerParams": null,
8796	
8797	
8798		/**
8799		 * Load the table state. With this function you can define from where, and how, the
8800		 * state of a table is loaded. By default DataTables will load from its state saving
8801		 * cookie, but you might wish to use local storage (HTML5) or a server-side database.
8802		 *  @type function
8803		 *  @member
8804		 *  @param {object} oSettings DataTables settings object
8805		 *  @return {object} The DataTables state object to be loaded
8806		 *  @dtopt Callbacks
8807		 * 
8808		 *  @example
8809		 *    $(document).ready( function() {
8810		 *      $('#example').dataTable( {
8811		 *        "bStateSave": true,
8812		 *        "fnStateLoad": function (oSettings) {
8813		 *          var o;
8814		 *          
8815		 *          // Send an Ajax request to the server to get the data. Note that
8816		 *          // this is a synchronous request.
8817		 *          $.ajax( {
8818		 *            "url": "/state_load",
8819		 *            "async": false,
8820		 *            "dataType": "json",
8821		 *            "success": function (json) {
8822		 *              o = json;
8823		 *            }
8824		 *          } );
8825		 *          
8826		 *          return o;
8827		 *        }
8828		 *      } );
8829		 *    } );
8830		 */
8831		"fnStateLoad": function ( oSettings ) {
8832			var sData = this.oApi._fnReadCookie( oSettings.sCookiePrefix+oSettings.sInstance );
8833			var oData;
8834	
8835			try {
8836				oData = (typeof $.parseJSON === 'function') ? 
8837					$.parseJSON(sData) : eval( '('+sData+')' );
8838			} catch (e) {
8839				oData = null;
8840			}
8841	
8842			return oData;
8843		},
8844	
8845	
8846		/**
8847		 * Callback which allows modification of the saved state prior to loading that state.
8848		 * This callback is called when the table is loading state from the stored data, but
8849		 * prior to the settings object being modified by the saved state. Note that for 
8850		 * plug-in authors, you should use the 'stateLoadParams' event to load parameters for 
8851		 * a plug-in.
8852		 *  @type function
8853		 *  @param {object} oSettings DataTables settings object
8854		 *  @param {object} oData The state object that is to be loaded
8855		 *  @dtopt Callbacks
8856		 * 
8857		 *  @example
8858		 *    // Remove a saved filter, so filtering is never loaded
8859		 *    $(document).ready( function() {
8860		 *      $('#example').dataTable( {
8861		 *        "bStateSave": true,
8862		 *        "fnStateLoadParams": function (oSettings, oData) {
8863		 *          oData.oSearch.sSearch = "";
8864		 *        }
8865		 *      } );
8866		 *    } );
8867		 * 
8868		 *  @example
8869		 *    // Disallow state loading by returning false
8870		 *    $(document).ready( function() {
8871		 *      $('#example').dataTable( {
8872		 *        "bStateSave": true,
8873		 *        "fnStateLoadParams": function (oSettings, oData) {
8874		 *          return false;
8875		 *        }
8876		 *      } );
8877		 *    } );
8878		 */
8879		"fnStateLoadParams": null,
8880	
8881	
8882		/**
8883		 * Callback that is called when the state has been loaded from the state saving method
8884		 * and the DataTables settings object has been modified as a result of the loaded state.
8885		 *  @type function
8886		 *  @param {object} oSettings DataTables settings object
8887		 *  @param {object} oData The state object that was loaded
8888		 *  @dtopt Callbacks
8889		 * 
8890		 *  @example
8891		 *    // Show an alert with the filtering value that was saved
8892		 *    $(document).ready( function() {
8893		 *      $('#example').dataTable( {
8894		 *        "bStateSave": true,
8895		 *        "fnStateLoaded": function (oSettings, oData) {
8896		 *          alert( 'Saved filter was: '+oData.oSearch.sSearch );
8897		 *        }
8898		 *      } );
8899		 *    } );
8900		 */
8901		"fnStateLoaded": null,
8902	
8903	
8904		/**
8905		 * Save the table state. This function allows you to define where and how the state
8906		 * information for the table is stored - by default it will use a cookie, but you
8907		 * might want to use local storage (HTML5) or a server-side database.
8908		 *  @type function
8909		 *  @member
8910		 *  @param {object} oSettings DataTables settings object
8911		 *  @param {object} oData The state object to be saved
8912		 *  @dtopt Callbacks
8913		 * 
8914		 *  @example
8915		 *    $(document).ready( function() {
8916		 *      $('#example').dataTable( {
8917		 *        "bStateSave": true,
8918		 *        "fnStateSave": function (oSettings, oData) {
8919		 *          // Send an Ajax request to the server with the state object
8920		 *          $.ajax( {
8921		 *            "url": "/state_save",
8922		 *            "data": oData,
8923		 *            "dataType": "json",
8924		 *            "method": "POST"
8925		 *            "success": function () {}
8926		 *          } );
8927		 *        }
8928		 *      } );
8929		 *    } );
8930		 */
8931		"fnStateSave": function ( oSettings, oData ) {
8932			this.oApi._fnCreateCookie( 
8933				oSettings.sCookiePrefix+oSettings.sInstance, 
8934				this.oApi._fnJsonString(oData), 
8935				oSettings.iCookieDuration, 
8936				oSettings.sCookiePrefix, 
8937				oSettings.fnCookieCallback
8938			);
8939		},
8940	
8941	
8942		/**
8943		 * Callback which allows modification of the state to be saved. Called when the table 
8944		 * has changed state a new state save is required. This method allows modification of
8945		 * the state saving object prior to actually doing the save, including addition or 
8946		 * other state properties or modification. Note that for plug-in authors, you should 
8947		 * use the 'stateSaveParams' event to save parameters for a plug-in.
8948		 *  @type function
8949		 *  @param {object} oSettings DataTables settings object
8950		 *  @param {object} oData The state object to be saved
8951		 *  @dtopt Callbacks
8952		 * 
8953		 *  @example
8954		 *    // Remove a saved filter, so filtering is never saved
8955		 *    $(document).ready( function() {
8956		 *      $('#example').dataTable( {
8957		 *        "bStateSave": true,
8958		 *        "fnStateSaveParams": function (oSettings, oData) {
8959		 *          oData.oSearch.sSearch = "";
8960		 *        }
8961		 *      } );
8962		 *    } );
8963		 */
8964		"fnStateSaveParams": null,
8965	
8966	
8967		/**
8968		 * Duration of the cookie which is used for storing session information. This
8969		 * value is given in seconds.
8970		 *  @type int
8971		 *  @default 7200 <i>(2 hours)</i>
8972		 *  @dtopt Options
8973		 * 
8974		 *  @example
8975		 *    $(document).ready( function() {
8976		 *      $('#example').dataTable( {
8977		 *        "iCookieDuration": 60*60*24; // 1 day
8978		 *      } );
8979		 *    } )
8980		 */
8981		"iCookieDuration": 7200,
8982	
8983	
8984		/**
8985		 * When enabled DataTables will not make a request to the server for the first
8986		 * page draw - rather it will use the data already on the page (no sorting etc
8987		 * will be applied to it), thus saving on an XHR at load time. iDeferLoading
8988		 * is used to indicate that deferred loading is required, but it is also used
8989		 * to tell DataTables how many records there are in the full table (allowing
8990		 * the information element and pagination to be displayed correctly). In the case
8991		 * where a filtering is applied to the table on initial load, this can be
8992		 * indicated by giving the parameter as an array, where the first element is
8993		 * the number of records available after filtering and the second element is the
8994		 * number of records without filtering (allowing the table information element
8995		 * to be shown correctly).
8996		 *  @type int | array
8997		 *  @default null
8998		 *  @dtopt Options
8999		 * 
9000		 *  @example
9001		 *    // 57 records available in the table, no filtering applied
9002		 *    $(document).ready( function() {
9003		 *      $('#example').dataTable( {
9004		 *        "bServerSide": true,
9005		 *        "sAjaxSource": "scripts/server_processing.php",
9006		 *        "iDeferLoading": 57
9007		 *      } );
9008		 *    } );
9009		 * 
9010		 *  @example
9011		 *    // 57 records after filtering, 100 without filtering (an initial filter applied)
9012		 *    $(document).ready( function() {
9013		 *      $('#example').dataTable( {
9014		 *        "bServerSide": true,
9015		 *        "sAjaxSource": "scripts/server_processing.php",
9016		 *        "iDeferLoading": [ 57, 100 ],
9017		 *        "oSearch": {
9018		 *          "sSearch": "my_filter"
9019		 *        }
9020		 *      } );
9021		 *    } );
9022		 */
9023		"iDeferLoading": null,
9024	
9025	
9026		/**
9027		 * Number of rows to display on a single page when using pagination. If
9028		 * feature enabled (bLengthChange) then the end user will be able to override
9029		 * this to a custom setting using a pop-up menu.
9030		 *  @type int
9031		 *  @default 10
9032		 *  @dtopt Options
9033		 * 
9034		 *  @example
9035		 *    $(document).ready( function() {
9036		 *      $('#example').dataTable( {
9037		 *        "iDisplayLength": 50
9038		 *      } );
9039		 *    } )
9040		 */
9041		"iDisplayLength": 10,
9042	
9043	
9044		/**
9045		 * Define the starting point for data display when using DataTables with
9046		 * pagination. Note that this parameter is the number of records, rather than
9047		 * the page number, so if you have 10 records per page and want to start on
9048		 * the third page, it should be "20".
9049		 *  @type int
9050		 *  @default 0
9051		 *  @dtopt Options
9052		 * 
9053		 *  @example
9054		 *    $(document).ready( function() {
9055		 *      $('#example').dataTable( {
9056		 *        "iDisplayStart": 20
9057		 *      } );
9058		 *    } )
9059		 */
9060		"iDisplayStart": 0,
9061	
9062	
9063		/**
9064		 * The scroll gap is the amount of scrolling that is left to go before
9065		 * DataTables will load the next 'page' of data automatically. You typically
9066		 * want a gap which is big enough that the scrolling will be smooth for the
9067		 * user, while not so large that it will load more data than need.
9068		 *  @type int
9069		 *  @default 100
9070		 *  @dtopt Options
9071		 * 
9072		 *  @example
9073		 *    $(document).ready( function() {
9074		 *      $('#example').dataTable( {
9075		 *        "bScrollInfinite": true,
9076		 *        "bScrollCollapse": true,
9077		 *        "sScrollY": "200px",
9078		 *        "iScrollLoadGap": 50
9079		 *      } );
9080		 *    } );
9081		 */
9082		"iScrollLoadGap": 100,
9083	
9084	
9085		/**
9086		 * By default DataTables allows keyboard navigation of the table (sorting, paging,
9087		 * and filtering) by adding a tabindex attribute to the required elements. This
9088		 * allows you to tab through the controls and press the enter key to activate them.
9089		 * The tabindex is default 0, meaning that the tab follows the flow of the document.
9090		 * You can overrule this using this parameter if you wish. Use a value of -1 to
9091		 * disable built-in keyboard navigation.
9092		 *  @type int
9093		 *  @default 0
9094		 *  @dtopt Options
9095		 * 
9096		 *  @example
9097		 *    $(document).ready( function() {
9098		 *      $('#example').dataTable( {
9099		 *        "iTabIndex": 1
9100		 *      } );
9101		 *    } );
9102		 */
9103		"iTabIndex": 0,
9104	
9105	
9106		/**
9107		 * All strings that DataTables uses in the user interface that it creates
9108		 * are defined in this object, allowing you to modified them individually or
9109		 * completely replace them all as required.
9110		 *  @namespace
9111		 */
9112		"oLanguage": {
9113			/**
9114			 * Strings that are used for WAI-ARIA labels and controls only (these are not
9115			 * actually visible on the page, but will be read by screenreaders, and thus
9116			 * must be internationalised as well).
9117			 *  @namespace
9118			 */
9119			"oAria": {
9120				/**
9121				 * ARIA label that is added to the table headers when the column may be
9122				 * sorted ascending by activing the column (click or return when focused).
9123				 * Note that the column header is prefixed to this string.
9124				 *  @type string
9125				 *  @default : activate to sort column ascending
9126				 *  @dtopt Language
9127				 * 
9128				 *  @example
9129				 *    $(document).ready( function() {
9130				 *      $('#example').dataTable( {
9131				 *        "oLanguage": {
9132				 *          "oAria": {
9133				 *            "sSortAscending": " - click/return to sort ascending"
9134				 *          }
9135				 *        }
9136				 *      } );
9137				 *    } );
9138				 */
9139				"sSortAscending": ": activate to sort column ascending",
9140	
9141				/**
9142				 * ARIA label that is added to the table headers when the column may be
9143				 * sorted descending by activing the column (click or return when focused).
9144				 * Note that the column header is prefixed to this string.
9145				 *  @type string
9146				 *  @default : activate to sort column ascending
9147				 *  @dtopt Language
9148				 * 
9149				 *  @example
9150				 *    $(document).ready( function() {
9151				 *      $('#example').dataTable( {
9152				 *        "oLanguage": {
9153				 *          "oAria": {
9154				 *            "sSortDescending": " - click/return to sort descending"
9155				 *          }
9156				 *        }
9157				 *      } );
9158				 *    } );
9159				 */
9160				"sSortDescending": ": activate to sort column descending"
9161			},
9162	
9163			/**
9164			 * Pagination string used by DataTables for the two built-in pagination
9165			 * control types ("two_button" and "full_numbers")
9166			 *  @namespace
9167			 */
9168			"oPaginate": {
9169				/**
9170				 * Text to use when using the 'full_numbers' type of pagination for the
9171				 * button to take the user to the first page.
9172				 *  @type string
9173				 *  @default First
9174				 *  @dtopt Language
9175				 * 
9176				 *  @example
9177				 *    $(document).ready( function() {
9178				 *      $('#example').dataTable( {
9179				 *        "oLanguage": {
9180				 *          "oPaginate": {
9181				 *            "sFirst": "First page"
9182				 *          }
9183				 *        }
9184				 *      } );
9185				 *    } );
9186				 */
9187				//"sFirst": "Première",
9188                "sFirst": "<<",
9189			
9190			
9191				/**
9192				 * Text to use when using the 'full_numbers' type of pagination for the
9193				 * button to take the user to the last page.
9194				 *  @type string
9195				 *  @default Last
9196				 *  @dtopt Language
9197				 * 
9198				 *  @example
9199				 *    $(document).ready( function() {
9200				 *      $('#example').dataTable( {
9201				 *        "oLanguage": {
9202				 *          "oPaginate": {
9203				 *            "sLast": "Last page"
9204				 *          }
9205				 *        }
9206				 *      } );
9207				 *    } );
9208				 */
9209				//"sLast": "Dernière",
9210                "sLast": ">>",
9211			
9212			
9213				/**
9214				 * Text to use for the 'next' pagination button (to take the user to the 
9215				 * next page).
9216				 *  @type string
9217				 *  @default Next
9218				 *  @dtopt Language
9219				 * 
9220				 *  @example
9221				 *    $(document).ready( function() {
9222				 *      $('#example').dataTable( {
9223				 *        "oLanguage": {
9224				 *          "oPaginate": {
9225				 *            "sNext": "Next page"
9226				 *          }
9227				 *        }
9228				 *      } );
9229				 *    } );
9230				 */
9231				//"sNext": "Suivant",
9232                "sNext": ">",
9233			
9234			
9235				/**
9236				 * Text to use for the 'previous' pagination button (to take the user to  
9237				 * the previous page).
9238				 *  @type string
9239				 *  @default Previous
9240				 *  @dtopt Language
9241				 * 
9242				 *  @example
9243				 *    $(document).ready( function() {
9244				 *      $('#example').dataTable( {
9245				 *        "oLanguage": {
9246				 *          "oPaginate": {
9247				 *            "sPrevious": "Previous page"
9248				 *          }
9249				 *        }
9250				 *      } );
9251				 *    } );
9252				 */
9253				//"sPrevious": "Précédent"
9254                "sPrevious": "<"
9255			},
9256		
9257			/**
9258			 * This string is shown in preference to sZeroRecords when the table is
9259			 * empty of data (regardless of filtering). Note that this is an optional
9260			 * parameter - if it is not given, the value of sZeroRecords will be used
9261			 * instead (either the default or given value).
9262			 *  @type string
9263			 *  @default No data available in table
9264			 *  @dtopt Language
9265			 * 
9266			 *  @example
9267			 *    $(document).ready( function() {
9268			 *      $('#example').dataTable( {
9269			 *        "oLanguage": {
9270			 *          "sEmptyTable": "No data available in table"
9271			 *        }
9272			 *      } );
9273			 *    } );
9274			 */
9275			"sEmptyTable": "Aucune donnée disponible",
9276		
9277		
9278			/**
9279			 * This string gives information to the end user about the information that 
9280			 * is current on display on the page. The _START_, _END_ and _TOTAL_ 
9281			 * variables are all dynamically replaced as the table display updates, and 
9282			 * can be freely moved or removed as the language requirements change.
9283			 *  @type string
9284			 *  @default Showing _START_ to _END_ of _TOTAL_ entries
9285			 *  @dtopt Language
9286			 * 
9287			 *  @example
9288			 *    $(document).ready( function() {
9289			 *      $('#example').dataTable( {
9290			 *        "oLanguage": {
9291			 *          "sInfo": "Got a total of _TOTAL_ entries to show (_START_ to _END_)"
9292			 *        }
9293			 *      } );
9294			 *    } );
9295			 */
9296			"sInfo": "Voir _START_ à _END_ de _TOTAL_ lignes",
9297		
9298		
9299			/**
9300			 * Display information string for when the table is empty. Typically the 
9301			 * format of this string should match sInfo.
9302			 *  @type string
9303			 *  @default Showing 0 to 0 of 0 entries
9304			 *  @dtopt Language
9305			 * 
9306			 *  @example
9307			 *    $(document).ready( function() {
9308			 *      $('#example').dataTable( {
9309			 *        "oLanguage": {
9310			 *          "sInfoEmpty": "Aucune ligne a afficher"
9311			 *        }
9312			 *      } );
9313			 *    } );
9314			 */
9315			"sInfoEmpty": "Voir 0 à 0 de 0 lignes",
9316		
9317		
9318			/**
9319			 * When a user filters the information in a table, this string is appended 
9320			 * to the information (sInfo) to give an idea of how strong the filtering 
9321			 * is. The variable _MAX_ is dynamically updated.
9322			 *  @type string
9323			 *  @default (filtered from _MAX_ total entries)
9324			 *  @dtopt Language
9325			 * 
9326			 *  @example
9327			 *    $(document).ready( function() {
9328			 *      $('#example').dataTable( {
9329			 *        "oLanguage": {
9330			 *          "sInfoFiltered": " - filtering from _MAX_ records"
9331			 *        }
9332			 *      } );
9333			 *    } );
9334			 */
9335			"sInfoFiltered": "(filtré de _MAX_ lignes)",
9336		
9337		
9338			/**
9339			 * If can be useful to append extra information to the info string at times,
9340			 * and this variable does exactly that. This information will be appended to
9341			 * the sInfo (sInfoEmpty and sInfoFiltered in whatever combination they are
9342			 * being used) at all times.
9343			 *  @type string
9344			 *  @default <i>Empty string</i>
9345			 *  @dtopt Language
9346			 * 
9347			 *  @example
9348			 *    $(document).ready( function() {
9349			 *      $('#example').dataTable( {
9350			 *        "oLanguage": {
9351			 *          "sInfoPostFix": "All records shown are derived from real information."
9352			 *        }
9353			 *      } );
9354			 *    } );
9355			 */
9356			"sInfoPostFix": "",
9357		
9358		
9359			/**
9360			 * DataTables has a build in number formatter (fnFormatNumber) which is used
9361			 * to format large numbers that are used in the table information. By
9362			 * default a comma is used, but this can be trivially changed to any
9363			 * character you wish with this parameter.
9364			 *  @type string
9365			 *  @default ,
9366			 *  @dtopt Language
9367			 * 
9368			 *  @example
9369			 *    $(document).ready( function() {
9370			 *      $('#example').dataTable( {
9371			 *        "oLanguage": {
9372			 *          "sInfoThousands": "'"
9373			 *        }
9374			 *      } );
9375			 *    } );
9376			 */
9377			"sInfoThousands": ",",
9378		
9379		
9380			/**
9381			 * Detail the action that will be taken when the drop down menu for the
9382			 * pagination length option is changed. The '_MENU_' variable is replaced
9383			 * with a default select list of 10, 25, 50 and 100, and can be replaced
9384			 * with a custom select box if required.
9385			 *  @type string
9386			 *  @default Show _MENU_ entries
9387			 *  @dtopt Language
9388			 * 
9389			 *  @example
9390			 *    // Language change only
9391			 *    $(document).ready( function() {
9392			 *      $('#example').dataTable( {
9393			 *        "oLanguage": {
9394			 *          "sLengthMenu": "Display _MENU_ records"
9395			 *        }
9396			 *      } );
9397			 *    } );
9398			 *    
9399			 *  @example
9400			 *    // Language and options change
9401			 *    $(document).ready( function() {
9402			 *      $('#example').dataTable( {
9403			 *        "oLanguage": {
9404			 *          "sLengthMenu": 'Display <select>'+
9405			 *            '<option value="10">10</option>'+
9406			 *            '<option value="20">20</option>'+
9407			 *            '<option value="30">30</option>'+
9408			 *            '<option value="40">40</option>'+
9409			 *            '<option value="50">50</option>'+
9410			 *            '<option value="-1">All</option>'+
9411			 *            '</select> records'
9412			 *        }
9413			 *      } );
9414			 *    } );
9415			 */
9416			"sLengthMenu": "Voir _MENU_ lignes",
9417		
9418		
9419			/**
9420			 * When using Ajax sourced data and during the first draw when DataTables is
9421			 * gathering the data, this message is shown in an empty row in the table to
9422			 * indicate to the end user the the data is being loaded. Note that this
9423			 * parameter is not used when loading data by server-side processing, just
9424			 * Ajax sourced data with client-side processing.
9425			 *  @type string
9426			 *  @default Loading...
9427			 *  @dtopt Language
9428			 * 
9429			 *  @example
9430			 *    $(document).ready( function() {
9431			 *      $('#example').dataTable( {
9432			 *        "oLanguage": {
9433			 *          "sLoadingRecords": "Please wait - loading..."
9434			 *        }
9435			 *      } );
9436			 *    } );
9437			 */
9438			"sLoadingRecords": "Chargement...",
9439		
9440		
9441			/**
9442			 * Text which is displayed when the table is processing a user action
9443			 * (usually a sort command or similar).
9444			 *  @type string
9445			 *  @default Processing...
9446			 *  @dtopt Language
9447			 * 
9448			 *  @example
9449			 *    $(document).ready( function() {
9450			 *      $('#example').dataTable( {
9451			 *        "oLanguage": {
9452			 *          "sProcessing": "DataTables is currently busy"
9453			 *        }
9454			 *      } );
9455			 *    } );
9456			 */
9457			"sProcessing": "Processing...",
9458		
9459		
9460			/**
9461			 * Details the actions that will be taken when the user types into the
9462			 * filtering input text box. The variable "_INPUT_", if used in the string,
9463			 * is replaced with the HTML text box for the filtering input allowing
9464			 * control over where it appears in the string. If "_INPUT_" is not given
9465			 * then the input box is appended to the string automatically.
9466			 *  @type string
9467			 *  @default Search:
9468			 *  @dtopt Language
9469			 * 
9470			 *  @example
9471			 *    // Input text box will be appended at the end automatically
9472			 *    $(document).ready( function() {
9473			 *      $('#example').dataTable( {
9474			 *        "oLanguage": {
9475			 *          "sSearch": "Filter records:"
9476			 *        }
9477			 *      } );
9478			 *    } );
9479			 *    
9480			 *  @example
9481			 *    // Specify where the filter should appear
9482			 *    $(document).ready( function() {
9483			 *      $('#example').dataTable( {
9484			 *        "oLanguage": {
9485			 *          "sSearch": "Apply filter _INPUT_ to table"
9486			 *        }
9487			 *      } );
9488			 *    } );
9489			 */
9490			"sSearch": "Recherche",
9491		
9492		
9493			/**
9494			 * All of the language information can be stored in a file on the
9495			 * server-side, which DataTables will look up if this parameter is passed.
9496			 * It must store the URL of the language file, which is in a JSON format,
9497			 * and the object has the same properties as the oLanguage object in the
9498			 * initialiser object (i.e. the above parameters). Please refer to one of
9499			 * the example language files to see how this works in action.
9500			 *  @type string
9501			 *  @default <i>Empty string - i.e. disabled</i>
9502			 *  @dtopt Language
9503			 * 
9504			 *  @example
9505			 *    $(document).ready( function() {
9506			 *      $('#example').dataTable( {
9507			 *        "oLanguage": {
9508			 *          "sUrl": "http://www.sprymedia.co.uk/dataTables/lang.txt"
9509			 *        }
9510			 *      } );
9511			 *    } );
9512			 */
9513			"sUrl": "",
9514		
9515		
9516			/**
9517			 * Text shown inside the table records when the is no information to be
9518			 * displayed after filtering. sEmptyTable is shown when there is simply no
9519			 * information in the table at all (regardless of filtering).
9520			 *  @type string
9521			 *  @default No matching records found
9522			 *  @dtopt Language
9523			 * 
9524			 *  @example
9525			 *    $(document).ready( function() {
9526			 *      $('#example').dataTable( {
9527			 *        "oLanguage": {
9528			 *          "sZeroRecords": "No records to display"
9529			 *        }
9530			 *      } );
9531			 *    } );
9532			 */
9533			"sZeroRecords": "Aucun enregistrement correspondant n'a été trouvé"
9534		},
9535	
9536	
9537		/**
9538		 * This parameter allows you to have define the global filtering state at
9539		 * initialisation time. As an object the "sSearch" parameter must be
9540		 * defined, but all other parameters are optional. When "bRegex" is true,
9541		 * the search string will be treated as a regular expression, when false
9542		 * (default) it will be treated as a straight string. When "bSmart"
9543		 * DataTables will use it's smart filtering methods (to word match at
9544		 * any point in the data), when false this will not be done.
9545		 *  @namespace
9546		 *  @extends DataTable.models.oSearch
9547		 *  @dtopt Options
9548		 * 
9549		 *  @example
9550		 *    $(document).ready( function() {
9551		 *      $('#example').dataTable( {
9552		 *        "oSearch": {"sSearch": "Initial search"}
9553		 *      } );
9554		 *    } )
9555		 */
9556		"oSearch": $.extend( {}, DataTable.models.oSearch ),
9557	
9558	
9559		/**
9560		 * By default DataTables will look for the property 'aaData' when obtaining
9561		 * data from an Ajax source or for server-side processing - this parameter
9562		 * allows that property to be changed. You can use Javascript dotted object
9563		 * notation to get a data source for multiple levels of nesting.
9564		 *  @type string
9565		 *  @default aaData
9566		 *  @dtopt Options
9567		 *  @dtopt Server-side
9568		 * 
9569		 *  @example
9570		 *    // Get data from { "data": [...] }
9571		 *    $(document).ready( function() {
9572		 *      var oTable = $('#example').dataTable( {
9573		 *        "sAjaxSource": "sources/data.txt",
9574		 *        "sAjaxDataProp": "data"
9575		 *      } );
9576		 *    } );
9577		 *    
9578		 *  @example
9579		 *    // Get data from { "data": { "inner": [...] } }
9580		 *    $(document).ready( function() {
9581		 *      var oTable = $('#example').dataTable( {
9582		 *        "sAjaxSource": "sources/data.txt",
9583		 *        "sAjaxDataProp": "data.inner"
9584		 *      } );
9585		 *    } );
9586		 */
9587		"sAjaxDataProp": "aaData",
9588	
9589	
9590		/**
9591		 * You can instruct DataTables to load data from an external source using this
9592		 * parameter (use aData if you want to pass data in you already have). Simply
9593		 * provide a url a JSON object can be obtained from. This object must include
9594		 * the parameter 'aaData' which is the data source for the table.
9595		 *  @type string
9596		 *  @default null
9597		 *  @dtopt Options
9598		 *  @dtopt Server-side
9599		 * 
9600		 *  @example
9601		 *    $(document).ready( function() {
9602		 *      $('#example').dataTable( {
9603		 *        "sAjaxSource": "http://www.sprymedia.co.uk/dataTables/json.php"
9604		 *      } );
9605		 *    } )
9606		 */
9607		"sAjaxSource": null,
9608	
9609	
9610		/**
9611		 * This parameter can be used to override the default prefix that DataTables
9612		 * assigns to a cookie when state saving is enabled.
9613		 *  @type string
9614		 *  @default SpryMedia_DataTables_
9615		 *  @dtopt Options
9616		 * 
9617		 *  @example
9618		 *    $(document).ready( function() {
9619		 *      $('#example').dataTable( {
9620		 *        "sCookiePrefix": "my_datatable_",
9621		 *      } );
9622		 *    } );
9623		 */
9624		"sCookiePrefix": "SpryMedia_DataTables_",
9625	
9626	
9627		/**
9628		 * This initialisation variable allows you to specify exactly where in the
9629		 * DOM you want DataTables to inject the various controls it adds to the page
9630		 * (for example you might want the pagination controls at the top of the
9631		 * table). DIV elements (with or without a custom class) can also be added to
9632		 * aid styling. The follow syntax is used:
9633		 *   <ul>
9634		 *     <li>The following options are allowed:	
9635		 *       <ul>
9636		 *         <li>'l' - Length changing</li
9637		 *         <li>'f' - Filtering input</li>
9638		 *         <li>'t' - The table!</li>
9639		 *         <li>'i' - Information</li>
9640		 *         <li>'p' - Pagination</li>
9641		 *         <li>'r' - pRocessing</li>
9642		 *       </ul>
9643		 *     </li>
9644		 *     <li>The following constants are allowed:
9645		 *       <ul>
9646		 *         <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
9647		 *         <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
9648		 *       </ul>
9649		 *     </li>
9650		 *     <li>The following syntax is expected:
9651		 *       <ul>
9652		 *         <li>'&lt;' and '&gt;' - div elements</li>
9653		 *         <li>'&lt;"class" and '&gt;' - div with a class</li>
9654		 *         <li>'&lt;"#id" and '&gt;' - div with an ID</li>
9655		 *       </ul>
9656		 *     </li>
9657		 *     <li>Examples:
9658		 *       <ul>
9659		 *         <li>'&lt;"wrapper"flipt&gt;'</li>
9660		 *         <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
9661		 *       </ul>
9662		 *     </li>
9663		 *   </ul>
9664		 *  @type string
9665		 *  @default lfrtip <i>(when bJQueryUI is false)</i> <b>or</b> 
9666		 *    <"H"lfr>t<"F"ip> <i>(when bJQueryUI is true)</i>
9667		 *  @dtopt Options
9668		 * 
9669		 *  @example
9670		 *    $(document).ready( function() {
9671		 *      $('#example').dataTable( {
9672		 *        "sDom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
9673		 *      } );
9674		 *    } );
9675		 */
9676		"sDom": "lfrtip",
9677	
9678	
9679		/**
9680		 * DataTables features two different built-in pagination interaction methods
9681		 * ('two_button' or 'full_numbers') which present different page controls to
9682		 * the end user. Further methods can be added using the API (see below).
9683		 *  @type string
9684		 *  @default two_button
9685		 *  @dtopt Options
9686		 * 
9687		 *  @example
9688		 *    $(document).ready( function() {
9689		 *      $('#example').dataTable( {
9690		 *        "sPaginationType": "full_numbers"
9691		 *      } );
9692		 *    } )
9693		 */
9694		"sPaginationType": "two_button",
9695	
9696	
9697		/**
9698		 * Enable horizontal scrolling. When a table is too wide to fit into a certain
9699		 * layout, or you have a large number of columns in the table, you can enable
9700		 * x-scrolling to show the table in a viewport, which can be scrolled. This
9701		 * property can be any CSS unit, or a number (in which case it will be treated
9702		 * as a pixel measurement).
9703		 *  @type string
9704		 *  @default <i>blank string - i.e. disabled</i>
9705		 *  @dtopt Features
9706		 * 
9707		 *  @example
9708		 *    $(document).ready( function() {
9709		 *      $('#example').dataTable( {
9710		 *        "sScrollX": "100%",
9711		 *        "bScrollCollapse": true
9712		 *      } );
9713		 *    } );
9714		 */
9715		"sScrollX": "",
9716	
9717	
9718		/**
9719		 * This property can be used to force a DataTable to use more width than it
9720		 * might otherwise do when x-scrolling is enabled. For example if you have a
9721		 * table which requires to be well spaced, this parameter is useful for
9722		 * "over-sizing" the table, and thus forcing scrolling. This property can by
9723		 * any CSS unit, or a number (in which case it will be treated as a pixel
9724		 * measurement).
9725		 *  @type string
9726		 *  @default <i>blank string - i.e. disabled</i>
9727		 *  @dtopt Options
9728		 * 
9729		 *  @example
9730		 *    $(document).ready( function() {
9731		 *      $('#example').dataTable( {
9732		 *        "sScrollX": "100%",
9733		 *        "sScrollXInner": "110%"
9734		 *      } );
9735		 *    } );
9736		 */
9737		"sScrollXInner": "",
9738	
9739	
9740		/**
9741		 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
9742		 * to the given height, and enable scrolling for any data which overflows the
9743		 * current viewport. This can be used as an alternative to paging to display
9744		 * a lot of data in a small area (although paging and scrolling can both be
9745		 * enabled at the same time). This property can be any CSS unit, or a number
9746		 * (in which case it will be treated as a pixel measurement).
9747		 *  @type string
9748		 *  @default <i>blank string - i.e. disabled</i>
9749		 *  @dtopt Features
9750		 * 
9751		 *  @example
9752		 *    $(document).ready( function() {
9753		 *      $('#example').dataTable( {
9754		 *        "sScrollY": "200px",
9755		 *        "bPaginate": false
9756		 *      } );
9757		 *    } );
9758		 */
9759		"sScrollY": "",
9760	
9761	
9762		/**
9763		 * Set the HTTP method that is used to make the Ajax call for server-side
9764		 * processing or Ajax sourced data.
9765		 *  @type string
9766		 *  @default GET
9767		 *  @dtopt Options
9768		 *  @dtopt Server-side
9769		 * 
9770		 *  @example
9771		 *    $(document).ready( function() {
9772		 *      $('#example').dataTable( {
9773		 *        "bServerSide": true,
9774		 *        "sAjaxSource": "scripts/post.php",
9775		 *        "sServerMethod": "POST"
9776		 *      } );
9777		 *    } );
9778		 */
9779		"sServerMethod": "GET"
9780	};
9781	
9782	
9783	
9784	/**
9785	 * Column options that can be given to DataTables at initialisation time.
9786	 *  @namespace
9787	 */
9788	DataTable.defaults.columns = {
9789		/**
9790		 * Allows a column's sorting to take multiple columns into account when 
9791		 * doing a sort. For example first name / last name columns make sense to 
9792		 * do a multi-column sort over the two columns.
9793		 *  @type array
9794		 *  @default null <i>Takes the value of the column index automatically</i>
9795		 *  @dtopt Columns
9796		 * 
9797		 *  @example
9798		 *    // Using aoColumnDefs
9799		 *    $(document).ready( function() {
9800		 *      $('#example').dataTable( {
9801		 *        "aoColumnDefs": [
9802		 *          { "aDataSort": [ 0, 1 ], "aTargets": [ 0 ] },
9803		 *          { "aDataSort": [ 1, 0 ], "aTargets": [ 1 ] },
9804		 *          { "aDataSort": [ 2, 3, 4 ], "aTargets": [ 2 ] }
9805		 *        ]
9806		 *      } );
9807		 *    } );
9808		 *    
9809		 *  @example
9810		 *    // Using aoColumns
9811		 *    $(document).ready( function() {
9812		 *      $('#example').dataTable( {
9813		 *        "aoColumns": [
9814		 *          { "aDataSort": [ 0, 1 ] },
9815		 *          { "aDataSort": [ 1, 0 ] },
9816		 *          { "aDataSort": [ 2, 3, 4 ] },
9817		 *          null,
9818		 *          null
9819		 *        ]
9820		 *      } );
9821		 *    } );
9822		 */
9823		"aDataSort": null,
9824	
9825	
9826		/**
9827		 * You can control the default sorting direction, and even alter the behaviour
9828		 * of the sort handler (i.e. only allow ascending sorting etc) using this
9829		 * parameter.
9830		 *  @type array
9831		 *  @default [ 'asc', 'desc' ]
9832		 *  @dtopt Columns
9833		 * 
9834		 *  @example
9835		 *    // Using aoColumnDefs
9836		 *    $(document).ready( function() {
9837		 *      $('#example').dataTable( {
9838		 *        "aoColumnDefs": [
9839		 *          { "asSorting": [ "asc" ], "aTargets": [ 1 ] },
9840		 *          { "asSorting": [ "desc", "asc", "asc" ], "aTargets": [ 2 ] },
9841		 *          { "asSorting": [ "desc" ], "aTargets": [ 3 ] }
9842		 *        ]
9843		 *      } );
9844		 *    } );
9845		 *    
9846		 *  @example
9847		 *    // Using aoColumns
9848		 *    $(document).ready( function() {
9849		 *      $('#example').dataTable( {
9850		 *        "aoColumns": [
9851		 *          null,
9852		 *          { "asSorting": [ "asc" ] },
9853		 *          { "asSorting": [ "desc", "asc", "asc" ] },
9854		 *          { "asSorting": [ "desc" ] },
9855		 *          null
9856		 *        ]
9857		 *      } );
9858		 *    } );
9859		 */
9860		"asSorting": [ 'asc', 'desc' ],
9861	
9862	
9863		/**
9864		 * Enable or disable filtering on the data in this column.
9865		 *  @type boolean
9866		 *  @default true
9867		 *  @dtopt Columns
9868		 * 
9869		 *  @example
9870		 *    // Using aoColumnDefs
9871		 *    $(document).ready( function() {
9872		 *      $('#example').dataTable( {
9873		 *        "aoColumnDefs": [ 
9874		 *          { "bSearchable": false, "aTargets": [ 0 ] }
9875		 *        ] } );
9876		 *    } );
9877		 *    
9878		 *  @example
9879		 *    // Using aoColumns
9880		 *    $(document).ready( function() {
9881		 *      $('#example').dataTable( {
9882		 *        "aoColumns": [ 
9883		 *          { "bSearchable": false },
9884		 *          null,
9885		 *          null,
9886		 *          null,
9887		 *          null
9888		 *        ] } );
9889		 *    } );
9890		 */
9891		"bSearchable": true,
9892	
9893	
9894		/**
9895		 * Enable or disable sorting on this column.
9896		 *  @type boolean
9897		 *  @default true
9898		 *  @dtopt Columns
9899		 * 
9900		 *  @example
9901		 *    // Using aoColumnDefs
9902		 *    $(document).ready( function() {
9903		 *      $('#example').dataTable( {
9904		 *        "aoColumnDefs": [ 
9905		 *          { "bSortable": false, "aTargets": [ 0 ] }
9906		 *        ] } );
9907		 *    } );
9908		 *    
9909		 *  @example
9910		 *    // Using aoColumns
9911		 *    $(document).ready( function() {
9912		 *      $('#example').dataTable( {
9913		 *        "aoColumns": [ 
9914		 *          { "bSortable": false },
9915		 *          null,
9916		 *          null,
9917		 *          null,
9918		 *          null
9919		 *        ] } );
9920		 *    } );
9921		 */
9922		"bSortable": true,
9923	
9924	
9925		/**
9926		 * <code>Deprecated</code> When using fnRender() for a column, you may wish 
9927		 * to use the original data (before rendering) for sorting and filtering 
9928		 * (the default is to used the rendered data that the user can see). This 
9929		 * may be useful for dates etc.
9930		 * 
9931		 * Please note that this option has now been deprecated and will be removed
9932		 * in the next version of DataTables. Please use mRender / mData rather than
9933		 * fnRender.
9934		 *  @type boolean
9935		 *  @default true
9936		 *  @dtopt Columns
9937		 *  @deprecated
9938		 */
9939		"bUseRendered": true,
9940	
9941	
9942		/**
9943		 * Enable or disable the display of this column.
9944		 *  @type boolean
9945		 *  @default true
9946		 *  @dtopt Columns
9947		 * 
9948		 *  @example
9949		 *    // Using aoColumnDefs
9950		 *    $(document).ready( function() {
9951		 *      $('#example').dataTable( {
9952		 *        "aoColumnDefs": [ 
9953		 *          { "bVisible": false, "aTargets": [ 0 ] }
9954		 *        ] } );
9955		 *    } );
9956		 *    
9957		 *  @example
9958		 *    // Using aoColumns
9959		 *    $(document).ready( function() {
9960		 *      $('#example').dataTable( {
9961		 *        "aoColumns": [ 
9962		 *          { "bVisible": false },
9963		 *          null,
9964		 *          null,
9965		 *          null,
9966		 *          null
9967		 *        ] } );
9968		 *    } );
9969		 */
9970		"bVisible": true,
9971		
9972		
9973		/**
9974		 * Developer definable function that is called whenever a cell is created (Ajax source,
9975		 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
9976		 * allowing you to modify the DOM element (add background colour for example) when the
9977		 * element is available.
9978		 *  @type function
9979		 *  @param {element} nTd The TD node that has been created
9980		 *  @param {*} sData The Data for the cell
9981		 *  @param {array|object} oData The data for the whole row
9982		 *  @param {int} iRow The row index for the aoData data store
9983		 *  @param {int} iCol The column index for aoColumns
9984		 *  @dtopt Columns
9985		 * 
9986		 *  @example
9987		 *    $(document).ready( function() {
9988		 *      $('#example').dataTable( {
9989		 *        "aoColumnDefs": [ {
9990		 *          "aTargets": [3],
9991		 *          "fnCreatedCell": function (nTd, sData, oData, iRow, iCol) {
9992		 *            if ( sData == "1.7" ) {
9993		 *              $(nTd).css('color', 'blue')
9994		 *            }
9995		 *          }
9996		 *        } ]
9997		 *      });
9998		 *    } );
9999		 */
10000		"fnCreatedCell": null,
10001	
10002	
10003		/**
10004		 * <code>Deprecated</code> Custom display function that will be called for the 
10005		 * display of each cell in this column.
10006		 *
10007		 * Please note that this option has now been deprecated and will be removed
10008		 * in the next version of DataTables. Please use mRender / mData rather than
10009		 * fnRender.
10010		 *  @type function
10011		 *  @param {object} o Object with the following parameters:
10012		 *  @param {int}    o.iDataRow The row in aoData
10013		 *  @param {int}    o.iDataColumn The column in question
10014		 *  @param {array}  o.aData The data for the row in question
10015		 *  @param {object} o.oSettings The settings object for this DataTables instance
10016		 *  @param {object} o.mDataProp The data property used for this column
10017		 *  @param {*}      val The current cell value
10018		 *  @returns {string} The string you which to use in the display
10019		 *  @dtopt Columns
10020		 *  @deprecated
10021		 */
10022		"fnRender": null,
10023	
10024	
10025		/**
10026		 * The column index (starting from 0!) that you wish a sort to be performed
10027		 * upon when this column is selected for sorting. This can be used for sorting
10028		 * on hidden columns for example.
10029		 *  @type int
10030		 *  @default -1 <i>Use automatically calculated column index</i>
10031		 *  @dtopt Columns
10032		 * 
10033		 *  @example
10034		 *    // Using aoColumnDefs
10035		 *    $(document).ready( function() {
10036		 *      $('#example').dataTable( {
10037		 *        "aoColumnDefs": [ 
10038		 *          { "iDataSort": 1, "aTargets": [ 0 ] }
10039		 *        ]
10040		 *      } );
10041		 *    } );
10042		 *    
10043		 *  @example
10044		 *    // Using aoColumns
10045		 *    $(document).ready( function() {
10046		 *      $('#example').dataTable( {
10047		 *        "aoColumns": [ 
10048		 *          { "iDataSort": 1 },
10049		 *          null,
10050		 *          null,
10051		 *          null,
10052		 *          null
10053		 *        ]
10054		 *      } );
10055		 *    } );
10056		 */
10057		"iDataSort": -1,
10058	
10059	
10060		/**
10061		 * This parameter has been replaced by mData in DataTables to ensure naming
10062		 * consistency. mDataProp can still be used, as there is backwards compatibility
10063		 * in DataTables for this option, but it is strongly recommended that you use
10064		 * mData in preference to mDataProp.
10065		 *  @name DataTable.defaults.columns.mDataProp
10066		 */
10067	
10068	
10069		/**
10070		 * This property can be used to read data from any JSON data source property,
10071		 * including deeply nested objects / properties. mData can be given in a
10072		 * number of different ways which effect its behaviour:
10073		 *   <ul>
10074		 *     <li>integer - treated as an array index for the data source. This is the
10075		 *       default that DataTables uses (incrementally increased for each column).</li>
10076		 *     <li>string - read an object property from the data source. Note that you can
10077		 *       use Javascript dotted notation to read deep properties / arrays from the
10078		 *       data source.</li>
10079		 *     <li>null - the sDefaultContent option will be used for the cell (null
10080		 *       by default, so you will need to specify the default content you want -
10081		 *       typically an empty string). This can be useful on generated columns such 
10082		 *       as edit / delete action columns.</li>
10083		 *     <li>function - the function given will be executed whenever DataTables 
10084		 *       needs to set or get the data for a cell in the column. The function 
10085		 *       takes three parameters:
10086		 *       <ul>
10087		 *         <li>{array|object} The data source for the row</li>
10088		 *         <li>{string} The type call data requested - this will be 'set' when
10089		 *           setting data or 'filter', 'display', 'type', 'sort' or undefined when 
10090		 *           gathering data. Note that when <i>undefined</i> is given for the type
10091		 *           DataTables expects to get the raw data for the object back</li>
10092		 *         <li>{*} Data to set when the second parameter is 'set'.</li>
10093		 *       </ul>
10094		 *       The return value from the function is not required when 'set' is the type
10095		 *       of call, but otherwise the return is what will be used for the data
10096		 *       requested.</li>
10097		 *    </ul>
10098		 *
10099		 * Note that prior to DataTables 1.9.2 mData was called mDataProp. The name change
10100		 * reflects the flexibility of this property and is consistent with the naming of
10101		 * mRender. If 'mDataProp' is given, then it will still be used by DataTables, as
10102		 * it automatically maps the old name to the new if required.
10103		 *  @type string|int|function|null
10104		 *  @default null <i>Use automatically calculated column index</i>
10105		 *  @dtopt Columns
10106		 * 
10107		 *  @example
10108		 *    // Read table data from objects
10109		 *    $(document).ready( function() {
10110		 *      var oTable = $('#example').dataTable( {
10111		 *        "sAjaxSource": "sources/deep.txt",
10112		 *        "aoColumns": [
10113		 *          { "mData": "engine" },
10114		 *          { "mData": "browser" },
10115		 *          { "mData": "platform.inner" },
10116		 *          { "mData": "platform.details.0" },
10117		 *          { "mData": "platform.details.1" }
10118		 *        ]
10119		 *      } );
10120		 *    } );
10121		 * 
10122		 *  @example
10123		 *    // Using mData as a function to provide different information for
10124		 *    // sorting, filtering and display. In this case, currency (price)
10125		 *    $(document).ready( function() {
10126		 *      var oTable = $('#example').dataTable( {
10127		 *        "aoColumnDefs": [ {
10128		 *          "aTargets": [ 0 ],
10129		 *          "mData": function ( source, type, val ) {
10130		 *            if (type === 'set') {
10131		 *              source.price = val;
10132		 *              // Store the computed dislay and filter values for efficiency
10133		 *              source.price_display = val=="" ? "" : "$"+numberFormat(val);
10134		 *              source.price_filter  = val=="" ? "" : "$"+numberFormat(val)+" "+val;
10135		 *              return;
10136		 *            }
10137		 *            else if (type === 'display') {
10138		 *              return source.price_display;
10139		 *            }
10140		 *            else if (type === 'filter') {
10141		 *              return source.price_filter;
10142		 *            }
10143		 *            // 'sort', 'type' and undefined all just use the integer
10144		 *            return source.price;
10145		 *          }
10146		 *        } ]
10147		 *      } );
10148		 *    } );
10149		 */
10150		"mData": null,
10151	
10152	
10153		/**
10154		 * This property is the rendering partner to mData and it is suggested that
10155		 * when you want to manipulate data for display (including filtering, sorting etc)
10156		 * but not altering the underlying data for the table, use this property. mData
10157		 * can actually do everything this property can and more, but this parameter is
10158		 * easier to use since there is no 'set' option. Like mData is can be given
10159		 * in a number of different ways to effect its behaviour, with the addition of 
10160		 * supporting array syntax for easy outputting of arrays (including arrays of
10161		 * objects):
10162		 *   <ul>
10163		 *     <li>integer - treated as an array index for the data source. This is the
10164		 *       default that DataTables uses (incrementally increased for each column).</li>
10165		 *     <li>string - read an object property from the data source. Note that you can
10166		 *       use Javascript dotted notation to read deep properties / arrays from the
10167		 *       data source and also array brackets to indicate that the data reader should
10168		 *       loop over the data source array. When characters are given between the array
10169		 *       brackets, these characters are used to join the data source array together.
10170		 *       For example: "accounts[, ].name" would result in a comma separated list with
10171		 *       the 'name' value from the 'accounts' array of objects.</li>
10172		 *     <li>function - the function given will be executed whenever DataTables 
10173		 *       needs to set or get the data for a cell in the column. The function 
10174		 *       takes three parameters:
10175		 *       <ul>
10176		 *         <li>{array|object} The data source for the row (based on mData)</li>
10177		 *         <li>{string} The type call data requested - this will be 'filter', 'display', 
10178		 *           'type' or 'sort'.</li>
10179		 *         <li>{array|object} The full data source for the row (not based on mData)</li>
10180		 *       </ul>
10181		 *       The return value from the function is what will be used for the data
10182		 *       requested.</li>
10183		 *    </ul>
10184		 *  @type string|int|function|null
10185		 *  @default null <i>Use mData</i>
10186		 *  @dtopt Columns
10187		 * 
10188		 *  @example
10189		 *    // Create a comma separated list from an array of objects
10190		 *    $(document).ready( function() {
10191		 *      var oTable = $('#example').dataTable( {
10192		 *        "sAjaxSource": "sources/deep.txt",
10193		 *        "aoColumns": [
10194		 *          { "mData": "engine" },
10195		 *          { "mData": "browser" },
10196		 *          {
10197		 *            "mData": "platform",
10198		 *            "mRender": "[, ].name"
10199		 *          }
10200		 *        ]
10201		 *      } );
10202		 *    } );
10203		 * 
10204		 *  @example
10205		 *    // Use as a function to create a link from the data source
10206		 *    $(document).ready( function() {
10207		 *      var oTable = $('#example').dataTable( {
10208		 *        "aoColumnDefs": [
10209		 *        {
10210		 *          "aTargets": [ 0 ],
10211		 *          "mData": "download_link",
10212		 *          "mRender": function ( data, type, full ) {
10213		 *            return '<a href="'+data+'">Download</a>';
10214		 *          }
10215		 *        ]
10216		 *      } );
10217		 *    } );
10218		 */
10219		"mRender": null,
10220	
10221	
10222		/**
10223		 * Change the cell type created for the column - either TD cells or TH cells. This
10224		 * can be useful as TH cells have semantic meaning in the table body, allowing them
10225		 * to act as a header for a row (you may wish to add scope='row' to the TH elements).
10226		 *  @type string
10227		 *  @default td
10228		 *  @dtopt Columns
10229		 * 
10230		 *  @example
10231		 *    // Make the first column use TH cells
10232		 *    $(document).ready( function() {
10233		 *      var oTable = $('#example').dataTable( {
10234		 *        "aoColumnDefs": [ {
10235		 *          "aTargets": [ 0 ],
10236		 *          "sCellType": "th"
10237		 *        } ]
10238		 *      } );
10239		 *    } );
10240		 */
10241		"sCellType": "td",
10242	
10243	
10244		/**
10245		 * Class to give to each cell in this column.
10246		 *  @type string
10247		 *  @default <i>Empty string</i>
10248		 *  @dtopt Columns
10249		 * 
10250		 *  @example
10251		 *    // Using aoColumnDefs
10252		 *    $(document).ready( function() {
10253		 *      $('#example').dataTable( {
10254		 *        "aoColumnDefs": [ 
10255		 *          { "sClass": "my_class", "aTargets": [ 0 ] }
10256		 *        ]
10257		 *      } );
10258		 *    } );
10259		 *    
10260		 *  @example
10261		 *    // Using aoColumns
10262		 *    $(document).ready( function() {
10263		 *      $('#example').dataTable( {
10264		 *        "aoColumns": [ 
10265		 *          { "sClass": "my_class" },
10266		 *          null,
10267		 *          null,
10268		 *          null,
10269		 *          null
10270		 *        ]
10271		 *      } );
10272		 *    } );
10273		 */
10274		"sClass": "",
10275		
10276		/**
10277		 * When DataTables calculates the column widths to assign to each column,
10278		 * it finds the longest string in each column and then constructs a
10279		 * temporary table and reads the widths from that. The problem with this
10280		 * is that "mmm" is much wider then "iiii", but the latter is a longer 
10281		 * string - thus the calculation can go wrong (doing it properly and putting
10282		 * it into an DOM object and measuring that is horribly(!) slow). Thus as
10283		 * a "work around" we provide this option. It will append its value to the
10284		 * text that is found to be the longest string for the column - i.e. padding.
10285		 * Generally you shouldn't need this, and it is not documented on the 
10286		 * general DataTables.net documentation
10287		 *  @type string
10288		 *  @default <i>Empty string<i>
10289		 *  @dtopt Columns
10290		 *    
10291		 *  @example
10292		 *    // Using aoColumns
10293		 *    $(document).ready( function() {
10294		 *      $('#example').dataTable( {
10295		 *        "aoColumns": [ 
10296		 *          null,
10297		 *          null,
10298		 *          null,
10299		 *          {
10300		 *            "sContentPadding": "mmm"
10301		 *          }
10302		 *        ]
10303		 *      } );
10304		 *    } );
10305		 */
10306		"sContentPadding": "",
10307	
10308	
10309		/**
10310		 * Allows a default value to be given for a column's data, and will be used
10311		 * whenever a null data source is encountered (this can be because mData
10312		 * is set to null, or because the data source itself is null).
10313		 *  @type string
10314		 *  @default null
10315		 *  @dtopt Columns
10316		 * 
10317		 *  @example
10318		 *    // Using aoColumnDefs
10319		 *    $(document).ready( function() {
10320		 *      $('#example').dataTable( {
10321		 *        "aoColumnDefs": [ 
10322		 *          {
10323		 *            "mData": null,
10324		 *            "sDefaultContent": "Edit",
10325		 *            "aTargets": [ -1 ]
10326		 *          }
10327		 *        ]
10328		 *      } );
10329		 *    } );
10330		 *    
10331		 *  @example
10332		 *    // Using aoColumns
10333		 *    $(document).ready( function() {
10334		 *      $('#example').dataTable( {
10335		 *        "aoColumns": [ 
10336		 *          null,
10337		 *          null,
10338		 *          null,
10339		 *          {
10340		 *            "mData": null,
10341		 *            "sDefaultContent": "Edit"
10342		 *          }
10343		 *        ]
10344		 *      } );
10345		 *    } );
10346		 */
10347		"sDefaultContent": null,
10348	
10349	
10350		/**
10351		 * This parameter is only used in DataTables' server-side processing. It can
10352		 * be exceptionally useful to know what columns are being displayed on the
10353		 * client side, and to map these to database fields. When defined, the names
10354		 * also allow DataTables to reorder information from the server if it comes
10355		 * back in an unexpected order (i.e. if you switch your columns around on the
10356		 * client-side, your server-side code does not also need updating).
10357		 *  @type string
10358		 *  @default <i>Empty string</i>
10359		 *  @dtopt Columns
10360		 * 
10361		 *  @example
10362		 *    // Using aoColumnDefs
10363		 *    $(document).ready( function() {
10364		 *      $('#example').dataTable( {
10365		 *        "aoColumnDefs": [ 
10366		 *          { "sName": "engine", "aTargets": [ 0 ] },
10367		 *          { "sName": "browser", "aTargets": [ 1 ] },
10368		 *          { "sName": "platform", "aTargets": [ 2 ] },
10369		 *          { "sName": "version", "aTargets": [ 3 ] },
10370		 *          { "sName": "grade", "aTargets": [ 4 ] }
10371		 *        ]
10372		 *      } );
10373		 *    } );
10374		 *    
10375		 *  @example
10376		 *    // Using aoColumns
10377		 *    $(document).ready( function() {
10378		 *      $('#example').dataTable( {
10379		 *        "aoColumns": [ 
10380		 *          { "sName": "engine" },
10381		 *          { "sName": "browser" },
10382		 *          { "sName": "platform" },
10383		 *          { "sName": "version" },
10384		 *          { "sName": "grade" }
10385		 *        ]
10386		 *      } );
10387		 *    } );
10388		 */
10389		"sName": "",
10390	
10391	
10392		/**
10393		 * Defines a data source type for the sorting which can be used to read
10394		 * real-time information from the table (updating the internally cached
10395		 * version) prior to sorting. This allows sorting to occur on user editable
10396		 * elements such as form inputs.
10397		 *  @type string
10398		 *  @default std
10399		 *  @dtopt Columns
10400		 * 
10401		 *  @example
10402		 *    // Using aoColumnDefs
10403		 *    $(document).ready( function() {
10404		 *      $('#example').dataTable( {
10405		 *        "aoColumnDefs": [
10406		 *          { "sSortDataType": "dom-text", "aTargets": [ 2, 3 ] },
10407		 *          { "sType": "numeric", "aTargets": [ 3 ] },
10408		 *          { "sSortDataType": "dom-select", "aTargets": [ 4 ] },
10409		 *          { "sSortDataType": "dom-checkbox", "aTargets": [ 5 ] }
10410		 *        ]
10411		 *      } );
10412		 *    } );
10413		 *    
10414		 *  @example
10415		 *    // Using aoColumns
10416		 *    $(document).ready( function() {
10417		 *      $('#example').dataTable( {
10418		 *        "aoColumns": [
10419		 *          null,
10420		 *          null,
10421		 *          { "sSortDataType": "dom-text" },
10422		 *          { "sSortDataType": "dom-text", "sType": "numeric" },
10423		 *          { "sSortDataType": "dom-select" },
10424		 *          { "sSortDataType": "dom-checkbox" }
10425		 *        ]
10426		 *      } );
10427		 *    } );
10428		 */
10429		"sSortDataType": "std",
10430	
10431	
10432		/**
10433		 * The title of this column.
10434		 *  @type string
10435		 *  @default null <i>Derived from the 'TH' value for this column in the 
10436		 *    original HTML table.</i>
10437		 *  @dtopt Columns
10438		 * 
10439		 *  @example
10440		 *    // Using aoColumnDefs
10441		 *    $(document).ready( function() {
10442		 *      $('#example').dataTable( {
10443		 *        "aoColumnDefs": [ 
10444		 *          { "sTitle": "My column title", "aTargets": [ 0 ] }
10445		 *        ]
10446		 *      } );
10447		 *    } );
10448		 *    
10449		 *  @example
10450		 *    // Using aoColumns
10451		 *    $(document).ready( function() {
10452		 *      $('#example').dataTable( {
10453		 *        "aoColumns": [ 
10454		 *          { "sTitle": "My column title" },
10455		 *          null,
10456		 *          null,
10457		 *          null,
10458		 *          null
10459		 *        ]
10460		 *      } );
10461		 *    } );
10462		 */
10463		"sTitle": null,
10464	
10465	
10466		/**
10467		 * The type allows you to specify how the data for this column will be sorted.
10468		 * Four types (string, numeric, date and html (which will strip HTML tags
10469		 * before sorting)) are currently available. Note that only date formats
10470		 * understood by Javascript's Date() object will be accepted as type date. For
10471		 * example: "Mar 26, 2008 5:03 PM". May take the values: 'string', 'numeric',
10472		 * 'date' or 'html' (by default). Further types can be adding through
10473		 * plug-ins.
10474		 *  @type string
10475		 *  @default null <i>Auto-detected from raw data</i>
10476		 *  @dtopt Columns
10477		 * 
10478		 *  @example
10479		 *    // Using aoColumnDefs
10480		 *    $(document).ready( function() {
10481		 *      $('#example').dataTable( {
10482		 *        "aoColumnDefs": [ 
10483		 *          { "sType": "html", "aTargets": [ 0 ] }
10484		 *        ]
10485		 *      } );
10486		 *    } );
10487		 *    
10488		 *  @example
10489		 *    // Using aoColumns
10490		 *    $(document).ready( function() {
10491		 *      $('#example').dataTable( {
10492		 *        "aoColumns": [ 
10493		 *          { "sType": "html" },
10494		 *          null,
10495		 *          null,
10496		 *          null,
10497		 *          null
10498		 *        ]
10499		 *      } );
10500		 *    } );
10501		 */
10502		"sType": null,
10503	
10504	
10505		/**
10506		 * Defining the width of the column, this parameter may take any CSS value
10507		 * (3em, 20px etc). DataTables apples 'smart' widths to columns which have not
10508		 * been given a specific width through this interface ensuring that the table
10509		 * remains readable.
10510		 *  @type string
10511		 *  @default null <i>Automatic</i>
10512		 *  @dtopt Columns
10513		 * 
10514		 *  @example
10515		 *    // Using aoColumnDefs
10516		 *    $(document).ready( function() {
10517		 *      $('#example').dataTable( {
10518		 *        "aoColumnDefs": [ 
10519		 *          { "sWidth": "20%", "aTargets": [ 0 ] }
10520		 *        ]
10521		 *      } );
10522		 *    } );
10523		 *    
10524		 *  @example
10525		 *    // Using aoColumns
10526		 *    $(document).ready( function() {
10527		 *      $('#example').dataTable( {
10528		 *        "aoColumns": [ 
10529		 *          { "sWidth": "20%" },
10530		 *          null,
10531		 *          null,
10532		 *          null,
10533		 *          null
10534		 *        ]
10535		 *      } );
10536		 *    } );
10537		 */
10538		"sWidth": null
10539	};
10540	
10541	
10542	
10543	/**
10544	 * DataTables settings object - this holds all the information needed for a
10545	 * given table, including configuration, data and current application of the
10546	 * table options. DataTables does not have a single instance for each DataTable
10547	 * with the settings attached to that instance, but rather instances of the
10548	 * DataTable "class" are created on-the-fly as needed (typically by a 
10549	 * $().dataTable() call) and the settings object is then applied to that
10550	 * instance.
10551	 * 
10552	 * Note that this object is related to {@link DataTable.defaults} but this 
10553	 * one is the internal data store for DataTables's cache of columns. It should
10554	 * NOT be manipulated outside of DataTables. Any configuration should be done
10555	 * through the initialisation options.
10556	 *  @namespace
10557	 *  @todo Really should attach the settings object to individual instances so we
10558	 *    don't need to create new instances on each $().dataTable() call (if the
10559	 *    table already exists). It would also save passing oSettings around and
10560	 *    into every single function. However, this is a very significant 
10561	 *    architecture change for DataTables and will almost certainly break
10562	 *    backwards compatibility with older installations. This is something that
10563	 *    will be done in 2.0.
10564	 */
10565	DataTable.models.oSettings = {
10566		/**
10567		 * Primary features of DataTables and their enablement state.
10568		 *  @namespace
10569		 */
10570		"oFeatures": {
10571			
10572			/**
10573			 * Flag to say if DataTables should automatically try to calculate the
10574			 * optimum table and columns widths (true) or not (false).
10575			 * Note that this parameter will be set by the initialisation routine. To
10576			 * set a default use {@link DataTable.defaults}.
10577			 *  @type boolean
10578			 */
10579			"bAutoWidth": null,
10580	
10581			/**
10582			 * Delay the creation of TR and TD elements until they are actually
10583			 * needed by a driven page draw. This can give a significant speed
10584			 * increase for Ajax source and Javascript source data, but makes no
10585			 * difference at all fro DOM and server-side processing tables.
10586			 * Note that this parameter will be set by the initialisation routine. To
10587			 * set a default use {@link DataTable.defaults}.
10588			 *  @type boolean
10589			 */
10590			"bDeferRender": null,
10591			
10592			/**
10593			 * Enable filtering on the table or not. Note that if this is disabled
10594			 * then there is no filtering at all on the table, including fnFilter.
10595			 * To just remove the filtering input use sDom and remove the 'f' option.
10596			 * Note that this parameter will be set by the initialisation routine. To
10597			 * set a default use {@link DataTable.defaults}.
10598			 *  @type boolean
10599			 */
10600			"bFilter": null,
10601			
10602			/**
10603			 * Table information element (the 'Showing x of y records' div) enable
10604			 * flag.
10605			 * Note that this parameter will be set by the initialisation routine. To
10606			 * set a default use {@link DataTable.defaults}.
10607			 *  @type boolean
10608			 */
10609			"bInfo": null,
10610			
10611			/**
10612			 * Present a user control allowing the end user to change the page size
10613			 * when pagination is enabled.
10614			 * Note that this parameter will be set by the initialisation routine. To
10615			 * set a default use {@link DataTable.defaults}.
10616			 *  @type boolean
10617			 */
10618			"bLengthChange": null,
10619	
10620			/**
10621			 * Pagination enabled or not. Note that if this is disabled then length
10622			 * changing must also be disabled.
10623			 * Note that this parameter will be set by the initialisation routine. To
10624			 * set a default use {@link DataTable.defaults}.
10625			 *  @type boolean
10626			 */
10627			"bPaginate": null,
10628			
10629			/**
10630			 * Processing indicator enable flag whenever DataTables is enacting a
10631			 * user request - typically an Ajax request for server-side processing.
10632			 * Note that this parameter will be set by the initialisation routine. To
10633			 * set a default use {@link DataTable.defaults}.
10634			 *  @type boolean
10635			 */
10636			"bProcessing": null,
10637			
10638			/**
10639			 * Server-side processing enabled flag - when enabled DataTables will
10640			 * get all data from the server for every draw - there is no filtering,
10641			 * sorting or paging done on the client-side.
10642			 * Note that this parameter will be set by the initialisation routine. To
10643			 * set a default use {@link DataTable.defaults}.
10644			 *  @type boolean
10645			 */
10646			"bServerSide": null,
10647			
10648			/**
10649			 * Sorting enablement flag.
10650			 * Note that this parameter will be set by the initialisation routine. To
10651			 * set a default use {@link DataTable.defaults}.
10652			 *  @type boolean
10653			 */
10654			"bSort": null,
10655			
10656			/**
10657			 * Apply a class to the columns which are being sorted to provide a
10658			 * visual highlight or not. This can slow things down when enabled since
10659			 * there is a lot of DOM interaction.
10660			 * Note that this parameter will be set by the initialisation routine. To
10661			 * set a default use {@link DataTable.defaults}.
10662			 *  @type boolean
10663			 */
10664			"bSortClasses": null,
10665			
10666			/**
10667			 * State saving enablement flag.
10668			 * Note that this parameter will be set by the initialisation routine. To
10669			 * set a default use {@link DataTable.defaults}.
10670			 *  @type boolean
10671			 */
10672			"bStateSave": null
10673		},
10674		
10675	
10676		/**
10677		 * Scrolling settings for a table.
10678		 *  @namespace
10679		 */
10680		"oScroll": {
10681			/**
10682			 * Indicate if DataTables should be allowed to set the padding / margin
10683			 * etc for the scrolling header elements or not. Typically you will want
10684			 * this.
10685			 * Note that this parameter will be set by the initialisation routine. To
10686			 * set a default use {@link DataTable.defaults}.
10687			 *  @type boolean
10688			 */
10689			"bAutoCss": null,
10690			
10691			/**
10692			 * When the table is shorter in height than sScrollY, collapse the
10693			 * table container down to the height of the table (when true).
10694			 * Note that this parameter will be set by the initialisation routine. To
10695			 * set a default use {@link DataTable.defaults}.
10696			 *  @type boolean
10697			 */
10698			"bCollapse": null,
10699			
10700			/**
10701			 * Infinite scrolling enablement flag. Now deprecated in favour of
10702			 * using the Scroller plug-in.
10703			 * Note that this parameter will be set by the initialisation routine. To
10704			 * set a default use {@link DataTable.defaults}.
10705			 *  @type boolean
10706			 */
10707			"bInfinite": null,
10708			
10709			/**
10710			 * Width of the scrollbar for the web-browser's platform. Calculated
10711			 * during table initialisation.
10712			 *  @type int
10713			 *  @default 0
10714			 */
10715			"iBarWidth": 0,
10716			
10717			/**
10718			 * Space (in pixels) between the bottom of the scrolling container and 
10719			 * the bottom of the scrolling viewport before the next page is loaded
10720			 * when using infinite scrolling.
10721			 * Note that this parameter will be set by the initialisation routine. To
10722			 * set a default use {@link DataTable.defaults}.
10723			 *  @type int
10724			 */
10725			"iLoadGap": null,
10726			
10727			/**
10728			 * Viewport width for horizontal scrolling. Horizontal scrolling is 
10729			 * disabled if an empty string.
10730			 * Note that this parameter will be set by the initialisation routine. To
10731			 * set a default use {@link DataTable.defaults}.
10732			 *  @type string
10733			 */
10734			"sX": null,
10735			
10736			/**
10737			 * Width to expand the table to when using x-scrolling. Typically you
10738			 * should not need to use this.
10739			 * Note that this parameter will be set by the initialisation routine. To
10740			 * set a default use {@link DataTable.defaults}.
10741			 *  @type string
10742			 *  @deprecated
10743			 */
10744			"sXInner": null,
10745			
10746			/**
10747			 * Viewport height for vertical scrolling. Vertical scrolling is disabled
10748			 * if an empty string.
10749			 * Note that this parameter will be set by the initialisation routine. To
10750			 * set a default use {@link DataTable.defaults}.
10751			 *  @type string
10752			 */
10753			"sY": null
10754		},
10755		
10756		/**
10757		 * Language information for the table.
10758		 *  @namespace
10759		 *  @extends DataTable.defaults.oLanguage
10760		 */
10761		"oLanguage": {
10762			/**
10763			 * Information callback function. See 
10764			 * {@link DataTable.defaults.fnInfoCallback}
10765			 *  @type function
10766			 *  @default null
10767			 */
10768			"fnInfoCallback": null
10769		},
10770		
10771		/**
10772		 * Browser support parameters
10773		 *  @namespace
10774		 */
10775		"oBrowser": {
10776			/**
10777			 * Indicate if the browser incorrectly calculates width:100% inside a
10778			 * scrolling element (IE6/7)
10779			 *  @type boolean
10780			 *  @default false
10781			 */
10782			"bScrollOversize": false
10783		},
10784		
10785		/**
10786		 * Array referencing the nodes which are used for the features. The 
10787		 * parameters of this object match what is allowed by sDom - i.e.
10788		 *   <ul>
10789		 *     <li>'l' - Length changing</li>
10790		 *     <li>'f' - Filtering input</li>
10791		 *     <li>'t' - The table!</li>
10792		 *     <li>'i' - Information</li>
10793		 *     <li>'p' - Pagination</li>
10794		 *     <li>'r' - pRocessing</li>
10795		 *   </ul>
10796		 *  @type array
10797		 *  @default []
10798		 */
10799		"aanFeatures": [],
10800		
10801		/**
10802		 * Store data information - see {@link DataTable.models.oRow} for detailed
10803		 * information.
10804		 *  @type array
10805		 *  @default []
10806		 */
10807		"aoData": [],
10808		
10809		/**
10810		 * Array of indexes which are in the current display (after filtering etc)
10811		 *  @type array
10812		 *  @default []
10813		 */
10814		"aiDisplay": [],
10815		
10816		/**
10817		 * Array of indexes for display - no filtering
10818		 *  @type array
10819		 *  @default []
10820		 */
10821		"aiDisplayMaster": [],
10822		
10823		/**
10824		 * Store information about each column that is in use
10825		 *  @type array
10826		 *  @default []
10827		 */
10828		"aoColumns": [],
10829		
10830		/**
10831		 * Store information about the table's header
10832		 *  @type array
10833		 *  @default []
10834		 */
10835		"aoHeader": [],
10836		
10837		/**
10838		 * Store information about the table's footer
10839		 *  @type array
10840		 *  @default []
10841		 */
10842		"aoFooter": [],
10843		
10844		/**
10845		 * Search data array for regular expression searching
10846		 *  @type array
10847		 *  @default []
10848		 */
10849		"asDataSearch": [],
10850		
10851		/**
10852		 * Store the applied global search information in case we want to force a 
10853		 * research or compare the old search to a new one.
10854		 * Note that this parameter will be set by the initialisation routine. To
10855		 * set a default use {@link DataTable.defaults}.
10856		 *  @namespace
10857		 *  @extends DataTable.models.oSearch
10858		 */
10859		"oPreviousSearch": {},
10860		
10861		/**
10862		 * Store the applied search for each column - see 
10863		 * {@link DataTable.models.oSearch} for the format that is used for the
10864		 * filtering information for each column.
10865		 *  @type array
10866		 *  @default []
10867		 */
10868		"aoPreSearchCols": [],
10869		
10870		/**
10871		 * Sorting that is applied to the table. Note that the inner arrays are
10872		 * used in the following manner:
10873		 * <ul>
10874		 *   <li>Index 0 - column number</li>
10875		 *   <li>Index 1 - current sorting direction</li>
10876		 *   <li>Index 2 - index of asSorting for this column</li>
10877		 * </ul>
10878		 * Note that this parameter will be set by the initialisation routine. To
10879		 * set a default use {@link DataTable.defaults}.
10880		 *  @type array
10881		 *  @todo These inner arrays should really be objects
10882		 */
10883		"aaSorting": null,
10884		
10885		/**
10886		 * Sorting that is always applied to the table (i.e. prefixed in front of
10887		 * aaSorting).
10888		 * Note that this parameter will be set by the initialisation routine. To
10889		 * set a default use {@link DataTable.defaults}.
10890		 *  @type array|null
10891		 *  @default null
10892		 */
10893		"aaSortingFixed": null,
10894		
10895		/**
10896		 * Classes to use for the striping of a table.
10897		 * Note that this parameter will be set by the initialisation routine. To
10898		 * set a default use {@link DataTable.defaults}.
10899		 *  @type array
10900		 *  @default []
10901		 */
10902		"asStripeClasses": null,
10903		
10904		/**
10905		 * If restoring a table - we should restore its striping classes as well
10906		 *  @type array
10907		 *  @default []
10908		 */
10909		"asDestroyStripes": [],
10910		
10911		/**
10912		 * If restoring a table - we should restore its width 
10913		 *  @type int
10914		 *  @default 0
10915		 */
10916		"sDestroyWidth": 0,
10917		
10918		/**
10919		 * Callback functions array for every time a row is inserted (i.e. on a draw).
10920		 *  @type array
10921		 *  @default []
10922		 */
10923		"aoRowCallback": [],
10924		
10925		/**
10926		 * Callback functions for the header on each draw.
10927		 *  @type array
10928		 *  @default []
10929		 */
10930		"aoHeaderCallback": [],
10931		
10932		/**
10933		 * Callback function for the footer on each draw.
10934		 *  @type array
10935		 *  @default []
10936		 */
10937		"aoFooterCallback": [],
10938		
10939		/**
10940		 * Array of callback functions for draw callback functions
10941		 *  @type array
10942		 *  @default []
10943		 */
10944		"aoDrawCallback": [],
10945		
10946		/**
10947		 * Array of callback functions for row created function
10948		 *  @type array
10949		 *  @default []
10950		 */
10951		"aoRowCreatedCallback": [],
10952		
10953		/**
10954		 * Callback functions for just before the table is redrawn. A return of 
10955		 * false will be used to cancel the draw.
10956		 *  @type array
10957		 *  @default []
10958		 */
10959		"aoPreDrawCallback": [],
10960		
10961		/**
10962		 * Callback functions for when the table has been initialised.
10963		 *  @type array
10964		 *  @default []
10965		 */
10966		"aoInitComplete": [],
10967	
10968		
10969		/**
10970		 * Callbacks for modifying the settings to be stored for state saving, prior to
10971		 * saving state.
10972		 *  @type array
10973		 *  @default []
10974		 */
10975		"aoStateSaveParams": [],
10976		
10977		/**
10978		 * Callbacks for modifying the settings that have been stored for state saving
10979		 * prior to using the stored values to restore the state.
10980		 *  @type array
10981		 *  @default []
10982		 */
10983		"aoStateLoadParams": [],
10984		
10985		/**
10986		 * Callbacks for operating on the settings object once the saved state has been
10987		 * loaded
10988		 *  @type array
10989		 *  @default []
10990		 */
10991		"aoStateLoaded": [],
10992		
10993		/**
10994		 * Cache the table ID for quick access
10995		 *  @type string
10996		 *  @default <i>Empty string</i>
10997		 */
10998		"sTableId": "",
10999		
11000		/**
11001		 * The TABLE node for the main table
11002		 *  @type node
11003		 *  @default null
11004		 */
11005		"nTable": null,
11006		
11007		/**
11008		 * Permanent ref to the thead element
11009		 *  @type node
11010		 *  @default null
11011		 */
11012		"nTHead": null,
11013		
11014		/**
11015		 * Permanent ref to the tfoot element - if it exists
11016		 *  @type node
11017		 *  @default null
11018		 */
11019		"nTFoot": null,
11020		
11021		/**
11022		 * Permanent ref to the tbody element
11023		 *  @type node
11024		 *  @default null
11025		 */
11026		"nTBody": null,
11027		
11028		/**
11029		 * Cache the wrapper node (contains all DataTables controlled elements)
11030		 *  @type node
11031		 *  @default null
11032		 */
11033		"nTableWrapper": null,
11034		
11035		/**
11036		 * Indicate if when using server-side processing the loading of data 
11037		 * should be deferred until the second draw.
11038		 * Note that this parameter will be set by the initialisation routine. To
11039		 * set a default use {@link DataTable.defaults}.
11040		 *  @type boolean
11041		 *  @default false
11042		 */
11043		"bDeferLoading": false,
11044		
11045		/**
11046		 * Indicate if all required information has been read in
11047		 *  @type boolean
11048		 *  @default false
11049		 */
11050		"bInitialised": false,
11051		
11052		/**
11053		 * Information about open rows. Each object in the array has the parameters
11054		 * 'nTr' and 'nParent'
11055		 *  @type array
11056		 *  @default []
11057		 */
11058		"aoOpenRows": [],
11059		
11060		/**
11061		 * Dictate the positioning of DataTables' control elements - see
11062		 * {@link DataTable.model.oInit.sDom}.
11063		 * Note that this parameter will be set by the initialisation routine. To
11064		 * set a default use {@link DataTable.defaults}.
11065		 *  @type string
11066		 *  @default null
11067		 */
11068		"sDom": null,
11069		
11070		/**
11071		 * Which type of pagination should be used.
11072		 * Note that this parameter will be set by the initialisation routine. To
11073		 * set a default use {@link DataTable.defaults}.
11074		 *  @type string 
11075		 *  @default two_button
11076		 */
11077		"sPaginationType": "two_button",
11078		
11079		/**
11080		 * The cookie duration (for bStateSave) in seconds.
11081		 * Note that this parameter will be set by the initialisation routine. To
11082		 * set a default use {@link DataTable.defaults}.
11083		 *  @type int
11084		 *  @default 0
11085		 */
11086		"iCookieDuration": 0,
11087		
11088		/**
11089		 * The cookie name prefix.
11090		 * Note that this parameter will be set by the initialisation routine. To
11091		 * set a default use {@link DataTable.defaults}.
11092		 *  @type string
11093		 *  @default <i>Empty string</i>
11094		 */
11095		"sCookiePrefix": "",
11096		
11097		/**
11098		 * Callback function for cookie creation.
11099		 * Note that this parameter will be set by the initialisation routine. To
11100		 * set a default use {@link DataTable.defaults}.
11101		 *  @type function
11102		 *  @default null
11103		 */
11104		"fnCookieCallback": null,
11105		
11106		/**
11107		 * Array of callback functions for state saving. Each array element is an 
11108		 * object with the following parameters:
11109		 *   <ul>
11110		 *     <li>function:fn - function to call. Takes two parameters, oSettings
11111		 *       and the JSON string to save that has been thus far created. Returns
11112		 *       a JSON string to be inserted into a json object 
11113		 *       (i.e. '"param": [ 0, 1, 2]')</li>
11114		 *     <li>string:sName - name of callback</li>
11115		 *   </ul>
11116		 *  @type array
11117		 *  @default []
11118		 */
11119		"aoStateSave": [],
11120		
11121		/**
11122		 * Array of callback functions for state loading. Each array element is an 
11123		 * object with the following parameters:
11124		 *   <ul>
11125		 *     <li>function:fn - function to call. Takes two parameters, oSettings 
11126		 *       and the object stored. May return false to cancel state loading</li>
11127		 *     <li>string:sName - name of callback</li>
11128		 *   </ul>
11129		 *  @type array
11130		 *  @default []
11131		 */
11132		"aoStateLoad": [],
11133		
11134		/**
11135		 * State that was loaded from the cookie. Useful for back reference
11136		 *  @type object
11137		 *  @default null
11138		 */
11139		"oLoadedState": null,
11140		
11141		/**
11142		 * Source url for AJAX data for the table.
11143		 * Note that this parameter will be set by the initialisation routine. To
11144		 * set a default use {@link DataTable.defaults}.
11145		 *  @type string
11146		 *  @default null
11147		 */
11148		"sAjaxSource": null,
11149		
11150		/**
11151		 * Property from a given object from which to read the table data from. This
11152		 * can be an empty string (when not server-side processing), in which case 
11153		 * it is  assumed an an array is given directly.
11154		 * Note that this parameter will be set by the initialisation routine. To
11155		 * set a default use {@link DataTable.defaults}.
11156		 *  @type string
11157		 */
11158		"sAjaxDataProp": null,
11159		
11160		/**
11161		 * Note if draw should be blocked while getting data
11162		 *  @type boolean
11163		 *  @default true
11164		 */
11165		"bAjaxDataGet": true,
11166		
11167		/**
11168		 * The last jQuery XHR object that was used for server-side data gathering. 
11169		 * This can be used for working with the XHR information in one of the 
11170		 * callbacks
11171		 *  @type object
11172		 *  @default null
11173		 */
11174		"jqXHR": null,
11175		
11176		/**
11177		 * Function to get the server-side data.
11178		 * Note that this parameter will be set by the initialisation routine. To
11179		 * set a default use {@link DataTable.defaults}.
11180		 *  @type function
11181		 */
11182		"fnServerData": null,
11183		
11184		/**
11185		 * Functions which are called prior to sending an Ajax request so extra 
11186		 * parameters can easily be sent to the server
11187		 *  @type array
11188		 *  @default []
11189		 */
11190		"aoServerParams": [],
11191		
11192		/**
11193		 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if 
11194		 * required).
11195		 * Note that this parameter will be set by the initialisation routine. To
11196		 * set a default use {@link DataTable.defaults}.
11197		 *  @type string
11198		 */
11199		"sServerMethod": null,
11200		
11201		/**
11202		 * Format numbers for display.
11203		 * Note that this parameter will be set by the initialisation routine. To
11204		 * set a default use {@link DataTable.defaults}.
11205		 *  @type function
11206		 */
11207		"fnFormatNumber": null,
11208		
11209		/**
11210		 * List of options that can be used for the user selectable length menu.
11211		 * Note that this parameter will be set by the initialisation routine. To
11212		 * set a default use {@link DataTable.defaults}.
11213		 *  @type array
11214		 *  @default []
11215		 */
11216		"aLengthMenu": null,
11217		
11218		/**
11219		 * Counter for the draws that the table does. Also used as a tracker for
11220		 * server-side processing
11221		 *  @type int
11222		 *  @default 0
11223		 */
11224		"iDraw": 0,
11225		
11226		/**
11227		 * Indicate if a redraw is being done - useful for Ajax
11228		 *  @type boolean
11229		 *  @default false
11230		 */
11231		"bDrawing": false,
11232		
11233		/**
11234		 * Draw index (iDraw) of the last error when parsing the returned data
11235		 *  @type int
11236		 *  @default -1
11237		 */
11238		"iDrawError": -1,
11239		
11240		/**
11241		 * Paging display length
11242		 *  @type int
11243		 *  @default 10
11244		 */
11245		"_iDisplayLength": 10,
11246	
11247		/**
11248		 * Paging start point - aiDisplay index
11249		 *  @type int
11250		 *  @default 0
11251		 */
11252		"_iDisplayStart": 0,
11253	
11254		/**
11255		 * Paging end point - aiDisplay index. Use fnDisplayEnd rather than
11256		 * this property to get the end point
11257		 *  @type int
11258		 *  @default 10
11259		 *  @private
11260		 */
11261		"_iDisplayEnd": 10,
11262		
11263		/**
11264		 * Server-side processing - number of records in the result set
11265		 * (i.e. before filtering), Use fnRecordsTotal rather than
11266		 * this property to get the value of the number of records, regardless of
11267		 * the server-side processing setting.
11268		 *  @type int
11269		 *  @default 0
11270		 *  @private
11271		 */
11272		"_iRecordsTotal": 0,
11273	
11274		/**
11275		 * Server-side processing - number of records in the current display set
11276		 * (i.e. after filtering). Use fnRecordsDisplay rather than
11277		 * this property to get the value of the number of records, regardless of
11278		 * the server-side processing setting.
11279		 *  @type boolean
11280		 *  @default 0
11281		 *  @private
11282		 */
11283		"_iRecordsDisplay": 0,
11284		
11285		/**
11286		 * Flag to indicate if jQuery UI marking and classes should be used.
11287		 * Note that this parameter will be set by the initialisation routine. To
11288		 * set a default use {@link DataTable.defaults}.
11289		 *  @type boolean
11290		 */
11291		"bJUI": null,
11292		
11293		/**
11294		 * The classes to use for the table
11295		 *  @type object
11296		 *  @default {}
11297		 */
11298		"oClasses": {},
11299		
11300		/**
11301		 * Flag attached to the settings object so you can check in the draw 
11302		 * callback if filtering has been done in the draw. Deprecated in favour of
11303		 * events.
11304		 *  @type boolean
11305		 *  @default false
11306		 *  @deprecated
11307		 */
11308		"bFiltered": false,
11309		
11310		/**
11311		 * Flag attached to the settings object so you can check in the draw 
11312		 * callback if sorting has been done in the draw. Deprecated in favour of
11313		 * events.
11314		 *  @type boolean
11315		 *  @default false
11316		 *  @deprecated
11317		 */
11318		"bSorted": false,
11319		
11320		/**
11321		 * Indicate that if multiple rows are in the header and there is more than 
11322		 * one unique cell per column, if the top one (true) or bottom one (false) 
11323		 * should be used for sorting / title by DataTables.
11324		 * Note that this parameter will be set by the initialisation routine. To
11325		 * set a default use {@link DataTable.defaults}.
11326		 *  @type boolean
11327		 */
11328		"bSortCellsTop": null,
11329		
11330		/**
11331		 * Initialisation object that is used for the table
11332		 *  @type object
11333		 *  @default null
11334		 */
11335		"oInit": null,
11336		
11337		/**
11338		 * Destroy callback functions - for plug-ins to attach themselves to the
11339		 * destroy so they can clean up markup and events.
11340		 *  @type array
11341		 *  @default []
11342		 */
11343		"aoDestroyCallback": [],
11344	
11345		
11346		/**
11347		 * Get the number of records in the current record set, before filtering
11348		 *  @type function
11349		 */
11350		"fnRecordsTotal": function ()
11351		{
11352			if ( this.oFeatures.bServerSide ) {
11353				return parseInt(this._iRecordsTotal, 10);
11354			} else {
11355				return this.aiDisplayMaster.length;
11356			}
11357		},
11358		
11359		/**
11360		 * Get the number of records in the current record set, after filtering
11361		 *  @type function
11362		 */
11363		"fnRecordsDisplay": function ()
11364		{
11365			if ( this.oFeatures.bServerSide ) {
11366				return parseInt(this._iRecordsDisplay, 10);
11367			} else {
11368				return this.aiDisplay.length;
11369			}
11370		},
11371		
11372		/**
11373		 * Set the display end point - aiDisplay index
11374		 *  @type function
11375		 *  @todo Should do away with _iDisplayEnd and calculate it on-the-fly here
11376		 */
11377		"fnDisplayEnd": function ()
11378		{
11379			if ( this.oFeatures.bServerSide ) {
11380				if ( this.oFeatures.bPaginate === false || this._iDisplayLength == -1 ) {
11381					return this._iDisplayStart+this.aiDisplay.length;
11382				} else {
11383					return Math.min( this._iDisplayStart+this._iDisplayLength, 
11384						this._iRecordsDisplay );
11385				}
11386			} else {
11387				return this._iDisplayEnd;
11388			}
11389		},
11390		
11391		/**
11392		 * The DataTables object for this table
11393		 *  @type object
11394		 *  @default null
11395		 */
11396		"oInstance": null,
11397		
11398		/**
11399		 * Unique identifier for each instance of the DataTables object. If there
11400		 * is an ID on the table node, then it takes that value, otherwise an
11401		 * incrementing internal counter is used.
11402		 *  @type string
11403		 *  @default null
11404		 */
11405		"sInstance": null,
11406	
11407		/**
11408		 * tabindex attribute value that is added to DataTables control elements, allowing
11409		 * keyboard navigation of the table and its controls.
11410		 */
11411		"iTabIndex": 0,
11412	
11413		/**
11414		 * DIV container for the footer scrolling table if scrolling
11415		 */
11416		"nScrollHead": null,
11417	
11418		/**
11419		 * DIV container for the footer scrolling table if scrolling
11420		 */
11421		"nScrollFoot": null
11422	};
11423
11424	/**
11425	 * Extension object for DataTables that is used to provide all extension options.
11426	 * 
11427	 * Note that the <i>DataTable.ext</i> object is available through
11428	 * <i>jQuery.fn.dataTable.ext</i> where it may be accessed and manipulated. It is
11429	 * also aliased to <i>jQuery.fn.dataTableExt</i> for historic reasons.
11430	 *  @namespace
11431	 *  @extends DataTable.models.ext
11432	 */
11433	DataTable.ext = $.extend( true, {}, DataTable.models.ext );
11434	
11435	$.extend( DataTable.ext.oStdClasses, {
11436		"sTable": "dataTable",
11437	
11438		/* Two buttons buttons */
11439		"sPagePrevEnabled": "paginate_enabled_previous",
11440		"sPagePrevDisabled": "paginate_disabled_previous",
11441		"sPageNextEnabled": "paginate_enabled_next",
11442		"sPageNextDisabled": "paginate_disabled_next",
11443		"sPageJUINext": "",
11444		"sPageJUIPrev": "",
11445		
11446		/* Full numbers paging buttons */
11447		"sPageButton": "paginate_button",
11448		"sPageButtonActive": "paginate_active",
11449		"sPageButtonStaticDisabled": "paginate_button paginate_button_disabled",
11450		"sPageFirst": "first",
11451		"sPagePrevious": "previous",
11452		"sPageNext": "next",
11453		"sPageLast": "last",
11454		
11455		/* Striping classes */
11456		"sStripeOdd": "odd",
11457		"sStripeEven": "even",
11458		
11459		/* Empty row */
11460		"sRowEmpty": "dataTables_empty",
11461		
11462		/* Features */
11463		"sWrapper": "dataTables_wrapper",
11464		"sFilter": "dataTables_filter",
11465		"sInfo": "dataTables_info",
11466		"sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
11467		"sLength": "dataTables_length",
11468		"sProcessing": "dataTables_processing",
11469		
11470		/* Sorting */
11471		"sSortAsc": "sorting_asc",
11472		"sSortDesc": "sorting_desc",
11473		"sSortable": "sorting", /* Sortable in both directions */
11474		"sSortableAsc": "sorting_asc_disabled",
11475		"sSortableDesc": "sorting_desc_disabled",
11476		"sSortableNone": "sorting_disabled",
11477		"sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
11478		"sSortJUIAsc": "",
11479		"sSortJUIDesc": "",
11480		"sSortJUI": "",
11481		"sSortJUIAscAllowed": "",
11482		"sSortJUIDescAllowed": "",
11483		"sSortJUIWrapper": "",
11484		"sSortIcon": "",
11485		
11486		/* Scrolling */
11487		"sScrollWrapper": "dataTables_scroll",
11488		"sScrollHead": "dataTables_scrollHead",
11489		"sScrollHeadInner": "dataTables_scrollHeadInner",
11490		"sScrollBody": "dataTables_scrollBody",
11491		"sScrollFoot": "dataTables_scrollFoot",
11492		"sScrollFootInner": "dataTables_scrollFootInner",
11493		
11494		/* Misc */
11495		"sFooterTH": "",
11496		"sJUIHeader": "",
11497		"sJUIFooter": ""
11498	} );
11499	
11500	
11501	$.extend( DataTable.ext.oJUIClasses, DataTable.ext.oStdClasses, {
11502		/* Two buttons buttons */
11503		"sPagePrevEnabled": "fg-button ui-button ui-state-default ui-corner-left",
11504		"sPagePrevDisabled": "fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",
11505		"sPageNextEnabled": "fg-button ui-button ui-state-default ui-corner-right",
11506		"sPageNextDisabled": "fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",
11507		"sPageJUINext": "ui-icon ui-icon-circle-arrow-e",
11508		"sPageJUIPrev": "ui-icon ui-icon-circle-arrow-w",
11509		
11510		/* Full numbers paging buttons */
11511		"sPageButton": "fg-button ui-button ui-state-default",
11512		"sPageButtonActive": "fg-button ui-button ui-state-default ui-state-disabled",
11513		"sPageButtonStaticDisabled": "fg-button ui-button ui-state-default ui-state-disabled",
11514		"sPageFirst": "first ui-corner-tl ui-corner-bl",
11515		"sPageLast": "last ui-corner-tr ui-corner-br",
11516		
11517		/* Features */
11518		"sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
11519			"ui-buttonset-multi paging_", /* Note that the type is postfixed */
11520		
11521		/* Sorting */
11522		"sSortAsc": "ui-state-default",
11523		"sSortDesc": "ui-state-default",
11524		"sSortable": "ui-state-default",
11525		"sSortableAsc": "ui-state-default",
11526		"sSortableDesc": "ui-state-default",
11527		"sSortableNone": "ui-state-default",
11528		"sSortJUIAsc": "css_right ui-icon ui-icon-triangle-1-n",
11529		"sSortJUIDesc": "css_right ui-icon ui-icon-triangle-1-s",
11530		"sSortJUI": "css_right ui-icon ui-icon-carat-2-n-s",
11531		"sSortJUIAscAllowed": "css_right ui-icon ui-icon-carat-1-n",
11532		"sSortJUIDescAllowed": "css_right ui-icon ui-icon-carat-1-s",
11533		"sSortJUIWrapper": "DataTables_sort_wrapper",
11534		"sSortIcon": "DataTables_sort_icon",
11535		
11536		/* Scrolling */
11537		"sScrollHead": "dataTables_scrollHead ui-state-default",
11538		"sScrollFoot": "dataTables_scrollFoot ui-state-default",
11539		
11540		/* Misc */
11541		"sFooterTH": "ui-state-default",
11542		"sJUIHeader": "fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix",
11543		"sJUIFooter": "fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix"
11544	} );
11545	
11546	/*
11547	 * Variable: oPagination
11548	 * Purpose:  
11549	 * Scope:    jQuery.fn.dataTableExt
11550	 */
11551	$.extend( DataTable.ext.oPagination, {
11552		/*
11553		 * Variable: two_button
11554		 * Purpose:  Standard two button (forward/back) pagination
11555		 * Scope:    jQuery.fn.dataTableExt.oPagination
11556		 */
11557		"two_button": {
11558			/*
11559			 * Function: oPagination.two_button.fnInit
11560			 * Purpose:  Initialise dom elements required for pagination with forward/back buttons only
11561			 * Returns:  -
11562			 * Inputs:   object:oSettings - dataTables settings object
11563			 *           node:nPaging - the DIV which contains this pagination control
11564			 *           function:fnCallbackDraw - draw function which must be called on update
11565			 */
11566			"fnInit": function ( oSettings, nPaging, fnCallbackDraw )
11567			{
11568				var oLang = oSettings.oLanguage.oPaginate;
11569				var oClasses = oSettings.oClasses;
11570				var fnClickHandler = function ( e ) {
11571					if ( oSettings.oApi._fnPageChange( oSettings, e.data.action ) )
11572					{
11573						fnCallbackDraw( oSettings );
11574					}
11575				};
11576	
11577				var sAppend = (!oSettings.bJUI) ?
11578					'<a class="'+oSettings.oClasses.sPagePrevDisabled+'" tabindex="'+oSettings.iTabIndex+'" role="button">'+oLang.sPrevious+'</a>'+
11579					'<a class="'+oSettings.oClasses.sPageNextDisabled+'" tabindex="'+oSettings.iTabIndex+'" role="button">'+oLang.sNext+'</a>'
11580					:
11581					'<a class="'+oSettings.oClasses.sPagePrevDisabled+'" tabindex="'+oSettings.iTabIndex+'" role="button"><span class="'+oSettings.oClasses.sPageJUIPrev+'"></span></a>'+
11582					'<a class="'+oSettings.oClasses.sPageNextDisabled+'" tabindex="'+oSettings.iTabIndex+'" role="button"><span class="'+oSettings.oClasses.sPageJUINext+'"></span></a>';
11583				$(nPaging).append( sAppend );
11584				
11585				var els = $('a', nPaging);
11586				var nPrevious = els[0],
11587					nNext = els[1];
11588				
11589				oSettings.oApi._fnBindAction( nPrevious, {action: "previous"}, fnClickHandler );
11590				oSettings.oApi._fnBindAction( nNext,     {action: "next"},     fnClickHandler );
11591				
11592				/* ID the first elements only */
11593				if ( !oSettings.aanFeatures.p )
11594				{
11595					nPaging.id = oSettings.sTableId+'_paginate';
11596					nPrevious.id = oSettings.sTableId+'_previous';
11597					nNext.id = oSettings.sTableId+'_next';
11598	
11599					nPrevious.setAttribute('aria-controls', oSettings.sTableId);
11600					nNext.setAttribute('aria-controls', oSettings.sTableId);
11601				}
11602			},
11603			
11604			/*
11605			 * Function: oPagination.two_button.fnUpdate
11606			 * Purpose:  Update the two button pagination at the end of the draw
11607			 * Returns:  -
11608			 * Inputs:   object:oSettings - dataTables settings object
11609			 *           function:fnCallbackDraw - draw function to call on page change
11610			 */
11611			"fnUpdate": function ( oSettings, fnCallbackDraw )
11612			{
11613				if ( !oSettings.aanFeatures.p )
11614				{
11615					return;
11616				}
11617				
11618				var oClasses = oSettings.oClasses;
11619				var an = oSettings.aanFeatures.p;
11620				var nNode;
11621	
11622				/* Loop over each instance of the pager */
11623				for ( var i=0, iLen=an.length ; i<iLen ; i++ )
11624				{
11625					nNode = an[i].firstChild;
11626					if ( nNode )
11627					{
11628						/* Previous page */
11629						nNode.className = ( oSettings._iDisplayStart === 0 ) ?
11630						    oClasses.sPagePrevDisabled : oClasses.sPagePrevEnabled;
11631						    
11632						/* Next page */
11633						nNode = nNode.nextSibling;
11634						nNode.className = ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() ) ?
11635						    oClasses.sPageNextDisabled : oClasses.sPageNextEnabled;
11636					}
11637				}
11638			}
11639		},
11640		
11641		
11642		/*
11643		 * Variable: iFullNumbersShowPages
11644		 * Purpose:  Change the number of pages which can be seen
11645		 * Scope:    jQuery.fn.dataTableExt.oPagination
11646		 */
11647		"iFullNumbersShowPages": 5,
11648		
11649		/*
11650		 * Variable: full_numbers
11651		 * Purpose:  Full numbers pagination
11652		 * Scope:    jQuery.fn.dataTableExt.oPagination
11653		 */
11654		"full_numbers": {
11655			/*
11656			 * Function: oPagination.full_numbers.fnInit
11657			 * Purpose:  Initialise dom elements required for pagination with a list of the pages
11658			 * Returns:  -
11659			 * Inputs:   object:oSettings - dataTables settings object
11660			 *           node:nPaging - the DIV which contains this pagination control
11661			 *           function:fnCallbackDraw - draw function which must be called on update
11662			 */
11663			"fnInit": function ( oSettings, nPaging, fnCallbackDraw )
11664			{
11665				var oLang = oSettings.oLanguage.oPaginate;
11666				var oClasses = oSettings.oClasses;
11667				var fnClickHandler = function ( e ) {
11668					if ( oSettings.oApi._fnPageChange( oSettings, e.data.action ) )
11669					{
11670						fnCallbackDraw( oSettings );
11671					}
11672				};
11673	
11674				$(nPaging).append(
11675					'<a  tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+" "+oClasses.sPageFirst+'">'+oLang.sFirst+'</a>'+
11676					'<a  tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+" "+oClasses.sPagePrevious+'">'+oLang.sPrevious+'</a>'+
11677					'<span></span>'+
11678					'<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+" "+oClasses.sPageNext+'">'+oLang.sNext+'</a>'+
11679					'<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+" "+oClasses.sPageLast+'">'+oLang.sLast+'</a>'
11680				);
11681				var els = $('a', nPaging);
11682				var nFirst = els[0],
11683					nPrev = els[1],
11684					nNext = els[2],
11685					nLast = els[3];
11686				
11687				oSettings.oApi._fnBindAction( nFirst, {action: "first"},    fnClickHandler );
11688				oSettings.oApi._fnBindAction( nPrev,  {action: "previous"}, fnClickHandler );
11689				oSettings.oApi._fnBindAction( nNext,  {action: "next"},     fnClickHandler );
11690				oSettings.oApi._fnBindAction( nLast,  {action: "last"},     fnClickHandler );
11691				
11692				/* ID the first elements only */
11693				if ( !oSettings.aanFeatures.p )
11694				{
11695					nPaging.id = oSettings.sTableId+'_paginate';
11696					nFirst.id =oSettings.sTableId+'_first';
11697					nPrev.id =oSettings.sTableId+'_previous';
11698					nNext.id =oSettings.sTableId+'_next';
11699					nLast.id =oSettings.sTableId+'_last';
11700				}
11701			},
11702			
11703			/*
11704			 * Function: oPagination.full_numbers.fnUpdate
11705			 * Purpose:  Update the list of page buttons shows
11706			 * Returns:  -
11707			 * Inputs:   object:oSettings - dataTables settings object
11708			 *           function:fnCallbackDraw - draw function to call on page change
11709			 */
11710			"fnUpdate": function ( oSettings, fnCallbackDraw )
11711			{
11712				if ( !oSettings.aanFeatures.p )
11713				{
11714					return;
11715				}
11716				
11717				var iPageCount = DataTable.ext.oPagination.iFullNumbersShowPages;
11718				var iPageCountHalf = Math.floor(iPageCount / 2);
11719				var iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength);
11720				var iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1;
11721				var sList = "";
11722				var iStartButton, iEndButton, i, iLen;
11723				var oClasses = oSettings.oClasses;
11724				var anButtons, anStatic, nPaginateList, nNode;
11725				var an = oSettings.aanFeatures.p;
11726				var fnBind = function (j) {
11727					oSettings.oApi._fnBindAction( this, {"page": j+iStartButton-1}, function(e) {
11728						/* Use the information in the element to jump to the required page */
11729						oSettings.oApi._fnPageChange( oSettings, e.data.page );
11730						fnCallbackDraw( oSettings );
11731						e.preventDefault();
11732					} );
11733				};
11734				
11735				/* Pages calculation */
11736				if ( oSettings._iDisplayLength === -1 )
11737				{
11738					iStartButton = 1;
11739					iEndButton = 1;
11740					iCurrentPage = 1;
11741				}
11742				else if (iPages < iPageCount)
11743				{
11744					iStartButton = 1;
11745					iEndButton = iPages;
11746				}
11747				else if (iCurrentPage <= iPageCountHalf)
11748				{
11749					iStartButton = 1;
11750					iEndButton = iPageCount;
11751				}
11752				else if (iCurrentPage >= (iPages - iPageCountHalf))
11753				{
11754					iStartButton = iPages - iPageCount + 1;
11755					iEndButton = iPages;
11756				}
11757				else
11758				{
11759					iStartButton = iCurrentPage - Math.ceil(iPageCount / 2) + 1;
11760					iEndButton = iStartButton + iPageCount - 1;
11761				}
11762	
11763				
11764				/* Build the dynamic list */
11765				for ( i=iStartButton ; i<=iEndButton ; i++ )
11766				{
11767					sList += (iCurrentPage !== i) ?
11768						'<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButton+'">'+oSettings.fnFormatNumber(i)+'</a>' :
11769						'<a tabindex="'+oSettings.iTabIndex+'" class="'+oClasses.sPageButtonActive+'">'+oSettings.fnFormatNumber(i)+'</a>';
11770				}
11771				
11772				/* Loop over each instance of the pager */
11773				for ( i=0, iLen=an.length ; i<iLen ; i++ )
11774				{
11775					nNode = an[i];
11776					if ( !nNode.hasChildNodes() )
11777					{
11778						continue;
11779					}
11780					
11781					/* Build up the dynamic list first - html and listeners */
11782					$('span:eq(0)', nNode)
11783						.html( sList )
11784						.children('a').each( fnBind );
11785					
11786					/* Update the permanent button's classes */
11787					anButtons = nNode.getElementsByTagName('a');
11788					anStatic = [
11789						anButtons[0], anButtons[1], 
11790						anButtons[anButtons.length-2], anButtons[anButtons.length-1]
11791					];
11792	
11793					$(anStatic).removeClass( oClasses.sPageButton+" "+oClasses.sPageButtonActive+" "+oClasses.sPageButtonStaticDisabled );
11794					$([anStatic[0], anStatic[1]]).addClass( 
11795						(iCurrentPage==1) ?
11796							oClasses.sPageButtonStaticDisabled :
11797							oClasses.sPageButton
11798					);
11799					$([anStatic[2], anStatic[3]]).addClass(
11800						(iPages===0 || iCurrentPage===iPages || oSettings._iDisplayLength===-1) ?
11801							oClasses.sPageButtonStaticDisabled :
11802							oClasses.sPageButton
11803					);
11804				}
11805			}
11806		}
11807	} );
11808	
11809	$.extend( DataTable.ext.oSort, {
11810		/*
11811		 * text sorting
11812		 */
11813		"string-pre": function ( a )
11814		{
11815			if ( typeof a != 'string' ) {
11816				a = (a !== null && a.toString) ? a.toString() : '';
11817			}
11818			return a.toLowerCase();
11819		},
11820	
11821		"string-asc": function ( x, y )
11822		{
11823			return ((x < y) ? -1 : ((x > y) ? 1 : 0));
11824		},
11825		
11826		"string-desc": function ( x, y )
11827		{
11828			return ((x < y) ? 1 : ((x > y) ? -1 : 0));
11829		},
11830		
11831		
11832		/*
11833		 * html sorting (ignore html tags)
11834		 */
11835		"html-pre": function ( a )
11836		{
11837			return a.replace( /<.*?>/g, "" ).toLowerCase();
11838		},
11839		
11840		"html-asc": function ( x, y )
11841		{
11842			return ((x < y) ? -1 : ((x > y) ? 1 : 0));
11843		},
11844		
11845		"html-desc": function ( x, y )
11846		{
11847			return ((x < y) ? 1 : ((x > y) ? -1 : 0));
11848		},
11849		
11850		
11851		/*
11852		 * date sorting
11853		 */
11854		"date-pre": function ( a )
11855		{
11856			var x = Date.parse( a );
11857			
11858			if ( isNaN(x) || x==="" )
11859			{
11860				x = Date.parse( "01/01/1970 00:00:00" );
11861			}
11862			return x;
11863		},
11864	
11865		"date-asc": function ( x, y )
11866		{
11867			return x - y;
11868		},
11869		
11870		"date-desc": function ( x, y )
11871		{
11872			return y - x;
11873		},
11874		
11875		
11876		/*
11877		 * numerical sorting
11878		 */
11879		"numeric-pre": function ( a )
11880		{
11881			return (a=="-" || a==="") ? 0 : a*1;
11882		},
11883	
11884		"numeric-asc": function ( x, y )
11885		{
11886			return x - y;
11887		},
11888		
11889		"numeric-desc": function ( x, y )
11890		{
11891			return y - x;
11892		}
11893	} );
11894	
11895	
11896	$.extend( DataTable.ext.aTypes, [
11897		/*
11898		 * Function: -
11899		 * Purpose:  Check to see if a string is numeric
11900		 * Returns:  string:'numeric' or null
11901		 * Inputs:   mixed:sText - string to check
11902		 */
11903		function ( sData )
11904		{
11905			/* Allow zero length strings as a number */
11906			if ( typeof sData === 'number' )
11907			{
11908				return 'numeric';
11909			}
11910			else if ( typeof sData !== 'string' )
11911			{
11912				return null;
11913			}
11914			
11915			var sValidFirstChars = "0123456789-";
11916			var sValidChars = "0123456789.";
11917			var Char;
11918			var bDecimal = false;
11919			
11920			/* Check for a valid first char (no period and allow negatives) */
11921			Char = sData.charAt(0); 
11922			if (sValidFirstChars.indexOf(Char) == -1) 
11923			{
11924				return null;
11925			}
11926			
11927			/* Check all the other characters are valid */
11928			for ( var i=1 ; i<sData.length ; i++ ) 
11929			{
11930				Char = sData.charAt(i); 
11931				if (sValidChars.indexOf(Char) == -1) 
11932				{
11933					return null;
11934				}
11935				
11936				/* Only allowed one decimal place... */
11937				if ( Char == "." )
11938				{
11939					if ( bDecimal )
11940					{
11941						return null;
11942					}
11943					bDecimal = true;
11944				}
11945			}
11946			
11947			return 'numeric';
11948		},
11949		
11950		/*
11951		 * Function: -
11952		 * Purpose:  Check to see if a string is actually a formatted date
11953		 * Returns:  string:'date' or null
11954		 * Inputs:   string:sText - string to check
11955		 */
11956		function ( sData )
11957		{
11958			var iParse = Date.parse(sData);
11959			if ( (iParse !== null && !isNaN(iParse)) || (typeof sData === 'string' && sData.length === 0) )
11960			{
11961				return 'date';
11962			}
11963			return null;
11964		},
11965		
11966		/*
11967		 * Function: -
11968		 * Purpose:  Check to see if a string should be treated as an HTML string
11969		 * Returns:  string:'html' or null
11970		 * Inputs:   string:sText - string to check
11971		 */
11972		function ( sData )
11973		{
11974			if ( typeof sData === 'string' && sData.indexOf('<') != -1 && sData.indexOf('>') != -1 )
11975			{
11976				return 'html';
11977			}
11978			return null;
11979		}
11980	] );
11981	
11982
11983	// jQuery aliases
11984	$.fn.DataTable = DataTable;
11985	$.fn.dataTable = DataTable;
11986	$.fn.dataTableSettings = DataTable.settings;
11987	$.fn.dataTableExt = DataTable.ext;
11988
11989
11990	// Information about events fired by DataTables - for documentation.
11991	/**
11992	 * Draw event, fired whenever the table is redrawn on the page, at the same point as
11993	 * fnDrawCallback. This may be useful for binding events or performing calculations when
11994	 * the table is altered at all.
11995	 *  @name DataTable#draw
11996	 *  @event
11997	 *  @param {event} e jQuery event object
11998	 *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
11999	 */
12000
12001	/**
12002	 * Filter event, fired when the filtering applied to the table (using the build in global
12003	 * global filter, or column filters) is altered.
12004	 *  @name DataTable#filter
12005	 *  @event
12006	 *  @param {event} e jQuery event object
12007	 *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
12008	 */
12009
12010	/**
12011	 * Page change event, fired when the paging of the table is altered.
12012	 *  @name DataTable#page
12013	 *  @event
12014	 *  @param {event} e jQuery event object
12015	 *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
12016	 */
12017
12018	/**
12019	 * Sort event, fired when the sorting applied to the table is altered.
12020	 *  @name DataTable#sort
12021	 *  @event
12022	 *  @param {event} e jQuery event object
12023	 *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
12024	 */
12025
12026	/**
12027	 * DataTables initialisation complete event, fired when the table is fully drawn,
12028	 * including Ajax data loaded, if Ajax data is required.
12029	 *  @name DataTable#init
12030	 *  @event
12031	 *  @param {event} e jQuery event object
12032	 *  @param {object} oSettings DataTables settings object
12033	 *  @param {object} json The JSON object request from the server - only
12034	 *    present if client-side Ajax sourced data is used</li></ol>
12035	 */
12036
12037	/**
12038	 * State save event, fired when the table has changed state a new state save is required.
12039	 * This method allows modification of the state saving object prior to actually doing the
12040	 * save, including addition or other state properties (for plug-ins) or modification
12041	 * of a DataTables core property.
12042	 *  @name DataTable#stateSaveParams
12043	 *  @event
12044	 *  @param {event} e jQuery event object
12045	 *  @param {object} oSettings DataTables settings object
12046	 *  @param {object} json The state information to be saved
12047	 */
12048
12049	/**
12050	 * State load event, fired when the table is loading state from the stored data, but
12051	 * prior to the settings object being modified by the saved state - allowing modification
12052	 * of the saved state is required or loading of state for a plug-in.
12053	 *  @name DataTable#stateLoadParams
12054	 *  @event
12055	 *  @param {event} e jQuery event object
12056	 *  @param {object} oSettings DataTables settings object
12057	 *  @param {object} json The saved state information
12058	 */
12059
12060	/**
12061	 * State loaded event, fired when state has been loaded from stored data and the settings
12062	 * object has been modified by the loaded data.
12063	 *  @name DataTable#stateLoaded
12064	 *  @event
12065	 *  @param {event} e jQuery event object
12066	 *  @param {object} oSettings DataTables settings object
12067	 *  @param {object} json The saved state information
12068	 */
12069
12070	/**
12071	 * Processing event, fired when DataTables is doing some kind of processing (be it,
12072	 * sort, filter or anything else). Can be used to indicate to the end user that
12073	 * there is something happening, or that something has finished.
12074	 *  @name DataTable#processing
12075	 *  @event
12076	 *  @param {event} e jQuery event object
12077	 *  @param {object} oSettings DataTables settings object
12078	 *  @param {boolean} bShow Flag for if DataTables is doing processing or not
12079	 */
12080
12081	/**
12082	 * Ajax (XHR) event, fired whenever an Ajax request is completed from a request to 
12083	 * made to the server for new data (note that this trigger is called 
12083in fnServerData,
12084	 * if you override fnServerData and which to use this event, you need to trigger it in
12085	 * you success function).
12086	 *  @name DataTable#xhr
12087	 *  @event
12088	 *  @param {event} e jQuery event object
12089	 *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
12090	 *  @param {object} json JSON returned from the server
12091	 */
12092
12093	/**
12094	 * Destroy event, fired when the DataTable is destroyed by calling fnDestroy or passing
12095	 * the bDestroy:true parameter in the initialisation object. This can be used to remove
12096	 * bound events, added DOM nodes, etc.
12097	 *  @name DataTable#destroy
12098	 *  @event
12099	 *  @param {event} e jQuery event object
12100	 *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
12101	 */
12102}));
12103
12104}(window, document));
12105

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.