[TASK] Use minimised version of jquery.dataTables-1.9.4 33/19033/2
authorStefan Neufeind <typo3.neufeind@speedpartner.de>
Wed, 21 Nov 2012 00:29:56 +0000 (01:29 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Mon, 18 Mar 2013 21:58:04 +0000 (22:58 +0100)
Change-Id: Ic8f2f046b6df70c885158f4c1f08cbad1c943cf1
Resolves: #43186
Releases: 6.1, 6.0
Reviewed-on: https://review.typo3.org/19033
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
typo3/sysext/lang/Resources/Private/Layouts/Default.html
typo3/sysext/lang/Resources/Public/Contrib/jquery.dataTables-1.9.4.js [deleted file]
typo3/sysext/lang/Resources/Public/Contrib/jquery.dataTables-1.9.4.min.js [new file with mode: 0644]

index d84f0b0..566bea1 100644 (file)
@@ -12,7 +12,7 @@
        }"
        addJsFiles="{
                0: '{f:uri.resource(path:\'Contrib/jquery-1.8.0.js\')}',
-               1: '{f:uri.resource(path:\'Contrib/jquery.dataTables-1.9.4.js\')}',
+               1: '{f:uri.resource(path:\'Contrib/jquery.dataTables-1.9.4.min.js\')}',
                2: '{f:uri.resource(path:\'JavaScript/LangModule.js\')}'
        }"
        addJsInlineLabels="{
