brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [11/51] [abbrv] [partial] brooklyn-ui git commit: move subdir from incubator up a level as it is promoted to its own repo (first non-incubator commit!)
Date Mon, 01 Feb 2016 17:52:29 GMT
http://git-wip-us.apache.org/repos/asf/brooklyn-ui/blob/18b073a9/src/main/webapp/assets/js/libs/jquery.dataTables.js
----------------------------------------------------------------------
diff --git a/src/main/webapp/assets/js/libs/jquery.dataTables.js b/src/main/webapp/assets/js/libs/jquery.dataTables.js
new file mode 100644
index 0000000..6b4d452
--- /dev/null
+++ b/src/main/webapp/assets/js/libs/jquery.dataTables.js
@@ -0,0 +1,12098 @@
+/**
+ * @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.

<TRUNCATED>

Mime
View raw message