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