diff --git a/typo3/sysext/lang/Resources/Public/Contrib/jquery.dataTables-1.9.4.js b/typo3/sysext/lang/Resources/Public/Contrib/jquery.dataTables-1.9.4.js
deleted file mode 100644 (file)
index 1d8a220..0000000
+++ /dev/null
@@ -1,12099 +0,0 @@
-/**
- * @summary     DataTables
- * @description Paginate, search and sort HTML tables
- * @version     1.9.4
- * @file        jquery.dataTables.js
- * @author      Allan Jardine (www.sprymedia.co.uk)
- * @contact     www.sprymedia.co.uk/contact
- *
- * @copyright Copyright 2008-2012 Allan Jardine, all rights reserved.
- *
- * This source file is free software, under either the GPL v2 license or a
- * BSD style license, available at:
- *   http://datatables.net/license_gpl2
- *   http://datatables.net/license_bsd
- * 
- * This source file is distributed in the hope that it will be useful, but 
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- * 
- * For details please refer to: http://www.datatables.net
- */
-
-/*jslint evil: true, undef: true, browser: true */
-/*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*/
-
-(/** @lends <global> */function( window, document, undefined ) {
-
-(function( factory ) {
-       "use strict";
-
-       // Define as an AMD module if possible
-       if ( typeof define === 'function' && define.amd )
-       {
-               define( ['jquery'], factory );
-       }
-       /* Define using browser globals otherwise
-        * Prevent multiple instantiations if the script is loaded twice
-        */
-       else if ( jQuery && !jQuery.fn.dataTable )
-       {
-               factory( jQuery );
-       }
-}
-(/** @lends <global> */function( $ ) {
-       "use strict";
-       /** 
-        * DataTables is a plug-in for the jQuery Javascript library. It is a 
-        * highly flexible tool, based upon the foundations of progressive 
-        * enhancement, which will add advanced interaction controls to any 
-        * HTML table. For a full list of features please refer to
-        * <a href="http://datatables.net">DataTables.net</a>.
-        *
-        * Note that the <i>DataTable</i> object is not a global variable but is
-        * aliased to <i>jQuery.fn.DataTable</i> and <i>jQuery.fn.dataTable</i> through which 
-        * it may be  accessed.
-        *
-        *  @class
-        *  @param {object} [oInit={}] Configuration object for DataTables. Options
-        *    are defined by {@link DataTable.defaults}
-        *  @requires jQuery 1.3+
-        * 
-        *  @example
-        *    // Basic initialisation
-        *    $(document).ready( function {
-        *      $('#example').dataTable();
-        *    } );
-        *  
-        *  @example
-        *    // Initialisation with configuration options - in this case, disable
-        *    // pagination and sorting.
-        *    $(document).ready( function {
-        *      $('#example').dataTable( {
-        *        "bPaginate": false,
-        *        "bSort": false 
-        *      } );
-        *    } );
-        */
-       var DataTable = function( oInit )
-       {
-               
-               
-               /**
-                * Add a column to the list used for the table with default values
-                *  @param {object} oSettings dataTables settings object
-                *  @param {node} nTh The th element for this column
-                *  @memberof DataTable#oApi
-                */
-               function _fnAddColumn( oSettings, nTh )
-               {
-                       var oDefaults = DataTable.defaults.columns;
-                       var iCol = oSettings.aoColumns.length;
-                       var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
-                               "sSortingClass": oSettings.oClasses.sSortable,
-                               "sSortingClassJUI": oSettings.oClasses.sSortJUI,
-                               "nTh": nTh ? nTh : document.createElement('th'),
-                               "sTitle":    oDefaults.sTitle    ? oDefaults.sTitle    : nTh ? nTh.innerHTML : '',
-                               "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
-                               "mData": oDefaults.mData ? oDefaults.oDefaults : iCol
-                       } );
-                       oSettings.aoColumns.push( oCol );
-                       
-                       /* Add a column specific filter */
-                       if ( oSettings.aoPreSearchCols[ iCol ] === undefined || oSettings.aoPreSearchCols[ iCol ] === null )
-                       {
-                               oSettings.aoPreSearchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch );
-                       }
-                       else
-                       {
-                               var oPre = oSettings.aoPreSearchCols[ iCol ];
-                               
-                               /* Don't require that the user must specify bRegex, bSmart or bCaseInsensitive */
-                               if ( oPre.bRegex === undefined )
-                               {
-                                       oPre.bRegex = true;
-                               }
-                               
-                               if ( oPre.bSmart === undefined )
-                               {
-                                       oPre.bSmart = true;
-                               }
-                               
-                               if ( oPre.bCaseInsensitive === undefined )
-                               {
-                                       oPre.bCaseInsensitive = true;
-                               }
-                       }
-                       
-                       /* Use the column options function to initialise classes etc */
-                       _fnColumnOptions( oSettings, iCol, null );
-               }
-               
-               
-               /**
-                * Apply options for a column
-                *  @param {object} oSettings dataTables settings object
-                *  @param {int} iCol column index to consider
-                *  @param {object} oOptions object with sType, bVisible and bSearchable etc
-                *  @memberof DataTable#oApi
-                */
-               function _fnColumnOptions( oSettings, iCol, oOptions )
-               {
-                       var oCol = oSettings.aoColumns[ iCol ];
-                       
-                       /* User specified column options */
-                       if ( oOptions !== undefined && oOptions !== null )
-                       {
-                               /* Backwards compatibility for mDataProp */
-                               if ( oOptions.mDataProp && !oOptions.mData )
-                               {
-                                       oOptions.mData = oOptions.mDataProp;
-                               }
-               
-                               if ( oOptions.sType !== undefined )
-                               {
-                                       oCol.sType = oOptions.sType;
-                                       oCol._bAutoType = false;
-                               }
-                               
-                               $.extend( oCol, oOptions );
-                               _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
-               
-                               /* iDataSort to be applied (backwards compatibility), but aDataSort will take
-                                * priority if defined
-                                */
-                               if ( oOptions.iDataSort !== undefined )
-                               {
-                                       oCol.aDataSort = [ oOptions.iDataSort ];
-                               }
-                               _fnMap( oCol, oOptions, "aDataSort" );
-                       }
-               
-                       /* Cache the data get and set functions for speed */
-                       var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
-                       var mData = _fnGetObjectDataFn( oCol.mData );
-               
-                       oCol.fnGetData = function (oData, sSpecific) {
-                               var innerData = mData( oData, sSpecific );
-               
-                               if ( oCol.mRender && (sSpecific && sSpecific !== '') )
-                               {
-                                       return mRender( innerData, sSpecific, oData );
-                               }
-                               return innerData;
-                       };
-                       oCol.fnSetData = _fnSetObjectDataFn( oCol.mData );
-                       
-                       /* Feature sorting overrides column specific when off */
-                       if ( !oSettings.oFeatures.bSort )
-                       {
-                               oCol.bSortable = false;
-                       }
-                       
-                       /* Check that the class assignment is correct for sorting */
-                       if ( !oCol.bSortable ||
-                                ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) )
-                       {
-                               oCol.sSortingClass = oSettings.oClasses.sSortableNone;
-                               oCol.sSortingClassJUI = "";
-                       }
-                       else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1 )
-                       {
-                               oCol.sSortingClass = oSettings.oClasses.sSortable;
-                               oCol.sSortingClassJUI = oSettings.oClasses.sSortJUI;
-                       }
-                       else if ( $.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1 )
-                       {
-                               oCol.sSortingClass = oSettings.oClasses.sSortableAsc;
-                               oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed;
-                       }
-                       else if ( $.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1 )
-                       {
-                               oCol.sSortingClass = oSettings.oClasses.sSortableDesc;
-                               oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed;
-                       }
-               }
-               
-               
-               /**
-                * Adjust the table column widths for new data. Note: you would probably want to 
-                * do a redraw after calling this function!
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnAdjustColumnSizing ( oSettings )
-               {
-                       /* Not interested in doing column width calculation if auto-width is disabled */
-                       if ( oSettings.oFeatures.bAutoWidth === false )
-                       {
-                               return false;
-                       }
-                       
-                       _fnCalculateColumnWidths( oSettings );
-                       for ( var i=0 , iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
-                       {
-                               oSettings.aoColumns[i].nTh.style.width = oSettings.aoColumns[i].sWidth;
-                       }
-               }
-               
-               
-               /**
-                * Covert the index of a visible column to the index in the data array (take account
-                * of hidden columns)
-                *  @param {object} oSettings dataTables settings object
-                *  @param {int} iMatch Visible column index to lookup
-                *  @returns {int} i the data index
-                *  @memberof DataTable#oApi
-                */
-               function _fnVisibleToColumnIndex( oSettings, iMatch )
-               {
-                       var aiVis = _fnGetColumns( oSettings, 'bVisible' );
-               
-                       return typeof aiVis[iMatch] === 'number' ?
-                               aiVis[iMatch] :
-                               null;
-               }
-               
-               
-               /**
-                * Covert the index of an index in the data array and convert it to the visible
-                *   column index (take account of hidden columns)
-                *  @param {int} iMatch Column index to lookup
-                *  @param {object} oSettings dataTables settings object
-                *  @returns {int} i the data index
-                *  @memberof DataTable#oApi
-                */
-               function _fnColumnIndexToVisible( oSettings, iMatch )
-               {
-                       var aiVis = _fnGetColumns( oSettings, 'bVisible' );
-                       var iPos = $.inArray( iMatch, aiVis );
-               
-                       return iPos !== -1 ? iPos : null;
-               }
-               
-               
-               /**
-                * Get the number of visible columns
-                *  @param {object} oSettings dataTables settings object
-                *  @returns {int} i the number of visible columns
-                *  @memberof DataTable#oApi
-                */
-               function _fnVisbleColumns( oSettings )
-               {
-                       return _fnGetColumns( oSettings, 'bVisible' ).length;
-               }
-               
-               
-               /**
-                * Get an array of column indexes that match a given property
-                *  @param {object} oSettings dataTables settings object
-                *  @param {string} sParam Parameter in aoColumns to look for - typically 
-                *    bVisible or bSearchable
-                *  @returns {array} Array of indexes with matched properties
-                *  @memberof DataTable#oApi
-                */
-               function _fnGetColumns( oSettings, sParam )
-               {
-                       var a = [];
-               
-                       $.map( oSettings.aoColumns, function(val, i) {
-                               if ( val[sParam] ) {
-                                       a.push( i );
-                               }
-                       } );
-               
-                       return a;
-               }
-               
-               
-               /**
-                * Get the sort type based on an input string
-                *  @param {string} sData data we wish to know the type of
-                *  @returns {string} type (defaults to 'string' if no type can be detected)
-                *  @memberof DataTable#oApi
-                */
-               function _fnDetectType( sData )
-               {
-                       var aTypes = DataTable.ext.aTypes;
-                       var iLen = aTypes.length;
-                       
-                       for ( var i=0 ; i<iLen ; i++ )
-                       {
-                               var sType = aTypes[i]( sData );
-                               if ( sType !== null )
-                               {
-                                       return sType;
-                               }
-                       }
-                       
-                       return 'string';
-               }
-               
-               
-               /**
-                * Figure out how to reorder a display list
-                *  @param {object} oSettings dataTables settings object
-                *  @returns array {int} aiReturn index list for reordering
-                *  @memberof DataTable#oApi
-                */
-               function _fnReOrderIndex ( oSettings, sColumns )
-               {
-                       var aColumns = sColumns.split(',');
-                       var aiReturn = [];
-                       
-                       for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
-                       {
-                               for ( var j=0 ; j<iLen ; j++ )
-                               {
-                                       if ( oSettings.aoColumns[i].sName == aColumns[j] )
-                                       {
-                                               aiReturn.push( j );
-                                               break;
-                                       }
-                               }
-                       }
-                       
-                       return aiReturn;
-               }
-               
-               
-               /**
-                * Get the column ordering that DataTables expects
-                *  @param {object} oSettings dataTables settings object
-                *  @returns {string} comma separated list of names
-                *  @memberof DataTable#oApi
-                */
-               function _fnColumnOrdering ( oSettings )
-               {
-                       var sNames = '';
-                       for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
-                       {
-                               sNames += oSettings.aoColumns[i].sName+',';
-                       }
-                       if ( sNames.length == iLen )
-                       {
-                               return "";
-                       }
-                       return sNames.slice(0, -1);
-               }
-               
-               
-               /**
-                * Take the column definitions and static columns arrays and calculate how
-                * they relate to column indexes. The callback function will then apply the
-                * definition found for a column to a suitable configuration object.
-                *  @param {object} oSettings dataTables settings object
-                *  @param {array} aoColDefs The aoColumnDefs array that is to be applied
-                *  @param {array} aoCols The aoColumns array that defines columns individually
-                *  @param {function} fn Callback function - takes two parameters, the calculated
-                *    column index and the definition for that column.
-                *  @memberof DataTable#oApi
-                */
-               function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
-               {
-                       var i, iLen, j, jLen, k, kLen;
-               
-                       // Column definitions with aTargets
-                       if ( aoColDefs )
-                       {
-                               /* Loop over the definitions array - loop in reverse so first instance has priority */
-                               for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
-                               {
-                                       /* Each definition can target multiple columns, as it is an array */
-                                       var aTargets = aoColDefs[i].aTargets;
-                                       if ( !$.isArray( aTargets ) )
-                                       {
-                                               _fnLog( oSettings, 1, 'aTargets must be an array of targets, not a '+(typeof aTargets) );
-                                       }
-               
-                                       for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
-                                       {
-                                               if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
-                                               {
-                                                       /* Add columns that we don't yet know about */
-                                                       while( oSettings.aoColumns.length <= aTargets[j] )
-                                                       {
-                                                               _fnAddColumn( oSettings );
-                                                       }
-               
-                                                       /* Integer, basic index */
-                                                       fn( aTargets[j], aoColDefs[i] );
-                                               }
-                                               else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
-                                               {
-                                                       /* Negative integer, right to left column counting */
-                                                       fn( oSettings.aoColumns.length+aTargets[j], aoColDefs[i] );
-                                               }
-                                               else if ( typeof aTargets[j] === 'string' )
-                                               {
-                                                       /* Class name matching on TH element */
-                                                       for ( k=0, kLen=oSettings.aoColumns.length ; k<kLen ; k++ )
-                                                       {
-                                                               if ( aTargets[j] == "_all" ||
-                                                                    $(oSettings.aoColumns[k].nTh).hasClass( aTargets[j] ) )
-                                                               {
-                                                                       fn( k, aoColDefs[i] );
-                                                               }
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-               
-                       // Statically defined columns array
-                       if ( aoCols )
-                       {
-                               for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
-                               {
-                                       fn( i, aoCols[i] );
-                               }
-                       }
-               }
-               
-               /**
-                * Add a data array to the table, creating DOM node etc. This is the parallel to 
-                * _fnGatherData, but for adding rows from a Javascript source, rather than a
-                * DOM source.
-                *  @param {object} oSettings dataTables settings object
-                *  @param {array} aData data array to be added
-                *  @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
-                *  @memberof DataTable#oApi
-                */
-               function _fnAddData ( oSettings, aDataSupplied )
-               {
-                       var oCol;
-                       
-                       /* Take an independent copy of the data source so we can bash it about as we wish */
-                       var aDataIn = ($.isArray(aDataSupplied)) ?
-                               aDataSupplied.slice() :
-                               $.extend( true, {}, aDataSupplied );
-                       
-                       /* Create the object for storing information about this new row */
-                       var iRow = oSettings.aoData.length;
-                       var oData = $.extend( true, {}, DataTable.models.oRow );
-                       oData._aData = aDataIn;
-                       oSettings.aoData.push( oData );
-               
-                       /* Create the cells */
-                       var nTd, sThisType;
-                       for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
-                       {
-                               oCol = oSettings.aoColumns[i];
-               
-                               /* Use rendered data for filtering / sorting */
-                               if ( typeof oCol.fnRender === 'function' && oCol.bUseRendered && oCol.mData !== null )
-                               {
-                                       _fnSetCellData( oSettings, iRow, i, _fnRender(oSettings, iRow, i) );
-                               }
-                               else
-                               {
-                                       _fnSetCellData( oSettings, iRow, i, _fnGetCellData( oSettings, iRow, i ) );
-                               }
-                               
-                               /* See if we should auto-detect the column type */
-                               if ( oCol._bAutoType && oCol.sType != 'string' )
-                               {
-                                       /* Attempt to auto detect the type - same as _fnGatherData() */
-                                       var sVarType = _fnGetCellData( oSettings, iRow, i, 'type' );
-                                       if ( sVarType !== null && sVarType !== '' )
-                                       {
-                                               sThisType = _fnDetectType( sVarType );
-                                               if ( oCol.sType === null )
-                                               {
-                                                       oCol.sType = sThisType;
-                                               }
-                                               else if ( oCol.sType != sThisType && oCol.sType != "html" )
-                                               {
-                                                       /* String is always the 'fallback' option */
-                                                       oCol.sType = 'string';
-                                               }
-                                       }
-                               }
-                       }
-                       
-                       /* Add to the display array */
-                       oSettings.aiDisplayMaster.push( iRow );
-               
-                       /* Create the DOM information */
-                       if ( !oSettings.oFeatures.bDeferRender )
-                       {
-                               _fnCreateTr( oSettings, iRow );
-                       }
-               
-                       return iRow;
-               }
-               
-               
-               /**
-                * Read in the data from the target table from the DOM
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnGatherData( oSettings )
-               {
-                       var iLoop, i, iLen, j, jLen, jInner,
-                               nTds, nTrs, nTd, nTr, aLocalData, iThisIndex,
-                               iRow, iRows, iColumn, iColumns, sNodeName,
-                               oCol, oData;
-                       
-                       /*
-                        * Process by row first
-                        * Add the data object for the whole table - storing the tr node. Note - no point in getting
-                        * DOM based data if we are going to go and replace it with Ajax source data.
-                        */
-                       if ( oSettings.bDeferLoading || oSettings.sAjaxSource === null )
-                       {
-                               nTr = oSettings.nTBody.firstChild;
-                               while ( nTr )
-                               {
-                                       if ( nTr.nodeName.toUpperCase() == "TR" )
-                                       {
-                                               iThisIndex = oSettings.aoData.length;
-                                               nTr._DT_RowIndex = iThisIndex;
-                                               oSettings.aoData.push( $.extend( true, {}, DataTable.models.oRow, {
-                                                       "nTr": nTr
-                                               } ) );
-               
-                                               oSettings.aiDisplayMaster.push( iThisIndex );
-                                               nTd = nTr.firstChild;
-                                               jInner = 0;
-                                               while ( nTd )
-                                               {
-                                                       sNodeName = nTd.nodeName.toUpperCase();
-                                                       if ( sNodeName == "TD" || sNodeName == "TH" )
-                                                       {
-                                                               _fnSetCellData( oSettings, iThisIndex, jInner, $.trim(nTd.innerHTML) );
-                                                               jInner++;
-                                                       }
-                                                       nTd = nTd.nextSibling;
-                                               }
-                                       }
-                                       nTr = nTr.nextSibling;
-                               }
-                       }
-                       
-                       /* Gather in the TD elements of the Table - note that this is basically the same as
-                        * fnGetTdNodes, but that function takes account of hidden columns, which we haven't yet
-                        * setup!
-                        */
-                       nTrs = _fnGetTrNodes( oSettings );
-                       nTds = [];
-                       for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
-                       {
-                               nTd = nTrs[i].firstChild;
-                               while ( nTd )
-                               {
-                                       sNodeName = nTd.nodeName.toUpperCase();
-                                       if ( sNodeName == "TD" || sNodeName == "TH" )
-                                       {
-                                               nTds.push( nTd );
-                                       }
-                                       nTd = nTd.nextSibling;
-                               }
-                       }
-                       
-                       /* Now process by column */
-                       for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ )
-                       {
-                               oCol = oSettings.aoColumns[iColumn];
-               
-                               /* Get the title of the column - unless there is a user set one */
-                               if ( oCol.sTitle === null )
-                               {
-                                       oCol.sTitle = oCol.nTh.innerHTML;
-                               }
-                               
-                               var
-                                       bAutoType = oCol._bAutoType,
-                                       bRender = typeof oCol.fnRender === 'function',
-                                       bClass = oCol.sClass !== null,
-                                       bVisible = oCol.bVisible,
-                                       nCell, sThisType, sRendered, sValType;
-                               
-                               /* A single loop to rule them all (and be more efficient) */
-                               if ( bAutoType || bRender || bClass || !bVisible )
-                               {
-                                       for ( iRow=0, iRows=oSettings.aoData.length ; iRow<iRows ; iRow++ )
-                                       {
-                                               oData = oSettings.aoData[iRow];
-                                               nCell = nTds[ (iRow*iColumns) + iColumn ];
-                                               
-                                               /* Type detection */
-                                               if ( bAutoType && oCol.sType != 'string' )
-                                               {
-                                                       sValType = _fnGetCellData( oSettings, iRow, iColumn, 'type' );
-                                                       if ( sValType !== '' )
-                                                       {
-                                                               sThisType = _fnDetectType( sValType );
-                                                               if ( oCol.sType === null )
-                                                               {
-                                                                       oCol.sType = sThisType;
-                                                               }
-                                                               else if ( oCol.sType != sThisType && 
-                                                                         oCol.sType != "html" )
-                                                               {
-                                                                       /* String is always the 'fallback' option */
-                                                                       oCol.sType = 'string';
-                                                               }
-                                                       }
-                                               }
-               
-                                               if ( oCol.mRender )
-                                               {
-                                                       // mRender has been defined, so we need to get the value and set it
-                                                       nCell.innerHTML = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
-                                               }
-                                               else if ( oCol.mData !== iColumn )
-                                               {
-                                                       // If mData is not the same as the column number, then we need to
-                                                       // get the dev set value. If it is the column, no point in wasting
-                                                       // time setting the value that is already there!
-                                                       nCell.innerHTML = _fnGetCellData( oSettings, iRow, iColumn, 'display' );
-                                               }
-                                               
-                                               /* Rendering */
-                                               if ( bRender )
-                                               {
-                                                       sRendered = _fnRender( oSettings, iRow, iColumn );
-                                                       nCell.innerHTML = sRendered;
-                                                       if ( oCol.bUseRendered )
-                                                       {
-                                                               /* Use the rendered data for filtering / sorting */
-                                                               _fnSetCellData( oSettings, iRow, iColumn, sRendered );
-                                                       }
-                                               }
-                                               
-                                               /* Classes */
-                                               if ( bClass )
-                                               {
-                                                       nCell.className += ' '+oCol.sClass;
-                                               }
-                                               
-                                               /* Column visibility */
-                                               if ( !bVisible )
-                                               {
-                                                       oData._anHidden[iColumn] = nCell;
-                                                       nCell.parentNode.removeChild( nCell );
-                                               }
-                                               else
-                                               {
-                                                       oData._anHidden[iColumn] = null;
-                                               }
-               
-                                               if ( oCol.fnCreatedCell )
-                                               {
-                                                       oCol.fnCreatedCell.call( oSettings.oInstance,
-                                                               nCell, _fnGetCellData( oSettings, iRow, iColumn, 'display' ), oData._aData, iRow, iColumn
-                                                       );
-                                               }
-                                       }
-                               }
-                       }
-               
-                       /* Row created callbacks */
-                       if ( oSettings.aoRowCreatedCallback.length !== 0 )
-                       {
-                               for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
-                               {
-                                       oData = oSettings.aoData[i];
-                                       _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [oData.nTr, oData._aData, i] );
-                               }
-                       }
-               }
-               
-               
-               /**
-                * Take a TR element and convert it to an index in aoData
-                *  @param {object} oSettings dataTables settings object
-                *  @param {node} n the TR element to find
-                *  @returns {int} index if the node is found, null if not
-                *  @memberof DataTable#oApi
-                */
-               function _fnNodeToDataIndex( oSettings, n )
-               {
-                       return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
-               }
-               
-               
-               /**
-                * Take a TD element and convert it into a column data index (not the visible index)
-                *  @param {object} oSettings dataTables settings object
-                *  @param {int} iRow The row number the TD/TH can be found in
-                *  @param {node} n The TD/TH element to find
-                *  @returns {int} index if the node is found, -1 if not
-                *  @memberof DataTable#oApi
-                */
-               function _fnNodeToColumnIndex( oSettings, iRow, n )
-               {
-                       var anCells = _fnGetTdNodes( oSettings, iRow );
-               
-                       for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
-                       {
-                               if ( anCells[i] === n )
-                               {
-                                       return i;
-                               }
-                       }
-                       return -1;
-               }
-               
-               
-               /**
-                * Get an array of data for a given row from the internal data cache
-                *  @param {object} oSettings dataTables settings object
-                *  @param {int} iRow aoData row id
-                *  @param {string} sSpecific data get type ('type' 'filter' 'sort')
-                *  @param {array} aiColumns Array of column indexes to get data from
-                *  @returns {array} Data array
-                *  @memberof DataTable#oApi
-                */
-               function _fnGetRowData( oSettings, iRow, sSpecific, aiColumns )
-               {
-                       var out = [];
-                       for ( var i=0, iLen=aiColumns.length ; i<iLen ; i++ )
-                       {
-                               out.push( _fnGetCellData( oSettings, iRow, aiColumns[i], sSpecific ) );
-                       }
-                       return out;
-               }
-               
-               
-               /**
-                * Get the data for a given cell from the internal cache, taking into account data mapping
-                *  @param {object} oSettings dataTables settings object
-                *  @param {int} iRow aoData row id
-                *  @param {int} iCol Column index
-                *  @param {string} sSpecific data get type ('display', 'type' 'filter' 'sort')
-                *  @returns {*} Cell data
-                *  @memberof DataTable#oApi
-                */
-               function _fnGetCellData( oSettings, iRow, iCol, sSpecific )
-               {
-                       var sData;
-                       var oCol = oSettings.aoColumns[iCol];
-                       var oData = oSettings.aoData[iRow]._aData;
-               
-                       if ( (sData=oCol.fnGetData( oData, sSpecific )) === undefined )
-                       {
-                               if ( oSettings.iDrawError != oSettings.iDraw && oCol.sDefaultContent === null )
-                               {
-                                       _fnLog( oSettings, 0, "Requested unknown parameter "+
-                                               (typeof oCol.mData=='function' ? '{mData function}' : "'"+oCol.mData+"'")+
-                                               " from the data source for row "+iRow );
-                                       oSettings.iDrawError = oSettings.iDraw;
-                               }
-                               return oCol.sDefaultContent;
-                       }
-               
-                       /* When the data source is null, we can use default column data */
-                       if ( sData === null && oCol.sDefaultContent !== null )
-                       {
-                               sData = oCol.sDefaultContent;
-                       }
-                       else if ( typeof sData === 'function' )
-                       {
-                               /* If the data source is a function, then we run it and use the return */
-                               return sData();
-                       }
-               
-                       if ( sSpecific == 'display' && sData === null )
-                       {
-                               return '';
-                       }
-                       return sData;
-               }
-               
-               
-               /**
-                * Set the value for a specific cell, into the internal data cache
-                *  @param {object} oSettings dataTables settings object
-                *  @param {int} iRow aoData row id
-                *  @param {int} iCol Column index
-                *  @param {*} val Value to set
-                *  @memberof DataTable#oApi
-                */
-               function _fnSetCellData( oSettings, iRow, iCol, val )
-               {
-                       var oCol = oSettings.aoColumns[iCol];
-                       var oData = oSettings.aoData[iRow]._aData;
-               
-                       oCol.fnSetData( oData, val );
-               }
-               
-               
-               // Private variable that is used to match array syntax in the data property object
-               var __reArray = /\[.*?\]$/;
-               
-               /**
-                * Return a function that can be used to get data from a source object, taking
-                * into account the ability to use nested objects as a source
-                *  @param {string|int|function} mSource The data source for the object
-                *  @returns {function} Data get function
-                *  @memberof DataTable#oApi
-                */
-               function _fnGetObjectDataFn( mSource )
-               {
-                       if ( mSource === null )
-                       {
-                               /* Give an empty string for rendering / sorting etc */
-                               return function (data, type) {
-                                       return null;
-                               };
-                       }
-                       else if ( typeof mSource === 'function' )
-                       {
-                               return function (data, type, extra) {
-                                       return mSource( data, type, extra );
-                               };
-                       }
-                       else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || mSource.indexOf('[') !== -1) )
-                       {
-                               /* If there is a . in the source string then the data source is in a 
-                                * nested object so we loop over the data for each level to get the next
-                                * level down. On each loop we test for undefined, and if found immediately
-                                * return. This allows entire objects to be missing and sDefaultContent to
-                                * be used if defined, rather than throwing an error
-                                */
-                               var fetchData = function (data, type, src) {
-                                       var a = src.split('.');
-                                       var arrayNotation, out, innerSrc;
-               
-                                       if ( src !== "" )
-                                       {
-                                               for ( var i=0, iLen=a.length ; i<iLen ; i++ )
-                                               {
-                                                       // Check if we are dealing with an array notation request
-                                                       arrayNotation = a[i].match(__reArray);
-               
-                                                       if ( arrayNotation ) {
-                                                               a[i] = a[i].replace(__reArray, '');
-               
-                                                               // Condition allows simply [] to be passed in
-                                                               if ( a[i] !== "" ) {
-                                                                       data = data[ a[i] ];
-                                                               }
-                                                               out = [];
-                                                               
-                                                               // Get the remainder of the nested object to get
-                                                               a.splice( 0, i+1 );
-                                                               innerSrc = a.join('.');
-               
-                                                               // Traverse each entry in the array getting the properties requested
-                                                               for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
-                                                                       out.push( fetchData( data[j], type, innerSrc ) );
-                                                               }
-               
-                                                               // If a string is given in between the array notation indicators, that
-                                                               // is used to join the strings together, otherwise an array is returned
-                                                               var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
-                                                               data = (join==="") ? out : out.join(join);
-               
-                                                               // The inner call to fetchData has already traversed through the remainder
-                                                               // of the source requested, so we exit from the loop
-                                                               break;
-                                                       }
-               
-                                                       if ( data === null || data[ a[i] ] === undefined )
-                                                       {
-                                                               return undefined;
-                                                       }
-                                                       data = data[ a[i] ];
-                                               }
-                                       }
-               
-                                       return data;
-                               };
-               
-                               return function (data, type) {
-                                       return fetchData( data, type, mSource );
-                               };
-                       }
-                       else
-                       {
-                               /* Array or flat object mapping */
-                               return function (data, type) {
-                                       return data[mSource];   
-                               };
-                       }
-               }
-               
-               
-               /**
-                * Return a function that can be used to set data from a source object, taking
-                * into account the ability to use nested objects as a source
-                *  @param {string|int|function} mSource The data source for the object
-                *  @returns {function} Data set function
-                *  @memberof DataTable#oApi
-                */
-               function _fnSetObjectDataFn( mSource )
-               {
-                       if ( mSource === null )
-                       {
-                               /* Nothing to do when the data source is null */
-                               return function (data, val) {};
-                       }
-                       else if ( typeof mSource === 'function' )
-                       {
-                               return function (data, val) {
-                                       mSource( data, 'set', val );
-                               };
-                       }
-                       else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || mSource.indexOf('[') !== -1) )
-                       {
-                               /* Like the get, we need to get data from a nested object */
-                               var setData = function (data, val, src) {
-                                       var a = src.split('.'), b;
-                                       var arrayNotation, o, innerSrc;
-               
-                                       for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
-                                       {
-                                               // Check if we are dealing with an array notation request
-                                               arrayNotation = a[i].match(__reArray);
-               
-                                               if ( arrayNotation )
-                                               {
-                                                       a[i] = a[i].replace(__reArray, '');
-                                                       data[ a[i] ] = [];
-                                                       
-                                                       // Get the remainder of the nested object to set so we can recurse
-                                                       b = a.slice();
-                                                       b.splice( 0, i+1 );
-                                                       innerSrc = b.join('.');
-               
-                                                       // Traverse each entry in the array setting the properties requested
-                                                       for ( var j=0, jLen=val.length ; j<jLen ; j++ )
-                                                       {
-                                                               o = {};
-                                                               setData( o, val[j], innerSrc );
-                                                               data[ a[i] ].push( o );
-                                                       }
-               
-                                                       // The inner call to setData has already traversed through the remainder
-                                                       // of the source and has set the data, thus we can exit here
-                                                       return;
-                                               }
-               
-                                               // If the nested object doesn't currently exist - since we are
-                                               // trying to set the value - create it
-                                               if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
-                                               {
-                                                       data[ a[i] ] = {};
-                                               }
-                                               data = data[ a[i] ];
-                                       }
-               
-                                       // If array notation is used, we just want to strip it and use the property name
-                                       // and assign the value. If it isn't used, then we get the result we want anyway
-                                       data[ a[a.length-1].replace(__reArray, '') ] = val;
-                               };
-               
-                               return function (data, val) {
-                                       return setData( data, val, mSource );
-                               };
-                       }
-                       else
-                       {
-                               /* Array or flat object mapping */
-                               return function (data, val) {
-                                       data[mSource] = val;    
-                               };
-                       }
-               }
-               
-               
-               /**
-                * Return an array with the full table data
-                *  @param {object} oSettings dataTables settings object
-                *  @returns array {array} aData Master data array
-                *  @memberof DataTable#oApi
-                */
-               function _fnGetDataMaster ( oSettings )
-               {
-                       var aData = [];
-                       var iLen = oSettings.aoData.length;
-                       for ( var i=0 ; i<iLen; i++ )
-                       {
-                               aData.push( oSettings.aoData[i]._aData );
-                       }
-                       return aData;
-               }
-               
-               
-               /**
-                * Nuke the table
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnClearTable( oSettings )
-               {
-                       oSettings.aoData.splice( 0, oSettings.aoData.length );
-                       oSettings.aiDisplayMaster.splice( 0, oSettings.aiDisplayMaster.length );
-                       oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length );
-                       _fnCalculateEnd( oSettings );
-               }
-               
-               
-                /**
-                * Take an array of integers (index array) and remove a target integer (value - not 
-                * the key!)
-                *  @param {array} a Index array to target
-                *  @param {int} iTarget value to find
-                *  @memberof DataTable#oApi
-                */
-               function _fnDeleteIndex( a, iTarget )
-               {
-                       var iTargetIndex = -1;
-                       
-                       for ( var i=0, iLen=a.length ; i<iLen ; i++ )
-                       {
-                               if ( a[i] == iTarget )
-                               {
-                                       iTargetIndex = i;
-                               }
-                               else if ( a[i] > iTarget )
-                               {
-                                       a[i]--;
-                               }
-                       }
-                       
-                       if ( iTargetIndex != -1 )
-                       {
-                               a.splice( iTargetIndex, 1 );
-                       }
-               }
-               
-               
-                /**
-                * Call the developer defined fnRender function for a given cell (row/column) with
-                * the required parameters and return the result.
-                *  @param {object} oSettings dataTables settings object
-                *  @param {int} iRow aoData index for the row
-                *  @param {int} iCol aoColumns index for the column
-                *  @returns {*} Return of the developer's fnRender function
-                *  @memberof DataTable#oApi
-                */
-               function _fnRender( oSettings, iRow, iCol )
-               {
-                       var oCol = oSettings.aoColumns[iCol];
-               
-                       return oCol.fnRender( {
-                               "iDataRow":    iRow,
-                               "iDataColumn": iCol,
-                               "oSettings":   oSettings,
-                               "aData":       oSettings.aoData[iRow]._aData,
-                               "mDataProp":   oCol.mData
-                       }, _fnGetCellData(oSettings, iRow, iCol, 'display') );
-               }
-               /**
-                * Create a new TR element (and it's TD children) for a row
-                *  @param {object} oSettings dataTables settings object
-                *  @param {int} iRow Row to consider
-                *  @memberof DataTable#oApi
-                */
-               function _fnCreateTr ( oSettings, iRow )
-               {
-                       var oData = oSettings.aoData[iRow];
-                       var nTd;
-               
-                       if ( oData.nTr === null )
-                       {
-                               oData.nTr = document.createElement('tr');
-               
-                               /* Use a private property on the node to allow reserve mapping from the node
-                                * to the aoData array for fast look up
-                                */
-                               oData.nTr._DT_RowIndex = iRow;
-               
-                               /* Special parameters can be given by the data source to be used on the row */
-                               if ( oData._aData.DT_RowId )
-                               {
-                                       oData.nTr.id = oData._aData.DT_RowId;
-                               }
-               
-                               if ( oData._aData.DT_RowClass )
-                               {
-                                       oData.nTr.className = oData._aData.DT_RowClass;
-                               }
-               
-                               /* Process each column */
-                               for ( var i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
-                               {
-                                       var oCol = oSettings.aoColumns[i];
-                                       nTd = document.createElement( oCol.sCellType );
-               
-                                       /* Render if needed - if bUseRendered is true then we already have the rendered
-                                        * value in the data source - so can just use that
-                                        */
-                                       nTd.innerHTML = (typeof oCol.fnRender === 'function' && (!oCol.bUseRendered || oCol.mData === null)) ?
-                                               _fnRender( oSettings, iRow, i ) :
-                                               _fnGetCellData( oSettings, iRow, i, 'display' );
-                               
-                                       /* Add user defined class */
-                                       if ( oCol.sClass !== null )
-                                       {
-                                               nTd.className = oCol.sClass;
-                                       }
-                                       
-                                       if ( oCol.bVisible )
-                                       {
-                                               oData.nTr.appendChild( nTd );
-                                               oData._anHidden[i] = null;
-                                       }
-                                       else
-                                       {
-                                               oData._anHidden[i] = nTd;
-                                       }
-               
-                                       if ( oCol.fnCreatedCell )
-                                       {
-                                               oCol.fnCreatedCell.call( oSettings.oInstance,
-                                                       nTd, _fnGetCellData( oSettings, iRow, i, 'display' ), oData._aData, iRow, i
-                                               );
-                                       }
-                               }
-               
-                               _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [oData.nTr, oData._aData, iRow] );
-                       }
-               }
-               
-               
-               /**
-                * Create the HTML header for the table
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnBuildHead( oSettings )
-               {
-                       var i, nTh, iLen, j, jLen;
-                       var iThs = $('th, td', oSettings.nTHead).length;
-                       var iCorrector = 0;
-                       var jqChildren;
-                       
-                       /* If there is a header in place - then use it - otherwise it's going to get nuked... */
-                       if ( iThs !== 0 )
-                       {
-                               /* We've got a thead from the DOM, so remove hidden columns and apply width to vis cols */
-                               for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
-                               {
-                                       nTh = oSettings.aoColumns[i].nTh;
-                                       nTh.setAttribute('role', 'columnheader');
-                                       if ( oSettings.aoColumns[i].bSortable )
-                                       {
-                                               nTh.setAttribute('tabindex', oSettings.iTabIndex);
-                                               nTh.setAttribute('aria-controls', oSettings.sTableId);
-                                       }
-               
-                                       if ( oSettings.aoColumns[i].sClass !== null )
-                                       {
-                                               $(nTh).addClass( oSettings.aoColumns[i].sClass );
-                                       }
-                                       
-                                       /* Set the title of the column if it is user defined (not what was auto detected) */
-                                       if ( oSettings.aoColumns[i].sTitle != nTh.innerHTML )
-                                       {
-                                               nTh.innerHTML = oSettings.aoColumns[i].sTitle;
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               /* We don't have a header in the DOM - so we are going to have to create one */
-                               var nTr = document.createElement( "tr" );
-                               
-                               for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
-                               {
-                                       nTh = oSettings.aoColumns[i].nTh;
-                                       nTh.innerHTML = oSettings.aoColumns[i].sTitle;
-                                       nTh.setAttribute('tabindex', '0');
-                                       
-                                       if ( oSettings.aoColumns[i].sClass !== null )
-                                       {
-                                               $(nTh).addClass( oSettings.aoColumns[i].sClass );
-                                       }
-                                       
-                                       nTr.appendChild( nTh );
-                               }
-                               $(oSettings.nTHead).html( '' )[0].appendChild( nTr );
-                               _fnDetectHeader( oSettings.aoHeader, oSettings.nTHead );
-                       }
-                       
-                       /* ARIA role for the rows */    
-                       $(oSettings.nTHead).children('tr').attr('role', 'row');
-                       
-                       /* Add the extra markup needed by jQuery UI's themes */
-                       if ( oSettings.bJUI )
-                       {
-                               for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
-                               {
-                                       nTh = oSettings.aoColumns[i].nTh;
-                                       
-                                       var nDiv = document.createElement('div');
-                                       nDiv.className = oSettings.oClasses.sSortJUIWrapper;
-                                       $(nTh).contents().appendTo(nDiv);
-                                       
-                                       var nSpan = document.createElement('span');
-                                       nSpan.className = oSettings.oClasses.sSortIcon;
-                                       nDiv.appendChild( nSpan );
-                                       nTh.appendChild( nDiv );
-                               }
-                       }
-                       
-                       if ( oSettings.oFeatures.bSort )
-                       {
-                               for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
-                               {
-                                       if ( oSettings.aoColumns[i].bSortable !== false )
-                                       {
-                                               _fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i );
-                                       }
-                                       else
-                                       {
-                                               $(oSettings.aoColumns[i].nTh).addClass( oSettings.oClasses.sSortableNone );
-                                       }
-                               }
-                       }
-                       
-                       /* Deal with the footer - add classes if required */
-                       if ( oSettings.oClasses.sFooterTH !== "" )
-                       {
-                               $(oSettings.nTFoot).children('tr').children('th').addClass( oSettings.oClasses.sFooterTH );
-                       }
-                       
-                       /* Cache the footer elements */
-                       if ( oSettings.nTFoot !== null )
-                       {
-                               var anCells = _fnGetUniqueThs( oSettings, null, oSettings.aoFooter );
-                               for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
-                               {
-                                       if ( anCells[i] )
-                                       {
-                                               oSettings.aoColumns[i].nTf = anCells[i];
-                                               if ( oSettings.aoColumns[i].sClass )
-                                               {
-                                                       $(anCells[i]).addClass( oSettings.aoColumns[i].sClass );
-                                               }
-                                       }
-                               }
-                       }
-               }
-               
-               
-               /**
-                * Draw the header (or footer) element based on the column visibility states. The
-                * methodology here is to use the layout array from _fnDetectHeader, modified for
-                * the instantaneous column visibility, to construct the new layout. The grid is
-                * traversed over cell at a time in a rows x columns grid fashion, although each 
-                * cell insert can cover multiple elements in the grid - which is tracks using the
-                * aApplied array. Cell inserts in the grid will only occur where there isn't
-                * already a cell in that position.
-                *  @param {object} oSettings dataTables settings object
-                *  @param array {objects} aoSource Layout array from _fnDetectHeader
-                *  @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc, 
-                *  @memberof DataTable#oApi
-                */
-               function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
-               {
-                       var i, iLen, j, jLen, k, kLen, n, nLocalTr;
-                       var aoLocal = [];
-                       var aApplied = [];
-                       var iColumns = oSettings.aoColumns.length;
-                       var iRowspan, iColspan;
-               
-                       if (  bIncludeHidden === undefined )
-                       {
-                               bIncludeHidden = false;
-                       }
-               
-                       /* Make a copy of the master layout array, but without the visible columns in it */
-                       for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
-                       {
-                               aoLocal[i] = aoSource[i].slice();
-                               aoLocal[i].nTr = aoSource[i].nTr;
-               
-                               /* Remove any columns which are currently hidden */
-                               for ( j=iColumns-1 ; j>=0 ; j-- )
-                               {
-                                       if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
-                                       {
-                                               aoLocal[i].splice( j, 1 );
-                                       }
-                               }
-               
-                               /* Prep the applied array - it needs an element for each row */
-                               aApplied.push( [] );
-                       }
-               
-                       for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
-                       {
-                               nLocalTr = aoLocal[i].nTr;
-                               
-                               /* All cells are going to be replaced, so empty out the row */
-                               if ( nLocalTr )
-                               {
-                                       while( (n = nLocalTr.firstChild) )
-                                       {
-                                               nLocalTr.removeChild( n );
-                                       }
-                               }
-               
-                               for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
-                               {
-                                       iRowspan = 1;
-                                       iColspan = 1;
-               
-                                       /* Check to see if there is already a cell (row/colspan) covering our target
-                                        * insert point. If there is, then there is nothing to do.
-                                        */
-                                       if ( aApplied[i][j] === undefined )
-                                       {
-                                               nLocalTr.appendChild( aoLocal[i][j].cell );
-                                               aApplied[i][j] = 1;
-               
-                                               /* Expand the cell to cover as many rows as needed */
-                                               while ( aoLocal[i+iRowspan] !== undefined &&
-                                                       aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
-                                               {
-                                                       aApplied[i+iRowspan][j] = 1;
-                                                       iRowspan++;
-                                               }
-               
-                                               /* Expand the cell to cover as many columns as needed */
-                                               while ( aoLocal[i][j+iColspan] !== undefined &&
-                                                       aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
-                                               {
-                                                       /* Must update the applied array over the rows for the columns */
-                                                       for ( k=0 ; k<iRowspan ; k++ )
-                                                       {
-                                                               aApplied[i+k][j+iColspan] = 1;
-                                                       }
-                                                       iColspan++;
-                                               }
-               
-                                               /* Do the actual expansion in the DOM */
-                                               aoLocal[i][j].cell.rowSpan = iRowspan;
-                                               aoLocal[i][j].cell.colSpan = iColspan;
-                                       }
-                               }
-                       }
-               }
-               
-               
-               /**
-                * Insert the required TR nodes into the table for display
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnDraw( oSettings )
-               {
-                       /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
-                       var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
-                       if ( $.inArray( false, aPreDraw ) !== -1 )
-                       {
-                               _fnProcessingDisplay( oSettings, false );
-                               return;
-                       }
-                       
-                       var i, iLen, n;
-                       var anRows = [];
-                       var iRowCount = 0;
-                       var iStripes = oSettings.asStripeClasses.length;
-                       var iOpenRows = oSettings.aoOpenRows.length;
-                       
-                       oSettings.bDrawing = true;
-                       
-                       /* Check and see if we have an initial draw position from state saving */
-                       if ( oSettings.iInitDisplayStart !== undefined && oSettings.iInitDisplayStart != -1 )
-                       {
-                               if ( oSettings.oFeatures.bServerSide )
-                               {
-                                       oSettings._iDisplayStart = oSettings.iInitDisplayStart;
-                               }
-                               else
-                               {
-                                       oSettings._iDisplayStart = (oSettings.iInitDisplayStart >= oSettings.fnRecordsDisplay()) ?
-                                               0 : oSettings.iInitDisplayStart;
-                               }
-                               oSettings.iInitDisplayStart = -1;
-                               _fnCalculateEnd( oSettings );
-                       }
-                       
-                       /* Server-side processing draw intercept */
-                       if ( oSettings.bDeferLoading )
-                       {
-                               oSettings.bDeferLoading = false;
-                               oSettings.iDraw++;
-                       }
-                       else if ( !oSettings.oFeatures.bServerSide )
-                       {
-                               oSettings.iDraw++;
-                       }
-                       else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
-                       {
-                               return;
-                       }
-                       
-                       if ( oSettings.aiDisplay.length !== 0 )
-                       {
-                               var iStart = oSettings._iDisplayStart;
-                               var iEnd = oSettings._iDisplayEnd;
-                               
-                               if ( oSettings.oFeatures.bServerSide )
-                               {
-                                       iStart = 0;
-                                       iEnd = oSettings.aoData.length;
-                               }
-                               
-                               for ( var j=iStart ; j<iEnd ; j++ )
-                               {
-                                       var aoData = oSettings.aoData[ oSettings.aiDisplay[j] ];
-                                       if ( aoData.nTr === null )
-                                       {
-                                               _fnCreateTr( oSettings, oSettings.aiDisplay[j] );
-                                       }
-               
-                                       var nRow = aoData.nTr;
-                                       
-                                       /* Remove the old striping classes and then add the new one */
-                                       if ( iStripes !== 0 )
-                                       {
-                                               var sStripe = oSettings.asStripeClasses[ iRowCount % iStripes ];
-                                               if ( aoData._sRowStripe != sStripe )
-                                               {
-                                                       $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
-                                                       aoData._sRowStripe = sStripe;
-                                               }
-                                       }
-                                       
-                                       /* Row callback functions - might want to manipulate the row */
-                                       _fnCallbackFire( oSettings, 'aoRowCallback', null, 
-                                               [nRow, oSettings.aoData[ oSettings.aiDisplay[j] ]._aData, iRowCount, j] );
-                                       
-                                       anRows.push( nRow );
-                                       iRowCount++;
-                                       
-                                       /* If there is an open row - and it is attached to this parent - attach it on redraw */
-                                       if ( iOpenRows !== 0 )
-                                       {
-                                               for ( var k=0 ; k<iOpenRows ; k++ )
-                                               {
-                                                       if ( nRow == oSettings.aoOpenRows[k].nParent )
-                                                       {
-                                                               anRows.push( oSettings.aoOpenRows[k].nTr );
-                                                               break;
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               /* Table is empty - create a row with an empty message in it */
-                               anRows[ 0 ] = document.createElement( 'tr' );
-                               
-                               if ( oSettings.asStripeClasses[0] )
-                               {
-                                       anRows[ 0 ].className = oSettings.asStripeClasses[0];
-                               }
-               
-                               var oLang = oSettings.oLanguage;
-                               var sZero = oLang.sZeroRecords;
-                               if ( oSettings.iDraw == 1 && oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide )
-                               {
-                                       sZero = oLang.sLoadingRecords;
-                               }
-                               else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
-                               {
-                                       sZero = oLang.sEmptyTable;
-                               }
-               
-                               var nTd = document.createElement( 'td' );
-                               nTd.setAttribute( 'valign', "top" );
-                               nTd.colSpan = _fnVisbleColumns( oSettings );
-                               nTd.className = oSettings.oClasses.sRowEmpty;
-                               nTd.innerHTML = _fnInfoMacros( oSettings, sZero );
-                               
-                               anRows[ iRowCount ].appendChild( nTd );
-                       }
-                       
-                       /* Header and footer callbacks */
-                       _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], 
-                               _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), oSettings.aiDisplay ] );
-                       
-                       _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], 
-                               _fnGetDataMaster( oSettings ), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), oSettings.aiDisplay ] );
-                       
-                       /* 
-                        * Need to remove any old row from the display - note we can't just empty the tbody using
-                        * $().html('') since this will unbind the jQuery event handlers (even although the node 
-                        * still exists!) - equally we can't use innerHTML, since IE throws an exception.
-                        */
-                       var
-                               nAddFrag = document.createDocumentFragment(),
-                               nRemoveFrag = document.createDocumentFragment(),
-                               nBodyPar, nTrs;
-                       
-                       if ( oSettings.nTBody )
-                       {
-                               nBodyPar = oSettings.nTBody.parentNode;
-                               nRemoveFrag.appendChild( oSettings.nTBody );
-                               
-                               /* When doing infinite scrolling, only remove child rows when sorting, filtering or start
-                                * up. When not infinite scroll, always do it.
-                                */
-                               if ( !oSettings.oScroll.bInfinite || !oSettings._bInitComplete ||
-                                       oSettings.bSorted || oSettings.bFiltered )
-                               {
-                                       while( (n = oSettings.nTBody.firstChild) )
-                                       {
-                                               oSettings.nTBody.removeChild( n );
-                                       }
-                               }
-                               
-                               /* Put the draw table into the dom */
-                               for ( i=0, iLen=anRows.length ; i<iLen ; i++ )
-                               {
-                                       nAddFrag.appendChild( anRows[i] );
-                               }
-                               
-                               oSettings.nTBody.appendChild( nAddFrag );
-                               if ( nBodyPar !== null )
-                               {
-                                       nBodyPar.appendChild( oSettings.nTBody );
-                               }
-                       }
-                       
-                       /* Call all required callback functions for the end of a draw */
-                       _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
-                       
-                       /* Draw is complete, sorting and filtering must be as well */
-                       oSettings.bSorted = false;
-                       oSettings.bFiltered = false;
-                       oSettings.bDrawing = false;
-                       
-                       if ( oSettings.oFeatures.bServerSide )
-                       {
-                               _fnProcessingDisplay( oSettings, false );
-                               if ( !oSettings._bInitComplete )
-                               {
-                                       _fnInitComplete( oSettings );
-                               }
-                       }
-               }
-               
-               
-               /**
-                * Redraw the table - taking account of the various features which are enabled
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnReDraw( oSettings )
-               {
-                       if ( oSettings.oFeatures.bSort )
-                       {
-                               /* Sorting will refilter and draw for us */
-                               _fnSort( oSettings, oSettings.oPreviousSearch );
-                       }
-                       else if ( oSettings.oFeatures.bFilter )
-                       {
-                               /* Filtering will redraw for us */
-                               _fnFilterComplete( oSettings, oSettings.oPreviousSearch );
-                       }
-                       else
-                       {
-                               _fnCalculateEnd( oSettings );
-                               _fnDraw( oSettings );
-                       }
-               }
-               
-               
-               /**
-                * Add the options to the page HTML for the table
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnAddOptionsHtml ( oSettings )
-               {
-                       /*
-                        * Create a temporary, empty, div which we can later on replace with what we have generated
-                        * we do it this way to rendering the 'options' html offline - speed :-)
-                        */
-                       var nHolding = $('<div></div>')[0];
-                       oSettings.nTable.parentNode.insertBefore( nHolding, oSettings.nTable );
-                       
-                       /* 
-                        * All DataTables are wrapped in a div
-                        */
-                       oSettings.nTableWrapper = $('<div id="'+oSettings.sTableId+'_wrapper" class="'+oSettings.oClasses.sWrapper+'" role="grid"></div>')[0];
-                       oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
-               
-                       /* Track where we want to insert the option */
-                       var nInsertNode = oSettings.nTableWrapper;
-                       
-                       /* Loop over the user set positioning and place the elements as needed */
-                       var aDom = oSettings.sDom.split('');
-                       var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j;
-                       for ( var i=0 ; i<aDom.length ; i++ )
-                       {
-                               iPushFeature = 0;
-                               cOption = aDom[i];
-                               
-                               if ( cOption == '<' )
-                               {
-                                       /* New container div */
-                                       nNewNode = $('<div></div>')[0];
-                                       
-                                       /* Check to see if we should append an id and/or a class name to the container */
-                                       cNext = aDom[i+1];
-                                       if ( cNext == "'" || cNext == '"' )
-                                       {
-                                               sAttr = "";
-                                               j = 2;
-                                               while ( aDom[i+j] != cNext )
-                                               {
-                                                       sAttr += aDom[i+j];
-                                                       j++;
-                                               }
-                                               
-                                               /* Replace jQuery UI constants */
-                                               if ( sAttr == "H" )
-                                               {
-                                                       sAttr = oSettings.oClasses.sJUIHeader;
-                                               }
-                                               else if ( sAttr == "F" )
-                                               {
-                                                       sAttr = oSettings.oClasses.sJUIFooter;
-                                               }
-                                               
-                                               /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
-                                                * breaks the string into parts and applies them as needed
-                                                */
-                                               if ( sAttr.indexOf('.') != -1 )
-                                               {
-                                                       var aSplit = sAttr.split('.');
-                                                       nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
-                                                       nNewNode.className = aSplit[1];
-                                               }
-                                               else if ( sAttr.charAt(0) == "#" )
-                                               {
-                                                       nNewNode.id = sAttr.substr(1, sAttr.length-1);
-                                               }
-                                               else
-                                               {
-                                                       nNewNode.className = sAttr;
-                                               }
-                                               
-                                               i += j; /* Move along the position array */
-                                       }
-                                       
-                                       nInsertNode.appendChild( nNewNode );
-                                       nInsertNode = nNewNode;
-                               }
-                               else if ( cOption == '>' )
-                               {
-                                       /* End container div */
-                                       nInsertNode = nInsertNode.parentNode;
-                               }
-                               else if ( cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange )
-                               {
-                                       /* Length */
-                                       nTmp = _fnFeatureHtmlLength( oSettings );
-                                       iPushFeature = 1;
-                               }
-                               else if ( cOption == 'f' && oSettings.oFeatures.bFilter )
-                               {
-                                       /* Filter */
-                                       nTmp = _fnFeatureHtmlFilter( oSettings );
-                                       iPushFeature = 1;
-                               }
-                               else if ( cOption == 'r' && oSettings.oFeatures.bProcessing )
-                               {
-                                       /* pRocessing */
-                                       nTmp = _fnFeatureHtmlProcessing( oSettings );
-                                       iPushFeature = 1;
-                               }
-                               else if ( cOption == 't' )
-                               {
-                                       /* Table */
-                                       nTmp = _fnFeatureHtmlTable( oSettings );
-                                       iPushFeature = 1;
-                               }
-                               else if ( cOption ==  'i' && oSettings.oFeatures.bInfo )
-                               {
-                                       /* Info */
-                                       nTmp = _fnFeatureHtmlInfo( oSettings );
-                                       iPushFeature = 1;
-                               }
-                               else if ( cOption == 'p' && oSettings.oFeatures.bPaginate )
-                               {
-                                       /* Pagination */
-                                       nTmp = _fnFeatureHtmlPaginate( oSettings );
-                                       iPushFeature = 1;
-                               }
-                               else if ( DataTable.ext.aoFeatures.length !== 0 )
-                               {
-                                       /* Plug-in features */
-                                       var aoFeatures = DataTable.ext.aoFeatures;
-                                       for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
-                                       {
-                                               if ( cOption == aoFeatures[k].cFeature )
-                                               {
-                                                       nTmp = aoFeatures[k].fnInit( oSettings );
-                                                       if ( nTmp )
-                                                       {
-                                                               iPushFeature = 1;
-                                                       }
-                                                       break;
-                                               }
-                                       }
-                               }
-                               
-                               /* Add to the 2D features array */
-                               if ( iPushFeature == 1 && nTmp !== null )
-                               {
-                                       if ( typeof oSettings.aanFeatures[cOption] !== 'object' )
-                                       {
-                                               oSettings.aanFeatures[cOption] = [];
-                                       }
-                                       oSettings.aanFeatures[cOption].push( nTmp );
-                                       nInsertNode.appendChild( nTmp );
-                               }
-                       }
-                       
-                       /* Built our DOM structure - replace the holding div with what we want */
-                       nHolding.parentNode.replaceChild( oSettings.nTableWrapper, nHolding );
-               }
-               
-               
-               /**
-                * Use the DOM source to create up an array of header cells. The idea here is to
-                * create a layout grid (array) of rows x columns, which contains a reference
-                * to the cell that that point in the grid (regardless of col/rowspan), such that
-                * any column / row could be removed and the new grid constructed
-                *  @param array {object} aLayout Array to store the calculated layout in
-                *  @param {node} nThead The header/footer element for the table
-                *  @memberof DataTable#oApi
-                */
-               function _fnDetectHeader ( aLayout, nThead )
-               {
-                       var nTrs = $(nThead).children('tr');
-                       var nTr, nCell;
-                       var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
-                       var bUnique;
-                       var fnShiftCol = function ( a, i, j ) {
-                               var k = a[i];
-                               while ( k[j] ) {
-                                       j++;
-                               }
-                               return j;
-                       };
-               
-                       aLayout.splice( 0, aLayout.length );
-                       
-                       /* We know how many rows there are in the layout - so prep it */
-                       for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
-                       {
-                               aLayout.push( [] );
-                       }
-                       
-                       /* Calculate a layout array */
-                       for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
-                       {
-                               nTr = nTrs[i];
-                               iColumn = 0;
-                               
-                               /* For every cell in the row... */
-                               nCell = nTr.firstChild;
-                               while ( nCell ) {
-                                       if ( nCell.nodeName.toUpperCase() == "TD" ||
-                                            nCell.nodeName.toUpperCase() == "TH" )
-                                       {
-                                               /* Get the col and rowspan attributes from the DOM and sanitise them */
-                                               iColspan = nCell.getAttribute('colspan') * 1;
-                                               iRowspan = nCell.getAttribute('rowspan') * 1;
-                                               iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
-                                               iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
-               
-                                               /* There might be colspan cells already in this row, so shift our target 
-                                                * accordingly
-                                                */
-                                               iColShifted = fnShiftCol( aLayout, i, iColumn );
-                                               
-                                               /* Cache calculation for unique columns */
-                                               bUnique = iColspan === 1 ? true : false;
-                                               
-                                               /* If there is col / rowspan, copy the information into the layout grid */
-                                               for ( l=0 ; l<iColspan ; l++ )
-                                               {
-                                                       for ( k=0 ; k<iRowspan ; k++ )
-                                                       {
-                                                               aLayout[i+k][iColShifted+l] = {
-                                                                       "cell": nCell,
-                                                                       "unique": bUnique
-                                                               };
-                                                               aLayout[i+k].nTr = nTr;
-                                                       }
-                                               }
-                                       }
-                                       nCell = nCell.nextSibling;
-                               }
-                       }
-               }
-               
-               
-               /**
-                * Get an array of unique th elements, one for each column
-                *  @param {object} oSettings dataTables settings object
-                *  @param {node} nHeader automatically detect the layout from this node - optional
-                *  @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
-                *  @returns array {node} aReturn list of unique th's
-                *  @memberof DataTable#oApi
-                */
-               function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
-               {
-                       var aReturn = [];
-                       if ( !aLayout )
-                       {
-                               aLayout = oSettings.aoHeader;
-                               if ( nHeader )
-                               {
-                                       aLayout = [];
-                                       _fnDetectHeader( aLayout, nHeader );
-                               }
-                       }
-               
-                       for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
-                       {
-                               for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
-                               {
-                                       if ( aLayout[i][j].unique && 
-                                                (!aReturn[j] || !oSettings.bSortCellsTop) )
-                                       {
-                                               aReturn[j] = aLayout[i][j].cell;
-                                       }
-                               }
-                       }
-                       
-                       return aReturn;
-               }
-               
-               
-               
-               /**
-                * Update the table using an Ajax call
-                *  @param {object} oSettings dataTables settings object
-                *  @returns {boolean} Block the table drawing or not
-                *  @memberof DataTable#oApi
-                */
-               function _fnAjaxUpdate( oSettings )
-               {
-                       if ( oSettings.bAjaxDataGet )
-                       {
-                               oSettings.iDraw++;
-                               _fnProcessingDisplay( oSettings, true );
-                               var iColumns = oSettings.aoColumns.length;
-                               var aoData = _fnAjaxParameters( oSettings );
-                               _fnServerParams( oSettings, aoData );
-                               
-                               oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aoData,
-                                       function(json) {
-                                               _fnAjaxUpdateDraw( oSettings, json );
-                                       }, oSettings );
-                               return false;
-                       }
-                       else
-                       {
-                               return true;
-                       }
-               }
-               
-               
-               /**
-                * Build up the parameters in an object needed for a server-side processing request
-                *  @param {object} oSettings dataTables settings object
-                *  @returns {bool} block the table drawing or not
-                *  @memberof DataTable#oApi
-                */
-               function _fnAjaxParameters( oSettings )
-               {
-                       var iColumns = oSettings.aoColumns.length;
-                       var aoData = [], mDataProp, aaSort, aDataSort;
-                       var i, j;
-                       
-                       aoData.push( { "name": "sEcho",          "value": oSettings.iDraw } );
-                       aoData.push( { "name": "iColumns",       "value": iColumns } );
-                       aoData.push( { "name": "sColumns",       "value": _fnColumnOrdering(oSettings) } );
-                       aoData.push( { "name": "iDisplayStart",  "value": oSettings._iDisplayStart } );
-                       aoData.push( { "name": "iDisplayLength", "value": oSettings.oFeatures.bPaginate !== false ?
-                               oSettings._iDisplayLength : -1 } );
-                               
-                       for ( i=0 ; i<iColumns ; i++ )
-                       {
-                         mDataProp = oSettings.aoColumns[i].mData;
-                               aoData.push( { "name": "mDataProp_"+i, "value": typeof(mDataProp)==="function" ? 'function' : mDataProp } );
-                       }
-                       
-                       /* Filtering */
-                       if ( oSettings.oFeatures.bFilter !== false )
-                       {
-                               aoData.push( { "name": "sSearch", "value": oSettings.oPreviousSearch.sSearch } );
-                               aoData.push( { "name": "bRegex",  "value": oSettings.oPreviousSearch.bRegex } );
-                               for ( i=0 ; i<iColumns ; i++ )
-                               {
-                                       aoData.push( { "name": "sSearch_"+i,     "value": oSettings.aoPreSearchCols[i].sSearch } );
-                                       aoData.push( { "name": "bRegex_"+i,      "value": oSettings.aoPreSearchCols[i].bRegex } );
-                                       aoData.push( { "name": "bSearchable_"+i, "value": oSettings.aoColumns[i].bSearchable } );
-                               }
-                       }
-                       
-                       /* Sorting */
-                       if ( oSettings.oFeatures.bSort !== false )
-                       {
-                               var iCounter = 0;
-               
-                               aaSort = ( oSettings.aaSortingFixed !== null ) ?
-                                       oSettings.aaSortingFixed.concat( oSettings.aaSorting ) :
-                                       oSettings.aaSorting.slice();
-                               
-                               for ( i=0 ; i<aaSort.length ; i++ )
-                               {
-                                       aDataSort = oSettings.aoColumns[ aaSort[i][0] ].aDataSort;
-                                       
-                                       for ( j=0 ; j<aDataSort.length ; j++ )
-                                       {
-                                               aoData.push( { "name": "iSortCol_"+iCounter,  "value": aDataSort[j] } );
-                                               aoData.push( { "name": "sSortDir_"+iCounter,  "value": aaSort[i][1] } );
-                                               iCounter++;
-                                       }
-                               }
-                               aoData.push( { "name": "iSortingCols",   "value": iCounter } );
-                               
-                               for ( i=0 ; i<iColumns ; i++ )
-                               {
-                                       aoData.push( { "name": "bSortable_"+i,  "value": oSettings.aoColumns[i].bSortable } );
-                               }
-                       }
-                       
-                       return aoData;
-               }
-               
-               
-               /**
-                * Add Ajax parameters from plug-ins
-                *  @param {object} oSettings dataTables settings object
-                *  @param array {objects} aoData name/value pairs to send to the server
-                *  @memberof DataTable#oApi
-                */
-               function _fnServerParams( oSettings, aoData )
-               {
-                       _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [aoData] );
-               }
-               
-               
-               /**
-                * Data the data from the server (nuking the old) and redraw the table
-                *  @param {object} oSettings dataTables settings object
-                *  @param {object} json json data return from the server.
-                *  @param {string} json.sEcho Tracking flag for DataTables to match requests
-                *  @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
-                *  @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
-                *  @param {array} json.aaData The data to display on this page
-                *  @param {string} [json.sColumns] Column ordering (sName, comma separated)
-                *  @memberof DataTable#oApi
-                */
-               function _fnAjaxUpdateDraw ( oSettings, json )
-               {
-                       if ( json.sEcho !== undefined )
-                       {
-                               /* Protect against old returns over-writing a new one. Possible when you get
-                                * very fast interaction, and later queries are completed much faster
-                                */
-                               if ( json.sEcho*1 < oSettings.iDraw )
-                               {
-                                       return;
-                               }
-                               else
-                               {
-                                       oSettings.iDraw = json.sEcho * 1;
-                               }
-                       }
-                       
-                       if ( !oSettings.oScroll.bInfinite ||
-                                  (oSettings.oScroll.bInfinite && (oSettings.bSorted || oSettings.bFiltered)) )
-                       {
-                               _fnClearTable( oSettings );
-                       }
-                       oSettings._iRecordsTotal = parseInt(json.iTotalRecords, 10);
-                       oSettings._iRecordsDisplay = parseInt(json.iTotalDisplayRecords, 10);
-                       
-                       /* Determine if reordering is required */
-                       var sOrdering = _fnColumnOrdering(oSettings);
-                       var bReOrder = (json.sColumns !== undefined && sOrdering !== "" && json.sColumns != sOrdering );
-                       var aiIndex;
-                       if ( bReOrder )
-                       {
-                               aiIndex = _fnReOrderIndex( oSettings, json.sColumns );
-                       }
-                       
-                       var aData = _fnGetObjectDataFn( oSettings.sAjaxDataProp )( json );
-                       for ( var i=0, iLen=aData.length ; i<iLen ; i++ )
-                       {
-                               if ( bReOrder )
-                               {
-                                       /* If we need to re-order, then create a new array with the correct order and add it */
-                                       var aDataSorted = [];
-                                       for ( var j=0, jLen=oSettings.aoColumns.length ; j<jLen ; j++ )
-                                       {
-                                               aDataSorted.push( aData[i][ aiIndex[j] ] );
-                                       }
-                                       _fnAddData( oSettings, aDataSorted );
-                               }
-                               else
-                               {
-                                       /* No re-order required, sever got it "right" - just straight add */
-                                       _fnAddData( oSettings, aData[i] );
-                               }
-                       }
-                       oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
-                       
-                       oSettings.bAjaxDataGet = false;
-                       _fnDraw( oSettings );
-                       oSettings.bAjaxDataGet = true;
-                       _fnProcessingDisplay( oSettings, false );
-               }
-               
-               
-               
-               /**
-                * Generate the node required for filtering text
-                *  @returns {node} Filter control element
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnFeatureHtmlFilter ( oSettings )
-               {
-                       var oPreviousSearch = oSettings.oPreviousSearch;
-                       
-                       var sSearchStr = oSettings.oLanguage.sSearch;
-                       sSearchStr = (sSearchStr.indexOf('_INPUT_') !== -1) ?
-                         sSearchStr.replace('_INPUT_', '<input type="text" />') :
-                         sSearchStr==="" ? '<input type="text" />' : sSearchStr+' <input type="text" />';
-                       
-                       var nFilter = document.createElement( 'div' );
-                       nFilter.className = oSettings.oClasses.sFilter;
-                       nFilter.innerHTML = '<label>'+sSearchStr+'</label>';
-                       if ( !oSettings.aanFeatures.f )
-                       {
-                               nFilter.id = oSettings.sTableId+'_filter';
-                       }
-                       
-                       var jqFilter = $('input[type="text"]', nFilter);
-               
-                       // Store a reference to the input element, so other input elements could be
-                       // added to the filter wrapper if needed (submit button for example)
-                       nFilter._DT_Input = jqFilter[0];
-               
-                       jqFilter.val( oPreviousSearch.sSearch.replace('"','&quot;') );
-                       jqFilter.bind( 'keyup.DT', function(e) {
-                               /* Update all other filter input elements for the new display */
-                               var n = oSettings.aanFeatures.f;
-                               var val = this.value==="" ? "" : this.value; // mental IE8 fix :-(
-               
-                               for ( var i=0, iLen=n.length ; i<iLen ; i++ )
-                               {
-                                       if ( n[i] != $(this).parents('div.dataTables_filter')[0] )
-                                       {
-                                               $(n[i]._DT_Input).val( val );
-                                       }
-                               }
-                               
-                               /* Now do the filter */
-                               if ( val != oPreviousSearch.sSearch )
-                               {
-                                       _fnFilterComplete( oSettings, { 
-                                               "sSearch": val, 
-                                               "bRegex": oPreviousSearch.bRegex,
-                                               "bSmart": oPreviousSearch.bSmart ,
-                                               "bCaseInsensitive": oPreviousSearch.bCaseInsensitive 
-                                       } );
-                               }
-                       } );
-               
-                       jqFilter
-                               .attr('aria-controls', oSettings.sTableId)
-                               .bind( 'keypress.DT', function(e) {
-                                       /* Prevent form submission */
-                                       if ( e.keyCode == 13 )
-                                       {
-                                               return false;
-                                       }
-                               }
-                       );
-                       
-                       return nFilter;
-               }
-               
-               
-               /**
-                * Filter the table using both the global filter and column based filtering
-                *  @param {object} oSettings dataTables settings object
-                *  @param {object} oSearch search information
-                *  @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
-                *  @memberof DataTable#oApi
-                */
-               function _fnFilterComplete ( oSettings, oInput, iForce )
-               {
-                       var oPrevSearch = oSettings.oPreviousSearch;
-                       var aoPrevSearch = oSettings.aoPreSearchCols;
-                       var fnSaveFilter = function ( oFilter ) {
-                               /* Save the filtering values */
-                               oPrevSearch.sSearch = oFilter.sSearch;
-                               oPrevSearch.bRegex = oFilter.bRegex;
-                               oPrevSearch.bSmart = oFilter.bSmart;
-                               oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
-                       };
-               
-                       /* In server-side processing all filtering is done by the server, so no point hanging around here */
-                       if ( !oSettings.oFeatures.bServerSide )
-                       {
-                               /* Global filter */
-                               _fnFilter( oSettings, oInput.sSearch, iForce, oInput.bRegex, oInput.bSmart, oInput.bCaseInsensitive );
-                               fnSaveFilter( oInput );
-               
-                               /* Now do the individual column filter */
-                               for ( var i=0 ; i<oSettings.aoPreSearchCols.length ; i++ )
-                               {
-                                       _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, aoPrevSearch[i].bRegex, 
-                                               aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
-                               }
-                               
-                               /* Custom filtering */
-                               _fnFilterCustom( oSettings );
-                       }
-                       else
-                       {
-                               fnSaveFilter( oInput );
-                       }
-                       
-                       /* Tell the draw function we have been filtering */
-                       oSettings.bFiltered = true;
-                       $(oSettings.oInstance).trigger('filter', oSettings);
-                       
-                       /* Redraw the table */
-                       oSettings._iDisplayStart = 0;
-                       _fnCalculateEnd( oSettings );
-                       _fnDraw( oSettings );
-                       
-                       /* Rebuild search array 'offline' */
-                       _fnBuildSearchArray( oSettings, 0 );
-               }
-               
-               
-               /**
-                * Apply custom filtering functions
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnFilterCustom( oSettings )
-               {
-                       var afnFilters = DataTable.ext.afnFiltering;
-                       var aiFilterColumns = _fnGetColumns( oSettings, 'bSearchable' );
-               
-                       for ( var i=0, iLen=afnFilters.length ; i<iLen ; i++ )
-                       {
-                               var iCorrector = 0;
-                               for ( var j=0, jLen=oSettings.aiDisplay.length ; j<jLen ; j++ )
-                               {
-                                       var iDisIndex = oSettings.aiDisplay[j-iCorrector];
-                                       var bTest = afnFilters[i](
-                                               oSettings,
-                                               _fnGetRowData( oSettings, iDisIndex, 'filter', aiFilterColumns ),
-                                               iDisIndex
-                                       );
-                                       
-                                       /* Check if we should use this row based on the filtering function */
-                                       if ( !bTest )
-                                       {
-                                               oSettings.aiDisplay.splice( j-iCorrector, 1 );
-                                               iCorrector++;
-                                       }
-                               }
-                       }
-               }
-               
-               
-               /**
-                * Filter the table on a per-column basis
-                *  @param {object} oSettings dataTables settings object
-                *  @param {string} sInput string to filter on
-                *  @param {int} iColumn column to filter
-                *  @param {bool} bRegex treat search string as a regular expression or not
-                *  @param {bool} bSmart use smart filtering or not
-                *  @param {bool} bCaseInsensitive Do case insenstive matching or not
-                *  @memberof DataTable#oApi
-                */
-               function _fnFilterColumn ( oSettings, sInput, iColumn, bRegex, bSmart, bCaseInsensitive )
-               {
-                       if ( sInput === "" )
-                       {
-                               return;
-                       }
-                       
-                       var iIndexCorrector = 0;
-                       var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart, bCaseInsensitive );
-                       
-                       for ( var i=oSettings.aiDisplay.length-1 ; i>=0 ; i-- )
-                       {
-                               var sData = _fnDataToSearch( _fnGetCellData( oSettings, oSettings.aiDisplay[i], iColumn, 'filter' ),
-                                       oSettings.aoColumns[iColumn].sType );
-                               if ( ! rpSearch.test( sData ) )
-                               {
-                                       oSettings.aiDisplay.splice( i, 1 );
-                                       iIndexCorrector++;
-                               }
-                       }
-               }
-               
-               
-               /**
-                * Filter the data table based on user input and draw the table
-                *  @param {object} oSettings dataTables settings object
-                *  @param {string} sInput string to filter on
-                *  @param {int} iForce optional - force a research of the master array (1) or not (undefined or 0)
-                *  @param {bool} bRegex treat as a regular expression or not
-                *  @param {bool} bSmart perform smart filtering or not
-                *  @param {bool} bCaseInsensitive Do case insenstive matching or not
-                *  @memberof DataTable#oApi
-                */
-               function _fnFilter( oSettings, sInput, iForce, bRegex, bSmart, bCaseInsensitive )
-               {
-                       var i;
-                       var rpSearch = _fnFilterCreateSearch( sInput, bRegex, bSmart, bCaseInsensitive );
-                       var oPrevSearch = oSettings.oPreviousSearch;
-                       
-                       /* Check if we are forcing or not - optional parameter */
-                       if ( !iForce )
-                       {
-                               iForce = 0;
-                       }
-                       
-                       /* Need to take account of custom filtering functions - always filter */
-                       if ( DataTable.ext.afnFiltering.length !== 0 )
-                       {
-                               iForce = 1;
-                       }
-                       
-                       /*
-                        * If the input is blank - we want the full data set
-                        */
-                       if ( sInput.length <= 0 )
-                       {
-                               oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
-                               oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
-                       }
-                       else
-                       {
-                               /*
-                                * We are starting a new search or the new search string is smaller 
-                                * then the old one (i.e. delete). Search from the master array
-                                */
-                               if ( oSettings.aiDisplay.length == oSettings.aiDisplayMaster.length ||
-                                          oPrevSearch.sSearch.length > sInput.length || iForce == 1 ||
-                                          sInput.indexOf(oPrevSearch.sSearch) !== 0 )
-                               {
-                                       /* Nuke the old display array - we are going to rebuild it */
-                                       oSettings.aiDisplay.splice( 0, oSettings.aiDisplay.length);
-                                       
-                                       /* Force a rebuild of the search array */
-                                       _fnBuildSearchArray( oSettings, 1 );
-                                       
-                                       /* Search through all records to populate the search array
-                                        * The the oSettings.aiDisplayMaster and asDataSearch arrays have 1 to 1 
-                                        * mapping
-                                        */
-                                       for ( i=0 ; i<oSettings.aiDisplayMaster.length ; i++ )
-                                       {
-                                               if ( rpSearch.test(oSettings.asDataSearch[i]) )
-                                               {
-                                                       oSettings.aiDisplay.push( oSettings.aiDisplayMaster[i] );
-                                               }
-                                       }
-                         }
-                         else
-                               {
-                               /* Using old search array - refine it - do it this way for speed
-                                * Don't have to search the whole master array again
-                                        */
-                               var iIndexCorrector = 0;
-                               
-                               /* Search the current results */
-                               for ( i=0 ; i<oSettings.asDataSearch.length ; i++ )
-                                       {
-                                       if ( ! rpSearch.test(oSettings.asDataSearch[i]) )
-                                               {
-                                               oSettings.aiDisplay.splice( i-iIndexCorrector, 1 );
-                                               iIndexCorrector++;
-                                       }
-                               }
-                         }
-                       }
-               }
-               
-               
-               /**
-                * Create an array which can be quickly search through
-                *  @param {object} oSettings dataTables settings object
-                *  @param {int} iMaster use the master data array - optional
-                *  @memberof DataTable#oApi
-                */
-               function _fnBuildSearchArray ( oSettings, iMaster )
-               {
-                       if ( !oSettings.oFeatures.bServerSide )
-                       {
-                               /* Clear out the old data */
-                               oSettings.asDataSearch = [];
-               
-                               var aiFilterColumns = _fnGetColumns( oSettings, 'bSearchable' );
-                               var aiIndex = (iMaster===1) ?
-                                       oSettings.aiDisplayMaster :
-                                       oSettings.aiDisplay;
-                               
-                               for ( var i=0, iLen=aiIndex.length ; i<iLen ; i++ )
-                               {
-                                       oSettings.asDataSearch[i] = _fnBuildSearchRow(
-                                               oSettings,
-                                               _fnGetRowData( oSettings, aiIndex[i], 'filter', aiFilterColumns )
-                                       );
-                               }
-                       }
-               }
-               
-               
-               /**
-                * Create a searchable string from a single data row
-                *  @param {object} oSettings dataTables settings object
-                *  @param {array} aData Row data array to use for the data to search
-                *  @memberof DataTable#oApi
-                */
-               function _fnBuildSearchRow( oSettings, aData )
-               {
-                       var sSearch = aData.join('  ');
-                       
-                       /* If it looks like there is an HTML entity in the string, attempt to decode it */
-                       if ( sSearch.indexOf('&') !== -1 )
-                       {
-                               sSearch = $('<div>').html(sSearch).text();
-                       }
-                       
-                       // Strip newline characters
-                       return sSearch.replace( /[\n\r]/g, " " );
-               }
-               
-               /**
-                * Build a regular expression object suitable for searching a table
-                *  @param {string} sSearch string to search for
-                *  @param {bool} bRegex treat as a regular expression or not
-                *  @param {bool} bSmart perform smart filtering or not
-                *  @param {bool} bCaseInsensitive Do case insensitive matching or not
-                *  @returns {RegExp} constructed object
-                *  @memberof DataTable#oApi
-                */
-               function _fnFilterCreateSearch( sSearch, bRegex, bSmart, bCaseInsensitive )
-               {
-                       var asSearch, sRegExpString;
-                       
-                       if ( bSmart )
-                       {
-                               /* Generate the regular expression to use. Something along the lines of:
-                                * ^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$
-                                */
-                               asSearch = bRegex ? sSearch.split( ' ' ) : _fnEscapeRegex( sSearch ).split( ' ' );
-                               sRegExpString = '^(?=.*?'+asSearch.join( ')(?=.*?' )+').*$';
-                               return new RegExp( sRegExpString, bCaseInsensitive ? "i" : "" );
-                       }
-                       else
-                       {
-                               sSearch = bRegex ? sSearch : _fnEscapeRegex( sSearch );
-                               return new RegExp( sSearch, bCaseInsensitive ? "i" : "" );
-                       }
-               }
-               
-               
-               /**
-                * Convert raw data into something that the user can search on
-                *  @param {string} sData data to be modified
-                *  @param {string} sType data type
-                *  @returns {string} search string
-                *  @memberof DataTable#oApi
-                */
-               function _fnDataToSearch ( sData, sType )
-               {
-                       if ( typeof DataTable.ext.ofnSearch[sType] === "function" )
-                       {
-                               return DataTable.ext.ofnSearch[sType]( sData );
-                       }
-                       else if ( sData === null )
-                       {
-                               return '';
-                       }
-                       else if ( sType == "html" )
-                       {
-                               return sData.replace(/[\r\n]/g," ").replace( /<.*?>/g, "" );
-                       }
-                       else if ( typeof sData === "string" )
-                       {
-                               return sData.replace(/[\r\n]/g," ");
-                       }
-                       return sData;
-               }
-               
-               
-               /**
-                * scape a string such that it can be used in a regular expression
-                *  @param {string} sVal string to escape
-                *  @returns {string} escaped string
-                *  @memberof DataTable#oApi
-                */
-               function _fnEscapeRegex ( sVal )
-               {
-                       var acEscape = [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ];
-                       var reReplace = new RegExp( '(\\' + acEscape.join('|\\') + ')', 'g' );
-                       return sVal.replace(reReplace, '\\$1');
-               }
-               
-               
-               /**
-                * Generate the node required for the info display
-                *  @param {object} oSettings dataTables settings object
-                *  @returns {node} Information element
-                *  @memberof DataTable#oApi
-                */
-               function _fnFeatureHtmlInfo ( oSettings )
-               {
-                       var nInfo = document.createElement( 'div' );
-                       nInfo.className = oSettings.oClasses.sInfo;
-                       
-                       /* Actions that are to be taken once only for this feature */
-                       if ( !oSettings.aanFeatures.i )
-                       {
-                               /* Add draw callback */
-                               oSettings.aoDrawCallback.push( {
-                                       "fn": _fnUpdateInfo,
-                                       "sName": "information"
-                               } );
-                               
-                               /* Add id */
-                               nInfo.id = oSettings.sTableId+'_info';
-                       }
-                       oSettings.nTable.setAttribute( 'aria-describedby', oSettings.sTableId+'_info' );
-                       
-                       return nInfo;
-               }
-               
-               
-               /**
-                * Update the information elements in the display
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnUpdateInfo ( oSettings )
-               {
-                       /* Show information about the table */
-                       if ( !oSettings.oFeatures.bInfo || oSettings.aanFeatures.i.length === 0 )
-                       {
-                               return;
-                       }
-                       
-                       var
-                               oLang = oSettings.oLanguage,
-                               iStart = oSettings._iDisplayStart+1,
-                               iEnd = oSettings.fnDisplayEnd(),
-                               iMax = oSettings.fnRecordsTotal(),
-                               iTotal = oSettings.fnRecordsDisplay(),
-                               sOut;
-                       
-                       if ( iTotal === 0 )
-                       {
-                               /* Empty record set */
-                               sOut = oLang.sInfoEmpty;
-                       }
-                       else {
-                               /* Normal record set */
-                               sOut = oLang.sInfo;
-                       }
-               
-                       if ( iTotal != iMax )
-                       {
-                               /* Record set after filtering */
-                               sOut += ' ' + oLang.sInfoFiltered;
-                       }
-               
-                       // Convert the macros
-                       sOut += oLang.sInfoPostFix;
-                       sOut = _fnInfoMacros( oSettings, sOut );
-                       
-                       if ( oLang.fnInfoCallback !== null )
-                       {
-                               sOut = oLang.fnInfoCallback.call( oSettings.oInstance, 
-                                       oSettings, iStart, iEnd, iMax, iTotal, sOut );
-                       }
-                       
-                       var n = oSettings.aanFeatures.i;
-                       for ( var i=0, iLen=n.length ; i<iLen ; i++ )
-                       {
-                               $(n[i]).html( sOut );
-                       }
-               }
-               
-               
-               function _fnInfoMacros ( oSettings, str )
-               {
-                       var
-                               iStart = oSettings._iDisplayStart+1,
-                               sStart = oSettings.fnFormatNumber( iStart ),
-                               iEnd = oSettings.fnDisplayEnd(),
-                               sEnd = oSettings.fnFormatNumber( iEnd ),
-                               iTotal = oSettings.fnRecordsDisplay(),
-                               sTotal = oSettings.fnFormatNumber( iTotal ),
-                               iMax = oSettings.fnRecordsTotal(),
-                               sMax = oSettings.fnFormatNumber( iMax );
-               
-                       // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
-                       // internally
-                       if ( oSettings.oScroll.bInfinite )
-                       {
-                               sStart = oSettings.fnFormatNumber( 1 );
-                       }
-               
-                       return str.
-                               replace(/_START_/g, sStart).
-                               replace(/_END_/g,   sEnd).
-                               replace(/_TOTAL_/g, sTotal).
-                               replace(/_MAX_/g,   sMax);
-               }
-               
-               
-               
-               /**
-                * Draw the table for the first time, adding all required features
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnInitialise ( oSettings )
-               {
-                       var i, iLen, iAjaxStart=oSettings.iInitDisplayStart;
-                       
-                       /* Ensure that the table data is fully initialised */
-                       if ( oSettings.bInitialised === false )
-                       {
-                               setTimeout( function(){ _fnInitialise( oSettings ); }, 200 );
-                               return;
-                       }
-                       
-                       /* Show the display HTML options */
-                       _fnAddOptionsHtml( oSettings );
-                       
-                       /* Build and draw the header / footer for the table */
-                       _fnBuildHead( oSettings );
-                       _fnDrawHead( oSettings, oSettings.aoHeader );
-                       if ( oSettings.nTFoot )
-                       {
-                               _fnDrawHead( oSettings, oSettings.aoFooter );
-                       }
-               
-                       /* Okay to show that something is going on now */
-                       _fnProcessingDisplay( oSettings, true );
-                       
-                       /* Calculate sizes for columns */
-                       if ( oSettings.oFeatures.bAutoWidth )
-                       {
-                               _fnCalculateColumnWidths( oSettings );
-                       }
-                       
-                       for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
-                       {
-                               if ( oSettings.aoColumns[i].sWidth !== null )
-                               {
-                                       oSettings.aoColumns[i].nTh.style.width = _fnStringToCss( oSettings.aoColumns[i].sWidth );
-                               }
-                       }
-                       
-                       /* If there is default sorting required - let's do it. The sort function will do the
-                        * drawing for us. Otherwise we draw the table regardless of the Ajax source - this allows
-                        * the table to look initialised for Ajax sourcing data (show 'loading' message possibly)
-                        */
-                       if ( oSettings.oFeatures.bSort )
-                       {
-                               _fnSort( oSettings );
-                       }
-                       else if ( oSettings.oFeatures.bFilter )
-                       {
-                               _fnFilterComplete( oSettings, oSettings.oPreviousSearch );
-                       }
-                       else
-                       {
-                               oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
-                               _fnCalculateEnd( oSettings );
-                               _fnDraw( oSettings );
-                       }
-                       
-                       /* if there is an ajax source load the data */
-                       if ( oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide )
-                       {
-                               var aoData = [];
-                               _fnServerParams( oSettings, aoData );
-                               oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aoData, function(json) {
-                                       var aData = (oSettings.sAjaxDataProp !== "") ?
-                                               _fnGetObjectDataFn( oSettings.sAjaxDataProp )(json) : json;
-               
-                                       /* Got the data - add it to the table */
-                                       for ( i=0 ; i<aData.length ; i++ )
-                                       {
-                                               _fnAddData( oSettings, aData[i] );
-                                       }
-                                       
-                                       /* Reset the init display for cookie saving. We've already done a filter, and
-                                        * therefore cleared it before. So we need to make it appear 'fresh'
-                                        */
-                                       oSettings.iInitDisplayStart = iAjaxStart;
-                                       
-                                       if ( oSettings.oFeatures.bSort )
-                                       {
-                                               _fnSort( oSettings );
-                                       }
-                                       else
-                                       {
-                                               oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
-                                               _fnCalculateEnd( oSettings );
-                                               _fnDraw( oSettings );
-                                       }
-                                       
-                                       _fnProcessingDisplay( oSettings, false );
-                                       _fnInitComplete( oSettings, json );
-                               }, oSettings );
-                               return;
-                       }
-                       
-                       /* Server-side processing initialisation complete is done at the end of _fnDraw */
-                       if ( !oSettings.oFeatures.bServerSide )
-                       {
-                               _fnProcessingDisplay( oSettings, false );
-                               _fnInitComplete( oSettings );
-                       }
-               }
-               
-               
-               /**
-                * Draw the table for the first time, adding all required features
-                *  @param {object} oSettings dataTables settings object
-                *  @param {object} [json] JSON from the server that completed the table, if using Ajax source
-                *    with client-side processing (optional)
-                *  @memberof DataTable#oApi
-                */
-               function _fnInitComplete ( oSettings, json )
-               {
-                       oSettings._bInitComplete = true;
-                       _fnCallbackFire( oSettings, 'aoInitComplete', 'init', [oSettings, json] );
-               }
-               
-               
-               /**
-                * Language compatibility - when certain options are given, and others aren't, we
-                * need to duplicate the values over, in order to provide backwards compatibility
-                * with older language files.
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnLanguageCompat( oLanguage )
-               {
-                       var oDefaults = DataTable.defaults.oLanguage;
-               
-                       /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
-                        * sZeroRecords - assuming that is given.
-                        */
-                       if ( !oLanguage.sEmptyTable && oLanguage.sZeroRecords &&
-                               oDefaults.sEmptyTable === "No data available in table" )
-                       {
-                               _fnMap( oLanguage, oLanguage, 'sZeroRecords', 'sEmptyTable' );
-                       }
-               
-                       /* Likewise with loading records */
-                       if ( !oLanguage.sLoadingRecords && oLanguage.sZeroRecords &&
-                               oDefaults.sLoadingRecords === "Loading..." )
-                       {
-                               _fnMap( oLanguage, oLanguage, 'sZeroRecords', 'sLoadingRecords' );
-                       }
-               }
-               
-               
-               
-               /**
-                * Generate the node required for user display length changing
-                *  @param {object} oSettings dataTables settings object
-                *  @returns {node} Display length feature node
-                *  @memberof DataTable#oApi
-                */
-               function _fnFeatureHtmlLength ( oSettings )
-               {
-                       if ( oSettings.oScroll.bInfinite )
-                       {
-                               return null;
-                       }
-                       
-                       /* This can be overruled by not using the _MENU_ var/macro in the language variable */
-                       var sName = 'name="'+oSettings.sTableId+'_length"';
-                       var sStdMenu = '<select size="1" '+sName+'>';
-                       var i, iLen;
-                       var aLengthMenu = oSettings.aLengthMenu;
-                       
-                       if ( aLengthMenu.length == 2 && typeof aLengthMenu[0] === 'object' && 
-                                       typeof aLengthMenu[1] === 'object' )
-                       {
-                               for ( i=0, iLen=aLengthMenu[0].length ; i<iLen ; i++ )
-                               {
-                                       sStdMenu += '<option value="'+aLengthMenu[0][i]+'">'+aLengthMenu[1][i]+'</option>';
-                               }
-                       }
-                       else
-                       {
-                               for ( i=0, iLen=aLengthMenu.length ; i<iLen ; i++ )
-                               {
-                                       sStdMenu += '<option value="'+aLengthMenu[i]+'">'+aLengthMenu[i]+'</option>';
-                               }
-                       }
-                       sStdMenu += '</select>';
-                       
-                       var nLength = document.createElement( 'div' );
-                       if ( !oSettings.aanFeatures.l )
-                       {
-                               nLength.id = oSettings.sTableId+'_length';
-                       }
-                       nLength.className = oSettings.oClasses.sLength;
-                       nLength.innerHTML = '<label>'+oSettings.oLanguage.sLengthMenu.replace( '_MENU_', sStdMenu )+'</label>';
-                       
-                       /*
-                        * Set the length to the current display length - thanks to Andrea Pavlovic for this fix,
-                        * and Stefan Skopnik for fixing the fix!
-                        */
-                       $('select option[value="'+oSettings._iDisplayLength+'"]', nLength).attr("selected", true);
-                       
-                       $('select', nLength).bind( 'change.DT', function(e) {
-                               var iVal = $(this).val();
-                               
-                               /* Update all other length options for the new display */
-                               var n = oSettings.aanFeatures.l;
-                               for ( i=0, iLen=n.length ; i<iLen ; i++ )
-                               {
-                                       if ( n[i] != this.parentNode )
-                                       {
-                                               $('select', n[i]).val( iVal );
-                                       }
-                               }
-                               
-                               /* Redraw the table */
-                               oSettings._iDisplayLength = parseInt(iVal, 10);
-                               _fnCalculateEnd( oSettings );
-                               
-                               /* If we have space to show extra rows (backing up from the end point - then do so */
-                               if ( oSettings.fnDisplayEnd() == oSettings.fnRecordsDisplay() )
-                               {
-                                       oSettings._iDisplayStart = oSettings.fnDisplayEnd() - oSettings._iDisplayLength;
-                                       if ( oSettings._iDisplayStart < 0 )
-                                       {
-                                               oSettings._iDisplayStart = 0;
-                                       }
-                               }
-                               
-                               if ( oSettings._iDisplayLength == -1 )
-                               {
-                                       oSettings._iDisplayStart = 0;
-                               }
-                               
-                               _fnDraw( oSettings );
-                       } );
-               
-               
-                       $('select', nLength).attr('aria-controls', oSettings.sTableId);
-                       
-                       return nLength;
-               }
-               
-               
-               /**
-                * Recalculate the end point based on the start point
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnCalculateEnd( oSettings )
-               {
-                       if ( oSettings.oFeatures.bPaginate === false )
-                       {
-                               oSettings._iDisplayEnd = oSettings.aiDisplay.length;
-                       }
-                       else
-                       {
-                               /* Set the end point of the display - based on how many elements there are
-                                * still to display
-                                */
-                               if ( oSettings._iDisplayStart + oSettings._iDisplayLength > oSettings.aiDisplay.length ||
-                                          oSettings._iDisplayLength == -1 )
-                               {
-                                       oSettings._iDisplayEnd = oSettings.aiDisplay.length;
-                               }
-                               else
-                               {
-                                       oSettings._iDisplayEnd = oSettings._iDisplayStart + oSettings._iDisplayLength;
-                               }
-                       }
-               }
-               
-               
-               
-               /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-                * Note that most of the paging logic is done in 
-                * DataTable.ext.oPagination
-                */
-               
-               /**
-                * Generate the node required for default pagination
-                *  @param {object} oSettings dataTables settings object
-                *  @returns {node} Pagination feature node
-                *  @memberof DataTable#oApi
-                */
-               function _fnFeatureHtmlPaginate ( oSettings )
-               {
-                       if ( oSettings.oScroll.bInfinite )
-                       {
-                               return null;
-                       }
-                       
-                       var nPaginate = document.createElement( 'div' );
-                       nPaginate.className = oSettings.oClasses.sPaging+oSettings.sPaginationType;
-                       
-                       DataTable.ext.oPagination[ oSettings.sPaginationType ].fnInit( oSettings, nPaginate, 
-                               function( oSettings ) {
-                                       _fnCalculateEnd( oSettings );
-                                       _fnDraw( oSettings );
-                               }
-                       );
-                       
-                       /* Add a draw callback for the pagination on first instance, to update the paging display */
-                       if ( !oSettings.aanFeatures.p )
-                       {
-                               oSettings.aoDrawCallback.push( {
-                                       "fn": function( oSettings ) {
-                                               DataTable.ext.oPagination[ oSettings.sPaginationType ].fnUpdate( oSettings, function( oSettings ) {
-                                                       _fnCalculateEnd( oSettings );
-                                                       _fnDraw( oSettings );
-                                               } );
-                                       },
-                                       "sName": "pagination"
-                               } );
-                       }
-                       return nPaginate;
-               }
-               
-               
-               /**
-                * Alter the display settings to change the page
-                *  @param {object} oSettings dataTables settings object
-                *  @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
-                *    or page number to jump to (integer)
-                *  @returns {bool} true page has changed, false - no change (no effect) eg 'first' on page 1
-                *  @memberof DataTable#oApi
-                */
-               function _fnPageChange ( oSettings, mAction )
-               {
-                       var iOldStart = oSettings._iDisplayStart;
-                       
-                       if ( typeof mAction === "number" )
-                       {
-                               oSettings._iDisplayStart = mAction * oSettings._iDisplayLength;
-                               if ( oSettings._iDisplayStart > oSettings.fnRecordsDisplay() )
-                               {
-                                       oSettings._iDisplayStart = 0;
-                               }
-                       }
-                       else if ( mAction == "first" )
-                       {
-                               oSettings._iDisplayStart = 0;
-                       }
-                       else if ( mAction == "previous" )
-                       {
-                               oSettings._iDisplayStart = oSettings._iDisplayLength>=0 ?
-                                       oSettings._iDisplayStart - oSettings._iDisplayLength :
-                                       0;
-                               
-                               /* Correct for under-run */
-                               if ( oSettings._iDisplayStart < 0 )
-                               {
-                                 oSettings._iDisplayStart = 0;
-                               }
-                       }
-                       else if ( mAction == "next" )
-                       {
-                               if ( oSettings._iDisplayLength >= 0 )
-                               {
-                                       /* Make sure we are not over running the display array */
-                                       if ( oSettings._iDisplayStart + oSettings._iDisplayLength < oSettings.fnRecordsDisplay() )
-                                       {
-                                               oSettings._iDisplayStart += oSettings._iDisplayLength;
-                                       }
-                               }
-                               else
-                               {
-                                       oSettings._iDisplayStart = 0;
-                               }
-                       }
-                       else if ( mAction == "last" )
-                       {
-                               if ( oSettings._iDisplayLength >= 0 )
-                               {
-                                       var iPages = parseInt( (oSettings.fnRecordsDisplay()-1) / oSettings._iDisplayLength, 10 ) + 1;
-                                       oSettings._iDisplayStart = (iPages-1) * oSettings._iDisplayLength;
-                               }
-                               else
-                               {
-                                       oSettings._iDisplayStart = 0;
-                               }
-                       }
-                       else
-                       {
-                               _fnLog( oSettings, 0, "Unknown paging action: "+mAction );
-                       }
-                       $(oSettings.oInstance).trigger('page', oSettings);
-                       
-                       return iOldStart != oSettings._iDisplayStart;
-               }
-               
-               
-               
-               /**
-                * Generate the node required for the processing node
-                *  @param {object} oSettings dataTables settings object
-                *  @returns {node} Processing element
-                *  @memberof DataTable#oApi
-                */
-               function _fnFeatureHtmlProcessing ( oSettings )
-               {
-                       var nProcessing = document.createElement( 'div' );
-                       
-                       if ( !oSettings.aanFeatures.r )
-                       {
-                               nProcessing.id = oSettings.sTableId+'_processing';
-                       }
-                       nProcessing.innerHTML = oSettings.oLanguage.sProcessing;
-                       nProcessing.className = oSettings.oClasses.sProcessing;
-                       oSettings.nTable.parentNode.insertBefore( nProcessing, oSettings.nTable );
-                       
-                       return nProcessing;
-               }
-               
-               
-               /**
-                * Display or hide the processing indicator
-                *  @param {object} oSettings dataTables settings object
-                *  @param {bool} bShow Show the processing indicator (true) or not (false)
-                *  @memberof DataTable#oApi
-                */
-               function _fnProcessingDisplay ( oSettings, bShow )
-               {
-                       if ( oSettings.oFeatures.bProcessing )
-                       {
-                               var an = oSettings.aanFeatures.r;
-                               for ( var i=0, iLen=an.length ; i<iLen ; i++ )
-                               {
-                                       an[i].style.visibility = bShow ? "visible" : "hidden";
-                               }
-                       }
-               
-                       $(oSettings.oInstance).trigger('processing', [oSettings, bShow]);
-               }
-               
-               /**
-                * Add any control elements for the table - specifically scrolling
-                *  @param {object} oSettings dataTables settings object
-                *  @returns {node} Node to add to the DOM
-                *  @memberof DataTable#oApi
-                */
-               function _fnFeatureHtmlTable ( oSettings )
-               {
-                       /* Check if scrolling is enabled or not - if not then leave the DOM unaltered */
-                       if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
-                       {
-                               return oSettings.nTable;
-                       }
-                       
-                       /*
-                        * The HTML structure that we want to generate in this function is:
-                        *  div - nScroller
-                        *    div - nScrollHead
-                        *      div - nScrollHeadInner
-                        *        table - nScrollHeadTable
-                        *          thead - nThead
-                        *    div - nScrollBody
-                        *      table - oSettings.nTable
-                        *        thead - nTheadSize
-                        *        tbody - nTbody
-                        *    div - nScrollFoot
-                        *      div - nScrollFootInner
-                        *        table - nScrollFootTable
-                        *          tfoot - nTfoot
-                        */
-                       var
-                               nScroller = document.createElement('div'),
-                               nScrollHead = document.createElement('div'),
-                               nScrollHeadInner = document.createElement('div'),
-                               nScrollBody = document.createElement('div'),
-                               nScrollFoot = document.createElement('div'),
-                               nScrollFootInner = document.createElement('div'),
-                               nScrollHeadTable = oSettings.nTable.cloneNode(false),
-                               nScrollFootTable = oSettings.nTable.cloneNode(false),
-                               nThead = oSettings.nTable.getElementsByTagName('thead')[0],
-                               nTfoot = oSettings.nTable.getElementsByTagName('tfoot').length === 0 ? null : 
-                                       oSettings.nTable.getElementsByTagName('tfoot')[0],
-                               oClasses = oSettings.oClasses;
-                       
-                       nScrollHead.appendChild( nScrollHeadInner );
-                       nScrollFoot.appendChild( nScrollFootInner );
-                       nScrollBody.appendChild( oSettings.nTable );
-                       nScroller.appendChild( nScrollHead );
-                       nScroller.appendChild( nScrollBody );
-                       nScrollHeadInner.appendChild( nScrollHeadTable );
-                       nScrollHeadTable.appendChild( nThead );
-                       if ( nTfoot !== null )
-                       {
-                               nScroller.appendChild( nScrollFoot );
-                               nScrollFootInner.appendChild( nScrollFootTable );
-                               nScrollFootTable.appendChild( nTfoot );
-                       }
-                       
-                       nScroller.className = oClasses.sScrollWrapper;
-                       nScrollHead.className = oClasses.sScrollHead;
-                       nScrollHeadInner.className = oClasses.sScrollHeadInner;
-                       nScrollBody.className = oClasses.sScrollBody;
-                       nScrollFoot.className = oClasses.sScrollFoot;
-                       nScrollFootInner.className = oClasses.sScrollFootInner;
-                       
-                       if ( oSettings.oScroll.bAutoCss )
-                       {
-                               nScrollHead.style.overflow = "hidden";
-                               nScrollHead.style.position = "relative";
-                               nScrollFoot.style.overflow = "hidden";
-                               nScrollBody.style.overflow = "auto";
-                       }
-                       
-                       nScrollHead.style.border = "0";
-                       nScrollHead.style.width = "100%";
-                       nScrollFoot.style.border = "0";
-                       nScrollHeadInner.style.width = oSettings.oScroll.sXInner !== "" ?
-                               oSettings.oScroll.sXInner : "100%"; /* will be overwritten */
-                       
-                       /* Modify attributes to respect the clones */
-                       nScrollHeadTable.removeAttribute('id');
-                       nScrollHeadTable.style.marginLeft = "0";
-                       oSettings.nTable.style.marginLeft = "0";
-                       if ( nTfoot !== null )
-                       {
-                               nScrollFootTable.removeAttribute('id');
-                               nScrollFootTable.style.marginLeft = "0";
-                       }
-                       
-                       /* Move caption elements from the body to the header, footer or leave where it is
-                        * depending on the configuration. Note that the DTD says there can be only one caption */
-                       var nCaption = $(oSettings.nTable).children('caption');
-                       if ( nCaption.length > 0 )
-                       {
-                               nCaption = nCaption[0];
-                               if ( nCaption._captionSide === "top" )
-                               {
-                                       nScrollHeadTable.appendChild( nCaption );
-                               }
-                               else if ( nCaption._captionSide === "bottom" && nTfoot )
-                               {
-                                       nScrollFootTable.appendChild( nCaption );
-                               }
-                       }
-                       
-                       /*
-                        * Sizing
-                        */
-                       /* When x-scrolling add the width and a scroller to move the header with the body */
-                       if ( oSettings.oScroll.sX !== "" )
-                       {
-                               nScrollHead.style.width = _fnStringToCss( oSettings.oScroll.sX );
-                               nScrollBody.style.width = _fnStringToCss( oSettings.oScroll.sX );
-                               
-                               if ( nTfoot !== null )
-                               {
-                                       nScrollFoot.style.width = _fnStringToCss( oSettings.oScroll.sX );       
-                               }
-                               
-                               /* When the body is scrolled, then we also want to scroll the headers */
-                               $(nScrollBody).scroll( function (e) {
-                                       nScrollHead.scrollLeft = this.scrollLeft;
-                                       
-                                       if ( nTfoot !== null )
-                                       {
-                                               nScrollFoot.scrollLeft = this.scrollLeft;
-                                       }
-                               } );
-                       }
-                       
-                       /* When yscrolling, add the height */
-                       if ( oSettings.oScroll.sY !== "" )
-                       {
-                               nScrollBody.style.height = _fnStringToCss( oSettings.oScroll.sY );
-                       }
-                       
-                       /* Redraw - align columns across the tables */
-                       oSettings.aoDrawCallback.push( {
-                               "fn": _fnScrollDraw,
-                               "sName": "scrolling"
-                       } );
-                       
-                       /* Infinite scrolling event handlers */
-                       if ( oSettings.oScroll.bInfinite )
-                       {
-                               $(nScrollBody).scroll( function() {
-                                       /* Use a blocker to stop scrolling from loading more data while other data is still loading */
-                                       if ( !oSettings.bDrawing && $(this).scrollTop() !== 0 )
-                                       {
-                                               /* Check if we should load the next data set */
-                                               if ( $(this).scrollTop() + $(this).height() > 
-                                                       $(oSettings.nTable).height() - oSettings.oScroll.iLoadGap )
-                                               {
-                                                       /* Only do the redraw if we have to - we might be at the end of the data */
-                                                       if ( oSettings.fnDisplayEnd() < oSettings.fnRecordsDisplay() )
-                                                       {
-                                                               _fnPageChange( oSettings, 'next' );
-                                                               _fnCalculateEnd( oSettings );
-                                                               _fnDraw( oSettings );
-                                                       }
-                                               }
-                                       }
-                               } );
-                       }
-                       
-                       oSettings.nScrollHead = nScrollHead;
-                       oSettings.nScrollFoot = nScrollFoot;
-                       
-                       return nScroller;
-               }
-               
-               
-               /**
-                * Update the various tables for resizing. It's a bit of a pig this function, but
-                * basically the idea to:
-                *   1. Re-create the table inside the scrolling div
-                *   2. Take live measurements from the DOM
-                *   3. Apply the measurements
-                *   4. Clean up
-                *  @param {object} o dataTables settings object
-                *  @returns {node} Node to add to the DOM
-                *  @memberof DataTable#oApi
-                */
-               function _fnScrollDraw ( o )
-               {
-                       var
-                               nScrollHeadInner = o.nScrollHead.getElementsByTagName('div')[0],
-                               nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
-                               nScrollBody = o.nTable.parentNode,
-                               i, iLen, j, jLen, anHeadToSize, anHeadSizers, anFootSizers, anFootToSize, oStyle, iVis,
-                               nTheadSize, nTfootSize,
-                               iWidth, aApplied=[], aAppliedFooter=[], iSanityWidth,
-                               nScrollFootInner = (o.nTFoot !== null) ? o.nScrollFoot.getElementsByTagName('div')[0] : null,
-                               nScrollFootTable = (o.nTFoot !== null) ? nScrollFootInner.getElementsByTagName('table')[0] : null,
-                               ie67 = o.oBrowser.bScrollOversize,
-                               zeroOut = function(nSizer) {
-                                       oStyle = nSizer.style;
-                                       oStyle.paddingTop = "0";
-                                       oStyle.paddingBottom = "0";
-                                       oStyle.borderTopWidth = "0";
-                                       oStyle.borderBottomWidth = "0";
-                                       oStyle.height = 0;
-                               };
-                       
-                       /*
-                        * 1. Re-create the table inside the scrolling div
-                        */
-                       
-                       /* Remove the old minimised thead and tfoot elements in the inner table */
-                       $(o.nTable).children('thead, tfoot').remove();
-               
-                       /* Clone the current header and footer elements and then place it into the inner table */
-                       nTheadSize = $(o.nTHead).clone()[0];
-                       o.nTable.insertBefore( nTheadSize, o.nTable.childNodes[0] );
-                       anHeadToSize = o.nTHead.getElementsByTagName('tr');
-                       anHeadSizers = nTheadSize.getElementsByTagName('tr');
-                       
-                       if ( o.nTFoot !== null )
-                       {
-                               nTfootSize = $(o.nTFoot).clone()[0];
-                               o.nTable.insertBefore( nTfootSize, o.nTable.childNodes[1] );
-                               anFootToSize = o.nTFoot.getElementsByTagName('tr');
-                               anFootSizers = nTfootSize.getElementsByTagName('tr');
-                       }
-                       
-                       /*
-                        * 2. Take live measurements from the DOM - do not alter the DOM itself!
-                        */
-                       
-                       /* Remove old sizing and apply the calculated column widths
-                        * Get the unique column headers in the newly created (cloned) header. We want to apply the
-                        * calculated sizes to this header
-                        */
-                       if ( o.oScroll.sX === "" )
-                       {
-                               nScrollBody.style.width = '100%';
-                               nScrollHeadInner.parentNode.style.width = '100%';
-                       }
-                       
-                       var nThs = _fnGetUniqueThs( o, nTheadSize );
-                       for ( i=0, iLen=nThs.length ; i<iLen ; i++ )
-                       {
-                               iVis = _fnVisibleToColumnIndex( o, i );
-                               nThs[i].style.width = o.aoColumns[iVis].sWidth;
-                       }
-                       
-                       if ( o.nTFoot !== null )
-                       {
-                               _fnApplyToChildren( function(n) {
-                                       n.style.width = "";
-                               }, anFootSizers );
-                       }
-               
-                       // If scroll collapse is enabled, when we put the headers back into the body for sizing, we
-                       // will end up forcing the scrollbar to appear, making our measurements wrong for when we
-                       // then hide it (end of this function), so add the header height to the body scroller.
-                       if ( o.oScroll.bCollapse && o.oScroll.sY !== "" )
-                       {
-                               nScrollBody.style.height = (nScrollBody.offsetHeight + o.nTHead.offsetHeight)+"px";
-                       }
-                       
-                       /* Size the table as a whole */
-                       iSanityWidth = $(o.nTable).outerWidth();
-                       if ( o.oScroll.sX === "" )
-                       {
-                               /* No x scrolling */
-                               o.nTable.style.width = "100%";
-                               
-                               /* I know this is rubbish - but IE7 will make the width of the table when 100% include
-                                * the scrollbar - which is shouldn't. When there is a scrollbar we need to take this
-                                * into account.
-                                */
-                               if ( ie67 && ($('tbody', nScrollBody).height() > nScrollBody.offsetHeight || 
-                                       $(nScrollBody).css('overflow-y') == "scroll")  )
-                               {
-                                       o.nTable.style.width = _fnStringToCss( $(o.nTable).outerWidth() - o.oScroll.iBarWidth);
-                               }
-                       }
-                       else
-                       {
-                               if ( o.oScroll.sXInner !== "" )
-                               {
-                                       /* x scroll inner has been given - use it */
-                                       o.nTable.style.width = _fnStringToCss(o.oScroll.sXInner);
-                               }
-                               else if ( iSanityWidth == $(nScrollBody).width() &&
-                                  $(nScrollBody).height() < $(o.nTable).height() )
-                               {
-                                       /* There is y-scrolling - try to take account of the y scroll bar */
-                                       o.nTable.style.width = _fnStringToCss( iSanityWidth-o.oScroll.iBarWidth );
-                                       if ( $(o.nTable).outerWidth() > iSanityWidth-o.oScroll.iBarWidth )
-                                       {
-                                               /* Not possible to take account of it */
-                                               o.nTable.style.width = _fnStringToCss( iSanityWidth );
-                                       }
-                               }
-                               else
-                               {
-                                       /* All else fails */
-                                       o.nTable.style.width = _fnStringToCss( iSanityWidth );
-                               }
-                       }
-                       
-                       /* Recalculate the sanity width - now that we've applied the required width, before it was
-                        * a temporary variable. This is required because the column width calculation is done
-                        * before this table DOM is created.
-                        */
-                       iSanityWidth = $(o.nTable).outerWidth();
-                       
-                       /* We want the hidden header to have zero height, so remove padding and borders. Then
-                        * set the width based on the real headers
-                        */
-                       
-                       // Apply all styles in one pass. Invalidates layout only once because we don't read any 
-                       // DOM properties.
-                       _fnApplyToChildren( zeroOut, anHeadSizers );
-                        
-                       // Read all widths in next pass. Forces layout only once because we do not change 
-                       // any DOM properties.
-                       _fnApplyToChildren( function(nSizer) {
-                               aApplied.push( _fnStringToCss( $(nSizer).width() ) );
-                       }, anHeadSizers );
-                        
-                       // Apply all widths in final pass. Invalidates layout only once because we do not
-                       // read any DOM properties.
-                       _fnApplyToChildren( function(nToSize, i) {
-                               nToSize.style.width = aApplied[i];
-                       }, anHeadToSize );
-               
-                       $(anHeadSizers).height(0);
-                       
-                       /* Same again with the footer if we have one */
-                       if ( o.nTFoot !== null )
-                       {
-                               _fnApplyToChildren( zeroOut, anFootSizers );
-                                
-                               _fnApplyToChildren( function(nSizer) {
-                                       aAppliedFooter.push( _fnStringToCss( $(nSizer).width() ) );
-                               }, anFootSizers );
-                                
-                               _fnApplyToChildren( function(nToSize, i) {
-                                       nToSize.style.width = aAppliedFooter[i];
-                               }, anFootToSize );
-               
-                               $(anFootSizers).height(0);
-                       }
-                       
-                       /*
-                        * 3. Apply the measurements
-                        */
-                       
-                       /* "Hide" the header and footer that we used for the sizing. We want to also fix their width
-                        * to what they currently are
-                        */
-                       _fnApplyToChildren( function(nSizer, i) {
-                               nSizer.innerHTML = "";
-                               nSizer.style.width = aApplied[i];
-                       }, anHeadSizers );
-                       
-                       if ( o.nTFoot !== null )
-                       {
-                               _fnApplyToChildren( function(nSizer, i) {
-                                       nSizer.innerHTML = "";
-                                       nSizer.style.width = aAppliedFooter[i];
-                               }, anFootSizers );
-                       }
-                       
-                       /* Sanity check that the table is of a sensible width. If not then we are going to get
-                        * misalignment - try to prevent this by not allowing the table to shrink below its min width
-                        */
-                       if ( $(o.nTable).outerWidth() < iSanityWidth )
-                       {
-                               /* The min width depends upon if we have a vertical scrollbar visible or not */
-                               var iCorrection = ((nScrollBody.scrollHeight > nScrollBody.offsetHeight || 
-                                       $(nScrollBody).css('overflow-y') == "scroll")) ?
-                                               iSanityWidth+o.oScroll.iBarWidth : iSanityWidth;
-                               
-                               /* IE6/7 are a law unto themselves... */
-                               if ( ie67 && (nScrollBody.scrollHeight > 
-                                       nScrollBody.offsetHeight || $(nScrollBody).css('overflow-y') == "scroll")  )
-                               {
-                                       o.nTable.style.width = _fnStringToCss( iCorrection-o.oScroll.iBarWidth );
-                               }
-                               
-                               /* Apply the calculated minimum width to the table wrappers */
-                               nScrollBody.style.width = _fnStringToCss( iCorrection );
-                               o.nScrollHead.style.width = _fnStringToCss( iCorrection );
-                               
-                               if ( o.nTFoot !== null )
-                               {
-                                       o.nScrollFoot.style.width = _fnStringToCss( iCorrection );
-                               }
-                               
-                               /* And give the user a warning that we've stopped the table getting too small */
-                               if ( o.oScroll.sX === "" )
-                               {
-                                       _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
-                                               " misalignment. The table has been drawn at its minimum possible width." );
-                               }
-                               else if ( o.oScroll.sXInner !== "" )
-                               {
-                                       _fnLog( o, 1, "The table cannot fit into the current element which will cause column"+
-                                               " misalignment. Increase the sScrollXInner value or remove it to allow automatic"+
-                                               " calculation" );
-                               }
-                       }
-                       else
-                       {
-                               nScrollBody.style.width = _fnStringToCss( '100%' );
-                               o.nScrollHead.style.width = _fnStringToCss( '100%' );
-                               
-                               if ( o.nTFoot !== null )
-                               {
-                                       o.nScrollFoot.style.width = _fnStringToCss( '100%' );
-                               }
-                       }
-                       
-                       
-                       /*
-                        * 4. Clean up
-                        */
-                       if ( o.oScroll.sY === "" )
-                       {
-                               /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
-                                * the scrollbar height from the visible display, rather than adding it on. We need to
-                                * set the height in order to sort this. Don't want to do it in any other browsers.
-                                */
-                               if ( ie67 )
-                               {
-                                       nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+o.oScroll.iBarWidth );
-                               }
-                       }
-                       
-                       if ( o.oScroll.sY !== "" && o.oScroll.bCollapse )
-                       {
-                               nScrollBody.style.height = _fnStringToCss( o.oScroll.sY );
-                               
-                               var iExtra = (o.oScroll.sX !== "" && o.nTable.offsetWidth > nScrollBody.offsetWidth) ?
-                                       o.oScroll.iBarWidth : 0;
-                               if ( o.nTable.offsetHeight < nScrollBody.offsetHeight )
-                               {
-                                       nScrollBody.style.height = _fnStringToCss( o.nTable.offsetHeight+iExtra );
-                               }
-                       }
-                       
-                       /* Finally set the width's of the header and footer tables */
-                       var iOuterWidth = $(o.nTable).outerWidth();
-                       nScrollHeadTable.style.width = _fnStringToCss( iOuterWidth );
-                       nScrollHeadInner.style.width = _fnStringToCss( iOuterWidth );
-               
-                       // Figure out if there are scrollbar present - if so then we need a the header and footer to
-                       // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
-                       var bScrolling = $(o.nTable).height() > nScrollBody.clientHeight || $(nScrollBody).css('overflow-y') == "scroll";
-                       nScrollHeadInner.style.paddingRight = bScrolling ? o.oScroll.iBarWidth+"px" : "0px";
-                       
-                       if ( o.nTFoot !== null )
-                       {
-                               nScrollFootTable.style.width = _fnStringToCss( iOuterWidth );
-                               nScrollFootInner.style.width = _fnStringToCss( iOuterWidth );
-                               nScrollFootInner.style.paddingRight = bScrolling ? o.oScroll.iBarWidth+"px" : "0px";
-                       }
-               
-                       /* Adjust the position of the header in case we loose the y-scrollbar */
-                       $(nScrollBody).scroll();
-                       
-                       /* If sorting or filtering has occurred, jump the scrolling back to the top */
-                       if ( o.bSorted || o.bFiltered )
-                       {
-                               nScrollBody.scrollTop = 0;
-                       }
-               }
-               
-               
-               /**
-                * Apply a given function to the display child nodes of an element array (typically
-                * TD children of TR rows
-                *  @param {function} fn Method to apply to the objects
-                *  @param array {nodes} an1 List of elements to look through for display children
-                *  @param array {nodes} an2 Another list (identical structure to the first) - optional
-                *  @memberof DataTable#oApi
-                */
-               function _fnApplyToChildren( fn, an1, an2 )
-               {
-                       var index=0, i=0, iLen=an1.length;
-                       var nNode1, nNode2;
-               
-                       while ( i < iLen )
-                       {
-                               nNode1 = an1[i].firstChild;
-                               nNode2 = an2 ? an2[i].firstChild : null;
-                               while ( nNode1 )
-                               {
-                                       if ( nNode1.nodeType === 1 )
-                                       {
-                                               if ( an2 )
-                                               {
-                                                       fn( nNode1, nNode2, index );
-                                               }
-                                               else
-                                               {
-                                                       fn( nNode1, index );
-                                               }
-                                               index++;
-                                       }
-                                       nNode1 = nNode1.nextSibling;
-                                       nNode2 = an2 ? nNode2.nextSibling : null;
-                               }
-                               i++;
-                       }
-               }
-               
-               /**
-                * Convert a CSS unit width to pixels (e.g. 2em)
-                *  @param {string} sWidth width to be converted
-                *  @param {node} nParent parent to get the with for (required for relative widths) - optional
-                *  @returns {int} iWidth width in pixels
-                *  @memberof DataTable#oApi
-                */
-               function _fnConvertToWidth ( sWidth, nParent )
-               {
-                       if ( !sWidth || sWidth === null || sWidth === '' )
-                       {
-                               return 0;
-                       }
-                       
-                       if ( !nParent )
-                       {
-                               nParent = document.body;
-                       }
-                       
-                       var iWidth;
-                       var nTmp = document.createElement( "div" );
-                       nTmp.style.width = _fnStringToCss( sWidth );
-                       
-                       nParent.appendChild( nTmp );
-                       iWidth = nTmp.offsetWidth;
-                       nParent.removeChild( nTmp );
-                       
-                       return ( iWidth );
-               }
-               
-               
-               /**
-                * Calculate the width of columns for the table
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnCalculateColumnWidths ( oSettings )
-               {
-                       var iTableWidth = oSettings.nTable.offsetWidth;
-                       var iUserInputs = 0;
-                       var iTmpWidth;
-                       var iVisibleColumns = 0;
-                       var iColums = oSettings.aoColumns.length;
-                       var i, iIndex, iCorrector, iWidth;
-                       var oHeaders = $('th', oSettings.nTHead);
-                       var widthAttr = oSettings.nTable.getAttribute('width');
-                       var nWrapper = oSettings.nTable.parentNode;
-                       
-                       /* Convert any user input sizes into pixel sizes */
-                       for ( i=0 ; i<iColums ; i++ )
-                       {
-                               if ( oSettings.aoColumns[i].bVisible )
-                               {
-                                       iVisibleColumns++;
-                                       
-                                       if ( oSettings.aoColumns[i].sWidth !== null )
-                                       {
-                                               iTmpWidth = _fnConvertToWidth( oSettings.aoColumns[i].sWidthOrig, 
-                                                       nWrapper );
-                                               if ( iTmpWidth !== null )
-                                               {
-                                                       oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
-                                               }
-                                                       
-                                               iUserInputs++;
-                                       }
-                               }
-                       }
-                       
-                       /* If the number of columns in the DOM equals the number that we have to process in 
-                        * DataTables, then we can use the offsets that are created by the web-browser. No custom 
-                        * sizes can be set in order for this to happen, nor scrolling used
-                        */
-                       if ( iColums == oHeaders.length && iUserInputs === 0 && iVisibleColumns == iColums &&
-                               oSettings.oScroll.sX === "" && oSettings.oScroll.sY === "" )
-                       {
-                               for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
-                               {
-                                       iTmpWidth = $(oHeaders[i]).width();
-                                       if ( iTmpWidth !== null )
-                                       {
-                                               oSettings.aoColumns[i].sWidth = _fnStringToCss( iTmpWidth );
-                                       }
-                               }
-                       }
-                       else
-                       {
-                               /* Otherwise we are going to have to do some calculations to get the width of each column.
-                                * Construct a 1 row table with the widest node in the data, and any user defined widths,
-                                * then insert it into the DOM and allow the browser to do all the hard work of
-                                * calculating table widths.
-                                */
-                               var
-                                       nCalcTmp = oSettings.nTable.cloneNode( false ),
-                                       nTheadClone = oSettings.nTHead.cloneNode(true),
-                                       nBody = document.createElement( 'tbody' ),
-                                       nTr = document.createElement( 'tr' ),
-                                       nDivSizing;
-                               
-                               nCalcTmp.removeAttribute( "id" );
-                               nCalcTmp.appendChild( nTheadClone );
-                               if ( oSettings.nTFoot !== null )
-                               {
-                                       nCalcTmp.appendChild( oSettings.nTFoot.cloneNode(true) );
-                                       _fnApplyToChildren( function(n) {
-                                               n.style.width = "";
-                                       }, nCalcTmp.getElementsByTagName('tr') );
-                               }
-                               
-                               nCalcTmp.appendChild( nBody );
-                               nBody.appendChild( nTr );
-                               
-                               /* Remove any sizing that was previously applied by the styles */
-                               var jqColSizing = $('thead th', nCalcTmp);
-                               if ( jqColSizing.length === 0 )
-                               {
-                                       jqColSizing = $('tbody tr:eq(0)>td', nCalcTmp);
-                               }
-               
-                               /* Apply custom sizing to the cloned header */
-                               var nThs = _fnGetUniqueThs( oSettings, nTheadClone );
-                               iCorrector = 0;
-                               for ( i=0 ; i<iColums ; i++ )
-                               {
-                                       var oColumn = oSettings.aoColumns[i];
-                                       if ( oColumn.bVisible && oColumn.sWidthOrig !== null && oColumn.sWidthOrig !== "" )
-                                       {
-                                               nThs[i-iCorrector].style.width = _fnStringToCss( oColumn.sWidthOrig );
-                                       }
-                                       else if ( oColumn.bVisible )
-                                       {
-                                               nThs[i-iCorrector].style.width = "";
-                                       }
-                                       else
-                                       {
-                                               iCorrector++;
-                                       }
-                               }
-               
-                               /* Find the biggest td for each column and put it into the table */
-                               for ( i=0 ; i<iColums ; i++ )
-                               {
-                                       if ( oSettings.aoColumns[i].bVisible )
-                                       {
-                                               var nTd = _fnGetWidestNode( oSettings, i );
-                                               if ( nTd !== null )
-                                               {
-                                                       nTd = nTd.cloneNode(true);
-                                                       if ( oSettings.aoColumns[i].sContentPadding !== "" )
-                                                       {
-                                                               nTd.innerHTML += oSettings.aoColumns[i].sContentPadding;
-                                                       }
-                                                       nTr.appendChild( nTd );
-                                               }
-                                       }
-                               }
-                               
-                               /* Build the table and 'display' it */
-                               nWrapper.appendChild( nCalcTmp );
-                               
-                               /* When scrolling (X or Y) we want to set the width of the table as appropriate. However,
-                                * when not scrolling leave the table width as it is. This results in slightly different,
-                                * but I think correct behaviour
-                                */
-                               if ( oSettings.oScroll.sX !== "" && oSettings.oScroll.sXInner !== "" )
-                               {
-                                       nCalcTmp.style.width = _fnStringToCss(oSettings.oScroll.sXInner);
-                               }
-                               else if ( oSettings.oScroll.sX !== "" )
-                               {
-                                       nCalcTmp.style.width = "";
-                                       if ( $(nCalcTmp).width() < nWrapper.offsetWidth )
-                                       {
-                                               nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
-                                       }
-                               }
-                               else if ( oSettings.oScroll.sY !== "" )
-                               {
-                                       nCalcTmp.style.width = _fnStringToCss( nWrapper.offsetWidth );
-                               }
-                               else if ( widthAttr )
-                               {
-                                       nCalcTmp.style.width = _fnStringToCss( widthAttr );
-                               }
-                               nCalcTmp.style.visibility = "hidden";
-                               
-                               /* Scrolling considerations */
-                               _fnScrollingWidthAdjust( oSettings, nCalcTmp );
-                               
-                               /* Read the width's calculated by the browser and store them for use by the caller. We
-                                * first of all try to use the elements in the body, but it is possible that there are
-                                * no elements there, under which circumstances we use the header elements
-                                */
-                               var oNodes = $("tbody tr:eq(0)", nCalcTmp).children();
-                               if ( oNodes.length === 0 )
-                               {
-                                       oNodes = _fnGetUniqueThs( oSettings, $('thead', nCalcTmp)[0] );
-                               }
-               
-                               /* Browsers need a bit of a hand when a width is assigned to any columns when 
-                                * x-scrolling as they tend to collapse the table to the min-width, even if
-                                * we sent the column widths. So we need to keep track of what the table width
-                                * should be by summing the user given values, and the automatic values
-                                */
-                               if ( oSettings.oScroll.sX !== "" )
-                               {
-                                       var iTotal = 0;
-                                       iCorrector = 0;
-                                       for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
-                                       {
-                                               if ( oSettings.aoColumns[i].bVisible )
-                                               {
-                                                       if ( oSettings.aoColumns[i].sWidthOrig === null )
-                                                       {
-                                                               iTotal += $(oNodes[iCorrector]).outerWidth();
-                                                       }
-                                                       else
-                                                       {
-                                                               iTotal += parseInt(oSettings.aoColumns[i].sWidth.replace('px',''), 10) +
-                                                                       ($(oNodes[iCorrector]).outerWidth() - $(oNodes[iCorrector]).width());
-                                                       }
-                                                       iCorrector++;
-                                               }
-                                       }
-                                       
-                                       nCalcTmp.style.width = _fnStringToCss( iTotal );
-                                       oSettings.nTable.style.width = _fnStringToCss( iTotal );
-                               }
-               
-                               iCorrector = 0;
-                               for ( i=0 ; i<oSettings.aoColumns.length ; i++ )
-                               {
-                                       if ( oSettings.aoColumns[i].bVisible )
-                                       {
-                                               iWidth = $(oNodes[iCorrector]).width();
-                                               if ( iWidth !== null && iWidth > 0 )
-                                               {
-                                                       oSettings.aoColumns[i].sWidth = _fnStringToCss( iWidth );
-                                               }
-                                               iCorrector++;
-                                       }
-                               }
-               
-                               var cssWidth = $(nCalcTmp).css('width');
-                               oSettings.nTable.style.width = (cssWidth.indexOf('%') !== -1) ?
-                                   cssWidth : _fnStringToCss( $(nCalcTmp).outerWidth() );
-                               nCalcTmp.parentNode.removeChild( nCalcTmp );
-                       }
-               
-                       if ( widthAttr )
-                       {
-                               oSettings.nTable.style.width = _fnStringToCss( widthAttr );
-                       }
-               }
-               
-               
-               /**
-                * Adjust a table's width to take account of scrolling
-                *  @param {object} oSettings dataTables settings object
-                *  @param {node} n table node
-                *  @memberof DataTable#oApi
-                */
-               function _fnScrollingWidthAdjust ( oSettings, n )
-               {
-                       if ( oSettings.oScroll.sX === "" && oSettings.oScroll.sY !== "" )
-                       {
-                               /* When y-scrolling only, we want to remove the width of the scroll bar so the table
-                                * + scroll bar will fit into the area avaialble.
-                                */
-                               var iOrigWidth = $(n).width();
-                               n.style.width = _fnStringToCss( $(n).outerWidth()-oSettings.oScroll.iBarWidth );
-                       }
-                       else if ( oSettings.oScroll.sX !== "" )
-                       {
-                               /* When x-scrolling both ways, fix the table at it's current size, without adjusting */
-                               n.style.width = _fnStringToCss( $(n).outerWidth() );
-                       }
-               }
-               
-               
-               /**
-                * Get the widest node
-                *  @param {object} oSettings dataTables settings object
-                *  @param {int} iCol column of interest
-                *  @returns {node} widest table node
-                *  @memberof DataTable#oApi
-                */
-               function _fnGetWidestNode( oSettings, iCol )
-               {
-                       var iMaxIndex = _fnGetMaxLenString( oSettings, iCol );
-                       if ( iMaxIndex < 0 )
-                       {
-                               return null;
-                       }
-               
-                       if ( oSettings.aoData[iMaxIndex].nTr === null )
-                       {
-                               var n = document.createElement('td');
-                               n.innerHTML = _fnGetCellData( oSettings, iMaxIndex, iCol, '' );
-                               return n;
-                       }
-                       return _fnGetTdNodes(oSettings, iMaxIndex)[iCol];
-               }
-               
-               
-               /**
-                * Get the maximum strlen for each data column
-                *  @param {object} oSettings dataTables settings object
-                *  @param {int} iCol column of interest
-                *  @returns {string} max string length for each column
-                *  @memberof DataTable#oApi
-                */
-               function _fnGetMaxLenString( oSettings, iCol )
-               {
-                       var iMax = -1;
-                       var iMaxIndex = -1;
-                       
-                       for ( var i=0 ; i<oSettings.aoData.length ; i++ )
-                       {
-                               var s = _fnGetCellData( oSettings, i, iCol, 'display' )+"";
-                               s = s.replace( /<.*?>/g, "" );
-                               if ( s.length > iMax )
-                               {
-                                       iMax = s.length;
-                                       iMaxIndex = i;
-                               }
-                       }
-                       
-                       return iMaxIndex;
-               }
-               
-               
-               /**
-                * Append a CSS unit (only if required) to a string
-                *  @param {array} aArray1 first array
-                *  @param {array} aArray2 second array
-                *  @returns {int} 0 if match, 1 if length is different, 2 if no match
-                *  @memberof DataTable#oApi
-                */
-               function _fnStringToCss( s )
-               {
-                       if ( s === null )
-                       {
-                               return "0px";
-                       }
-                       
-                       if ( typeof s == 'number' )
-                       {
-                               if ( s < 0 )
-                               {
-                                       return "0px";
-                               }
-                               return s+"px";
-                       }
-                       
-                       /* Check if the last character is not 0-9 */
-                       var c = s.charCodeAt( s.length-1 );
-                       if (c < 0x30 || c > 0x39)
-                       {
-                               return s;
-                       }
-                       return s+"px";
-               }
-               
-               
-               /**
-                * Get the width of a scroll bar in this browser being used
-                *  @returns {int} width in pixels
-                *  @memberof DataTable#oApi
-                */
-               function _fnScrollBarWidth ()
-               {  
-                       var inner = document.createElement('p');
-                       var style = inner.style;
-                       style.width = "100%";
-                       style.height = "200px";
-                       style.padding = "0px";
-                       
-                       var outer = document.createElement('div');
-                       style = outer.style;
-                       style.position = "absolute";
-                       style.top = "0px";
-                       style.left = "0px";
-                       style.visibility = "hidden";
-                       style.width = "200px";
-                       style.height = "150px";
-                       style.padding = "0px";
-                       style.overflow = "hidden";
-                       outer.appendChild(inner);
-                       
-                       document.body.appendChild(outer);
-                       var w1 = inner.offsetWidth;
-                       outer.style.overflow = 'scroll';
-                       var w2 = inner.offsetWidth;
-                       if ( w1 == w2 )
-                       {
-                               w2 = outer.clientWidth;
-                       }
-                       
-                       document.body.removeChild(outer);
-                       return (w1 - w2);  
-               }
-               
-               /**
-                * Change the order of the table
-                *  @param {object} oSettings dataTables settings object
-                *  @param {bool} bApplyClasses optional - should we apply classes or not
-                *  @memberof DataTable#oApi
-                */
-               function _fnSort ( oSettings, bApplyClasses )
-               {
-                       var
-                               i, iLen, j, jLen, k, kLen,
-                               sDataType, nTh,
-                               aaSort = [],
-                               aiOrig = [],
-                               oSort = DataTable.ext.oSort,
-                               aoData = oSettings.aoData,
-                               aoColumns = oSettings.aoColumns,
-                               oAria = oSettings.oLanguage.oAria;
-                       
-                       /* No sorting required if server-side or no sorting array */
-                       if ( !oSettings.oFeatures.bServerSide && 
-                               (oSettings.aaSorting.length !== 0 || oSettings.aaSortingFixed !== null) )
-                       {
-                               aaSort = ( oSettings.aaSortingFixed !== null ) ?
-                                       oSettings.aaSortingFixed.concat( oSettings.aaSorting ) :
-                                       oSettings.aaSorting.slice();
-                               
-                               /* If there is a sorting data type, and a function belonging to it, then we need to
-                                * get the data from the developer's function and apply it for this column
-                                */
-                               for ( i=0 ; i<aaSort.length ; i++ )
-                               {
-                                       var iColumn = aaSort[i][0];
-                                       var iVisColumn = _fnColumnIndexToVisible( oSettings, iColumn );
-                                       sDataType = oSettings.aoColumns[ iColumn ].sSortDataType;
-                                       if ( DataTable.ext.afnSortData[sDataType] )
-                                       {
-                                               var aData = DataTable.ext.afnSortData[sDataType].call( 
-                                                       oSettings.oInstance, oSettings, iColumn, iVisColumn
-                                               );
-                                               if ( aData.length === aoData.length )
-                                               {
-                                                       for ( j=0, jLen=aoData.length ; j<jLen ; j++ )
-                                                       {
-                                                               _fnSetCellData( oSettings, j, iColumn, aData[j] );
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       _fnLog( oSettings, 0, "Returned data sort array (col "+iColumn+") is the wrong length" );
-                                               }
-                                       }
-                               }
-                               
-                               /* Create a value - key array of the current row positions such that we can use their
-                                * current position during the sort, if values match, in order to perform stable sorting
-                                */
-                               for ( i=0, iLen=oSettings.aiDisplayMaster.length ; i<iLen ; i++ )
-                               {
-                                       aiOrig[ oSettings.aiDisplayMaster[i] ] = i;
-                               }
-               
-                               /* Build an internal data array which is specific to the sort, so we can get and prep
-                                * the data to be sorted only once, rather than needing to do it every time the sorting
-                                * function runs. This make the sorting function a very simple comparison
-                                */
-                               var iSortLen = aaSort.length;
-                               var fnSortFormat, aDataSort;
-                               for ( i=0, iLen=aoData.length ; i<iLen ; i++ )
-                               {
-                                       for ( j=0 ; j<iSortLen ; j++ )
-                                       {
-                                               aDataSort = aoColumns[ aaSort[j][0] ].aDataSort;
-               
-                                               for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
-                                               {
-                                                       sDataType = aoColumns[ aDataSort[k] ].sType;
-                                                       fnSortFormat = oSort[ (sDataType ? sDataType : 'string')+"-pre" ];
-                                                       
-                                                       aoData[i]._aSortData[ aDataSort[k] ] = fnSortFormat ?
-                                                               fnSortFormat( _fnGetCellData( oSettings, i, aDataSort[k], 'sort' ) ) :
-                                                               _fnGetCellData( oSettings, i, aDataSort[k], 'sort' );
-                                               }
-                                       }
-                               }
-                               
-                               /* Do the sort - here we want multi-column sorting based on a given data source (column)
-                                * and sorting function (from oSort) in a certain direction. It's reasonably complex to
-                                * follow on it's own, but this is what we want (example two column sorting):
-                                *  fnLocalSorting = function(a,b){
-                                *      var iTest;
-                                *      iTest = oSort['string-asc']('data11', 'data12');
-                                *      if (iTest !== 0)
-                                *              return iTest;
-                                *    iTest = oSort['numeric-desc']('data21', 'data22');
-                                *    if (iTest !== 0)
-                                *              return iTest;
-                                *      return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
-                                *  }
-                                * Basically we have a test for each sorting column, if the data in that column is equal,
-                                * test the next column. If all columns match, then we use a numeric sort on the row 
-                                * positions in the original data array to provide a stable sort.
-                                */
-                               oSettings.aiDisplayMaster.sort( function ( a, b ) {
-                                       var k, l, lLen, iTest, aDataSort, sDataType;
-                                       for ( k=0 ; k<iSortLen ; k++ )
-                                       {
-                                               aDataSort = aoColumns[ aaSort[k][0] ].aDataSort;
-               
-                                               for ( l=0, lLen=aDataSort.length ; l<lLen ; l++ )
-                                               {
-                                                       sDataType = aoColumns[ aDataSort[l] ].sType;
-                                                       
-                                                       iTest = oSort[ (sDataType ? sDataType : 'string')+"-"+aaSort[k][1] ](
-                                                               aoData[a]._aSortData[ aDataSort[l] ],
-                                                               aoData[b]._aSortData[ aDataSort[l] ]
-                                                       );
-                                               
-                                                       if ( iTest !== 0 )
-                                                       {
-                                                               return iTest;
-                                                       }
-                                               }
-                                       }
-                                       
-                                       return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
-                               } );
-                       }
-                       
-                       /* Alter the sorting classes to take account of the changes */
-                       if ( (bApplyClasses === undefined || bApplyClasses) && !oSettings.oFeatures.bDeferRender )
-                       {
-                               _fnSortingClasses( oSettings );
-                       }
-               
-                       for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
-                       {
-                               var sTitle = aoColumns[i].sTitle.replace( /<.*?>/g, "" );
-                               nTh = aoColumns[i].nTh;
-                               nTh.removeAttribute('aria-sort');
-                               nTh.removeAttribute('aria-label');
-                               
-                               /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
-                               if ( aoColumns[i].bSortable )
-                               {
-                                       if ( aaSort.length > 0 && aaSort[0][0] == i )
-                                       {
-                                               nTh.setAttribute('aria-sort', aaSort[0][1]=="asc" ? "ascending" : "descending" );
-                                               
-                                               var nextSort = (aoColumns[i].asSorting[ aaSort[0][2]+1 ]) ? 
-                                                       aoColumns[i].asSorting[ aaSort[0][2]+1 ] : aoColumns[i].asSorting[0];
-                                               nTh.setAttribute('aria-label', sTitle+
-                                                       (nextSort=="asc" ? oAria.sSortAscending : oAria.sSortDescending) );
-                                       }
-                                       else
-                                       {
-                                               nTh.setAttribute('aria-label', sTitle+
-                                                       (aoColumns[i].asSorting[0]=="asc" ? oAria.sSortAscending : oAria.sSortDescending) );
-                                       }
-                               }
-                               else
-                               {
-                                       nTh.setAttribute('aria-label', sTitle);
-                               }
-                       }
-                       
-                       /* Tell the draw function that we have sorted the data */
-                       oSettings.bSorted = true;
-                       $(oSettings.oInstance).trigger('sort', oSettings);
-                       
-                       /* Copy the master data into the draw array and re-draw */
-                       if ( oSettings.oFeatures.bFilter )
-                       {
-                               /* _fnFilter() will redraw the table for us */
-                               _fnFilterComplete( oSettings, oSettings.oPreviousSearch, 1 );
-                       }
-                       else
-                       {
-                               oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
-                               oSettings._iDisplayStart = 0; /* reset display back to page 0 */
-                               _fnCalculateEnd( oSettings );
-                               _fnDraw( oSettings );
-                       }
-               }
-               
-               
-               /**
-                * Attach a sort handler (click) to a node
-                *  @param {object} oSettings dataTables settings object
-                *  @param {node} nNode node to attach the handler to
-                *  @param {int} iDataIndex column sorting index
-                *  @param {function} [fnCallback] callback function
-                *  @memberof DataTable#oApi
-                */
-               function _fnSortAttachListener ( oSettings, nNode, iDataIndex, fnCallback )
-               {
-                       _fnBindAction( nNode, {}, function (e) {
-                               /* If the column is not sortable - don't to anything */
-                               if ( oSettings.aoColumns[iDataIndex].bSortable === false )
-                               {
-                                       return;
-                               }
-                               
-                               /*
-                                * This is a little bit odd I admit... I declare a temporary function inside the scope of
-                                * _fnBuildHead and the click handler in order that the code presented here can be used 
-                                * twice - once for when bProcessing is enabled, and another time for when it is 
-                                * disabled, as we need to perform slightly different actions.
-                                *   Basically the issue here is that the Javascript engine in modern browsers don't 
-                                * appear to allow the rendering engine to update the display while it is still executing
-                                * it's thread (well - it does but only after long intervals). This means that the 
-                                * 'processing' display doesn't appear for a table sort. To break the js thread up a bit
-                                * I force an execution break by using setTimeout - but this breaks the expected 
-                                * thread continuation for the end-developer's point of view (their code would execute
-                                * too early), so we only do it when we absolutely have to.
-                                */
-                               var fnInnerSorting = function () {
-                                       var iColumn, iNextSort;
-                                       
-                                       /* If the shift key is pressed then we are multiple column sorting */
-                                       if ( e.shiftKey )
-                                       {
-                                               /* Are we already doing some kind of sort on this column? */
-                                               var bFound = false;
-                                               for ( var i=0 ; i<oSettings.aaSorting.length ; i++ )
-                                               {
-                                                       if ( oSettings.aaSorting[i][0] == iDataIndex )
-                                                       {
-                                                               bFound = true;
-                                                               iColumn = oSettings.aaSorting[i][0];
-                                                               iNextSort = oSettings.aaSorting[i][2]+1;
-                                                               
-                                                               if ( !oSettings.aoColumns[iColumn].asSorting[iNextSort] )
-                                                               {
-                                                                       /* Reached the end of the sorting options, remove from multi-col sort */
-                                                                       oSettings.aaSorting.splice( i, 1 );
-                                                               }
-                                                               else
-                                                               {
-                                                                       /* Move onto next sorting direction */
-                                                                       oSettings.aaSorting[i][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
-                                                                       oSettings.aaSorting[i][2] = iNextSort;
-                                                               }
-                                                               break;
-                                                       }
-                                               }
-                                               
-                                               /* No sort yet - add it in */
-                                               if ( bFound === false )
-                                               {
-                                                       oSettings.aaSorting.push( [ iDataIndex, 
-                                                               oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
-                                               }
-                                       }
-                                       else
-                                       {
-                                               /* If no shift key then single column sort */
-                                               if ( oSettings.aaSorting.length == 1 && oSettings.aaSorting[0][0] == iDataIndex )
-                                               {
-                                                       iColumn = oSettings.aaSorting[0][0];
-                                                       iNextSort = oSettings.aaSorting[0][2]+1;
-                                                       if ( !oSettings.aoColumns[iColumn].asSorting[iNextSort] )
-                                                       {
-                                                               iNextSort = 0;
-                                                       }
-                                                       oSettings.aaSorting[0][1] = oSettings.aoColumns[iColumn].asSorting[iNextSort];
-                                                       oSettings.aaSorting[0][2] = iNextSort;
-                                               }
-                                               else
-                                               {
-                                                       oSettings.aaSorting.splice( 0, oSettings.aaSorting.length );
-                                                       oSettings.aaSorting.push( [ iDataIndex, 
-                                                               oSettings.aoColumns[iDataIndex].asSorting[0], 0 ] );
-                                               }
-                                       }
-                                       
-                                       /* Run the sort */
-                                       _fnSort( oSettings );
-                               }; /* /fnInnerSorting */
-                               
-                               if ( !oSettings.oFeatures.bProcessing )
-                               {
-                                       fnInnerSorting();
-                               }
-                               else
-                               {
-                                       _fnProcessingDisplay( oSettings, true );
-                                       setTimeout( function() {
-                                               fnInnerSorting();
-                                               if ( !oSettings.oFeatures.bServerSide )
-                                               {
-                                                       _fnProcessingDisplay( oSettings, false );
-                                               }
-                                       }, 0 );
-                               }
-                               
-                               /* Call the user specified callback function - used for async user interaction */
-                               if ( typeof fnCallback == 'function' )
-                               {
-                                       fnCallback( oSettings );
-                               }
-                       } );
-               }
-               
-               
-               /**
-                * Set the sorting classes on the header, Note: it is safe to call this function 
-                * when bSort and bSortClasses are false
-                *  @param {object} oSettings dataTables settings object
-                *  @memberof DataTable#oApi
-                */
-               function _fnSortingClasses( oSettings )
-               {
-                       var i, iLen, j, jLen, iFound;
-                       var aaSort, sClass;
-                       var iColumns = oSettings.aoColumns.length;
-                       var oClasses = oSettings.oClasses;
-                       
-                       for ( i=0 ; i<iColumns ; i++ )
-                       {
-                               if ( oSettings.aoColumns[i].bSortable )
-                               {
-                                       $(oSettings.aoColumns[i].nTh).removeClass( oClasses.sSortAsc +" "+ oClasses.sSortDesc +
-                                               " "+ oSettings.aoColumns[i].sSortingClass );
-                               }
-                       }
-                       
-                       if ( oSettings.aaSortingFixed !== null )
-                       {
-                               aaSort = oSettings.aaSortingFixed.concat( oSettings.aaSorting );
-                       }
-                       else
-                       {
-                               aaSort = oSe