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