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