text: /**
   * @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"class="form-control" />')
  								: sSearchStr === "" ? '<input type="text" class="form-control"/>'
  										: sSearchStr
  												+ ' <input type="text" class="form-control"/>';
  
  						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 === "Aucune donnée disponible dans la table") {
  							_fnMap(oLanguage, oLanguage, 'sZeroRecords',
  									'sEmptyTable');
  						}
  
  						/* Likewise with loading records */
  						if (!oLanguage.sLoadingRecords
  								&& oLanguage.sZeroRecords
  								&& oDefaults.sLoadingRecords === "Chargement...") {
  							_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 class="form-control" 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 = oSettings.aaSorting.slice();
  						}
  
  						/* Apply the required classes to the header */
  						for (i = 0; i < oSettings.aoColumns.length; i++) {
  							if (oSettings.aoColumns[i].bSortable) {
  								sClass = oSettings.aoColumns[i].sSortingClass;
  								iFound = -1;
  								for (j = 0; j < aaSort.length; j++) {
  									if (aaSort[j][0] == i) {
  										sClass = (aaSort[j][1] == "asc") ? oClasses.sSortAsc
  												: oClasses.sSortDesc;
  										iFound = j;
  										break;
  									}
  								}
  								$(oSettings.aoColumns[i].nTh).addClass(sClass);
  
  								if (oSettings.bJUI) {
  									/* jQuery UI uses extra markup */
  									var jqSpan = $(
  											"span." + oClasses.sSortIcon,
  											oSettings.aoColumns[i].nTh);
  									jqSpan.removeClass(oClasses.sSortJUIAsc
  											+ " " + oClasses.sSortJUIDesc + " "
  											+ oClasses.sSortJUI + " "
  											+ oClasses.sSortJUIAscAllowed + " "
  											+ oClasses.sSortJUIDescAllowed);
  
  									var sSpanClass;
  									if (iFound == -1) {
  										sSpanClass = oSettings.aoColumns[i].sSortingClassJUI;
  									} else if (aaSort[iFound][1] == "asc") {
  										sSpanClass = oClasses.sSortJUIAsc;
  									} else {
  										sSpanClass = oClasses.sSortJUIDesc;
  									}
  
  									jqSpan.addClass(sSpanClass);
  								}
  							} else {
  								/*
  								 * No sorting on this column, so add the base
  								 * class. This will have been assigned by
  								 * _fnAddColumn
  								 */
  								$(oSettings.aoColumns[i].nTh).addClass(
  										oSettings.aoColumns[i].sSortingClass);
  							}
  						}
  
  						/*
  						 * Apply the required classes to the table body Note
  						 * that this is given as a feature switch since it can
  						 * significantly slow down a sort on large data sets
  						 * (adding and removing of classes is always slow at the
  						 * best of times..) Further to this, note that this code
  						 * is admittedly fairly ugly. It could be made a lot
  						 * simpler using jQuery selectors and add/removeClass,
  						 * but that is significantly slower (on the order of 5
  						 * times slower) - hence the direct DOM manipulation
  						 * here. Note that for deferred drawing we do use jQuery -
  						 * the reason being that taking the first row found to
  						 * see if the whole column needs processed can miss
  						 * classes since the first column might be new.
  						 */
  						sClass = oClasses.sSortColumn;
  
  						if (oSettings.oFeatures.bSort
  								&& oSettings.oFeatures.bSortClasses) {
  							var nTds = _fnGetTdNodes(oSettings);
  
  							/*
  							 * Determine what the sorting class for each column
  							 * should be
  							 */
  							var iClass, iTargetCol;
  							var asClasses = [];
  							for (i = 0; i < iColumns; i++) {
  								asClasses.push("");
  							}
  							for (i = 0, iClass = 1; i < aaSort.length; i++) {
  								iTargetCol = parseInt(aaSort[i][0], 10);
  								asClasses[iTargetCol] = sClass + iClass;
  
  								if (iClass < 3) {
  									iClass++;
  								}
  							}
  
  							/*
  							 * Make changes to the classes for each cell as
  							 * needed
  							 */
  							var reClass = new RegExp(sClass + "[123]");
  							var sTmpClass, sCurrentClass, sNewClass;
  							for (i = 0, iLen = nTds.length; i < iLen; i++) {
  								/* Determine which column we're looking at */
  								iTargetCol = i % iColumns;
  
  								/* What is the full list of classes now */
  								sCurrentClass = nTds[i].className;
  								/* What sorting class should be applied? */
  								sNewClass = asClasses[iTargetCol];
  								/*
  								 * What would the new full list be if we did a
  								 * replacement?
  								 */
  								sTmpClass = sCurrentClass.replace(reClass,
  										sNewClass);
  
  								if (sTmpClass != sCurrentClass) {
  									/* We changed something */
  									nTds[i].className = $.trim(sTmpClass);
  								} else if (sNewClass.length > 0
  										&& sCurrentClass.indexOf(sNewClass) == -1) {
  									/* We need to add a class */
  									nTds[i].className = sCurrentClass + " "
  											+ sNewClass;
  								}
  							}
  						}
  					}
  
  					/**
  					 * Save the state of a table in a cookie such that the page
  					 * can be reloaded
  					 * 
  					 * @param {object}
  					 *            oSettings dataTables settings object
  					 * @memberof DataTable#oApi
  					 */
  					function _fnSaveState(oSettings) {
  						if (!oSettings.oFeatures.bStateSave
  								|| oSettings.bDestroying) {
  							return;
  						}
  
  						/* Store the interesting variables */
  						var i, iLen, bInfinite = oSettings.oScroll.bInfinite;
  						var oState = {
  							"iCreate" : new Date().getTime(),
  							"iStart" : (bInfinite ? 0
  									: oSettings._iDisplayStart),
  							"iEnd" : (bInfinite ? oSettings._iDisplayLength
  									: oSettings._iDisplayEnd),
  							"iLength" : oSettings._iDisplayLength,
  							"aaSorting" : $.extend(true, [],
  									oSettings.aaSorting),
  							"oSearch" : $.extend(true, {},
  									oSettings.oPreviousSearch),
  							"aoSearchCols" : $.extend(true, [],
  									oSettings.aoPreSearchCols),
  							"abVisCols" : []
  						};
  
  						for (i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
  							oState.abVisCols
  									.push(oSettings.aoColumns[i].bVisible);
  						}
  
  						_fnCallbackFire(oSettings, "aoStateSaveParams",
  								'stateSaveParams', [ oSettings, oState ]);
  
  						oSettings.fnStateSave.call(oSettings.oInstance,
  								oSettings, oState);
  					}
  
  					/**
  					 * Attempt to load a saved table state from a cookie
  					 * 
  					 * @param {object}
  					 *            oSettings dataTables settings object
  					 * @param {object}
  					 *            oInit DataTables init object so we can
  					 *            override settings
  					 * @memberof DataTable#oApi
  					 */
  					function _fnLoadState(oSettings, oInit) {
  						if (!oSettings.oFeatures.bStateSave) {
  							return;
  						}
  
  						var oData = oSettings.fnStateLoad.call(
  								oSettings.oInstance, oSettings);
  						if (!oData) {
  							return;
  						}
  
  						/*
  						 * Allow custom and plug-in manipulation functions to
  						 * alter the saved data set and cancelling of loading by
  						 * returning false
  						 */
  						var abStateLoad = _fnCallbackFire(oSettings,
  								'aoStateLoadParams', 'stateLoadParams', [
  										oSettings, oData ]);
  						if ($.inArray(false, abStateLoad) !== -1) {
  							return;
  						}
  
  						/*
  						 * Store the saved state so it might be accessed at any
  						 * time
  						 */
  						oSettings.oLoadedState = $.extend(true, {}, oData);
  
  						/* Restore key features */
  						oSettings._iDisplayStart = oData.iStart;
  						oSettings.iInitDisplayStart = oData.iStart;
  						oSettings._iDisplayEnd = oData.iEnd;
  						oSettings._iDisplayLength = oData.iLength;
  						oSettings.aaSorting = oData.aaSorting.slice();
  						oSettings.saved_aaSorting = oData.aaSorting.slice();
  
  						/* Search filtering */
  						$.extend(oSettings.oPreviousSearch, oData.oSearch);
  						$.extend(true, oSettings.aoPreSearchCols,
  								oData.aoSearchCols);
  
  						/*
  						 * Column visibility state Pass back visibility settings
  						 * to the init handler, but to do not here override the
  						 * init object that the user might have passed in
  						 */
  						oInit.saved_aoColumns = [];
  						for (var i = 0; i < oData.abVisCols.length; i++) {
  							oInit.saved_aoColumns[i] = {};
  							oInit.saved_aoColumns[i].bVisible = oData.abVisCols[i];
  						}
  
  						_fnCallbackFire(oSettings, 'aoStateLoaded',
  								'stateLoaded', [ oSettings, oData ]);
  					}
  
  					/**
  					 * Create a new cookie with a value to store the state of a
  					 * table
  					 * 
  					 * @param {string}
  					 *            sName name of the cookie to create
  					 * @param {string}
  					 *            sValue the value the cookie should take
  					 * @param {int}
  					 *            iSecs duration of the cookie
  					 * @param {string}
  					 *            sBaseName sName is made up of the base + file
  					 *            name - this is the base
  					 * @param {function}
  					 *            fnCallback User definable function to modify
  					 *            the cookie
  					 * @memberof DataTable#oApi
  					 */
  					function _fnCreateCookie(sName, sValue, iSecs, sBaseName,
  							fnCallback) {
  						var date = new Date();
  						date.setTime(date.getTime() + (iSecs * 1000));
  
  						/*
  						 * Shocking but true - it would appear IE has major
  						 * issues with having the path not having a trailing
  						 * slash on it. We need the cookie to be available based
  						 * on the path, so we have to append the file name to
  						 * the cookie name. Appalling. Thanks to vex for adding
  						 * the patch to use at least some of the path
  						 */
  						var aParts = window.location.pathname.split('/');
  						var sNameFile = sName
  								+ '_'
  								+ aParts.pop().replace(/[\/:]/g, "")
  										.toLowerCase();
  						var sFullCookie, oData;
  
  						if (fnCallback !== null) {
  							oData = (typeof $.parseJSON === 'function') ? $
  									.parseJSON(sValue) : eval('(' + sValue
  									+ ')');
  							sFullCookie = fnCallback(sNameFile, oData, date
  									.toGMTString(), aParts.join('/') + "/");
  						} else {
  							sFullCookie = sNameFile + "="
  									+ encodeURIComponent(sValue) + "; expires="
  									+ date.toGMTString() + "; path="
  									+ aParts.join('/') + "/";
  						}
  
  						/*
  						 * Are we going to go over the cookie limit of 4KiB? If
  						 * so, try to delete a cookies belonging to DataTables.
  						 */
  						var aCookies = document.cookie.split(';'), iNewCookieLen = sFullCookie
  								.split(';')[0].length, aOldCookies = [];
  
  						if (iNewCookieLen + document.cookie.length + 10 > 4096) /*
  																				 * Magic
  																				 * 10
  																				 * for
  																				 * padding
  																				 */
  						{
  							for (var i = 0, iLen = aCookies.length; i < iLen; i++) {
  								if (aCookies[i].indexOf(sBaseName) != -1) {
  									/*
  									 * It's a DataTables cookie, so eval it and
  									 * check the time stamp
  									 */
  									var aSplitCookie = aCookies[i].split('=');
  									try {
  										oData = eval('('
  												+ decodeURIComponent(aSplitCookie[1])
  												+ ')');
  
  										if (oData && oData.iCreate) {
  											aOldCookies.push({
  												"name" : aSplitCookie[0],
  												"time" : oData.iCreate
  											});
  										}
  									} catch (e) {
  									}
  								}
  							}
  
  							// Make sure we delete the oldest ones first
  							aOldCookies.sort(function(a, b) {
  								return b.time - a.time;
  							});
  
  							// Eliminate as many old DataTables cookies as we
  							// need to
  							while (iNewCookieLen + document.cookie.length + 10 > 4096) {
  								if (aOldCookies.length === 0) {
  									// Deleted all DT cookies and still not
  									// enough space. Can't state save
  									return;
  								}
  
  								var old = aOldCookies.pop();
  								document.cookie = old.name
  										+ "=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="
  										+ aParts.join('/') + "/";
  							}
  						}
  
  						document.cookie = sFullCookie;
  					}
  
  					/**
  					 * Read an old cookie to get a cookie with an old table
  					 * state
  					 * 
  					 * @param {string}
  					 *            sName name of the cookie to read
  					 * @returns {string} contents of the cookie - or null if no
  					 *          cookie with that name found
  					 * @memberof DataTable#oApi
  					 */
  					function _fnReadCookie(sName) {
  						var aParts = window.location.pathname.split('/'), sNameEQ = sName
  								+ '_'
  								+ aParts[aParts.length - 1].replace(/[\/:]/g,
  										"").toLowerCase() + '=', sCookieContents = document.cookie
  								.split(';');
  
  						for (var i = 0; i < sCookieContents.length; i++) {
  							var c = sCookieContents[i];
  
  							while (c.charAt(0) == ' ') {
  								c = c.substring(1, c.length);
  							}
  
  							if (c.indexOf(sNameEQ) === 0) {
  								return decodeURIComponent(c.substring(
  										sNameEQ.length, c.length));
  							}
  						}
  						return null;
  					}
  
  					/**
  					 * Return the settings object for a particular table
  					 * 
  					 * @param {node}
  					 *            nTable table we are using as a dataTable
  					 * @returns {object} Settings object - or null if not found
  					 * @memberof DataTable#oApi
  					 */
  					function _fnSettingsFromNode(nTable) {
  						for (var i = 0; i < DataTable.settings.length; i++) {
  							if (DataTable.settings[i].nTable === nTable) {
  								return DataTable.settings[i];
  							}
  						}
  
  						return null;
  					}
  
  					/**
  					 * Return an array with the TR nodes for the table
  					 * 
  					 * @param {object}
  					 *            oSettings dataTables settings object
  					 * @returns {array} TR array
  					 * @memberof DataTable#oApi
  					 */
  					function _fnGetTrNodes(oSettings) {
  						var aNodes = [];
  						var aoData = oSettings.aoData;
  						for (var i = 0, iLen = aoData.length; i < iLen; i++) {
  							if (aoData[i].nTr !== null) {
  								aNodes.push(aoData[i].nTr);
  							}
  						}
  						return aNodes;
  					}
  
  					/**
  					 * Return an flat array with all TD nodes for the table, or
  					 * row
  					 * 
  					 * @param {object}
  					 *            oSettings dataTables settings object
  					 * @param {int}
  					 *            [iIndividualRow] aoData index to get the nodes
  					 *            for - optional if not given then the return
  					 *            array will contain all nodes for the table
  					 * @returns {array} TD array
  					 * @memberof DataTable#oApi
  					 */
  					function _fnGetTdNodes(oSettings, iIndividualRow) {
  						var anReturn = [];
  						var iCorrector;
  						var anTds, nTd;
  						var iRow, iRows = oSettings.aoData.length, iColumn, iColumns, oData, sNodeName, iStart = 0, iEnd = iRows;
  
  						/* Allow the collection to be limited to just one row */
  						if (iIndividualRow !== undefined) {
  							iStart = iIndividualRow;
  							iEnd = iIndividualRow + 1;
  						}
  
  						for (iRow = iStart; iRow < iEnd; iRow++) {
  							oData = oSettings.aoData[iRow];
  							if (oData.nTr !== null) {
  								/*
  								 * get the TD child nodes - taking into account
  								 * text etc nodes
  								 */
  								anTds = [];
  								nTd = oData.nTr.firstChild;
  								while (nTd) {
  									sNodeName = nTd.nodeName.toLowerCase();
  									if (sNodeName == 'td' || sNodeName == 'th') {
  										anTds.push(nTd);
  									}
  									nTd = nTd.nextSibling;
  								}
  
  								iCorrector = 0;
  								for (iColumn = 0,
  										iColumns = oSettings.aoColumns.length; iColumn < iColumns; iColumn++) {
  									if (oSettings.aoColumns[iColumn].bVisible) {
  										anReturn.push(anTds[iColumn
  												- iCorrector]);
  									} else {
  										anReturn.push(oData._anHidden[iColumn]);
  										iCorrector++;
  									}
  								}
  							}
  						}
  
  						return anReturn;
  					}
  
  					/**
  					 * Log an error message
  					 * 
  					 * @param {object}
  					 *            oSettings dataTables settings object
  					 * @param {int}
  					 *            iLevel log error messages, or display them to
  					 *            the user
  					 * @param {string}
  					 *            sMesg error message
  					 * @memberof DataTable#oApi
  					 */
  					function _fnLog(oSettings, iLevel, sMesg) {
  						var sAlert = (oSettings === null) ? "DataTables warning: "
  								+ sMesg
  								: "DataTables warning (table id = '"
  										+ oSettings.sTableId + "'): " + sMesg;
  
  						if (iLevel === 0) {
  							if (DataTable.ext.sErrMode == 'alert') {
  								alert(sAlert);
  							} else {
  								throw new Error(sAlert);
  							}
  							return;
  						} else if (window.console && console.log) {
  							console.log(sAlert);
  						}
  					}
  
  					/**
  					 * See if a property is defined on one object, if so assign
  					 * it to the other object
  					 * 
  					 * @param {object}
  					 *            oRet target object
  					 * @param {object}
  					 *            oSrc source object
  					 * @param {string}
  					 *            sName property
  					 * @param {string}
  					 *            [sMappedName] name to map too - optional,
  					 *            sName used if not given
  					 * @memberof DataTable#oApi
  					 */
  					function _fnMap(oRet, oSrc, sName, sMappedName) {
  						if (sMappedName === undefined) {
  							sMappedName = sName;
  						}
  						if (oSrc[sName] !== undefined) {
  							oRet[sMappedName] = oSrc[sName];
  						}
  					}
  
  					/**
  					 * Extend objects - very similar to jQuery.extend, but deep
  					 * copy objects, and shallow copy arrays. The reason we need
  					 * to do this, is that we don't want to deep copy array init
  					 * values (such as aaSorting) since the dev wouldn't be able
  					 * to override them, but we do want to deep copy arrays.
  					 * 
  					 * @param {object}
  					 *            oOut Object to extend
  					 * @param {object}
  					 *            oExtender Object from which the properties
  					 *            will be applied to oOut
  					 * @returns {object} oOut Reference, just for convenience -
  					 *          oOut === the return.
  					 * @memberof DataTable#oApi
  					 * @todo This doesn't take account of arrays inside the deep
  					 *       copied objects.
  					 */
  					function _fnExtend(oOut, oExtender) {
  						var val;
  
  						for ( var prop in oExtender) {
  							if (oExtender.hasOwnProperty(prop)) {
  								val = oExtender[prop];
  
  								if (typeof oInit[prop] === 'object'
  										&& val !== null
  										&& $.isArray(val) === false) {
  									$.extend(true, oOut[prop], val);
  								} else {
  									oOut[prop] = val;
  								}
  							}
  						}
  
  						return oOut;
  					}
  
  					/**
  					 * Bind an event handers to allow a click or return key to
  					 * activate the callback. This is good for accessibility
  					 * since a return on the keyboard will have the same effect
  					 * as a click, if the element has focus.
  					 * 
  					 * @param {element}
  					 *            n Element to bind the action to
  					 * @param {object}
  					 *            oData Data object to pass to the triggered
  					 *            function
  					 * @param {function}
  					 *            fn Callback function for when the event is
  					 *            triggered
  					 * @memberof DataTable#oApi
  					 */
  					function _fnBindAction(n, oData, fn) {
  						$(n).bind('click.DT', oData, function(e) {
  							n.blur(); // Remove focus outline for mouse users
  							fn(e);
  						}).bind('keypress.DT', oData, function(e) {
  							if (e.which === 13) {
  								fn(e);
  							}
  						}).bind('selectstart.DT', function() {
  							/*
  							 * Take the brutal approach to cancelling text
  							 * selection
  							 */
  							return false;
  						});
  					}
  
  					/**
  					 * Register a callback function. Easily allows a callback
  					 * function to be added to an array store of callback
  					 * functions that can then all be called together.
  					 * 
  					 * @param {object}
  					 *            oSettings dataTables settings object
  					 * @param {string}
  					 *            sStore Name of the array storage for the
  					 *            callbacks in oSettings
  					 * @param {function}
  					 *            fn Function to be called back
  					 * @param {string}
  					 *            sName Identifying name for the callback (i.e.
  					 *            a label)
  					 * @memberof DataTable#oApi
  					 */
  					function _fnCallbackReg(oSettings, sStore, fn, sName) {
  						if (fn) {
  							oSettings[sStore].push({
  								"fn" : fn,
  								"sName" : sName
  							});
  						}
  					}
  
  					/**
  					 * Fire callback functions and trigger events. Note that the
  					 * loop over the callback array store is done backwards!
  					 * Further note that you do not want to fire off triggers in
  					 * time sensitive applications (for example cell creation)
  					 * as its slow.
  					 * 
  					 * @param {object}
  					 *            oSettings dataTables settings object
  					 * @param {string}
  					 *            sStore Name of the array storage for the
  					 *            callbacks in oSettings
  					 * @param {string}
  					 *            sTrigger Name of the jQuery custom event to
  					 *            trigger. If null no trigger is fired
  					 * @param {array}
  					 *            aArgs Array of arguments to pass to the
  					 *            callback function / trigger
  					 * @memberof DataTable#oApi
  					 */
  					function _fnCallbackFire(oSettings, sStore, sTrigger, aArgs) {
  						var aoStore = oSettings[sStore];
  						var aRet = [];
  
  						for (var i = aoStore.length - 1; i >= 0; i--) {
  							aRet.push(aoStore[i].fn.apply(oSettings.oInstance,
  									aArgs));
  						}
  
  						if (sTrigger !== null) {
  							$(oSettings.oInstance).trigger(sTrigger, aArgs);
  						}
  
  						return aRet;
  					}
  
  					/**
  					 * JSON stringify. If JSON.stringify it provided by the
  					 * browser, json2.js or any other library, then we use that
  					 * as it is fast, safe and accurate. If the function isn't
  					 * available then we need to built it ourselves - the
  					 * inspiration for this function comes from Craig Buckler (
  					 * http://www.sitepoint.com/javascript-json-serialization/ ).
  					 * It is not perfect and absolutely should not be used as a
  					 * replacement to json2.js - but it does do what we need,
  					 * without requiring a dependency for DataTables.
  					 * 
  					 * @param {object}
  					 *            o JSON object to be converted
  					 * @returns {string} JSON string
  					 * @memberof DataTable#oApi
  					 */
  					var _fnJsonString = (window.JSON) ? JSON.stringify
  							: function(o) {
  								/* Not an object or array */
  								var sType = typeof o;
  								if (sType !== "object" || o === null) {
  									// simple data type
  									if (sType === "string") {
  										o = '"' + o + '"';
  									}
  									return o + "";
  								}
  
  								/* If object or array, need to recurse over it */
  								var sProp, mValue, json = [], bArr = $
  										.isArray(o);
  
  								for (sProp in o) {
  									mValue = o[sProp];
  									sType = typeof mValue;
  
  									if (sType === "string") {
  										mValue = '"' + mValue + '"';
  									} else if (sType === "object"
  											&& mValue !== null) {
  										mValue = _fnJsonString(mValue);
  									}
  
  									json.push((bArr ? "" : '"' + sProp + '":')
  											+ mValue);
  								}
  
  								return (bArr ? "[" : "{") + json
  										+ (bArr ? "]" : "}");
  							};
  
  					/**
  					 * From some browsers (specifically IE6/7) we need special
  					 * handling to work around browser bugs - this function is
  					 * used to detect when these workarounds are needed.
  					 * 
  					 * @param {object}
  					 *            oSettings dataTables settings object
  					 * @memberof DataTable#oApi
  					 */
  					function _fnBrowserDetect(oSettings) {
  						/*
  						 * IE6/7 will oversize a width 100% element inside a
  						 * scrolling element, to include the width of the
  						 * scrollbar, while other browsers ensure the inner
  						 * element is contained without forcing scrolling
  						 */
  						var n = $('<div style="position:absolute; top:0; left:0; height:1px; width:1px; overflow:hidden">'
  								+ '<div style="position:absolute; top:1px; left:1px; width:100px; overflow:scroll;">'
  								+ '<div id="DT_BrowserTest" style="width:100%; height:10px;"></div>'
  								+ '</div>' + '</div>')[0];
  
  						document.body.appendChild(n);
  						oSettings.oBrowser.bScrollOversize = $(
  								'#DT_BrowserTest', n)[0].offsetWidth === 100 ? true
  								: false;
  						document.body.removeChild(n);
  					}
  
  					/**
  					 * Perform a jQuery selector action on the table's TR
  					 * elements (from the tbody) and return the resulting jQuery
  					 * object.
  					 * 
  					 * @param {string|node|jQuery}
  					 *            sSelector jQuery selector or node collection
  					 *            to act on
  					 * @param {object}
  					 *            [oOpts] Optional parameters for modifying the
  					 *            rows to be included
  					 * @param {string}
  					 *            [oOpts.filter=none] Select TR elements that
  					 *            meet the current filter criterion ("applied")
  					 *            or all TR elements (i.e. no filter).
  					 * @param {string}
  					 *            [oOpts.order=current] Order of the TR elements
  					 *            in the processed array. Can be either
  					 *            'current', whereby the current sorting of the
  					 *            table is used, or 'original' whereby the
  					 *            original order the data was read into the
  					 *            table is used.
  					 * @param {string}
  					 *            [oOpts.page=all] Limit the selection to the
  					 *            currently displayed page ("current") or not
  					 *            ("all"). If 'current' is given, then order is
  					 *            assumed to be 'current' and filter is
  					 *            'applied', regardless of what they might be
  					 *            given as.
  					 * @returns {object} jQuery object, filtered by the given
  					 *          selector.
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable();
  					 *  // Highlight every second row
  					 * oTable.$('tr:odd').css('backgroundColor', 'blue'); } );
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable();
  					 *  // Filter to rows with 'Webkit' in them, add a
  					 * background colour and then // remove the filter, thus
  					 * highlighting the 'Webkit' rows only.
  					 * oTable.fnFilter('Webkit'); oTable.$('tr', {"filter":
  					 * "applied"}).css('backgroundColor', 'blue');
  					 * oTable.fnFilter(''); } );
  					 */
  					this.$ = function(sSelector, oOpts) {
  						var i, iLen, a = [], tr;
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  						var aoData = oSettings.aoData;
  						var aiDisplay = oSettings.aiDisplay;
  						var aiDisplayMaster = oSettings.aiDisplayMaster;
  
  						if (!oOpts) {
  							oOpts = {};
  						}
  
  						oOpts = $.extend({}, {
  							"filter" : "none", // applied
  							"order" : "current", // "original"
  							"page" : "all" // current
  						}, oOpts);
  
  						// Current page implies that order=current and
  						// fitler=applied, since it is fairly
  						// senseless otherwise
  						if (oOpts.page == 'current') {
  							for (i = oSettings._iDisplayStart, iLen = oSettings
  									.fnDisplayEnd(); i < iLen; i++) {
  								tr = aoData[aiDisplay[i]].nTr;
  								if (tr) {
  									a.push(tr);
  								}
  							}
  						} else if (oOpts.order == "current"
  								&& oOpts.filter == "none") {
  							for (i = 0, iLen = aiDisplayMaster.length; i < iLen; i++) {
  								tr = aoData[aiDisplayMaster[i]].nTr;
  								if (tr) {
  									a.push(tr);
  								}
  							}
  						} else if (oOpts.order == "current"
  								&& oOpts.filter == "applied") {
  							for (i = 0, iLen = aiDisplay.length; i < iLen; i++) {
  								tr = aoData[aiDisplay[i]].nTr;
  								if (tr) {
  									a.push(tr);
  								}
  							}
  						} else if (oOpts.order == "original"
  								&& oOpts.filter == "none") {
  							for (i = 0, iLen = aoData.length; i < iLen; i++) {
  								tr = aoData[i].nTr;
  								if (tr) {
  									a.push(tr);
  								}
  							}
  						} else if (oOpts.order == "original"
  								&& oOpts.filter == "applied") {
  							for (i = 0, iLen = aoData.length; i < iLen; i++) {
  								tr = aoData[i].nTr;
  								if ($.inArray(i, aiDisplay) !== -1 && tr) {
  									a.push(tr);
  								}
  							}
  						} else {
  							_fnLog(oSettings, 1, "Unknown selection options");
  						}
  
  						/*
  						 * We need to filter on the TR elements and also 'find'
  						 * in their descendants to make the selector act like it
  						 * would in a full table - so we need to build both
  						 * results and then combine them together
  						 */
  						var jqA = $(a);
  						var jqTRs = jqA.filter(sSelector);
  						var jqDescendants = jqA.find(sSelector);
  
  						return $([].concat($.makeArray(jqTRs), $
  								.makeArray(jqDescendants)));
  					};
  
  					/**
  					 * Almost identical to $ in operation, but in this case
  					 * returns the data for the matched rows - as such, the
  					 * jQuery selector used should match TR row nodes or TD/TH
  					 * cell nodes rather than any descendants, so the data can
  					 * be obtained for the row/cell. If matching rows are found,
  					 * the data returned is the original data array/object that
  					 * was used to create the row (or a generated array if from
  					 * a DOM source).
  					 * 
  					 * This method is often useful in-combination with $ where
  					 * both functions are given the same parameters and the
  					 * array indexes will match identically.
  					 * 
  					 * @param {string|node|jQuery}
  					 *            sSelector jQuery selector or node collection
  					 *            to act on
  					 * @param {object}
  					 *            [oOpts] Optional parameters for modifying the
  					 *            rows to be included
  					 * @param {string}
  					 *            [oOpts.filter=none] Select elements that meet
  					 *            the current filter criterion ("applied") or
  					 *            all elements (i.e. no filter).
  					 * @param {string}
  					 *            [oOpts.order=current] Order of the data in the
  					 *            processed array. Can be either 'current',
  					 *            whereby the current sorting of the table is
  					 *            used, or 'original' whereby the original order
  					 *            the data was read into the table is used.
  					 * @param {string}
  					 *            [oOpts.page=all] Limit the selection to the
  					 *            currently displayed page ("current") or not
  					 *            ("all"). If 'current' is given, then order is
  					 *            assumed to be 'current' and filter is
  					 *            'applied', regardless of what they might be
  					 *            given as.
  					 * @returns {array} Data for the matched elements. If any
  					 *          elements, as a result of the selector, were not
  					 *          TR, TD or TH elements in the DataTable, they
  					 *          will have a null entry in the array.
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable();
  					 *  // Get the data from the first row in the table
  					 * var data = oTable._('tr:first');
  					 *  // Do something useful with the data alert( "First cell
  					 * is: "+data[0] ); } );
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable();
  					 *  // Filter to 'Webkit' and get all data for
  					 * oTable.fnFilter('Webkit'); var data = oTable._('tr',
  					 * {"filter": "applied"});
  					 *  // Do something with the data alert( data.length+" rows
  					 * matched the filter" ); } );
  					 */
  					this._ = function(sSelector, oOpts) {
  						var aOut = [];
  						var i, iLen, iIndex;
  						var aTrs = this.$(sSelector, oOpts);
  
  						for (i = 0, iLen = aTrs.length; i < iLen; i++) {
  							aOut.push(this.fnGetData(aTrs[i]));
  						}
  
  						return aOut;
  					};
  
  					/**
  					 * Add a single new row or multiple rows of data to the
  					 * table. Please note that this is suitable for client-side
  					 * processing only - if you are using server-side processing
  					 * (i.e. "bServerSide": true), then to add data, you must
  					 * add it to the data source, i.e. the server-side, through
  					 * an Ajax call.
  					 * 
  					 * @param {array|object}
  					 *            mData The data to be added to the table. This
  					 *            can be:
  					 *            <ul>
  					 *            <li>1D array of data - add a single row with
  					 *            the data provided</li>
  					 *            <li>2D array of arrays - add multiple rows in
  					 *            a single call</li>
  					 *            <li>object - data object when using <i>mData</i></li>
  					 *            <li>array of objects - multiple data objects
  					 *            when using <i>mData</i></li>
  					 *            </ul>
  					 * @param {bool}
  					 *            [bRedraw=true] redraw the table or not
  					 * @returns {array} An array of integers, representing the
  					 *          list of indexes in <i>aoData</i> ({@link DataTable.models.oSettings})
  					 *          that have been added to the table.
  					 * @dtopt API
  					 * 
  					 * @example // Global var for counter var giCount = 2;
  					 * 
  					 * $(document).ready(function() { $('#example').dataTable(); } );
  					 * 
  					 * function fnClickAddRow() {
  					 * $('#example').dataTable().fnAddData( [ giCount+".1",
  					 * giCount+".2", giCount+".3", giCount+".4" ] );
  					 * 
  					 * giCount++; }
  					 */
  					this.fnAddData = function(mData, bRedraw) {
  						if (mData.length === 0) {
  							return [];
  						}
  
  						var aiReturn = [];
  						var iTest;
  
  						/* Find settings from table node */
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  
  						/* Check if we want to add multiple rows or not */
  						if (typeof mData[0] === "object" && mData[0] !== null) {
  							for (var i = 0; i < mData.length; i++) {
  								iTest = _fnAddData(oSettings, mData[i]);
  								if (iTest == -1) {
  									return aiReturn;
  								}
  								aiReturn.push(iTest);
  							}
  						} else {
  							iTest = _fnAddData(oSettings, mData);
  							if (iTest == -1) {
  								return aiReturn;
  							}
  							aiReturn.push(iTest);
  						}
  
  						oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
  
  						if (bRedraw === undefined || bRedraw) {
  							_fnReDraw(oSettings);
  						}
  						return aiReturn;
  					};
  
  					/**
  					 * This function will make DataTables recalculate the column
  					 * sizes, based on the data contained in the table and the
  					 * sizes applied to the columns (in the DOM, CSS or through
  					 * the sWidth parameter). This can be useful when the width
  					 * of the table's parent element changes (for example a
  					 * window resize).
  					 * 
  					 * @param {boolean}
  					 *            [bRedraw=true] Redraw the table or not, you
  					 *            will typically want to
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable( { "sScrollY": "200px",
  					 *          "bPaginate": false } );
  					 * 
  					 * $(window).bind('resize', function () {
  					 * oTable.fnAdjustColumnSizing(); } ); } );
  					 */
  					this.fnAdjustColumnSizing = function(bRedraw) {
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  						_fnAdjustColumnSizing(oSettings);
  
  						if (bRedraw === undefined || bRedraw) {
  							this.fnDraw(false);
  						} else if (oSettings.oScroll.sX !== ""
  								|| oSettings.oScroll.sY !== "") {
  							/*
  							 * If not redrawing, but scrolling, we want to apply
  							 * the new column sizes anyway
  							 */
  							this.oApi._fnScrollDraw(oSettings);
  						}
  					};
  
  					/**
  					 * Quickly and simply clear a table
  					 * 
  					 * @param {bool}
  					 *            [bRedraw=true] redraw the table or not
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable();
  					 *  // Immediately 'nuke' the current rows (perhaps
  					 * waiting for an Ajax callback...) oTable.fnClearTable(); } );
  					 */
  					this.fnClearTable = function(bRedraw) {
  						/* Find settings from table node */
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  						_fnClearTable(oSettings);
  
  						if (bRedraw === undefined || bRedraw) {
  							_fnDraw(oSettings);
  						}
  					};
  
  					/**
  					 * The exact opposite of 'opening' a row, this function will
  					 * close any rows which are currently 'open'.
  					 * 
  					 * @param {node}
  					 *            nTr the table row to 'close'
  					 * @returns {int} 0 on success, or 1 if failed (can't find
  					 *          the row)
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable;
  					 *  // 'open' an information row when a row is
  					 * clicked on $('#example tbody tr').click( function () { if (
  					 * oTable.fnIsOpen(this) ) { oTable.fnClose( this ); } else {
  					 * oTable.fnOpen( this, "Temporary row opened", "info_row" ); } } );
  					 * 
  					 * oTable = $('#example').dataTable(); } );
  					 */
  					this.fnClose = function(nTr) {
  						/* Find settings from table node */
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  
  						for (var i = 0; i < oSettings.aoOpenRows.length; i++) {
  							if (oSettings.aoOpenRows[i].nParent == nTr) {
  								var nTrParent = oSettings.aoOpenRows[i].nTr.parentNode;
  								if (nTrParent) {
  									/* Remove it if it is currently on display */
  									nTrParent
  											.removeChild(oSettings.aoOpenRows[i].nTr);
  								}
  								oSettings.aoOpenRows.splice(i, 1);
  								return 0;
  							}
  						}
  						return 1;
  					};
  
  					/**
  					 * Remove a row for the table
  					 * 
  					 * @param {mixed}
  					 *            mTarget The index of the row from aoData to be
  					 *            deleted, or the TR element you want to delete
  					 * @param {function|null}
  					 *            [fnCallBack] Callback function
  					 * @param {bool}
  					 *            [bRedraw=true] Redraw the table or not
  					 * @returns {array} The row that was deleted
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable();
  					 *  // Immediately remove the first row
  					 * oTable.fnDeleteRow( 0 ); } );
  					 */
  					this.fnDeleteRow = function(mTarget, fnCallBack, bRedraw) {
  						/* Find settings from table node */
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  						var i, iLen, iAODataIndex;
  
  						iAODataIndex = (typeof mTarget === 'object') ? _fnNodeToDataIndex(
  								oSettings, mTarget)
  								: mTarget;
  
  						/* Return the data array from this row */
  						var oData = oSettings.aoData.splice(iAODataIndex, 1);
  
  						/* Update the _DT_RowIndex parameter */
  						for (i = 0, iLen = oSettings.aoData.length; i < iLen; i++) {
  							if (oSettings.aoData[i].nTr !== null) {
  								oSettings.aoData[i].nTr._DT_RowIndex = i;
  							}
  						}
  
  						/* Remove the target row from the search array */
  						var iDisplayIndex = $.inArray(iAODataIndex,
  								oSettings.aiDisplay);
  						oSettings.asDataSearch.splice(iDisplayIndex, 1);
  
  						/* Delete from the display arrays */
  						_fnDeleteIndex(oSettings.aiDisplayMaster, iAODataIndex);
  						_fnDeleteIndex(oSettings.aiDisplay, iAODataIndex);
  
  						/* If there is a user callback function - call it */
  						if (typeof fnCallBack === "function") {
  							fnCallBack.call(this, oSettings, oData);
  						}
  
  						/*
  						 * Check for an 'overflow' they case for displaying the
  						 * table
  						 */
  						if (oSettings._iDisplayStart >= oSettings
  								.fnRecordsDisplay()) {
  							oSettings._iDisplayStart -= oSettings._iDisplayLength;
  							if (oSettings._iDisplayStart < 0) {
  								oSettings._iDisplayStart = 0;
  							}
  						}
  
  						if (bRedraw === undefined || bRedraw) {
  							_fnCalculateEnd(oSettings);
  							_fnDraw(oSettings);
  						}
  
  						return oData;
  					};
  
  					/**
  					 * Restore the table to it's original state in the DOM by
  					 * removing all of DataTables enhancements, alterations to
  					 * the DOM structure of the table and event listeners.
  					 * 
  					 * @param {boolean}
  					 *            [bRemove=false] Completely remove the table
  					 *            from the DOM
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { // This example
  					 *          is fairly pointless in reality, but shows how
  					 *          fnDestroy can be used var oTable =
  					 *          $('#example').dataTable(); oTable.fnDestroy(); } );
  					 */
  					this.fnDestroy = function(bRemove) {
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  						var nOrig = oSettings.nTableWrapper.parentNode;
  						var nBody = oSettings.nTBody;
  						var i, iLen;
  
  						bRemove = (bRemove === undefined) ? false : bRemove;
  
  						/*
  						 * Flag to note that the table is currently being
  						 * destroyed - no action should be taken
  						 */
  						oSettings.bDestroying = true;
  
  						/* Fire off the destroy callbacks for plug-ins etc */
  						_fnCallbackFire(oSettings, "aoDestroyCallback",
  								"destroy", [ oSettings ]);
  
  						/*
  						 * If the table is not being removed, restore the hidden
  						 * columns
  						 */
  						if (!bRemove) {
  							for (i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
  								if (oSettings.aoColumns[i].bVisible === false) {
  									this.fnSetColumnVis(i, true);
  								}
  							}
  						}
  
  						/* Blitz all DT events */
  						$(oSettings.nTableWrapper).find('*').andSelf().unbind(
  								'.DT');
  
  						/* If there is an 'empty' indicator row, remove it */
  						$('tbody>tr>td.' + oSettings.oClasses.sRowEmpty,
  								oSettings.nTable).parent().remove();
  
  						/*
  						 * When scrolling we had to break the table up - restore
  						 * it
  						 */
  						if (oSettings.nTable != oSettings.nTHead.parentNode) {
  							$(oSettings.nTable).children('thead').remove();
  							oSettings.nTable.appendChild(oSettings.nTHead);
  						}
  
  						if (oSettings.nTFoot
  								&& oSettings.nTable != oSettings.nTFoot.parentNode) {
  							$(oSettings.nTable).children('tfoot').remove();
  							oSettings.nTable.appendChild(oSettings.nTFoot);
  						}
  
  						/*
  						 * Remove the DataTables generated nodes, events and
  						 * classes
  						 */
  						oSettings.nTable.parentNode
  								.removeChild(oSettings.nTable);
  						$(oSettings.nTableWrapper).remove();
  
  						oSettings.aaSorting = [];
  						oSettings.aaSortingFixed = [];
  						_fnSortingClasses(oSettings);
  
  						$(_fnGetTrNodes(oSettings)).removeClass(
  								oSettings.asStripeClasses.join(' '));
  
  						$('th, td', oSettings.nTHead).removeClass(
  								[ oSettings.oClasses.sSortable,
  										oSettings.oClasses.sSortableAsc,
  										oSettings.oClasses.sSortableDesc,
  										oSettings.oClasses.sSortableNone ]
  										.join(' '));
  						if (oSettings.bJUI) {
  							$(
  									'th span.' + oSettings.oClasses.sSortIcon
  											+ ', td span.'
  											+ oSettings.oClasses.sSortIcon,
  									oSettings.nTHead).remove();
  
  							$('th, td', oSettings.nTHead)
  									.each(
  											function() {
  												var jqWrapper = $(
  														'div.'
  																+ oSettings.oClasses.sSortJUIWrapper,
  														this);
  												var kids = jqWrapper.contents();
  												$(this).append(kids);
  												jqWrapper.remove();
  											});
  						}
  
  						/*
  						 * Add the TR elements back into the table in their
  						 * original order
  						 */
  						if (!bRemove && oSettings.nTableReinsertBefore) {
  							nOrig.insertBefore(oSettings.nTable,
  									oSettings.nTableReinsertBefore);
  						} else if (!bRemove) {
  							nOrig.appendChild(oSettings.nTable);
  						}
  
  						for (i = 0, iLen = oSettings.aoData.length; i < iLen; i++) {
  							if (oSettings.aoData[i].nTr !== null) {
  								nBody.appendChild(oSettings.aoData[i].nTr);
  							}
  						}
  
  						/* Restore the width of the original table */
  						if (oSettings.oFeatures.bAutoWidth === true) {
  							oSettings.nTable.style.width = _fnStringToCss(oSettings.sDestroyWidth);
  						}
  
  						/*
  						 * If the were originally stripe classes - then we add
  						 * them back here. Note this is not fool proof (for
  						 * example if not all rows had stripe classes - but it's
  						 * a good effort without getting carried away
  						 */
  						iLen = oSettings.asDestroyStripes.length;
  						if (iLen) {
  							var anRows = $(nBody).children('tr');
  							for (i = 0; i < iLen; i++) {
  								anRows
  										.filter(
  												':nth-child(' + iLen + 'n + '
  														+ i + ')').addClass(
  												oSettings.asDestroyStripes[i]);
  							}
  						}
  
  						/* Remove the settings object from the settings array */
  						for (i = 0, iLen = DataTable.settings.length; i < iLen; i++) {
  							if (DataTable.settings[i] == oSettings) {
  								DataTable.settings.splice(i, 1);
  							}
  						}
  
  						/* End it all */
  						oSettings = null;
  						oInit = null;
  					};
  
  					/**
  					 * Redraw the table
  					 * 
  					 * @param {bool}
  					 *            [bComplete=true] Re-filter and resort (if
  					 *            enabled) the table before the draw.
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable();
  					 *  // Re-draw the table - you wouldn't want to do
  					 * it here, but it's an example :-) oTable.fnDraw(); } );
  					 */
  					this.fnDraw = function(bComplete) {
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  						if (bComplete === false) {
  							_fnCalculateEnd(oSettings);
  							_fnDraw(oSettings);
  						} else {
  							_fnReDraw(oSettings);
  						}
  					};
  
  					/**
  					 * Filter the input based on data
  					 * 
  					 * @param {string}
  					 *            sInput String to filter the table on
  					 * @param {int|null}
  					 *            [iColumn] Column to limit filtering to
  					 * @param {bool}
  					 *            [bRegex=false] Treat as regular expression or
  					 *            not
  					 * @param {bool}
  					 *            [bSmart=true] Perform smart filtering or not
  					 * @param {bool}
  					 *            [bShowGlobal=true] Show the input global
  					 *            filter in it's input box(es)
  					 * @param {bool}
  					 *            [bCaseInsensitive=true] Do case-insensitive
  					 *            matching (true) or not (false)
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable();
  					 *  // Sometime later - filter... oTable.fnFilter(
  					 * 'test string' ); } );
  					 */
  					this.fnFilter = function(sInput, iColumn, bRegex, bSmart,
  							bShowGlobal, bCaseInsensitive) {
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  
  						if (!oSettings.oFeatures.bFilter) {
  							return;
  						}
  
  						if (bRegex === undefined || bRegex === null) {
  							bRegex = false;
  						}
  
  						if (bSmart === undefined || bSmart === null) {
  							bSmart = true;
  						}
  
  						if (bShowGlobal === undefined || bShowGlobal === null) {
  							bShowGlobal = true;
  						}
  
  						if (bCaseInsensitive === undefined
  								|| bCaseInsensitive === null) {
  							bCaseInsensitive = true;
  						}
  
  						if (iColumn === undefined || iColumn === null) {
  							/* Global filter */
  							_fnFilterComplete(oSettings, {
  								"sSearch" : sInput + "",
  								"bRegex" : bRegex,
  								"bSmart" : bSmart,
  								"bCaseInsensitive" : bCaseInsensitive
  							}, 1);
  
  							if (bShowGlobal && oSettings.aanFeatures.f) {
  								var n = oSettings.aanFeatures.f;
  								for (var i = 0, iLen = n.length; i < iLen; i++) {
  									// IE9 throws an 'unknown error' if
  									// document.activeElement is used
  									// inside an iframe or frame...
  									try {
  										if (n[i]._DT_Input != document.activeElement) {
  											$(n[i]._DT_Input).val(sInput);
  										}
  									} catch (e) {
  										$(n[i]._DT_Input).val(sInput);
  									}
  								}
  							}
  						} else {
  							/* Single column filter */
  							$.extend(oSettings.aoPreSearchCols[iColumn], {
  								"sSearch" : sInput + "",
  								"bRegex" : bRegex,
  								"bSmart" : bSmart,
  								"bCaseInsensitive" : bCaseInsensitive
  							});
  							_fnFilterComplete(oSettings,
  									oSettings.oPreviousSearch, 1);
  						}
  					};
  
  					/**
  					 * Get the data for the whole table, an individual row or an
  					 * individual cell based on the provided parameters.
  					 * 
  					 * @param {int|node}
  					 *            [mRow] A TR row node, TD/TH cell node or an
  					 *            integer. If given as a TR node then the data
  					 *            source for the whole row will be returned. If
  					 *            given as a TD/TH cell node then iCol will be
  					 *            automatically calculated and the data for the
  					 *            cell returned. If given as an integer, then
  					 *            this is treated as the aoData internal data
  					 *            index for the row (see fnGetPosition) and the
  					 *            data for that row used.
  					 * @param {int}
  					 *            [iCol] Optional column index that you want the
  					 *            data of.
  					 * @returns {array|object|string} If mRow is undefined, then
  					 *          the data for all rows is returned. If mRow is
  					 *          defined, just data for that row, and is iCol is
  					 *          defined, only data for the designated cell is
  					 *          returned.
  					 * @dtopt API
  					 * 
  					 * @example // Row data $(document).ready(function() {
  					 *          oTable = $('#example').dataTable();
  					 * 
  					 * oTable.$('tr').click( function () { var data =
  					 * oTable.fnGetData( this ); // ... do something with the
  					 * array / object of data for the row } ); } );
  					 * 
  					 * @example // Individual cell data
  					 *          $(document).ready(function() { oTable =
  					 *          $('#example').dataTable();
  					 * 
  					 * oTable.$('td').click( function () { var sData =
  					 * oTable.fnGetData( this ); alert( 'The cell clicked on had
  					 * the value of '+sData ); } ); } );
  					 */
  					this.fnGetData = function(mRow, iCol) {
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  
  						if (mRow !== undefined) {
  							var iRow = mRow;
  							if (typeof mRow === 'object') {
  								var sNode = mRow.nodeName.toLowerCase();
  								if (sNode === "tr") {
  									iRow = _fnNodeToDataIndex(oSettings, mRow);
  								} else if (sNode === "td") {
  									iRow = _fnNodeToDataIndex(oSettings,
  											mRow.parentNode);
  									iCol = _fnNodeToColumnIndex(oSettings,
  											iRow, mRow);
  								}
  							}
  
  							if (iCol !== undefined) {
  								return _fnGetCellData(oSettings, iRow, iCol, '');
  							}
  							return (oSettings.aoData[iRow] !== undefined) ? oSettings.aoData[iRow]._aData
  									: null;
  						}
  						return _fnGetDataMaster(oSettings);
  					};
  
  					/**
  					 * Get an array of the TR nodes that are used in the table's
  					 * body. Note that you will typically want to use the '$'
  					 * API method in preference to this as it is more flexible.
  					 * 
  					 * @param {int}
  					 *            [iRow] Optional row index for the TR element
  					 *            you want
  					 * @returns {array|node} If iRow is undefined, returns an
  					 *          array of all TR elements in the table's body, or
  					 *          iRow is defined, just the TR element requested.
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable();
  					 *  // Get the nodes from the table var nNodes =
  					 * oTable.fnGetNodes( ); } );
  					 */
  					this.fnGetNodes = function(iRow) {
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  
  						if (iRow !== undefined) {
  							return (oSettings.aoData[iRow] !== undefined) ? oSettings.aoData[iRow].nTr
  									: null;
  						}
  						return _fnGetTrNodes(oSettings);
  					};
  
  					/**
  					 * Get the array indexes of a particular cell from it's DOM
  					 * element and column index including hidden columns
  					 * 
  					 * @param {node}
  					 *            nNode this can either be a TR, TD or TH in the
  					 *            table's body
  					 * @returns {int} If nNode is given as a TR, then a single
  					 *          index is returned, or if given as a cell, an
  					 *          array of [row index, column index (visible),
  					 *          column index (all)] is given.
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { $('#example tbody
  					 *          td').click( function () { // Get the position of
  					 *          the current data from the node var aPos =
  					 *          oTable.fnGetPosition( this );
  					 *  // Get the data array for this row var aData =
  					 * oTable.fnGetData( aPos[0] );
  					 *  // Update the data array and return the value aData[
  					 * aPos[1] ] = 'clicked'; this.innerHTML = 'clicked'; } );
  					 *  // Init DataTables oTable = $('#example').dataTable(); } );
  					 */
  					this.fnGetPosition = function(nNode) {
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  						var sNodeName = nNode.nodeName.toUpperCase();
  
  						if (sNodeName == "TR") {
  							return _fnNodeToDataIndex(oSettings, nNode);
  						} else if (sNodeName == "TD" || sNodeName == "TH") {
  							var iDataIndex = _fnNodeToDataIndex(oSettings,
  									nNode.parentNode);
  							var iColumnIndex = _fnNodeToColumnIndex(oSettings,
  									iDataIndex, nNode);
  							return [
  									iDataIndex,
  									_fnColumnIndexToVisible(oSettings,
  											iColumnIndex), iColumnIndex ];
  						}
  						return null;
  					};
  
  					/**
  					 * Check to see if a row is 'open' or not.
  					 * 
  					 * @param {node}
  					 *            nTr the table row to check
  					 * @returns {boolean} true if the row is currently open,
  					 *          false otherwise
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable;
  					 *  // 'open' an information row when a row is
  					 * clicked on $('#example tbody tr').click( function () { if (
  					 * oTable.fnIsOpen(this) ) { oTable.fnClose( this ); } else {
  					 * oTable.fnOpen( this, "Temporary row opened", "info_row" ); } } );
  					 * 
  					 * oTable = $('#example').dataTable(); } );
  					 */
  					this.fnIsOpen = function(nTr) {
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  						var aoOpenRows = oSettings.aoOpenRows;
  
  						for (var i = 0; i < oSettings.aoOpenRows.length; i++) {
  							if (oSettings.aoOpenRows[i].nParent == nTr) {
  								$('.image-profil').off("click", "**");
  								$('.image-profil').click(false);
  								return true;
  							}
  						}
  						$('.image-profil').off("click", "**");
  						$('.image-profil').click(false);
  						return false;
  					};
  
  					/**
  					 * This function will place a new row directly after a row
  					 * which is currently on display on the page, with the HTML
  					 * contents that is passed into the function. This can be
  					 * used, for example, to ask for confirmation that a
  					 * particular record should be deleted.
  					 * 
  					 * @param {node}
  					 *            nTr The table row to 'open'
  					 * @param {string|node|jQuery}
  					 *            mHtml The HTML to put into the row
  					 * @param {string}
  					 *            sClass Class to give the new TD cell
  					 * @returns {node} The row opened. Note that if the table
  					 *          row passed in as the first parameter, is not
  					 *          found in the table, this method will silently
  					 *          return.
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable;
  					 *  // 'open' an information row when a row is
  					 * clicked on $('#example tbody tr').click( function () { if (
  					 * oTable.fnIsOpen(this) ) { oTable.fnClose( this ); } else {
  					 * oTable.fnOpen( this, "Temporary row opened", "info_row" ); } } );
  					 * 
  					 * oTable = $('#example').dataTable(); } );
  					 */
  					this.fnOpen = function(nTr, mHtml, sClass) {
  						/* Find settings from table node */
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  
  						/* Check that the row given is in the table */
  						var nTableRows = _fnGetTrNodes(oSettings);
  						if ($.inArray(nTr, nTableRows) === -1) {
  							return;
  						}
  
  						/* the old open one if there is one */
  						this.fnClose(nTr);
  
  						var nNewRow = document.createElement("tr");
  						var nNewCell = document.createElement("td");
  						nNewRow.appendChild(nNewCell);
  						nNewCell.className = sClass;
  						nNewCell.colSpan = _fnVisbleColumns(oSettings);
  
  						if (typeof mHtml === "string") {
  							nNewCell.innerHTML = mHtml;
  						} else {
  							$(nNewCell).html(mHtml);
  						}
  
  						/*
  						 * If the nTr isn't on the page at the moment - then we
  						 * don't insert at the moment
  						 */
  						var nTrs = $('tr', oSettings.nTBody);
  						if ($.inArray(nTr, nTrs) != -1) {
  							$(nNewRow).insertAfter(nTr);
  						}
  
  						oSettings.aoOpenRows.push({
  							"nTr" : nNewRow,
  							"nParent" : nTr
  						});
  
  						return nNewRow;
  					};
  
  					/**
  					 * Change the pagination - provides the internal logic for
  					 * pagination in a simple API function. With this function
  					 * you can have a DataTables table go to the next, previous,
  					 * first or last pages.
  					 * 
  					 * @param {string|int}
  					 *            mAction Paging action to take: "first",
  					 *            "previous", "next" or "last" or page number to
  					 *            jump to (integer), note that page 0 is the
  					 *            first page.
  					 * @param {bool}
  					 *            [bRedraw=true] Redraw the table or not
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable(); oTable.fnPageChange(
  					 *          'next' ); } );
  					 */
  					this.fnPageChange = function(mAction, bRedraw) {
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  						_fnPageChange(oSettings, mAction);
  						_fnCalculateEnd(oSettings);
  
  						if (bRedraw === undefined || bRedraw) {
  							_fnDraw(oSettings);
  						}
  					};
  
  					/**
  					 * Show a particular column
  					 * 
  					 * @param {int}
  					 *            iCol The column whose display should be
  					 *            changed
  					 * @param {bool}
  					 *            bShow Show (true) or hide (false) the column
  					 * @param {bool}
  					 *            [bRedraw=true] Redraw the table or not
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable();
  					 *  // Hide the second column after initialisation
  					 * oTable.fnSetColumnVis( 1, false ); } );
  					 */
  					this.fnSetColumnVis = function(iCol, bShow, bRedraw) {
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  						var i, iLen;
  						var aoColumns = oSettings.aoColumns;
  						var aoData = oSettings.aoData;
  						var nTd, bAppend, iBefore;
  
  						/*
  						 * No point in doing anything if we are requesting what
  						 * is already true
  						 */
  						if (aoColumns[iCol].bVisible == bShow) {
  							return;
  						}
  
  						/* Show the column */
  						if (bShow) {
  							var iInsert = 0;
  							for (i = 0; i < iCol; i++) {
  								if (aoColumns[i].bVisible) {
  									iInsert++;
  								}
  							}
  
  							/*
  							 * Need to decide if we should use appendChild or
  							 * insertBefore
  							 */
  							bAppend = (iInsert >= _fnVisbleColumns(oSettings));
  
  							/* Which coloumn should we be inserting before? */
  							if (!bAppend) {
  								for (i = iCol; i < aoColumns.length; i++) {
  									if (aoColumns[i].bVisible) {
  										iBefore = i;
  										break;
  									}
  								}
  							}
  
  							for (i = 0, iLen = aoData.length; i < iLen; i++) {
  								if (aoData[i].nTr !== null) {
  									if (bAppend) {
  										aoData[i].nTr
  												.appendChild(aoData[i]._anHidden[iCol]);
  									} else {
  										aoData[i].nTr
  												.insertBefore(
  														aoData[i]._anHidden[iCol],
  														_fnGetTdNodes(
  																oSettings, i)[iBefore]);
  									}
  								}
  							}
  						} else {
  							/* Remove a column from display */
  							for (i = 0, iLen = aoData.length; i < iLen; i++) {
  								if (aoData[i].nTr !== null) {
  									nTd = _fnGetTdNodes(oSettings, i)[iCol];
  									aoData[i]._anHidden[iCol] = nTd;
  									nTd.parentNode.removeChild(nTd);
  								}
  							}
  						}
  
  						/* Clear to set the visible flag */
  						aoColumns[iCol].bVisible = bShow;
  
  						/*
  						 * Redraw the header and footer based on the new column
  						 * visibility
  						 */
  						_fnDrawHead(oSettings, oSettings.aoHeader);
  						if (oSettings.nTFoot) {
  							_fnDrawHead(oSettings, oSettings.aoFooter);
  						}
  
  						/*
  						 * If there are any 'open' rows, then we need to alter
  						 * the colspan for this col change
  						 */
  						for (i = 0, iLen = oSettings.aoOpenRows.length; i < iLen; i++) {
  							oSettings.aoOpenRows[i].nTr.colSpan = _fnVisbleColumns(oSettings);
  						}
  
  						/*
  						 * Do a redraw incase anything depending on the table
  						 * columns needs it (built-in: scrolling)
  						 */
  						if (bRedraw === undefined || bRedraw) {
  							_fnAdjustColumnSizing(oSettings);
  							_fnDraw(oSettings);
  						}
  
  						_fnSaveState(oSettings);
  					};
  
  					/**
  					 * Get the settings for a particular table for external
  					 * manipulation
  					 * 
  					 * @returns {object} DataTables settings object. See
  					 *          {@link DataTable.models.oSettings}
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable(); var oSettings =
  					 *          oTable.fnSettings();
  					 *  // Show an example parameter from the settings
  					 * alert( oSettings._iDisplayStart ); } );
  					 */
  					this.fnSettings = function() {
  						return _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  					};
  
  					/**
  					 * Sort the table by a particular column
  					 * 
  					 * @param {int}
  					 *            iCol the data index to sort on. Note that this
  					 *            will not match the 'display index' if you have
  					 *            hidden data entries
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable();
  					 *  // Sort immediately with columns 0 and 1
  					 * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] ); } );
  					 */
  					this.fnSort = function(aaSort) {
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  						oSettings.aaSorting = aaSort;
  						_fnSort(oSettings);
  					};
  
  					/**
  					 * Attach a sort listener to an element for a given column
  					 * 
  					 * @param {node}
  					 *            nNode the element to attach the sort listener
  					 *            to
  					 * @param {int}
  					 *            iColumn the column that a click on this node
  					 *            will sort on
  					 * @param {function}
  					 *            [fnCallback] callback function when sort is
  					 *            run
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable();
  					 *  // Sort on column 1, when 'sorter' is clicked
  					 * on oTable.fnSortListener(
  					 * document.getElementById('sorter'), 1 ); } );
  					 */
  					this.fnSortListener = function(nNode, iColumn, fnCallback) {
  						_fnSortAttachListener(
  								_fnSettingsFromNode(this[DataTable.ext.iApiIndex]),
  								nNode, iColumn, fnCallback);
  					};
  
  					/**
  					 * Update a table cell or row - this method will accept
  					 * either a single value to update the cell with, an array
  					 * of values with one element for each column or an object
  					 * in the same format as the original data source. The
  					 * function is self-referencing in order to make the multi
  					 * column updates easier.
  					 * 
  					 * @param {object|array|string}
  					 *            mData Data to update the cell/row with
  					 * @param {node|int}
  					 *            mRow TR element you want to update or the
  					 *            aoData index
  					 * @param {int}
  					 *            [iColumn] The column to update (not used of
  					 *            mData is an array or object)
  					 * @param {bool}
  					 *            [bRedraw=true] Redraw the table or not
  					 * @param {bool}
  					 *            [bAction=true] Perform pre-draw actions or not
  					 * @returns {int} 0 on success, 1 on error
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable(); oTable.fnUpdate(
  					 *          'Example update', 0, 0 ); // Single cell
  					 *          oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], 1, 0 ); //
  					 *          Row } );
  					 */
  					this.fnUpdate = function(mData, mRow, iColumn, bRedraw,
  							bAction) {
  						var oSettings = _fnSettingsFromNode(this[DataTable.ext.iApiIndex]);
  						var i, iLen, sDisplay;
  						var iRow = (typeof mRow === 'object') ? _fnNodeToDataIndex(
  								oSettings, mRow)
  								: mRow;
  
  						if ($.isArray(mData) && iColumn === undefined) {
  							/* Array update - update the whole row */
  							oSettings.aoData[iRow]._aData = mData.slice();
  
  							/* Flag to the function that we are recursing */
  							for (i = 0; i < oSettings.aoColumns.length; i++) {
  								this.fnUpdate(
  										_fnGetCellData(oSettings, iRow, i),
  										iRow, i, false, false);
  							}
  						} else if ($.isPlainObject(mData)
  								&& iColumn === undefined) {
  							/*
  							 * Object update - update the whole row - assume the
  							 * developer gets the object right
  							 */
  							oSettings.aoData[iRow]._aData = $.extend(true, {},
  									mData);
  
  							for (i = 0; i < oSettings.aoColumns.length; i++) {
  								this.fnUpdate(
  										_fnGetCellData(oSettings, iRow, i),
  										iRow, i, false, false);
  							}
  						} else {
  							/* Individual cell update */
  							_fnSetCellData(oSettings, iRow, iColumn, mData);
  							sDisplay = _fnGetCellData(oSettings, iRow, iColumn,
  									'display');
  
  							var oCol = oSettings.aoColumns[iColumn];
  							if (oCol.fnRender !== null) {
  								sDisplay = _fnRender(oSettings, iRow, iColumn);
  								if (oCol.bUseRendered) {
  									_fnSetCellData(oSettings, iRow, iColumn,
  											sDisplay);
  								}
  							}
  
  							if (oSettings.aoData[iRow].nTr !== null) {
  								/* Do the actual HTML update */
  								_fnGetTdNodes(oSettings, iRow)[iColumn].innerHTML = sDisplay;
  							}
  						}
  
  						/*
  						 * Modify the search index for this row (strictly this
  						 * is likely not needed, since fnReDraw will rebuild the
  						 * search array - however, the redraw might be disabled
  						 * by the user)
  						 */
  						var iDisplayIndex = $
  								.inArray(iRow, oSettings.aiDisplay);
  						oSettings.asDataSearch[iDisplayIndex] = _fnBuildSearchRow(
  								oSettings, _fnGetRowData(oSettings, iRow,
  										'filter', _fnGetColumns(oSettings,
  												'bSearchable')));
  
  						/* Perform pre-draw actions */
  						if (bAction === undefined || bAction) {
  							_fnAdjustColumnSizing(oSettings);
  						}
  
  						/* Redraw the table */
  						if (bRedraw === undefined || bRedraw) {
  							_fnReDraw(oSettings);
  						}
  						return 0;
  					};
  
  					/**
  					 * Provide a common method for plug-ins to check the version
  					 * of DataTables being used, in order to ensure
  					 * compatibility.
  					 * 
  					 * @param {string}
  					 *            sVersion Version string to check for, in the
  					 *            format "X.Y.Z". Note that the formats "X" and
  					 *            "X.Y" are also acceptable.
  					 * @returns {boolean} true if this version of DataTables is
  					 *          greater or equal to the required version, or
  					 *          false if this version of DataTales is not
  					 *          suitable
  					 * @method
  					 * @dtopt API
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable(); alert(
  					 *          oTable.fnVersionCheck( '1.9.0' ) ); } );
  					 */
  					this.fnVersionCheck = DataTable.ext.fnVersionCheck;
  
  					/*
  					 * This is really a good bit rubbish this method of exposing
  					 * the internal methods publicly... - To be fixed in 2.0
  					 * using methods on the prototype
  					 */
  
  					/**
  					 * Create a wrapper function for exporting an internal
  					 * functions to an external API.
  					 * 
  					 * @param {string}
  					 *            sFunc API function name
  					 * @returns {function} wrapped function
  					 * @memberof DataTable#oApi
  					 */
  					function _fnExternApiFunc(sFunc) {
  						return function() {
  							var aArgs = [ _fnSettingsFromNode(this[DataTable.ext.iApiIndex]) ]
  									.concat(Array.prototype.slice
  											.call(arguments));
  							return DataTable.ext.oApi[sFunc].apply(this, aArgs);
  						};
  					}
  
  					/**
  					 * Reference to internal functions for use by plug-in
  					 * developers. Note that these methods are references to
  					 * internal functions and are considered to be private. If
  					 * you use these methods, be aware that they are liable to
  					 * change between versions (check the upgrade notes).
  					 * 
  					 * @namespace
  					 */
  					this.oApi = {
  						"_fnExternApiFunc" : _fnExternApiFunc,
  						"_fnInitialise" : _fnInitialise,
  						"_fnInitComplete" : _fnInitComplete,
  						"_fnLanguageCompat" : _fnLanguageCompat,
  						"_fnAddColumn" : _fnAddColumn,
  						"_fnColumnOptions" : _fnColumnOptions,
  						"_fnAddData" : _fnAddData,
  						"_fnCreateTr" : _fnCreateTr,
  						"_fnGatherData" : _fnGatherData,
  						"_fnBuildHead" : _fnBuildHead,
  						"_fnDrawHead" : _fnDrawHead,
  						"_fnDraw" : _fnDraw,
  						"_fnReDraw" : _fnReDraw,
  						"_fnAjaxUpdate" : _fnAjaxUpdate,
  						"_fnAjaxParameters" : _fnAjaxParameters,
  						"_fnAjaxUpdateDraw" : _fnAjaxUpdateDraw,
  						"_fnServerParams" : _fnServerParams,
  						"_fnAddOptionsHtml" : _fnAddOptionsHtml,
  						"_fnFeatureHtmlTable" : _fnFeatureHtmlTable,
  						"_fnScrollDraw" : _fnScrollDraw,
  						"_fnAdjustColumnSizing" : _fnAdjustColumnSizing,
  						"_fnFeatureHtmlFilter" : _fnFeatureHtmlFilter,
  						"_fnFilterComplete" : _fnFilterComplete,
  						"_fnFilterCustom" : _fnFilterCustom,
  						"_fnFilterColumn" : _fnFilterColumn,
  						"_fnFilter" : _fnFilter,
  						"_fnBuildSearchArray" : _fnBuildSearchArray,
  						"_fnBuildSearchRow" : _fnBuildSearchRow,
  						"_fnFilterCreateSearch" : _fnFilterCreateSearch,
  						"_fnDataToSearch" : _fnDataToSearch,
  						"_fnSort" : _fnSort,
  						"_fnSortAttachListener" : _fnSortAttachListener,
  						"_fnSortingClasses" : _fnSortingClasses,
  						"_fnFeatureHtmlPaginate" : _fnFeatureHtmlPaginate,
  						"_fnPageChange" : _fnPageChange,
  						"_fnFeatureHtmlInfo" : _fnFeatureHtmlInfo,
  						"_fnUpdateInfo" : _fnUpdateInfo,
  						"_fnFeatureHtmlLength" : _fnFeatureHtmlLength,
  						"_fnFeatureHtmlProcessing" : _fnFeatureHtmlProcessing,
  						"_fnProcessingDisplay" : _fnProcessingDisplay,
  						"_fnVisibleToColumnIndex" : _fnVisibleToColumnIndex,
  						"_fnColumnIndexToVisible" : _fnColumnIndexToVisible,
  						"_fnNodeToDataIndex" : _fnNodeToDataIndex,
  						"_fnVisbleColumns" : _fnVisbleColumns,
  						"_fnCalculateEnd" : _fnCalculateEnd,
  						"_fnConvertToWidth" : _fnConvertToWidth,
  						"_fnCalculateColumnWidths" : _fnCalculateColumnWidths,
  						"_fnScrollingWidthAdjust" : _fnScrollingWidthAdjust,
  						"_fnGetWidestNode" : _fnGetWidestNode,
  						"_fnGetMaxLenString" : _fnGetMaxLenString,
  						"_fnStringToCss" : _fnStringToCss,
  						"_fnDetectType" : _fnDetectType,
  						"_fnSettingsFromNode" : _fnSettingsFromNode,
  						"_fnGetDataMaster" : _fnGetDataMaster,
  						"_fnGetTrNodes" : _fnGetTrNodes,
  						"_fnGetTdNodes" : _fnGetTdNodes,
  						"_fnEscapeRegex" : _fnEscapeRegex,
  						"_fnDeleteIndex" : _fnDeleteIndex,
  						"_fnReOrderIndex" : _fnReOrderIndex,
  						"_fnColumnOrdering" : _fnColumnOrdering,
  						"_fnLog" : _fnLog,
  						"_fnClearTable" : _fnClearTable,
  						"_fnSaveState" : _fnSaveState,
  						"_fnLoadState" : _fnLoadState,
  						"_fnCreateCookie" : _fnCreateCookie,
  						"_fnReadCookie" : _fnReadCookie,
  						"_fnDetectHeader" : _fnDetectHeader,
  						"_fnGetUniqueThs" : _fnGetUniqueThs,
  						"_fnScrollBarWidth" : _fnScrollBarWidth,
  						"_fnApplyToChildren" : _fnApplyToChildren,
  						"_fnMap" : _fnMap,
  						"_fnGetRowData" : _fnGetRowData,
  						"_fnGetCellData" : _fnGetCellData,
  						"_fnSetCellData" : _fnSetCellData,
  						"_fnGetObjectDataFn" : _fnGetObjectDataFn,
  						"_fnSetObjectDataFn" : _fnSetObjectDataFn,
  						"_fnApplyColumnDefs" : _fnApplyColumnDefs,
  						"_fnBindAction" : _fnBindAction,
  						"_fnExtend" : _fnExtend,
  						"_fnCallbackReg" : _fnCallbackReg,
  						"_fnCallbackFire" : _fnCallbackFire,
  						"_fnJsonString" : _fnJsonString,
  						"_fnRender" : _fnRender,
  						"_fnNodeToColumnIndex" : _fnNodeToColumnIndex,
  						"_fnInfoMacros" : _fnInfoMacros,
  						"_fnBrowserDetect" : _fnBrowserDetect,
  						"_fnGetColumns" : _fnGetColumns
  					};
  
  					$.extend(DataTable.ext.oApi, this.oApi);
  
  					for ( var sFunc in DataTable.ext.oApi) {
  						if (sFunc) {
  							this[sFunc] = _fnExternApiFunc(sFunc);
  						}
  					}
  
  					var _that = this;
  					this
  							.each(function() {
  								var i = 0, iLen, j, jLen, k, kLen;
  								var sId = this.getAttribute('id');
  								var bInitHandedOff = false;
  								var bUsePassedData = false;
  
  								/* Sanity check */
  								if (this.nodeName.toLowerCase() != 'table') {
  									_fnLog(null, 0,
  											"Attempted to initialise DataTables on a node which is not a "
  													+ "table: " + this.nodeName);
  									return;
  								}
  
  								/*
  								 * Check to see if we are re-initialising a
  								 * table
  								 */
  								for (i = 0, iLen = DataTable.settings.length; i < iLen; i++) {
  									/* Base check on table node */
  									if (DataTable.settings[i].nTable == this) {
  										if (oInit === undefined
  												|| oInit.bRetrieve) {
  											return DataTable.settings[i].oInstance;
  										} else if (oInit.bDestroy) {
  											DataTable.settings[i].oInstance
  													.fnDestroy();
  											break;
  										} else {
  											_fnLog(
  													DataTable.settings[i],
  													0,
  													"Cannot reinitialise DataTable.\n\n"
  															+ "To retrieve the DataTables object for this table, pass no arguments or see "
  															+ "the docs for bRetrieve and bDestroy");
  											return;
  										}
  									}
  
  									/*
  									 * If the element we are initialising has
  									 * the same ID as a table which was
  									 * previously initialised, but the table
  									 * nodes don't match (from before) then we
  									 * destroy the old instance by simply
  									 * deleting it. This is under the assumption
  									 * that the table has been destroyed by
  									 * other methods. Anyone using non-id
  									 * selectors will need to do this manually
  									 */
  									if (DataTable.settings[i].sTableId == this.id) {
  										DataTable.settings.splice(i, 1);
  										break;
  									}
  								}
  
  								/*
  								 * Ensure the table has an ID - required for
  								 * accessibility
  								 */
  								if (sId === null || sId === "") {
  									sId = "DataTables_Table_"
  											+ (DataTable.ext._oExternConfig.iNextUnique++);
  									this.id = sId;
  								}
  
  								/*
  								 * Create the settings object for this table and
  								 * set some of the default parameters
  								 */
  								var oSettings = $.extend(true, {},
  										DataTable.models.oSettings, {
  											"nTable" : this,
  											"oApi" : _that.oApi,
  											"oInit" : oInit,
  											"sDestroyWidth" : $(this).width(),
  											"sInstance" : sId,
  											"sTableId" : sId
  										});
  								DataTable.settings.push(oSettings);
  
  								// Need to add the instance after the instance
  								// after the settings object has been added
  								// to the settings array, so we can self
  								// reference the table instance if more than one
  								oSettings.oInstance = (_that.length === 1) ? _that
  										: $(this).dataTable();
  
  								/* Setting up the initialisation object */
  								if (!oInit) {
  									oInit = {};
  								}
  
  								// Backwards compatibility, before we apply all
  								// the defaults
  								if (oInit.oLanguage) {
  									_fnLanguageCompat(oInit.oLanguage);
  								}
  
  								oInit = _fnExtend($.extend(true, {},
  										DataTable.defaults), oInit);
  
  								// Map the initialisation options onto the
  								// settings object
  								_fnMap(oSettings.oFeatures, oInit, "bPaginate");
  								_fnMap(oSettings.oFeatures, oInit,
  										"bLengthChange");
  								_fnMap(oSettings.oFeatures, oInit, "bFilter");
  								_fnMap(oSettings.oFeatures, oInit, "bSort");
  								_fnMap(oSettings.oFeatures, oInit, "bInfo");
  								_fnMap(oSettings.oFeatures, oInit,
  										"bProcessing");
  								_fnMap(oSettings.oFeatures, oInit, "bAutoWidth");
  								_fnMap(oSettings.oFeatures, oInit,
  										"bSortClasses");
  								_fnMap(oSettings.oFeatures, oInit,
  										"bServerSide");
  								_fnMap(oSettings.oFeatures, oInit,
  										"bDeferRender");
  								_fnMap(oSettings.oScroll, oInit, "sScrollX",
  										"sX");
  								_fnMap(oSettings.oScroll, oInit,
  										"sScrollXInner", "sXInner");
  								_fnMap(oSettings.oScroll, oInit, "sScrollY",
  										"sY");
  								_fnMap(oSettings.oScroll, oInit,
  										"bScrollCollapse", "bCollapse");
  								_fnMap(oSettings.oScroll, oInit,
  										"bScrollInfinite", "bInfinite");
  								_fnMap(oSettings.oScroll, oInit,
  										"iScrollLoadGap", "iLoadGap");
  								_fnMap(oSettings.oScroll, oInit,
  										"bScrollAutoCss", "bAutoCss");
  								_fnMap(oSettings, oInit, "asStripeClasses");
  								_fnMap(oSettings, oInit, "asStripClasses",
  										"asStripeClasses"); // legacy
  								_fnMap(oSettings, oInit, "fnServerData");
  								_fnMap(oSettings, oInit, "fnFormatNumber");
  								_fnMap(oSettings, oInit, "sServerMethod");
  								_fnMap(oSettings, oInit, "aaSorting");
  								_fnMap(oSettings, oInit, "aaSortingFixed");
  								_fnMap(oSettings, oInit, "aLengthMenu");
  								_fnMap(oSettings, oInit, "sPaginationType");
  								_fnMap(oSettings, oInit, "sAjaxSource");
  								_fnMap(oSettings, oInit, "sAjaxDataProp");
  								_fnMap(oSettings, oInit, "iCookieDuration");
  								_fnMap(oSettings, oInit, "sCookiePrefix");
  								_fnMap(oSettings, oInit, "sDom");
  								_fnMap(oSettings, oInit, "bSortCellsTop");
  								_fnMap(oSettings, oInit, "iTabIndex");
  								_fnMap(oSettings, oInit, "oSearch",
  										"oPreviousSearch");
  								_fnMap(oSettings, oInit, "aoSearchCols",
  										"aoPreSearchCols");
  								_fnMap(oSettings, oInit, "iDisplayLength",
  										"_iDisplayLength");
  								_fnMap(oSettings, oInit, "bJQueryUI", "bJUI");
  								_fnMap(oSettings, oInit, "fnCookieCallback");
  								_fnMap(oSettings, oInit, "fnStateLoad");
  								_fnMap(oSettings, oInit, "fnStateSave");
  								_fnMap(oSettings.oLanguage, oInit,
  										"fnInfoCallback");
  
  								/* Callback functions which are array driven */
  								_fnCallbackReg(oSettings, 'aoDrawCallback',
  										oInit.fnDrawCallback, 'user');
  								_fnCallbackReg(oSettings, 'aoServerParams',
  										oInit.fnServerParams, 'user');
  								_fnCallbackReg(oSettings, 'aoStateSaveParams',
  										oInit.fnStateSaveParams, 'user');
  								_fnCallbackReg(oSettings, 'aoStateLoadParams',
  										oInit.fnStateLoadParams, 'user');
  								_fnCallbackReg(oSettings, 'aoStateLoaded',
  										oInit.fnStateLoaded, 'user');
  								_fnCallbackReg(oSettings, 'aoRowCallback',
  										oInit.fnRowCallback, 'user');
  								_fnCallbackReg(oSettings,
  										'aoRowCreatedCallback',
  										oInit.fnCreatedRow, 'user');
  								_fnCallbackReg(oSettings, 'aoHeaderCallback',
  										oInit.fnHeaderCallback, 'user');
  								_fnCallbackReg(oSettings, 'aoFooterCallback',
  										oInit.fnFooterCallback, 'user');
  								_fnCallbackReg(oSettings, 'aoInitComplete',
  										oInit.fnInitComplete, 'user');
  								_fnCallbackReg(oSettings, 'aoPreDrawCallback',
  										oInit.fnPreDrawCallback, 'user');
  
  								if (oSettings.oFeatures.bServerSide
  										&& oSettings.oFeatures.bSort
  										&& oSettings.oFeatures.bSortClasses) {
  									/*
  									 * Enable sort classes for server-side
  									 * processing. Safe to do it here, since
  									 * server-side processing must be enabled by
  									 * the developer
  									 */
  									_fnCallbackReg(oSettings, 'aoDrawCallback',
  											_fnSortingClasses,
  											'server_side_sort_classes');
  								} else if (oSettings.oFeatures.bDeferRender) {
  									_fnCallbackReg(oSettings, 'aoDrawCallback',
  											_fnSortingClasses,
  											'defer_sort_classes');
  								}
  
  								if (oInit.bJQueryUI) {
  									/*
  									 * Use the JUI classes object for display.
  									 * You could clone the oStdClasses object if
  									 * you want to have multiple tables with
  									 * multiple independent classes
  									 */
  									$.extend(oSettings.oClasses,
  											DataTable.ext.oJUIClasses);
  
  									if (oInit.sDom === DataTable.defaults.sDom
  											&& DataTable.defaults.sDom === "lfrtip") {
  										/*
  										 * Set the DOM to use a layout suitable
  										 * for jQuery UI's theming
  										 */
  										oSettings.sDom = '<"H"lfr>t<"F"ip>';
  									}
  								} else {
  									$.extend(oSettings.oClasses,
  											DataTable.ext.oStdClasses);
  								}
  								$(this).addClass(oSettings.oClasses.sTable);
  
  								/*
  								 * Calculate the scroll bar width and cache it
  								 * for use later on
  								 */
  								if (oSettings.oScroll.sX !== ""
  										|| oSettings.oScroll.sY !== "") {
  									oSettings.oScroll.iBarWidth = _fnScrollBarWidth();
  								}
  
  								if (oSettings.iInitDisplayStart === undefined) {
  									/*
  									 * Display start point, taking into account
  									 * the save saving
  									 */
  									oSettings.iInitDisplayStart = oInit.iDisplayStart;
  									oSettings._iDisplayStart = oInit.iDisplayStart;
  								}
  
  								/*
  								 * Must be done after everything which can be
  								 * overridden by a cookie!
  								 */
  								if (oInit.bStateSave) {
  									oSettings.oFeatures.bStateSave = true;
  									_fnLoadState(oSettings, oInit);
  									_fnCallbackReg(oSettings, 'aoDrawCallback',
  											_fnSaveState, 'state_save');
  								}
  
  								if (oInit.iDeferLoading !== null) {
  									oSettings.bDeferLoading = true;
  									var tmp = $.isArray(oInit.iDeferLoading);
  									oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0]
  											: oInit.iDeferLoading;
  									oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1]
  											: oInit.iDeferLoading;
  								}
  
  								if (oInit.aaData !== null) {
  									bUsePassedData = true;
  								}
  
  								/* Language definitions */
  								if (oInit.oLanguage.sUrl !== "") {
  									/*
  									 * Get the language definitions from a file -
  									 * because this Ajax call makes the language
  									 * get async to the remainder of this
  									 * function we use bInitHandedOff to
  									 * indicate that _fnInitialise will be fired
  									 * by the returned Ajax handler, rather than
  									 * the constructor
  									 */
  									oSettings.oLanguage.sUrl = oInit.oLanguage.sUrl;
  									$.getJSON(oSettings.oLanguage.sUrl, null,
  											function(json) {
  												_fnLanguageCompat(json);
  												$.extend(true,
  														oSettings.oLanguage,
  														oInit.oLanguage, json);
  												_fnInitialise(oSettings);
  											});
  									bInitHandedOff = true;
  								} else {
  									$.extend(true, oSettings.oLanguage,
  											oInit.oLanguage);
  								}
  
  								/*
  								 * Stripes
  								 */
  								if (oInit.asStripeClasses === null) {
  									oSettings.asStripeClasses = [
  											oSettings.oClasses.sStripeOdd,
  											oSettings.oClasses.sStripeEven ];
  								}
  
  								/*
  								 * Remove row stripe classes if they are already
  								 * on the table row
  								 */
  								iLen = oSettings.asStripeClasses.length;
  								oSettings.asDestroyStripes = [];
  								if (iLen) {
  									var bStripeRemove = false;
  									var anRows = $(this).children('tbody')
  											.children('tr:lt(' + iLen + ')');
  									for (i = 0; i < iLen; i++) {
  										if (anRows
  												.hasClass(oSettings.asStripeClasses[i])) {
  											bStripeRemove = true;
  
  											/*
  											 * Store the classes which we are
  											 * about to remove so they can be
  											 * re-added on destroy
  											 */
  											oSettings.asDestroyStripes
  													.push(oSettings.asStripeClasses[i]);
  										}
  									}
  
  									if (bStripeRemove) {
  										anRows
  												.removeClass(oSettings.asStripeClasses
  														.join(' '));
  									}
  								}
  
  								/*
  								 * Columns See if we should load columns
  								 * automatically or use defined ones
  								 */
  								var anThs = [];
  								var aoColumnsInit;
  								var nThead = this.getElementsByTagName('thead');
  								if (nThead.length !== 0) {
  									_fnDetectHeader(oSettings.aoHeader,
  											nThead[0]);
  									anThs = _fnGetUniqueThs(oSettings);
  								}
  
  								/*
  								 * If not given a column array, generate one
  								 * with nulls
  								 */
  								if (oInit.aoColumns === null) {
  									aoColumnsInit = [];
  									for (i = 0, iLen = anThs.length; i < iLen; i++) {
  										aoColumnsInit.push(null);
  									}
  								} else {
  									aoColumnsInit = oInit.aoColumns;
  								}
  
  								/* Add the columns */
  								for (i = 0, iLen = aoColumnsInit.length; i < iLen; i++) {
  									/*
  									 * Short cut - use the loop to check if we
  									 * have column visibility state to restore
  									 */
  									if (oInit.saved_aoColumns !== undefined
  											&& oInit.saved_aoColumns.length == iLen) {
  										if (aoColumnsInit[i] === null) {
  											aoColumnsInit[i] = {};
  										}
  										aoColumnsInit[i].bVisible = oInit.saved_aoColumns[i].bVisible;
  									}
  
  									_fnAddColumn(oSettings, anThs ? anThs[i]
  											: null);
  								}
  
  								/* Apply the column definitions */
  								_fnApplyColumnDefs(oSettings,
  										oInit.aoColumnDefs, aoColumnsInit,
  										function(iCol, oDef) {
  											_fnColumnOptions(oSettings, iCol,
  													oDef);
  										});
  
  								/*
  								 * Sorting Check the aaSorting array
  								 */
  								for (i = 0, iLen = oSettings.aaSorting.length; i < iLen; i++) {
  									if (oSettings.aaSorting[i][0] >= oSettings.aoColumns.length) {
  										oSettings.aaSorting[i][0] = 0;
  									}
  									var oColumn = oSettings.aoColumns[oSettings.aaSorting[i][0]];
  
  									/* Add a default sorting index */
  									if (oSettings.aaSorting[i][2] === undefined) {
  										oSettings.aaSorting[i][2] = 0;
  									}
  
  									/*
  									 * If aaSorting is not defined, then we use
  									 * the first indicator in asSorting
  									 */
  									if (oInit.aaSorting === undefined
  											&& oSettings.saved_aaSorting === undefined) {
  										oSettings.aaSorting[i][1] = oColumn.asSorting[0];
  									}
  
  									/*
  									 * Set the current sorting index based on
  									 * aoColumns.asSorting
  									 */
  									for (j = 0, jLen = oColumn.asSorting.length; j < jLen; j++) {
  										if (oSettings.aaSorting[i][1] == oColumn.asSorting[j]) {
  											oSettings.aaSorting[i][2] = j;
  											break;
  										}
  									}
  								}
  
  								/*
  								 * Do a first pass on the sorting classes
  								 * (allows any size changes to be taken into
  								 * account, and also will apply sorting disabled
  								 * classes if disabled
  								 */
  								_fnSortingClasses(oSettings);
  
  								/*
  								 * Final init Cache the header, body and footer
  								 * as required, creating them if needed
  								 */
  
  								/* Browser support detection */
  								_fnBrowserDetect(oSettings);
  
  								// Work around for Webkit bug 83867 - store the
  								// caption-side before removing from doc
  								var captions = $(this)
  										.children('caption')
  										.each(
  												function() {
  													this._captionSide = $(this)
  															.css('caption-side');
  												});
  
  								var thead = $(this).children('thead');
  								if (thead.length === 0) {
  									thead = [ document.createElement('thead') ];
  									this.appendChild(thead[0]);
  								}
  								oSettings.nTHead = thead[0];
  
  								var tbody = $(this).children('tbody');
  								if (tbody.length === 0) {
  									tbody = [ document.createElement('tbody') ];
  									this.appendChild(tbody[0]);
  								}
  								oSettings.nTBody = tbody[0];
  								oSettings.nTBody.setAttribute("role", "alert");
  								oSettings.nTBody.setAttribute("aria-live",
  										"polite");
  								oSettings.nTBody.setAttribute("aria-relevant",
  										"all");
  
  								var tfoot = $(this).children('tfoot');
  								if (tfoot.length === 0
  										&& captions.length > 0
  										&& (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "")) {
  									// If we are a scrolling table, and no
  									// footer has been given, then we need to
  									// create
  									// a tfoot element for the caption element
  									// to be appended to
  									tfoot = [ document.createElement('tfoot') ];
  									this.appendChild(tfoot[0]);
  								}
  
  								if (tfoot.length > 0) {
  									oSettings.nTFoot = tfoot[0];
  									_fnDetectHeader(oSettings.aoFooter,
  											oSettings.nTFoot);
  								}
  
  								/*
  								 * Check if there is data passing into the
  								 * constructor
  								 */
  								if (bUsePassedData) {
  									for (i = 0; i < oInit.aaData.length; i++) {
  										_fnAddData(oSettings, oInit.aaData[i]);
  									}
  								} else {
  									/* Grab the data from the page */
  									_fnGatherData(oSettings);
  								}
  
  								/* Copy the data index array */
  								oSettings.aiDisplay = oSettings.aiDisplayMaster
  										.slice();
  
  								/* Initialisation complete - table can be drawn */
  								oSettings.bInitialised = true;
  
  								/*
  								 * Check if we need to initialise the table (it
  								 * might not have been handed off to the
  								 * language processor)
  								 */
  								if (bInitHandedOff === false) {
  									_fnInitialise(oSettings);
  								}
  							});
  					_that = null;
  					return this;
  				};
  
  				/**
  				 * Provide a common method for plug-ins to check the version of
  				 * DataTables being used, in order to ensure compatibility.
  				 * 
  				 * @param {string}
  				 *            sVersion Version string to check for, in the
  				 *            format "X.Y.Z". Note that the formats "X" and
  				 *            "X.Y" are also acceptable.
  				 * @returns {boolean} true if this version of DataTables is
  				 *          greater or equal to the required version, or false
  				 *          if this version of DataTales is not suitable
  				 * @static
  				 * @dtopt API-Static
  				 * 
  				 * @example alert( $.fn.dataTable.fnVersionCheck( '1.9.0' ) );
  				 */
  				DataTable.fnVersionCheck = function(sVersion) {
  					/* This is cheap, but effective */
  					var fnZPad = function(Zpad, count) {
  						while (Zpad.length < count) {
  							Zpad += '0';
  						}
  						return Zpad;
  					};
  					var aThis = DataTable.ext.sVersion.split('.');
  					var aThat = sVersion.split('.');
  					var sThis = '', sThat = '';
  
  					for (var i = 0, iLen = aThat.length; i < iLen; i++) {
  						sThis += fnZPad(aThis[i], 3);
  						sThat += fnZPad(aThat[i], 3);
  					}
  
  					return parseInt(sThis, 10) >= parseInt(sThat, 10);
  				};
  
  				/**
  				 * Check if a TABLE node is a DataTable table already or not.
  				 * 
  				 * @param {node}
  				 *            nTable The TABLE node to check if it is a
  				 *            DataTable or not (note that other node types can
  				 *            be passed in, but will always return false).
  				 * @returns {boolean} true the table given is a DataTable, or
  				 *          false otherwise
  				 * @static
  				 * @dtopt API-Static
  				 * 
  				 * @example var ex = document.getElementById('example'); if ( !
  				 *          $.fn.DataTable.fnIsDataTable( ex ) ) {
  				 *          $(ex).dataTable(); }
  				 */
  				DataTable.fnIsDataTable = function(nTable) {
  					var o = DataTable.settings;
  
  					for (var i = 0; i < o.length; i++) {
  						if (o[i].nTable === nTable
  								|| o[i].nScrollHead === nTable
  								|| o[i].nScrollFoot === nTable) {
  							return true;
  						}
  					}
  
  					return false;
  				};
  
  				/**
  				 * Get all DataTable tables that have been initialised -
  				 * optionally you can select to get only currently visible
  				 * tables.
  				 * 
  				 * @param {boolean}
  				 *            [bVisible=false] Flag to indicate if you want all
  				 *            (default) or visible tables only.
  				 * @returns {array} Array of TABLE nodes (not DataTable
  				 *          instances) which are DataTables
  				 * @static
  				 * @dtopt API-Static
  				 * 
  				 * @example var table = $.fn.dataTable.fnTables(true); if (
  				 *          table.length > 0 ) {
  				 *          $(table).dataTable().fnAdjustColumnSizing(); }
  				 */
  				DataTable.fnTables = function(bVisible) {
  					var out = [];
  
  					jQuery.each(DataTable.settings, function(i, o) {
  						if (!bVisible
  								|| (bVisible === true && $(o.nTable).is(
  										':visible'))) {
  							out.push(o.nTable);
  						}
  					});
  
  					return out;
  				};
  
  				/**
  				 * Version string for plug-ins to check compatibility. Allowed
  				 * format is a.b.c.d.e where: a:int, b:int, c:int,
  				 * d:string(dev|beta), e:int. d and e are optional
  				 * 
  				 * @member
  				 * @type string
  				 * @default Version number
  				 */
  				DataTable.version = "1.9.4";
  
  				/**
  				 * Private data store, containing all of the settings objects
  				 * that are created for the tables on a given page.
  				 * 
  				 * Note that the <i>DataTable.settings</i> object is aliased to
  				 * <i>jQuery.fn.dataTableExt</i> through which it may be
  				 * accessed and manipulated, or <i>jQuery.fn.dataTable.settings</i>.
  				 * 
  				 * @member
  				 * @type array
  				 * @default []
  				 * @private
  				 */
  				DataTable.settings = [];
  
  				/**
  				 * Object models container, for the various models that
  				 * DataTables has available to it. These models define the
  				 * objects that are used to hold the active state and
  				 * configuration of the table.
  				 * 
  				 * @namespace
  				 */
  				DataTable.models = {};
  
  				/**
  				 * DataTables extension options and plug-ins. This namespace
  				 * acts as a collection "area" for plug-ins that can be used to
  				 * extend the default DataTables behaviour - indeed many of the
  				 * build in methods use this method to provide their own
  				 * capabilities (sorting methods for example).
  				 * 
  				 * Note that this namespace is aliased to jQuery.fn.dataTableExt
  				 * so it can be readily accessed and modified by plug-ins.
  				 * 
  				 * @namespace
  				 */
  				DataTable.models.ext = {
  					/**
  					 * Plug-in filtering functions - this method of filtering is
  					 * complimentary to the default type based filtering, and a
  					 * lot more comprehensive as it allows you complete control
  					 * over the filtering logic. Each element in this array is a
  					 * function (parameters described below) that is called for
  					 * every row in the table, and your logic decides if it
  					 * should be included in the filtered data set or not.
  					 * <ul>
  					 * <li> Function input parameters:
  					 * <ul>
  					 * <li>{object} DataTables settings object: see
  					 * {@link DataTable.models.oSettings}.</li>
  					 * <li>{array|object} Data for the row to be processed
  					 * (same as the original format that was passed in as the
  					 * data source, or an array from a DOM data source</li>
  					 * <li>{int} Row index in aoData ({@link DataTable.models.oSettings.aoData}),
  					 * which can be useful to retrieve the TR element if you
  					 * need DOM interaction.</li>
  					 * </ul>
  					 * </li>
  					 * <li> Function return:
  					 * <ul>
  					 * <li>{boolean} Include the row in the filtered result set
  					 * (true) or not (false)</li>
  					 * </ul>
  					 * </il>
  					 * </ul>
  					 * 
  					 * @type array
  					 * @default []
  					 * 
  					 * @example // The following example shows custom filtering
  					 *          being applied to the fourth column (i.e. // the
  					 *          aData[3] index) based on two input values from
  					 *          the end-user, matching the data in // a certain
  					 *          range. $.fn.dataTableExt.afnFiltering.push(
  					 *          function( oSettings, aData, iDataIndex ) { var
  					 *          iMin = document.getElementById('min').value * 1;
  					 *          var iMax = document.getElementById('max').value *
  					 *          1; var iVersion = aData[3] == "-" ? 0 :
  					 *          aData[3]*1; if ( iMin == "" && iMax == "" ) {
  					 *          return true; } else if ( iMin == "" && iVersion <
  					 *          iMax ) { return true; } else if ( iMin <
  					 *          iVersion && "" == iMax ) { return true; } else
  					 *          if ( iMin < iVersion && iVersion < iMax ) {
  					 *          return true; } return false; } );
  					 */
  					"afnFiltering" : [],
  
  					/**
  					 * Plug-in sorting functions - this method of sorting is
  					 * complimentary to the default type based sorting that
  					 * DataTables does automatically, allowing much greater
  					 * control over the the data that is being used to sort a
  					 * column. This is useful if you want to do sorting based on
  					 * live data (for example the contents of an 'input'
  					 * element) rather than just the static string that
  					 * DataTables knows of. The way these plug-ins work is that
  					 * you create an array of the values you wish to be sorted
  					 * for the column in question and then return that array.
  					 * Which pre-sorting function is run here depends on the
  					 * sSortDataType parameter that is used for the column (if
  					 * any). This is the corollary of <i>ofnSearch</i> for sort
  					 * data.
  					 * <ul>
  					 * <li> Function input parameters:
  					 * <ul>
  					 * <li>{object} DataTables settings object: see
  					 * {@link DataTable.models.oSettings}.</li>
  					 * <li>{int} Target column index</li>
  					 * </ul>
  					 * </li>
  					 * <li> Function return:
  					 * <ul>
  					 * <li>{array} Data for the column to be sorted upon</li>
  					 * </ul>
  					 * </il>
  					 * </ul>
  					 * 
  					 * Note that as of v1.9, it is typically preferable to use
  					 * <i>mData</i> to prepare data for the different uses that
  					 * DataTables can put the data to. Specifically <i>mData</i>
  					 * when used as a function will give you a 'type' (sorting,
  					 * filtering etc) that you can use to prepare the data as
  					 * required for the different types. As such, this method is
  					 * deprecated.
  					 * 
  					 * @type array
  					 * @default []
  					 * @deprecated
  					 * 
  					 * @example // Updating the cached sorting information with
  					 *          user entered values in HTML input elements
  					 *          jQuery.fn.dataTableExt.afnSortData['dom-text'] =
  					 *          function ( oSettings, iColumn ) { var aData =
  					 *          []; $( 'td:eq('+iColumn+') input',
  					 *          oSettings.oApi._fnGetTrNodes(oSettings) ).each(
  					 *          function () { aData.push( this.value ); } );
  					 *          return aData; }
  					 */
  					"afnSortData" : [],
  
  					/**
  					 * Feature plug-ins - This is an array of objects which
  					 * describe the feature plug-ins that are available to
  					 * DataTables. These feature plug-ins are accessible through
  					 * the sDom initialisation option. As such, each feature
  					 * plug-in must describe a function that is used to
  					 * initialise itself (fnInit), a character so the feature
  					 * can be enabled by sDom (cFeature) and the name of the
  					 * feature (sFeature). Thus the objects attached to this
  					 * method must provide:
  					 * <ul>
  					 * <li>{function} fnInit Initialisation of the plug-in
  					 * <ul>
  					 * <li> Function input parameters:
  					 * <ul>
  					 * <li>{object} DataTables settings object: see
  					 * {@link DataTable.models.oSettings}.</li>
  					 * </ul>
  					 * </li>
  					 * <li> Function return:
  					 * <ul>
  					 * <li>{node|null} The element which contains your feature.
  					 * Note that the return may also be void if your plug-in
  					 * does not require to inject any DOM elements into
  					 * DataTables control (sDom) - for example this might be
  					 * useful when developing a plug-in which allows table
  					 * control via keyboard entry.</li>
  					 * </ul>
  					 * </il>
  					 * </ul>
  					 * </li>
  					 * <li>{character} cFeature Character that will be matched
  					 * in sDom - case sensitive</li>
  					 * <li>{string} sFeature Feature name</li>
  					 * </ul>
  					 * 
  					 * @type array
  					 * @default []
  					 * 
  					 * @example // How TableTools initialises itself.
  					 *          $.fn.dataTableExt.aoFeatures.push( { "fnInit":
  					 *          function( oSettings ) { return new TableTools( {
  					 *          "oDTSettings": oSettings } ); }, "cFeature":
  					 *          "T", "sFeature": "TableTools" } );
  					 */
  					"aoFeatures" : [],
  
  					/**
  					 * Type detection plug-in functions - DataTables utilises
  					 * types to define how sorting and filtering behave, and
  					 * types can be either be defined by the developer (sType
  					 * for the column) or they can be automatically detected by
  					 * the methods in this array. The functions defined in the
  					 * array are quite simple, taking a single parameter (the
  					 * data to analyse) and returning the type if it is a known
  					 * type, or null otherwise.
  					 * <ul>
  					 * <li> Function input parameters:
  					 * <ul>
  					 * <li>{*} Data from the column cell to be analysed</li>
  					 * </ul>
  					 * </li>
  					 * <li> Function return:
  					 * <ul>
  					 * <li>{string|null} Data type detected, or null if unknown
  					 * (and thus pass it on to the other type detection
  					 * functions.</li>
  					 * </ul>
  					 * </il>
  					 * </ul>
  					 * 
  					 * @type array
  					 * @default []
  					 * 
  					 * @example // Currency type detection plug-in:
  					 *          jQuery.fn.dataTableExt.aTypes.push( function (
  					 *          sData ) { var sValidChars = "0123456789.-"; var
  					 *          Char;
  					 *  // Check the numeric part for ( i=1 ; i<sData.length ;
  					 * i++ ) { Char = sData.charAt(i); if
  					 * (sValidChars.indexOf(Char) == -1) { return null; } }
  					 *  // Check prefixed by currency if ( sData.charAt(0) ==
  					 * '$' || sData.charAt(0) == '&pound;' ) { return
  					 * 'currency'; } return null; } );
  					 */
  					"aTypes" : [],
  
  					/**
  					 * Provide a common method for plug-ins to check the version
  					 * of DataTables being used, in order to ensure
  					 * compatibility.
  					 * 
  					 * @type function
  					 * @param {string}
  					 *            sVersion Version string to check for, in the
  					 *            format "X.Y.Z". Note that the formats "X" and
  					 *            "X.Y" are also acceptable.
  					 * @returns {boolean} true if this version of DataTables is
  					 *          greater or equal to the required version, or
  					 *          false if this version of DataTales is not
  					 *          suitable
  					 * 
  					 * @example $(document).ready(function() { var oTable =
  					 *          $('#example').dataTable(); alert(
  					 *          oTable.fnVersionCheck( '1.9.0' ) ); } );
  					 */
  					"fnVersionCheck" : DataTable.fnVersionCheck,
  
  					/**
  					 * Index for what 'this' index API functions should use
  					 * 
  					 * @type int
  					 * @default 0
  					 */
  					"iApiIndex" : 0,
  
  					/**
  					 * Pre-processing of filtering data plug-ins - When you
  					 * assign the sType for a column (or have it automatically
  					 * detected for you by DataTables or a type detection
  					 * plug-in), you will typically be using this for custom
  					 * sorting, but it can also be used to provide custom
  					 * filtering by allowing you to pre-processing the data and
  					 * returning the data in the format that should be filtered
  					 * upon. This is done by adding functions this object with a
  					 * parameter name which matches the sType for that target
  					 * column. This is the corollary of <i>afnSortData</i> for
  					 * filtering data.
  					 * <ul>
  					 * <li> Function input parameters:
  					 * <ul>
  					 * <li>{*} Data from the column cell to be prepared for
  					 * filtering</li>
  					 * </ul>
  					 * </li>
  					 * <li> Function return:
  					 * <ul>
  					 * <li>{string|null} Formatted string that will be used for
  					 * the filtering.</li>
  					 * </ul>
  					 * </il>
  					 * </ul>
  					 * 
  					 * Note that as of v1.9, it is typically preferable to use
  					 * <i>mData</i> to prepare data for the different uses that
  					 * DataTables can put the data to. Specifically <i>mData</i>
  					 * when used as a function will give you a 'type' (sorting,
  					 * filtering etc) that you can use to prepare the data as
  					 * required for the different types. As such, this method is
  					 * deprecated.
  					 * 
  					 * @type object
  					 * @default {}
  					 * @deprecated
  					 * 
  					 * @example $.fn.dataTableExt.ofnSearch['title-numeric'] =
  					 *          function ( sData ) { return
  					 *          sData.replace(/\n/g," ").replace( /<.*?>/g, "" ); }
  					 */
  					"ofnSearch" : {},
  
  					/**
  					 * Container for all private functions in DataTables so they
  					 * can be exposed externally
  					 * 
  					 * @type object
  					 * @default {}
  					 */
  					"oApi" : {},
  
  					/**
  					 * Storage for the various classes that DataTables uses
  					 * 
  					 * @type object
  					 * @default {}
  					 */
  					"oStdClasses" : {},
  
  					/**
  					 * Storage for the various classes that DataTables uses -
  					 * jQuery UI suitable
  					 * 
  					 * @type object
  					 * @default {}
  					 */
  					"oJUIClasses" : {},
  
  					/**
  					 * Pagination plug-in methods - The style and controls of
  					 * the pagination can significantly impact on how the end
  					 * user interacts with the data in your table, and
  					 * DataTables allows the addition of pagination controls by
  					 * extending this object, which can then be enabled through
  					 * the <i>sPaginationType</i> initialisation parameter.
  					 * Each pagination type that is added is an object (the
  					 * property name of which is what <i>sPaginationType</i>
  					 * refers to) that has two properties, both methods that are
  					 * used by DataTables to update the control's state.
  					 * <ul>
  					 * <li> fnInit - Initialisation of the paging controls.
  					 * Called only during initialisation of the table. It is
  					 * expected that this function will add the required DOM
  					 * elements to the page for the paging controls to work. The
  					 * element pointer 'oSettings.aanFeatures.p' array is
  					 * provided by DataTables to contain the paging controls
  					 * (note that this is a 2D array to allow for multiple
  					 * instances of each DataTables DOM element). It is
  					 * suggested that you add the controls to this element as
  					 * children
  					 * <ul>
  					 * <li> Function input parameters:
  					 * <ul>
  					 * <li>{object} DataTables settings object: see
  					 * {@link DataTable.models.oSettings}.</li>
  					 * <li>{node} Container into which the pagination controls
  					 * must be inserted</li>
  					 * <li>{function} Draw callback function - whenever the
  					 * controls cause a page change, this method must be called
  					 * to redraw the table.</li>
  					 * </ul>
  					 * </li>
  					 * <li> Function return:
  					 * <ul>
  					 * <li>No return required</li>
  					 * </ul>
  					 * </il>
  					 * </ul>
  					 * </il>
  					 * <li> fnInit - This function is called whenever the paging
  					 * status of the table changes and is typically used to
  					 * update classes and/or text of the paging controls to
  					 * reflex the new status.
  					 * <ul>
  					 * <li> Function input parameters:
  					 * <ul>
  					 * <li>{object} DataTables settings object: see
  					 * {@link DataTable.models.oSettings}.</li>
  					 * <li>{function} Draw callback function - in case you need
  					 * to redraw the table again or attach new event listeners</li>
  					 * </ul>
  					 * </li>
  					 * <li> Function return:
  					 * <ul>
  					 * <li>No return required</li>
  					 * </ul>
  					 * </il>
  					 * </ul>
  					 * </il>
  					 * </ul>
  					 * 
  					 * @type object
  					 * @default {}
  					 * 
  					 * @example $.fn.dataTableExt.oPagination.four_button = {
  					 *          "fnInit": function ( oSettings, nPaging,
  					 *          fnCallbackDraw ) { nFirst =
  					 *          document.createElement( 'span' ); nPrevious =
  					 *          document.createElement( 'span' ); nNext =
  					 *          document.createElement( 'span' ); nLast =
  					 *          document.createElement( 'span' );
  					 * 
  					 * nFirst.appendChild( document.createTextNode(
  					 * oSettings.oLanguage.oPaginate.sFirst ) );
  					 * nPrevious.appendChild( document.createTextNode(
  					 * oSettings.oLanguage.oPaginate.sPrevious ) );
  					 * nNext.appendChild( document.createTextNode(
  					 * oSettings.oLanguage.oPaginate.sNext ) );
  					 * nLast.appendChild( document.createTextNode(
  					 * oSettings.oLanguage.oPaginate.sLast ) );
  					 * 
  					 * nFirst.className = "paginate_button first";
  					 * nPrevious.className = "paginate_button previous";
  					 * nNext.className="paginate_button next"; nLast.className =
  					 * "paginate_button last";
  					 * 
  					 * nPaging.appendChild( nFirst ); nPaging.appendChild(
  					 * nPrevious ); nPaging.appendChild( nNext );
  					 * nPaging.appendChild( nLast );
  					 * 
  					 * $(nFirst).click( function () {
  					 * oSettings.oApi._fnPageChange( oSettings, "first" );
  					 * fnCallbackDraw( oSettings ); } );
  					 * 
  					 * $(nPrevious).click( function() {
  					 * oSettings.oApi._fnPageChange( oSettings, "previous" );
  					 * fnCallbackDraw( oSettings ); } );
  					 * 
  					 * $(nNext).click( function() {
  					 * oSettings.oApi._fnPageChange( oSettings, "next" );
  					 * fnCallbackDraw( oSettings ); } );
  					 * 
  					 * $(nLast).click( function() {
  					 * oSettings.oApi._fnPageChange( oSettings, "last" );
  					 * fnCallbackDraw( oSettings ); } );
  					 * 
  					 * $(nFirst).bind( 'selectstart', function () { return
  					 * false; } ); $(nPrevious).bind( 'selectstart', function () {
  					 * return false; } ); $(nNext).bind( 'selectstart', function () {
  					 * return false; } ); $(nLast).bind( 'selectstart', function () {
  					 * return false; } ); },
  					 * 
  					 * "fnUpdate": function ( oSettings, fnCallbackDraw ) { if (
  					 * !oSettings.aanFeatures.p ) { return; }
  					 *  // Loop over each instance of the pager var an =
  					 * oSettings.aanFeatures.p; for ( var i=0, iLen=an.length ;
  					 * i<iLen ; i++ ) { var buttons =
  					 * an[i].getElementsByTagName('span'); if (
  					 * oSettings._iDisplayStart === 0 ) { buttons[0].className =
  					 * "paginate_disabled_previous"; buttons[1].className =
  					 * "paginate_disabled_previous"; } else {
  					 * buttons[0].className = "paginate_enabled_previous";
  					 * buttons[1].className = "paginate_enabled_previous"; }
  					 * 
  					 * if ( oSettings.fnDisplayEnd() ==
  					 * oSettings.fnRecordsDisplay() ) { buttons[2].className =
  					 * "paginate_disabled_next"; buttons[3].className =
  					 * "paginate_disabled_next"; } else { buttons[2].className =
  					 * "paginate_enabled_next"; buttons[3].className =
  					 * "paginate_enabled_next"; } } } };
  					 */
  					"oPagination" : {},
  
  					/**
  					 * Sorting plug-in methods - Sorting in DataTables is based
  					 * on the detected type of the data column (you can add your
  					 * own type detection functions, or override automatic
  					 * detection using sType). With this specific type given to
  					 * the column, DataTables will apply the required sort from
  					 * the functions in the object. Each sort type must provide
  					 * two mandatory methods, one each for ascending and
  					 * descending sorting, and can optionally provide a
  					 * pre-formatting method that will help speed up sorting by
  					 * allowing DataTables to pre-format the sort data only once
  					 * (rather than every time the actual sort functions are
  					 * run). The two sorting functions are typical Javascript
  					 * sort methods:
  					 * <ul>
  					 * <li> Function input parameters:
  					 * <ul>
  					 * <li>{*} Data to compare to the second parameter</li>
  					 * <li>{*} Data to compare to the first parameter</li>
  					 * </ul>
  					 * </li>
  					 * <li> Function return:
  					 * <ul>
  					 * <li>{int} Sorting match: <0 if first parameter should be
  					 * sorted lower than the second parameter, ===0 if the two
  					 * parameters are equal and >0 if the first parameter should
  					 * be sorted height than the second parameter.</li>
  					 * </ul>
  					 * </il>
  					 * </ul>
  					 * 
  					 * @type object
  					 * @default {}
  					 * 
  					 * @example // Case-sensitive string sorting, with no
  					 *          pre-formatting method $.extend(
  					 *          $.fn.dataTableExt.oSort, { "string-case-asc":
  					 *          function(x,y) { return ((x < y) ? -1 : ((x > y) ?
  					 *          1 : 0)); }, "string-case-desc": function(x,y) {
  					 *          return ((x < y) ? 1 : ((x > y) ? -1 : 0)); } } );
  					 * 
  					 * @example // Case-insensitive string sorting, with
  					 *          pre-formatting $.extend(
  					 *          $.fn.dataTableExt.oSort, { "string-pre":
  					 *          function(x) { return x.toLowerCase(); },
  					 *          "string-asc": function(x,y) { return ((x < y) ?
  					 *          -1 : ((x > y) ? 1 : 0)); }, "string-desc":
  					 *          function(x,y) { return ((x < y) ? 1 : ((x > y) ?
  					 *          -1 : 0)); } } );
  					 */
  					"oSort" : {},
  
  					/**
  					 * Version string for plug-ins to check compatibility.
  					 * Allowed format is a.b.c.d.e where: a:int, b:int, c:int,
  					 * d:string(dev|beta), e:int. d and e are optional
  					 * 
  					 * @type string
  					 * @default Version number
  					 */
  					"sVersion" : DataTable.version,
  
  					/**
  					 * How should DataTables report an error. Can take the value
  					 * 'alert' or 'throw'
  					 * 
  					 * @type string
  					 * @default alert
  					 */
  					"sErrMode" : "alert",
  
  					/**
  					 * Store information for DataTables to access globally about
  					 * other instances
  					 * 
  					 * @namespace
  					 * @private
  					 */
  					"_oExternConfig" : {
  						/* int:iNextUnique - next unique number for an instance */
  						"iNextUnique" : 0
  					}
  				};
  
  				/**
  				 * Template object for the way in which DataTables holds
  				 * information about search information for the global filter
  				 * and individual column filters.
  				 * 
  				 * @namespace
  				 */
  				DataTable.models.oSearch = {
  					/**
  					 * Flag to indicate if the filtering should be case
  					 * insensitive or not
  					 * 
  					 * @type boolean
  					 * @default true
  					 */
  					"bCaseInsensitive" : true,
  
  					/**
  					 * Applied search term
  					 * 
  					 * @type string
  					 * @default <i>Empty string</i>
  					 */
  					"sSearch" : "",
  
  					/**
  					 * Flag to indicate if the search term should be interpreted
  					 * as a regular expression (true) or not (false) and
  					 * therefore and special regex characters escaped.
  					 * 
  					 * @type boolean
  					 * @default false
  					 */
  					"bRegex" : false,
  
  					/**
  					 * Flag to indicate if DataTables is to use its smart
  					 * filtering or not.
  					 * 
  					 * @type boolean
  					 * @default true
  					 */
  					"bSmart" : true
  				};
  
  				/**
  				 * Template object for the way in which DataTables holds
  				 * information about each individual row. This is the object
  				 * format used for the settings aoData array.
  				 * 
  				 * @namespace
  				 */
  				DataTable.models.oRow = {
  					/**
  					 * TR element for the row
  					 * 
  					 * @type node
  					 * @default null
  					 */
  					"nTr" : null,
  
  					/**
  					 * Data object from the original data source for the row.
  					 * This is either an array if using the traditional form of
  					 * DataTables, or an object if using mData options. The
  					 * exact type will depend on the passed in data from the
  					 * data source, or will be an array if using DOM a data
  					 * source.
  					 * 
  					 * @type array|object
  					 * @default []
  					 */
  					"_aData" : [],
  
  					/**
  					 * Sorting data cache - this array is ostensibly the same
  					 * length as the number of columns (although each index is
  					 * generated only as it is needed), and holds the data that
  					 * is used for sorting each column in the row. We do this
  					 * cache generation at the start of the sort in order that
  					 * the formatting of the sort data need be done only once
  					 * for each cell per sort. This array should not be read
  					 * from or written to by anything other than the master
  					 * sorting methods.
  					 * 
  					 * @type array
  					 * @default []
  					 * @private
  					 */
  					"_aSortData" : [],
  
  					/**
  					 * Array of TD elements that are cached for hidden rows, so
  					 * they can be reinserted into the table if a column is made
  					 * visible again (or to act as a store if a column is made
  					 * hidden). Only hidden columns have a reference in the
  					 * array. For non-hidden columns the value is either
  					 * undefined or null.
  					 * 
  					 * @type array nodes
  					 * @default []
  					 * @private
  					 */
  					"_anHidden" : [],
  
  					/**
  					 * Cache of the class name that DataTables has applied to
  					 * the row, so we can quickly look at this variable rather
  					 * than needing to do a DOM check on className for the nTr
  					 * property.
  					 * 
  					 * @type string
  					 * @default <i>Empty string</i>
  					 * @private
  					 */
  					"_sRowStripe" : ""
  				};
  
  				/**
  				 * Template object for the column information object in
  				 * DataTables. This object is held in the settings aoColumns
  				 * array and contains all the information that DataTables needs
  				 * about each individual column.
  				 * 
  				 * Note that this object is related to
  				 * {@link DataTable.defaults.columns} but this one is the
  				 * internal data store for DataTables's cache of columns. It
  				 * should NOT be manipulated outside of DataTables. Any
  				 * configuration should be done through the initialisation
  				 * options.
  				 * 
  				 * @namespace
  				 */
  				DataTable.models.oColumn = {
  					/**
  					 * A list of the columns that sorting should occur on when
  					 * this column is sorted. That this property is an array
  					 * allows multi-column sorting to be defined for a column
  					 * (for example first name / last name columns would benefit
  					 * from this). The values are integers pointing to the
  					 * columns to be sorted on (typically it will be a single
  					 * integer pointing at itself, but that doesn't need to be
  					 * the case).
  					 * 
  					 * @type array
  					 */
  					"aDataSort" : null,
  
  					/**
  					 * Define the sorting directions that are applied to the
  					 * column, in sequence as the column is repeatedly sorted
  					 * upon - i.e. the first value is used as the sorting
  					 * direction when the column if first sorted (clicked on).
  					 * Sort it again (click again) and it will move on to the
  					 * next index. Repeat until loop.
  					 * 
  					 * @type array
  					 */
  					"asSorting" : null,
  
  					/**
  					 * Flag to indicate if the column is searchable, and thus
  					 * should be included in the filtering or not.
  					 * 
  					 * @type boolean
  					 */
  					"bSearchable" : null,
  
  					/**
  					 * Flag to indicate if the column is sortable or not.
  					 * 
  					 * @type boolean
  					 */
  					"bSortable" : null,
  
  					/**
  					 * <code>Deprecated</code> When using fnRender, you have
  					 * two options for what to do with the data, and this
  					 * property serves as the switch. Firstly, you can have the
  					 * sorting and filtering use the rendered value (true -
  					 * default), or you can have the sorting and filtering us
  					 * the original value (false).
  					 * 
  					 * Please note that this option has now been deprecated and
  					 * will be removed in the next version of DataTables. Please
  					 * use mRender / mData rather than fnRender.
  					 * 
  					 * @type boolean
  					 * @deprecated
  					 */
  					"bUseRendered" : null,
  
  					/**
  					 * Flag to indicate if the column is currently visible in
  					 * the table or not
  					 * 
  					 * @type boolean
  					 */
  					"bVisible" : null,
  
  					/**
  					 * Flag to indicate to the type detection method if the
  					 * automatic type detection should be used, or if a column
  					 * type (sType) has been specified
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @private
  					 */
  					"_bAutoType" : true,
  
  					/**
  					 * Developer definable function that is called whenever a
  					 * cell is created (Ajax source, etc) or processed for input
  					 * (DOM source). This can be used as a compliment to mRender
  					 * allowing you to modify the DOM element (add background
  					 * colour for example) when the element is available.
  					 * 
  					 * @type function
  					 * @param {element}
  					 *            nTd The TD node that has been created
  					 * @param {*}
  					 *            sData The Data for the cell
  					 * @param {array|object}
  					 *            oData The data for the whole row
  					 * @param {int}
  					 *            iRow The row index for the aoData data store
  					 * @default null
  					 */
  					"fnCreatedCell" : null,
  
  					/**
  					 * Function to get data from a cell in a column. You should
  					 * <b>never</b> access data directly through _aData
  					 * internally in DataTables - always use the method attached
  					 * to this property. It allows mData to function as
  					 * required. This function is automatically assigned by the
  					 * column initialisation method
  					 * 
  					 * @type function
  					 * @param {array|object}
  					 *            oData The data array/object for the array
  					 *            (i.e. aoData[]._aData)
  					 * @param {string}
  					 *            sSpecific The specific data type you want to
  					 *            get - 'display', 'type' 'filter' 'sort'
  					 * @returns {*} The data for the cell from the given row's
  					 *          data
  					 * @default null
  					 */
  					"fnGetData" : null,
  
  					/**
  					 * <code>Deprecated</code> Custom display function that
  					 * will be called for the display of each cell in this
  					 * column.
  					 * 
  					 * Please note that this option has now been deprecated and
  					 * will be removed in the next version of DataTables. Please
  					 * use mRender / mData rather than fnRender.
  					 * 
  					 * @type function
  					 * @param {object}
  					 *            o Object with the following parameters:
  					 * @param {int}
  					 *            o.iDataRow The row in aoData
  					 * @param {int}
  					 *            o.iDataColumn The column in question
  					 * @param {array}
  					 *            o.aData The data for the row in question
  					 * @param {object}
  					 *            o.oSettings The settings object for this
  					 *            DataTables instance
  					 * @returns {string} The string you which to use in the
  					 *          display
  					 * @default null
  					 * @deprecated
  					 */
  					"fnRender" : null,
  
  					/**
  					 * Function to set data for a cell in the column. You should
  					 * <b>never</b> set the data directly to _aData internally
  					 * in DataTables - always use this method. It allows mData
  					 * to function as required. This function is automatically
  					 * assigned by the column initialisation method
  					 * 
  					 * @type function
  					 * @param {array|object}
  					 *            oData The data array/object for the array
  					 *            (i.e. aoData[]._aData)
  					 * @param {*}
  					 *            sValue Value to set
  					 * @default null
  					 */
  					"fnSetData" : null,
  
  					/**
  					 * Property to read the value for the cells in the column
  					 * from the data source array / object. If null, then the
  					 * default content is used, if a function is given then the
  					 * return from the function is used.
  					 * 
  					 * @type function|int|string|null
  					 * @default null
  					 */
  					"mData" : null,
  
  					/**
  					 * Partner property to mData which is used (only when
  					 * defined) to get the data - i.e. it is basically the same
  					 * as mData, but without the 'set' option, and also the data
  					 * fed to it is the result from mData. This is the rendering
  					 * method to match the data method of mData.
  					 * 
  					 * @type function|int|string|null
  					 * @default null
  					 */
  					"mRender" : null,
  
  					/**
  					 * Unique header TH/TD element for this column - this is
  					 * what the sorting listener is attached to (if sorting is
  					 * enabled.)
  					 * 
  					 * @type node
  					 * @default null
  					 */
  					"nTh" : null,
  
  					/**
  					 * Unique footer TH/TD element for this column (if there is
  					 * one). Not used in DataTables as such, but can be used for
  					 * plug-ins to reference the footer for each column.
  					 * 
  					 * @type node
  					 * @default null
  					 */
  					"nTf" : null,
  
  					/**
  					 * The class to apply to all TD elements in the table's
  					 * TBODY for the column
  					 * 
  					 * @type string
  					 * @default null
  					 */
  					"sClass" : null,
  
  					/**
  					 * When DataTables calculates the column widths to assign to
  					 * each column, it finds the longest string in each column
  					 * and then constructs a temporary table and reads the
  					 * widths from that. The problem with this is that "mmm" is
  					 * much wider then "iiii", but the latter is a longer string -
  					 * thus the calculation can go wrong (doing it properly and
  					 * putting it into an DOM object and measuring that is
  					 * horribly(!) slow). Thus as a "work around" we provide
  					 * this option. It will append its value to the text that is
  					 * found to be the longest string for the column - i.e.
  					 * padding.
  					 * 
  					 * @type string
  					 */
  					"sContentPadding" : null,
  
  					/**
  					 * Allows a default value to be given for a column's data,
  					 * and will be used whenever a null data source is
  					 * encountered (this can be because mData is set to null, or
  					 * because the data source itself is null).
  					 * 
  					 * @type string
  					 * @default null
  					 */
  					"sDefaultContent" : null,
  
  					/**
  					 * Name for the column, allowing reference to the column by
  					 * name as well as by index (needs a lookup to work by
  					 * name).
  					 * 
  					 * @type string
  					 */
  					"sName" : null,
  
  					/**
  					 * Custom sorting data type - defines which of the available
  					 * plug-ins in afnSortData the custom sorting will use - if
  					 * any is defined.
  					 * 
  					 * @type string
  					 * @default std
  					 */
  					"sSortDataType" : 'std',
  
  					/**
  					 * Class to be applied to the header element when sorting on
  					 * this column
  					 * 
  					 * @type string
  					 * @default null
  					 */
  					"sSortingClass" : null,
  
  					/**
  					 * Class to be applied to the header element when sorting on
  					 * this column - when jQuery UI theming is used.
  					 * 
  					 * @type string
  					 * @default null
  					 */
  					"sSortingClassJUI" : null,
  
  					/**
  					 * Title of the column - what is seen in the TH element
  					 * (nTh).
  					 * 
  					 * @type string
  					 */
  					"sTitle" : null,
  
  					/**
  					 * Column sorting and filtering type
  					 * 
  					 * @type string
  					 * @default null
  					 */
  					"sType" : null,
  
  					/**
  					 * Width of the column
  					 * 
  					 * @type string
  					 * @default null
  					 */
  					"sWidth" : null,
  
  					/**
  					 * Width of the column when it was first "encountered"
  					 * 
  					 * @type string
  					 * @default null
  					 */
  					"sWidthOrig" : null
  				};
  
  				/**
  				 * Initialisation options that can be given to DataTables at
  				 * initialisation time.
  				 * 
  				 * @namespace
  				 */
  				DataTable.defaults = {
  					/**
  					 * An array of data to use for the table, passed in at
  					 * initialisation which will be used in preference to any
  					 * data which is already in the DOM. This is particularly
  					 * useful for constructing tables purely in Javascript, for
  					 * example with a custom Ajax call.
  					 * 
  					 * @type array
  					 * @default null
  					 * @dtopt Option
  					 * 
  					 * @example // Using a 2D array data source
  					 *          $(document).ready( function () {
  					 *          $('#example').dataTable( { "aaData": [
  					 *          ['Trident', 'Internet Explorer 4.0', 'Win 95+',
  					 *          4, 'X'], ['Trident', 'Internet Explorer 5.0',
  					 *          'Win 95+', 5, 'C'], ], "aoColumns": [ {
  					 *          "sTitle": "Engine" }, { "sTitle": "Browser" }, {
  					 *          "sTitle": "Platform" }, { "sTitle": "Version" }, {
  					 *          "sTitle": "Grade" } ] } ); } );
  					 * 
  					 * @example // Using an array of objects as a data source
  					 *          (mData) $(document).ready( function () {
  					 *          $('#example').dataTable( { "aaData": [ {
  					 *          "engine": "Trident", "browser": "Internet
  					 *          Explorer 4.0", "platform": "Win 95+", "version":
  					 *          4, "grade": "X" }, { "engine": "Trident",
  					 *          "browser": "Internet Explorer 5.0", "platform":
  					 *          "Win 95+", "version": 5, "grade": "C" } ],
  					 *          "aoColumns": [ { "sTitle": "Engine", "mData":
  					 *          "engine" }, { "sTitle": "Browser", "mData":
  					 *          "browser" }, { "sTitle": "Platform", "mData":
  					 *          "platform" }, { "sTitle": "Version", "mData":
  					 *          "version" }, { "sTitle": "Grade", "mData":
  					 *          "grade" } ] } ); } );
  					 */
  					"aaData" : null,
  
  					/**
  					 * If sorting is enabled, then DataTables will perform a
  					 * first pass sort on initialisation. You can define which
  					 * column(s) the sort is performed upon, and the sorting
  					 * direction, with this variable. The aaSorting array should
  					 * contain an array for each column to be sorted initially
  					 * containing the column's index and a direction string
  					 * ('asc' or 'desc').
  					 * 
  					 * @type array
  					 * @default [[0,'asc']]
  					 * @dtopt Option
  					 * 
  					 * @example // Sort by 3rd column first, and then 4th column
  					 *          $(document).ready( function() {
  					 *          $('#example').dataTable( { "aaSorting":
  					 *          [[2,'asc'], [3,'desc']] } ); } );
  					 *  // No initial sorting $(document).ready(
  					 * function() { $('#example').dataTable( { "aaSorting": [] } ); } );
  					 */
  					"aaSorting" : [ [ 0, 'asc' ] ],
  
  					/**
  					 * This parameter is basically identical to the aaSorting
  					 * parameter, but cannot be overridden by user interaction
  					 * with the table. What this means is that you could have a
  					 * column (visible or hidden) which the sorting will always
  					 * be forced on first - any sorting after that (from the
  					 * user) will then be performed as required. This can be
  					 * useful for grouping rows together.
  					 * 
  					 * @type array
  					 * @default null
  					 * @dtopt Option
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "aaSortingFixed":
  					 *          [[0,'asc']] } ); } )
  					 */
  					"aaSortingFixed" : null,
  
  					/**
  					 * This parameter allows you to readily specify the entries
  					 * in the length drop down menu that DataTables shows when
  					 * pagination is enabled. It can be either a 1D array of
  					 * options which will be used for both the displayed option
  					 * and the value, or a 2D array which will use the array in
  					 * the first position as the value, and the array in the
  					 * second position as the displayed options (useful for
  					 * language strings such as 'All').
  					 * 
  					 * @type array
  					 * @default [ 10, 25, 50, 100 ]
  					 * @dtopt Option
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "aLengthMenu": [[10,
  					 *          25, 50, -1], [10, 25, 50, "All"]] } ); } );
  					 * 
  					 * @example // Setting the default display length as well as
  					 *          length menu // This is likely to be wanted if
  					 *          you remove the '10' option which // is the
  					 *          iDisplayLength default. $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "iDisplayLength": 25, "aLengthMenu": [[25, 50,
  					 *          100, -1], [25, 50, 100, "All"]] } ); } );
  					 */
  					"aLengthMenu" : [ 10, 25, 50, 100 ],
  
  					/**
  					 * The aoColumns option in the initialisation parameter
  					 * allows you to define details about the way individual
  					 * columns behave. For a full list of column options that
  					 * can be set, please see {@link DataTable.defaults.columns}.
  					 * Note that if you use aoColumns to define your columns,
  					 * you must have an entry in the array for every single
  					 * column that you have in your table (these can be null if
  					 * you don't which to specify any options).
  					 * 
  					 * @member
  					 */
  					"aoColumns" : null,
  
  					/**
  					 * Very similar to aoColumns, aoColumnDefs allows you to
  					 * target a specific column, multiple columns, or all
  					 * columns, using the aTargets property of each object in
  					 * the array. This allows great flexibility when creating
  					 * tables, as the aoColumnDefs arrays can be of any length,
  					 * targeting the columns you specifically want. aoColumnDefs
  					 * may use any of the column options available:
  					 * {@link DataTable.defaults.columns}, but it _must_ have
  					 * aTargets defined in each object in the array. Values in
  					 * the aTargets array may be:
  					 * <ul>
  					 * <li>a string - class name will be matched on the TH for
  					 * the column</li>
  					 * <li>0 or a positive integer - column index counting from
  					 * the left</li>
  					 * <li>a negative integer - column index counting from the
  					 * right</li>
  					 * <li>the string "_all" - all columns (i.e. assign a
  					 * default)</li>
  					 * </ul>
  					 * 
  					 * @member
  					 */
  					"aoColumnDefs" : null,
  
  					/**
  					 * Basically the same as oSearch, this parameter defines the
  					 * individual column filtering state at initialisation time.
  					 * The array must be of the same size as the number of
  					 * columns, and each element be an object with the
  					 * parameters "sSearch" and "bEscapeRegex" (the latter is
  					 * optional). 'null' is also accepted and the default will
  					 * be used.
  					 * 
  					 * @type array
  					 * @default []
  					 * @dtopt Option
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoSearchCols": [
  					 *          null, { "sSearch": "My filter" }, null, {
  					 *          "sSearch": "^[0-9]", "bEscapeRegex": false } ] } ); } )
  					 */
  					"aoSearchCols" : [],
  
  					/**
  					 * An array of CSS classes that should be applied to
  					 * displayed rows. This array may be of any length, and
  					 * DataTables will apply each class sequentially, looping
  					 * when required.
  					 * 
  					 * @type array
  					 * @default null <i>Will take the values determined by the
  					 *          oClasses.sStripe* options</i>
  					 * @dtopt Option
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "asStripeClasses": [
  					 *          'strip1', 'strip2', 'strip3' ] } ); } )
  					 */
  					"asStripeClasses" : null,
  
  					/**
  					 * Enable or disable automatic column width calculation.
  					 * This can be disabled as an optimisation (it takes some
  					 * time to calculate the widths) if the tables widths are
  					 * passed in using aoColumns.
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function () {
  					 *          $('#example').dataTable( { "bAutoWidth": false } ); } );
  					 */
  					"bAutoWidth" : true,
  
  					/**
  					 * Deferred rendering can provide DataTables with a huge
  					 * speed boost when you are using an Ajax or JS data source
  					 * for the table. This option, when set to true, will cause
  					 * DataTables to defer the creation of the table elements
  					 * for each row until they are needed for a draw - saving a
  					 * significant amount of time.
  					 * 
  					 * @type boolean
  					 * @default false
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function() { var oTable =
  					 *          $('#example').dataTable( { "sAjaxSource":
  					 *          "sources/arrays.txt", "bDeferRender": true } ); } );
  					 */
  					"bDeferRender" : false,
  
  					/**
  					 * Replace a DataTable which matches the given selector and
  					 * replace it with one which has the properties of the new
  					 * initialisation object passed. If no table matches the
  					 * selector, then the new DataTable will be constructed as
  					 * per normal.
  					 * 
  					 * @type boolean
  					 * @default false
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "sScrollY": "200px",
  					 *          "bPaginate": false } );
  					 *  // Some time later.... $('#example').dataTable( {
  					 * "bFilter": false, "bDestroy": true } ); } );
  					 */
  					"bDestroy" : false,
  
  					/**
  					 * Enable or disable filtering of data. Filtering in
  					 * DataTables is "smart" in that it allows the end user to
  					 * input multiple words (space separated) and will match a
  					 * row containing those words, even if not in the order that
  					 * was specified (this allow matching across multiple
  					 * columns). Note that if you wish to use filtering in
  					 * DataTables this must remain 'true' - to remove the
  					 * default filtering input box and retain filtering
  					 * abilities, please use {@link DataTable.defaults.sDom}.
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function () {
  					 *          $('#example').dataTable( { "bFilter": false } ); } );
  					 */
  					"bFilter" : true,
  
  					/**
  					 * Enable or disable the table information display. This
  					 * shows information about the data that is currently
  					 * visible on the page, including information about filtered
  					 * data if that action is being performed.
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function () {
  					 *          $('#example').dataTable( { "bInfo": false } ); } );
  					 */
  					"bInfo" : true,
  
  					/**
  					 * Enable jQuery UI ThemeRoller support (required as
  					 * ThemeRoller requires some slightly different and
  					 * additional mark-up from what DataTables has traditionally
  					 * used).
  					 * 
  					 * @type boolean
  					 * @default false
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "bJQueryUI": true } ); } );
  					 */
  					"bJQueryUI" : false,
  
  					/**
  					 * Allows the end user to select the size of a formatted
  					 * page from a select menu (sizes are 10, 25, 50 and 100).
  					 * Requires pagination (bPaginate).
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function () {
  					 *          $('#example').dataTable( { "bLengthChange":
  					 *          false } ); } );
  					 */
  					"bLengthChange" : true,
  
  					/**
  					 * Enable or disable pagination.
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function () {
  					 *          $('#example').dataTable( { "bPaginate": false } ); } );
  					 */
  					"bPaginate" : true,
  
  					/**
  					 * Enable or disable the display of a 'processing' indicator
  					 * when the table is being processed (e.g. a sort). This is
  					 * particularly useful for tables with large amounts of data
  					 * where it can take a noticeable amount of time to sort the
  					 * entries.
  					 * 
  					 * @type boolean
  					 * @default false
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function () {
  					 *          $('#example').dataTable( { "bProcessing": true } ); } );
  					 */
  					"bProcessing" : false,
  
  					/**
  					 * Retrieve the DataTables object for the given selector.
  					 * Note that if the table has already been initialised, this
  					 * parameter will cause DataTables to simply return the
  					 * object that has already been set up - it will not take
  					 * account of any changes you might have made to the
  					 * initialisation object passed to DataTables (setting this
  					 * parameter to true is an acknowledgement that you
  					 * understand this). bDestroy can be used to reinitialise a
  					 * table if you need.
  					 * 
  					 * @type boolean
  					 * @default false
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() { initTable();
  					 *          tableActions(); } );
  					 * 
  					 * function initTable () { return $('#example').dataTable( {
  					 * "sScrollY": "200px", "bPaginate": false, "bRetrieve":
  					 * true } ); }
  					 * 
  					 * function tableActions () { var oTable = initTable(); //
  					 * perform API operations with oTable }
  					 */
  					"bRetrieve" : false,
  
  					/**
  					 * Indicate if DataTables should be allowed to set the
  					 * padding / margin etc for the scrolling header elements or
  					 * not. Typically you will want this.
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "bScrollAutoCss":
  					 *          false, "sScrollY": "200px" } ); } );
  					 */
  					"bScrollAutoCss" : true,
  
  					/**
  					 * When vertical (y) scrolling is enabled, DataTables will
  					 * force the height of the table's viewport to the given
  					 * height at all times (useful for layout). However, this
  					 * can look odd when filtering data down to a small data
  					 * set, and the footer is left "floating" further down. This
  					 * parameter (when enabled) will cause DataTables to
  					 * collapse the table's viewport down when the result set
  					 * will fit within the given Y height.
  					 * 
  					 * @type boolean
  					 * @default false
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "sScrollY": "200",
  					 *          "bScrollCollapse": true } ); } );
  					 */
  					"bScrollCollapse" : false,
  
  					/**
  					 * Enable infinite scrolling for DataTables (to be used in
  					 * combination with sScrollY). Infinite scrolling means that
  					 * DataTables will continually load data as a user scrolls
  					 * through a table, which is very useful for large dataset.
  					 * This cannot be used with pagination, which is
  					 * automatically disabled. Note - the Scroller extra for
  					 * DataTables is recommended in in preference to this
  					 * option.
  					 * 
  					 * @type boolean
  					 * @default false
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "bScrollInfinite":
  					 *          true, "bScrollCollapse": true, "sScrollY":
  					 *          "200px" } ); } );
  					 */
  					"bScrollInfinite" : false,
  
  					/**
  					 * Configure DataTables to use server-side processing. Note
  					 * that the sAjaxSource parameter must also be given in
  					 * order to give DataTables a source to obtain the required
  					 * data for each draw.
  					 * 
  					 * @type boolean
  					 * @default false
  					 * @dtopt Features
  					 * @dtopt Server-side
  					 * 
  					 * @example $(document).ready( function () {
  					 *          $('#example').dataTable( { "bServerSide": true,
  					 *          "sAjaxSource": "xhr.php" } ); } );
  					 */
  					"bServerSide" : false,
  
  					/**
  					 * Enable or disable sorting of columns. Sorting of
  					 * individual columns can be disabled by the "bSortable"
  					 * option for each column.
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function () {
  					 *          $('#example').dataTable( { "bSort": false } ); } );
  					 */
  					"bSort" : true,
  
  					/**
  					 * Allows control over whether DataTables should use the top
  					 * (true) unique cell that is found for a single column, or
  					 * the bottom (false - default). This is useful when using
  					 * complex headers.
  					 * 
  					 * @type boolean
  					 * @default false
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "bSortCellsTop": true } ); } );
  					 */
  					"bSortCellsTop" : false,
  
  					/**
  					 * Enable or disable the addition of the classes
  					 * 'sorting_1', 'sorting_2' and 'sorting_3' to the columns
  					 * which are currently being sorted on. This is presented as
  					 * a feature switch as it can increase processing time
  					 * (while classes are removed and added) so for large data
  					 * sets you might want to turn this off.
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function () {
  					 *          $('#example').dataTable( { "bSortClasses": false } ); } );
  					 */
  					"bSortClasses" : true,
  
  					/**
  					 * Enable or disable state saving. When enabled a cookie
  					 * will be used to save table display information such as
  					 * pagination information, display length, filtering and
  					 * sorting. As such when the end user reloads the page the
  					 * display display will match what thy had previously set
  					 * up.
  					 * 
  					 * @type boolean
  					 * @default false
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function () {
  					 *          $('#example').dataTable( { "bStateSave": true } ); } );
  					 */
  					"bStateSave" : false,
  
  					/**
  					 * Customise the cookie and / or the parameters being stored
  					 * when using DataTables with state saving enabled. This
  					 * function is called whenever the cookie is modified, and
  					 * it expects a fully formed cookie string to be returned.
  					 * Note that the data object passed in is a Javascript
  					 * object which must be converted to a string
  					 * (JSON.stringify for example).
  					 * 
  					 * @type function
  					 * @param {string}
  					 *            sName Name of the cookie defined by DataTables
  					 * @param {object}
  					 *            oData Data to be stored in the cookie
  					 * @param {string}
  					 *            sExpires Cookie expires string
  					 * @param {string}
  					 *            sPath Path of the cookie to set
  					 * @returns {string} Cookie formatted string (which should
  					 *          be encoded by using encodeURIComponent())
  					 * @dtopt Callbacks
  					 * 
  					 * @example $(document).ready( function () {
  					 *          $('#example').dataTable( { "fnCookieCallback":
  					 *          function (sName, oData, sExpires, sPath) { //
  					 *          Customise oData or sName or whatever else here
  					 *          return sName + "="+JSON.stringify(oData)+";
  					 *          expires=" + sExpires +"; path=" + sPath; } } ); } );
  					 */
  					"fnCookieCallback" : null,
  
  					/**
  					 * This function is called when a TR element is created (and
  					 * all TD child elements have been inserted), or registered
  					 * if using a DOM source, allowing manipulation of the TR
  					 * element (adding classes etc).
  					 * 
  					 * @type function
  					 * @param {node}
  					 *            nRow "TR" element for the current row
  					 * @param {array}
  					 *            aData Raw data array for this row
  					 * @param {int}
  					 *            iDataIndex The index of this row in aoData
  					 * @dtopt Callbacks
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "fnCreatedRow":
  					 *          function( nRow, aData, iDataIndex ) { // Bold
  					 *          the grade for all 'A' grade browsers if (
  					 *          aData[4] == "A" ) { $('td:eq(4)', nRow).html( '<b>A</b>' ); } } } ); } );
  					 */
  					"fnCreatedRow" : null,
  
  					/**
  					 * This function is called on every 'draw' event, and allows
  					 * you to dynamically modify any aspect you want about the
  					 * created DOM.
  					 * 
  					 * @type function
  					 * @param {object}
  					 *            oSettings DataTables settings object
  					 * @dtopt Callbacks
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "fnDrawCallback":
  					 *          function( oSettings ) { alert( 'DataTables has
  					 *          redrawn the table' ); } } ); } );
  					 */
  					"fnDrawCallback" : null,
  
  					/**
  					 * Identical to fnHeaderCallback() but for the table footer
  					 * this function allows you to modify the table footer on
  					 * every 'draw' even.
  					 * 
  					 * @type function
  					 * @param {node}
  					 *            nFoot "TR" element for the footer
  					 * @param {array}
  					 *            aData Full table data (as derived from the
  					 *            original HTML)
  					 * @param {int}
  					 *            iStart Index for the current display starting
  					 *            point in the display array
  					 * @param {int}
  					 *            iEnd Index for the current display ending
  					 *            point in the display array
  					 * @param {array
  					 *            int} aiDisplay Index array to translate the
  					 *            visual position to the full data array
  					 * @dtopt Callbacks
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "fnFooterCallback":
  					 *          function( nFoot, aData, iStart, iEnd, aiDisplay ) {
  					 *          nFoot.getElementsByTagName('th')[0].innerHTML =
  					 *          "Starting index is "+iStart; } } ); } )
  					 */
  					"fnFooterCallback" : null,
  
  					/**
  					 * When rendering large numbers in the information element
  					 * for the table (i.e. "Showing 1 to 10 of 57 entries")
  					 * DataTables will render large numbers to have a comma
  					 * separator for the 'thousands' units (e.g. 1 million is
  					 * rendered as "1,000,000") to help readability for the end
  					 * user. This function will override the default method
  					 * DataTables uses.
  					 * 
  					 * @type function
  					 * @member
  					 * @param {int}
  					 *            iIn number to be formatted
  					 * @returns {string} formatted string for DataTables to show
  					 *          the number
  					 * @dtopt Callbacks
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "fnFormatNumber":
  					 *          function ( iIn ) { if ( iIn &lt; 1000 ) { return
  					 *          iIn; } else { var s=(iIn+""), a=s.split(""),
  					 *          out="", iLen=s.length;
  					 * 
  					 * for ( var i=0 ; i&lt;iLen ; i++ ) { if ( i%3 === 0
  					 * &amp;&amp; i !== 0 ) { out = "'"+out; } out =
  					 * a[iLen-i-1]+out; } } return out; }; } ); } );
  					 */
  					"fnFormatNumber" : function(iIn) {
  						if (iIn < 1000) {
  							// A small optimisation for what is likely to be the
  							// majority of use cases
  							return iIn;
  						}
  
  						var s = (iIn + ""), a = s.split(""), out = "", iLen = s.length;
  
  						for (var i = 0; i < iLen; i++) {
  							if (i % 3 === 0 && i !== 0) {
  								out = this.oLanguage.sInfoThousands + out;
  							}
  							out = a[iLen - i - 1] + out;
  						}
  						return out;
  					},
  
  					/**
  					 * This function is called on every 'draw' event, and allows
  					 * you to dynamically modify the header row. This can be
  					 * used to calculate and display useful information about
  					 * the table.
  					 * 
  					 * @type function
  					 * @param {node}
  					 *            nHead "TR" element for the header
  					 * @param {array}
  					 *            aData Full table data (as derived from the
  					 *            original HTML)
  					 * @param {int}
  					 *            iStart Index for the current display starting
  					 *            point in the display array
  					 * @param {int}
  					 *            iEnd Index for the current display ending
  					 *            point in the display array
  					 * @param {array
  					 *            int} aiDisplay Index array to translate the
  					 *            visual position to the full data array
  					 * @dtopt Callbacks
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "fnHeaderCallback":
  					 *          function( nHead, aData, iStart, iEnd, aiDisplay ) {
  					 *          nHead.getElementsByTagName('th')[0].innerHTML =
  					 *          "Displaying "+(iEnd-iStart)+" records"; } } ); } )
  					 */
  					"fnHeaderCallback" : null,
  
  					/**
  					 * The information element can be used to convey information
  					 * about the current state of the table. Although the
  					 * internationalisation options presented by DataTables are
  					 * quite capable of dealing with most customisations, there
  					 * may be times where you wish to customise the string
  					 * further. This callback allows you to do exactly that.
  					 * 
  					 * @type function
  					 * @param {object}
  					 *            oSettings DataTables settings object
  					 * @param {int}
  					 *            iStart Starting position in data for the draw
  					 * @param {int}
  					 *            iEnd End position in data for the draw
  					 * @param {int}
  					 *            iMax Total number of rows in the table
  					 *            (regardless of filtering)
  					 * @param {int}
  					 *            iTotal Total number of rows in the data set,
  					 *            after filtering
  					 * @param {string}
  					 *            sPre The string that DataTables has formatted
  					 *            using it's own rules
  					 * @returns {string} The string to be displayed in the
  					 *          information element.
  					 * @dtopt Callbacks
  					 * 
  					 * @example $('#example').dataTable( { "fnInfoCallback":
  					 *          function( oSettings, iStart, iEnd, iMax, iTotal,
  					 *          sPre ) { return iStart +" to "+ iEnd; } } );
  					 */
  					"fnInfoCallback" : null,
  
  					/**
  					 * Called when the table has been initialised. Normally
  					 * DataTables will initialise sequentially and there will be
  					 * no need for this function, however, this does not hold
  					 * true when using external language information since that
  					 * is obtained using an async XHR call.
  					 * 
  					 * @type function
  					 * @param {object}
  					 *            oSettings DataTables settings object
  					 * @param {object}
  					 *            json The JSON object request from the server -
  					 *            only present if client-side Ajax sourced data
  					 *            is used
  					 * @dtopt Callbacks
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "fnInitComplete":
  					 *          function(oSettings, json) { alert( 'DataTables
  					 *          has finished its initialisation.' ); } } ); } )
  					 */
  					"fnInitComplete" : null,
  
  					/**
  					 * Called at the very start of each table draw and can be
  					 * used to cancel the draw by returning false, any other
  					 * return (including undefined) results in the full draw
  					 * occurring).
  					 * 
  					 * @type function
  					 * @param {object}
  					 *            oSettings DataTables settings object
  					 * @returns {boolean} False will cancel the draw, anything
  					 *          else (including no return) will allow it to
  					 *          complete.
  					 * @dtopt Callbacks
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "fnPreDrawCallback":
  					 *          function( oSettings ) { if ( $('#test').val() ==
  					 *          1 ) { return false; } } } ); } );
  					 */
  					"fnPreDrawCallback" : null,
  
  					/**
  					 * This function allows you to 'post process' each row after
  					 * it have been generated for each table draw, but before it
  					 * is rendered on screen. This function might be used for
  					 * setting the row class name etc.
  					 * 
  					 * @type function
  					 * @param {node}
  					 *            nRow "TR" element for the current row
  					 * @param {array}
  					 *            aData Raw data array for this row
  					 * @param {int}
  					 *            iDisplayIndex The display index for the
  					 *            current table draw
  					 * @param {int}
  					 *            iDisplayIndexFull The index of the data in the
  					 *            full list of rows (after filtering)
  					 * @dtopt Callbacks
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "fnRowCallback":
  					 *          function( nRow, aData, iDisplayIndex,
  					 *          iDisplayIndexFull ) { // Bold the grade for all
  					 *          'A' grade browsers if ( aData[4] == "A" ) {
  					 *          $('td:eq(4)', nRow).html( '<b>A</b>' ); } } } ); } );
  					 */
  					"fnRowCallback" : null,
  
  					/**
  					 * This parameter allows you to override the default
  					 * function which obtains the data from the server
  					 * ($.getJSON) so something more suitable for your
  					 * application. For example you could use POST data, or pull
  					 * information from a Gears or AIR database.
  					 * 
  					 * @type function
  					 * @member
  					 * @param {string}
  					 *            sSource HTTP source to obtain the data from
  					 *            (sAjaxSource)
  					 * @param {array}
  					 *            aoData A key/value pair object containing the
  					 *            data to send to the server
  					 * @param {function}
  					 *            fnCallback to be called on completion of the
  					 *            data get process that will draw the data on
  					 *            the page.
  					 * @param {object}
  					 *            oSettings DataTables settings object
  					 * @dtopt Callbacks
  					 * @dtopt Server-side
  					 * 
  					 * @example // POST data to server $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "bProcessing": true, "bServerSide": true,
  					 *          "sAjaxSource": "xhr.php", "fnServerData":
  					 *          function ( sSource, aoData, fnCallback,
  					 *          oSettings ) { oSettings.jqXHR = $.ajax( {
  					 *          "dataType": 'json', "type": "POST", "url":
  					 *          sSource, "data": aoData, "success": fnCallback } ); } } ); } );
  					 */
  					"fnServerData" : function(sUrl, aoData, fnCallback,
  							oSettings) {
  						oSettings.jqXHR = $
  								.ajax({
  									"url" : sUrl,
  									"data" : aoData,
  									"success" : function(json) {
  										if (json.sError) {
  											oSettings.oApi._fnLog(oSettings, 0,
  													json.sError);
  										}
  
  										$(oSettings.oInstance).trigger('xhr',
  												[ oSettings, json ]);
  										fnCallback(json);
  									},
  									"dataType" : "json",
  									"cache" : false,
  									"type" : oSettings.sServerMethod,
  									"error" : function(xhr, error, thrown) {
  										if (error == "parsererror") {
  											oSettings.oApi
  													._fnLog(
  															oSettings,
  															0,
  															"DataTables warning: JSON data from "
  																	+ "server could not be parsed. This is caused by a JSON formatting error.");
  										}
  									}
  								});
  					},
  
  					/**
  					 * It is often useful to send extra data to the server when
  					 * making an Ajax request - for example custom filtering
  					 * information, and this callback function makes it trivial
  					 * to send extra information to the server. The passed in
  					 * parameter is the data set that has been constructed by
  					 * DataTables, and you can add to this or modify it as you
  					 * require.
  					 * 
  					 * @type function
  					 * @param {array}
  					 *            aoData Data array (array of objects which are
  					 *            name/value pairs) that has been constructed by
  					 *            DataTables and will be sent to the server. In
  					 *            the case of Ajax sourced data with server-side
  					 *            processing this will be an empty array, for
  					 *            server-side processing there will be a
  					 *            significant number of parameters!
  					 * @returns {undefined} Ensure that you modify the aoData
  					 *          array passed in, as this is passed by reference.
  					 * @dtopt Callbacks
  					 * @dtopt Server-side
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "bProcessing": true,
  					 *          "bServerSide": true, "sAjaxSource":
  					 *          "scripts/server_processing.php",
  					 *          "fnServerParams": function ( aoData ) {
  					 *          aoData.push( { "name": "more_data", "value":
  					 *          "my_value" } ); } } ); } );
  					 */
  					"fnServerParams" : null,
  
  					/**
  					 * Load the table state. With this function you can define
  					 * from where, and how, the state of a table is loaded. By
  					 * default DataTables will load from its state saving
  					 * cookie, but you might wish to use local storage (HTML5)
  					 * or a server-side database.
  					 * 
  					 * @type function
  					 * @member
  					 * @param {object}
  					 *            oSettings DataTables settings object
  					 * @return {object} The DataTables state object to be loaded
  					 * @dtopt Callbacks
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "bStateSave": true,
  					 *          "fnStateLoad": function (oSettings) { var o;
  					 *  // Send an Ajax request to the server to get
  					 * the data. Note that // this is a synchronous request.
  					 * $.ajax( { "url": "/state_load", "async": false,
  					 * "dataType": "json", "success": function (json) { o =
  					 * json; } } );
  					 * 
  					 * return o; } } ); } );
  					 */
  					"fnStateLoad" : function(oSettings) {
  						var sData = this.oApi
  								._fnReadCookie(oSettings.sCookiePrefix
  										+ oSettings.sInstance);
  						var oData;
  
  						try {
  							oData = (typeof $.parseJSON === 'function') ? $
  									.parseJSON(sData) : eval('(' + sData + ')');
  						} catch (e) {
  							oData = null;
  						}
  
  						return oData;
  					},
  
  					/**
  					 * Callback which allows modification of the saved state
  					 * prior to loading that state. This callback is called when
  					 * the table is loading state from the stored data, but
  					 * prior to the settings object being modified by the saved
  					 * state. Note that for plug-in authors, you should use the
  					 * 'stateLoadParams' event to load parameters for a plug-in.
  					 * 
  					 * @type function
  					 * @param {object}
  					 *            oSettings DataTables settings object
  					 * @param {object}
  					 *            oData The state object that is to be loaded
  					 * @dtopt Callbacks
  					 * 
  					 * @example // Remove a saved filter, so filtering is never
  					 *          loaded $(document).ready( function() {
  					 *          $('#example').dataTable( { "bStateSave": true,
  					 *          "fnStateLoadParams": function (oSettings, oData) {
  					 *          oData.oSearch.sSearch = ""; } } ); } );
  					 * 
  					 * @example // Disallow state loading by returning false
  					 *          $(document).ready( function() {
  					 *          $('#example').dataTable( { "bStateSave": true,
  					 *          "fnStateLoadParams": function (oSettings, oData) {
  					 *          return false; } } ); } );
  					 */
  					"fnStateLoadParams" : null,
  
  					/**
  					 * Callback that is called when the state has been loaded
  					 * from the state saving method and the DataTables settings
  					 * object has been modified as a result of the loaded state.
  					 * 
  					 * @type function
  					 * @param {object}
  					 *            oSettings DataTables settings object
  					 * @param {object}
  					 *            oData The state object that was loaded
  					 * @dtopt Callbacks
  					 * 
  					 * @example // Show an alert with the filtering value that
  					 *          was saved $(document).ready( function() {
  					 *          $('#example').dataTable( { "bStateSave": true,
  					 *          "fnStateLoaded": function (oSettings, oData) {
  					 *          alert( 'Saved filter was:
  					 *          '+oData.oSearch.sSearch ); } } ); } );
  					 */
  					"fnStateLoaded" : null,
  
  					/**
  					 * Save the table state. This function allows you to define
  					 * where and how the state information for the table is
  					 * stored - by default it will use a cookie, but you might
  					 * want to use local storage (HTML5) or a server-side
  					 * database.
  					 * 
  					 * @type function
  					 * @member
  					 * @param {object}
  					 *            oSettings DataTables settings object
  					 * @param {object}
  					 *            oData The state object to be saved
  					 * @dtopt Callbacks
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "bStateSave": true,
  					 *          "fnStateSave": function (oSettings, oData) { //
  					 *          Send an Ajax request to the server with the
  					 *          state object $.ajax( { "url": "/state_save",
  					 *          "data": oData, "dataType": "json", "method":
  					 *          "POST" "success": function () {} } ); } } ); } );
  					 */
  					"fnStateSave" : function(oSettings, oData) {
  						this.oApi._fnCreateCookie(oSettings.sCookiePrefix
  								+ oSettings.sInstance, this.oApi
  								._fnJsonString(oData),
  								oSettings.iCookieDuration,
  								oSettings.sCookiePrefix,
  								oSettings.fnCookieCallback);
  					},
  
  					/**
  					 * Callback which allows modification of the state to be
  					 * saved. Called when the table has changed state a new
  					 * state save is required. This method allows modification
  					 * of the state saving object prior to actually doing the
  					 * save, including addition or other state properties or
  					 * modification. Note that for plug-in authors, you should
  					 * use the 'stateSaveParams' event to save parameters for a
  					 * plug-in.
  					 * 
  					 * @type function
  					 * @param {object}
  					 *            oSettings DataTables settings object
  					 * @param {object}
  					 *            oData The state object to be saved
  					 * @dtopt Callbacks
  					 * 
  					 * @example // Remove a saved filter, so filtering is never
  					 *          saved $(document).ready( function() {
  					 *          $('#example').dataTable( { "bStateSave": true,
  					 *          "fnStateSaveParams": function (oSettings, oData) {
  					 *          oData.oSearch.sSearch = ""; } } ); } );
  					 */
  					"fnStateSaveParams" : null,
  
  					/**
  					 * Duration of the cookie which is used for storing session
  					 * information. This value is given in seconds.
  					 * 
  					 * @type int
  					 * @default 7200 <i>(2 hours)</i>
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "iCookieDuration":
  					 *          60*60*24; // 1 day } ); } )
  					 */
  					"iCookieDuration" : 7200,
  
  					/**
  					 * When enabled DataTables will not make a request to the
  					 * server for the first page draw - rather it will use the
  					 * data already on the page (no sorting etc will be applied
  					 * to it), thus saving on an XHR at load time. iDeferLoading
  					 * is used to indicate that deferred loading is required,
  					 * but it is also used to tell DataTables how many records
  					 * there are in the full table (allowing the information
  					 * element and pagination to be displayed correctly). In the
  					 * case where a filtering is applied to the table on initial
  					 * load, this can be indicated by giving the parameter as an
  					 * array, where the first element is the number of records
  					 * available after filtering and the second element is the
  					 * number of records without filtering (allowing the table
  					 * information element to be shown correctly).
  					 * 
  					 * @type int | array
  					 * @default null
  					 * @dtopt Options
  					 * 
  					 * @example // 57 records available in the table, no
  					 *          filtering applied $(document).ready( function() {
  					 *          $('#example').dataTable( { "bServerSide": true,
  					 *          "sAjaxSource": "scripts/server_processing.php",
  					 *          "iDeferLoading": 57 } ); } );
  					 * 
  					 * @example // 57 records after filtering, 100 without
  					 *          filtering (an initial filter applied)
  					 *          $(document).ready( function() {
  					 *          $('#example').dataTable( { "bServerSide": true,
  					 *          "sAjaxSource": "scripts/server_processing.php",
  					 *          "iDeferLoading": [ 57, 100 ], "oSearch": {
  					 *          "sSearch": "my_filter" } } ); } );
  					 */
  					"iDeferLoading" : null,
  
  					/**
  					 * Number of rows to display on a single page when using
  					 * pagination. If feature enabled (bLengthChange) then the
  					 * end user will be able to override this to a custom
  					 * setting using a pop-up menu.
  					 * 
  					 * @type int
  					 * @default 10
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "iDisplayLength": 50 } ); } )
  					 */
  					"iDisplayLength" : 10,
  
  					/**
  					 * Define the starting point for data display when using
  					 * DataTables with pagination. Note that this parameter is
  					 * the number of records, rather than the page number, so if
  					 * you have 10 records per page and want to start on the
  					 * third page, it should be "20".
  					 * 
  					 * @type int
  					 * @default 0
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "iDisplayStart": 20 } ); } )
  					 */
  					"iDisplayStart" : 0,
  
  					/**
  					 * The scroll gap is the amount of scrolling that is left to
  					 * go before DataTables will load the next 'page' of data
  					 * automatically. You typically want a gap which is big
  					 * enough that the scrolling will be smooth for the user,
  					 * while not so large that it will load more data than need.
  					 * 
  					 * @type int
  					 * @default 100
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "bScrollInfinite":
  					 *          true, "bScrollCollapse": true, "sScrollY":
  					 *          "200px", "iScrollLoadGap": 50 } ); } );
  					 */
  					"iScrollLoadGap" : 100,
  
  					/**
  					 * By default DataTables allows keyboard navigation of the
  					 * table (sorting, paging, and filtering) by adding a
  					 * tabindex attribute to the required elements. This allows
  					 * you to tab through the controls and press the enter key
  					 * to activate them. The tabindex is default 0, meaning that
  					 * the tab follows the flow of the document. You can
  					 * overrule this using this parameter if you wish. Use a
  					 * value of -1 to disable built-in keyboard navigation.
  					 * 
  					 * @type int
  					 * @default 0
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "iTabIndex": 1 } ); } );
  					 */
  					"iTabIndex" : 0,
  
  					/**
  					 * All strings that DataTables uses in the user interface
  					 * that it creates are defined in this object, allowing you
  					 * to modified them individually or completely replace them
  					 * all as required.
  					 * 
  					 * @namespace
  					 */
  					"oLanguage" : {
  						/**
  						 * Strings that are used for WAI-ARIA labels and
  						 * controls only (these are not actually visible on the
  						 * page, but will be read by screenreaders, and thus
  						 * must be internationalised as well).
  						 * 
  						 * @namespace
  						 */
  						"oAria" : {
  							/**
  							 * ARIA label that is added to the table headers
  							 * when the column may be sorted ascending by
  							 * activing the column (click or return when
  							 * focused). Note that the column header is prefixed
  							 * to this string.
  							 * 
  							 * @type string
  							 * @default : activate to sort column ascending
  							 * @dtopt Language
  							 * 
  							 * @example $(document).ready( function() {
  							 *          $('#example').dataTable( { "oLanguage": {
  							 *          "oAria": { "sSortAscending": " -
  							 *          click/return to sort ascending" } } } ); } );
  							 */
  							"sSortAscending" : ": activate to sort column ascending",
  
  							/**
  							 * ARIA label that is added to the table headers
  							 * when the column may be sorted descending by
  							 * activing the column (click or return when
  							 * focused). Note that the column header is prefixed
  							 * to this string.
  							 * 
  							 * @type string
  							 * @default : activate to sort column ascending
  							 * @dtopt Language
  							 * 
  							 * @example $(document).ready( function() {
  							 *          $('#example').dataTable( { "oLanguage": {
  							 *          "oAria": { "sSortDescending": " -
  							 *          click/return to sort descending" } } } ); } );
  							 */
  							"sSortDescending" : ": activate to sort column descending"
  						},
  
  						/**
  						 * Pagination string used by DataTables for the two
  						 * built-in pagination control types ("two_button" and
  						 * "full_numbers")
  						 * 
  						 * @namespace
  						 */
  						"oPaginate" : {
  							/**
  							 * Text to use when using the 'full_numbers' type of
  							 * pagination for the button to take the user to the
  							 * first page.
  							 * 
  							 * @type string
  							 * @default First
  							 * @dtopt Language
  							 * 
  							 * @example $(document).ready( function() {
  							 *          $('#example').dataTable( { "oLanguage": {
  							 *          "oPaginate": { "sFirst": "First page" } } } ); } );
  							 */
  							"sFirst" : "First",
  
  							/**
  							 * Text to use when using the 'full_numbers' type of
  							 * pagination for the button to take the user to the
  							 * last page.
  							 * 
  							 * @type string
  							 * @default Last
  							 * @dtopt Language
  							 * 
  							 * @example $(document).ready( function() {
  							 *          $('#example').dataTable( { "oLanguage": {
  							 *          "oPaginate": { "sLast": "Last page" } } } ); } );
  							 */
  							"sLast" : "Last",
  
  							/**
  							 * Text to use for the 'next' pagination button (to
  							 * take the user to the next page).
  							 * 
  							 * @type string
  							 * @default Next
  							 * @dtopt Language
  							 * 
  							 * @example $(document).ready( function() {
  							 *          $('#example').dataTable( { "oLanguage": {
  							 *          "oPaginate": { "sNext": "Next page" } } } ); } );
  							 */
  							"sNext" : "Suivant",
  
  							/**
  							 * Text to use for the 'previous' pagination button
  							 * (to take the user to the previous page).
  							 * 
  							 * @type string
  							 * @default Previous
  							 * @dtopt Language
  							 * 
  							 * @example $(document).ready( function() {
  							 *          $('#example').dataTable( { "oLanguage": {
  							 *          "oPaginate": { "sPrevious": "Previous
  							 *          page" } } } ); } );
  							 */
  							"sPrevious" : "Précédent"
  						},
  
  						/**
  						 * This string is shown in preference to sZeroRecords
  						 * when the table is empty of data (regardless of
  						 * filtering). Note that this is an optional parameter -
  						 * if it is not given, the value of sZeroRecords will be
  						 * used instead (either the default or given value).
  						 * 
  						 * @type string
  						 * @default No data available in table
  						 * @dtopt Language
  						 * 
  						 * @example $(document).ready( function() {
  						 *          $('#example').dataTable( { "oLanguage": {
  						 *          "sEmptyTable": "No data available in table" } } ); } );
  						 */
  						"sEmptyTable" : "Pas de données disponibles dans le tableau",
  
  						/**
  						 * This string gives information to the end user about
  						 * the information that is current on display on the
  						 * page. The _START_, _END_ and _TOTAL_ variables are
  						 * all dynamically replaced as the table display
  						 * updates, and can be freely moved or removed as the
  						 * language requirements change.
  						 * 
  						 * @type string
  						 * @default Showing _START_ to _END_ of _TOTAL_ entries
  						 * @dtopt Language
  						 * 
  						 * @example $(document).ready( function() {
  						 *          $('#example').dataTable( { "oLanguage": {
  						 *          "sInfo": "Got a total of _TOTAL_ entries to
  						 *          show (_START_ to _END_)" } } ); } );
  						 */
  						"sInfo" : "Affichage de _START_ à _END_ parmi _TOTAL_ enregistrements",
  
  						/**
  						 * Display information string for when the table is
  						 * empty. Typically the format of this string should
  						 * match sInfo.
  						 * 
  						 * @type string
  						 * @default Showing 0 to 0 of 0 entries
  						 * @dtopt Language
  						 * 
  						 * @example $(document).ready( function() {
  						 *          $('#example').dataTable( { "oLanguage": {
  						 *          "sInfoEmpty": "No entries to show" } } ); } );
  						 */
  						"sInfoEmpty" : "Affichage de 0 à 0 parmi 0 enregistrement",
  
  						/**
  						 * When a user filters the information in a table, this
  						 * string is appended to the information (sInfo) to give
  						 * an idea of how strong the filtering is. The variable
  						 * _MAX_ is dynamically updated.
  						 * 
  						 * @type string
  						 * @default (filtered from _MAX_ total entries)
  						 * @dtopt Language
  						 * 
  						 * @example $(document).ready( function() {
  						 *          $('#example').dataTable( { "oLanguage": {
  						 *          "sInfoFiltered": " - filtering from _MAX_
  						 *          records" } } ); } );
  						 */
  						"sInfoFiltered" : "(filtered from _MAX_ total entries)",
  
  						/**
  						 * If can be useful to append extra information to the
  						 * info string at times, and this variable does exactly
  						 * that. This information will be appended to the sInfo
  						 * (sInfoEmpty and sInfoFiltered in whatever combination
  						 * they are being used) at all times.
  						 * 
  						 * @type string
  						 * @default <i>Empty string</i>
  						 * @dtopt Language
  						 * 
  						 * @example $(document).ready( function() {
  						 *          $('#example').dataTable( { "oLanguage": {
  						 *          "sInfoPostFix": "All records shown are
  						 *          derived from real information." } } ); } );
  						 */
  						"sInfoPostFix" : "",
  
  						/**
  						 * DataTables has a build in number formatter
  						 * (fnFormatNumber) which is used to format large
  						 * numbers that are used in the table information. By
  						 * default a comma is used, but this can be trivially
  						 * changed to any character you wish with this
  						 * parameter.
  						 * 
  						 * @type string
  						 * @default ,
  						 * @dtopt Language
  						 * 
  						 * @example $(document).ready( function() {
  						 *          $('#example').dataTable( { "oLanguage": {
  						 *          "sInfoThousands": "'" } } ); } );
  						 */
  						"sInfoThousands" : ",",
  
  						/**
  						 * Detail the action that will be taken when the drop
  						 * down menu for the pagination length option is
  						 * changed. The '_MENU_' variable is replaced with a
  						 * default select list of 10, 25, 50 and 100, and can be
  						 * replaced with a custom select box if required.
  						 * 
  						 * @type string
  						 * @default Show _MENU_ entries
  						 * @dtopt Language
  						 * 
  						 * @example // Language change only $(document).ready(
  						 *          function() { $('#example').dataTable( {
  						 *          "oLanguage": { "sLengthMenu": "Display
  						 *          _MENU_ records" } } ); } );
  						 * 
  						 * @example // Language and options change
  						 *          $(document).ready( function() {
  						 *          $('#example').dataTable( { "oLanguage": {
  						 *          "sLengthMenu": 'Display <select>'+ '<option
  						 *          value="10">10</option>'+ '<option
  						 *          value="20">20</option>'+ '<option
  						 *          value="30">30</option>'+ '<option
  						 *          value="40">40</option>'+ '<option
  						 *          value="50">50</option>'+ '<option
  						 *          value="-1">All</option>'+ '</select>
  						 *          records' } } ); } );
  						 */
  						"sLengthMenu" : "Show _MENU_ entries",
  
  						/**
  						 * When using Ajax sourced data and during the first
  						 * draw when DataTables is gathering the data, this
  						 * message is shown in an empty row in the table to
  						 * indicate to the end user the the data is being
  						 * loaded. Note that this parameter is not used when
  						 * loading data by server-side processing, just Ajax
  						 * sourced data with client-side processing.
  						 * 
  						 * @type string
  						 * @default Loading...
  						 * @dtopt Language
  						 * 
  						 * @example $(document).ready( function() {
  						 *          $('#example').dataTable( { "oLanguage": {
  						 *          "sLoadingRecords": "Please wait -
  						 *          loading..." } } ); } );
  						 */
  						"sLoadingRecords" : "Chargement...",
  
  						/**
  						 * Text which is displayed when the table is processing
  						 * a user action (usually a sort command or similar).
  						 * 
  						 * @type string
  						 * @default Processing...
  						 * @dtopt Language
  						 * 
  						 * @example $(document).ready( function() {
  						 *          $('#example').dataTable( { "oLanguage": {
  						 *          "sProcessing": "DataTables is currently
  						 *          busy" } } ); } );
  						 */
  						"sProcessing" : "en cours...",
  
  						/**
  						 * Details the actions that will be taken when the user
  						 * types into the filtering input text box. The variable
  						 * "_INPUT_", if used in the string, is replaced with
  						 * the HTML text box for the filtering input allowing
  						 * control over where it appears in the string. If
  						 * "_INPUT_" is not given then the input box is appended
  						 * to the string automatically.
  						 * 
  						 * @type string
  						 * @default Search:
  						 * @dtopt Language
  						 * 
  						 * @example // Input text box will be appended at the
  						 *          end automatically $(document).ready(
  						 *          function() { $('#example').dataTable( {
  						 *          "oLanguage": { "sSearch": "Filter records:" } } ); } );
  						 * 
  						 * @example // Specify where the filter should appear
  						 *          $(document).ready( function() {
  						 *          $('#example').dataTable( { "oLanguage": {
  						 *          "sSearch": "Apply filter _INPUT_ to table" } } ); } );
  						 */
  						"sSearch" : "Recherche:",
  
  						/**
  						 * All of the language information can be stored in a
  						 * file on the server-side, which DataTables will look
  						 * up if this parameter is passed. It must store the URL
  						 * of the language file, which is in a JSON format, and
  						 * the object has the same properties as the oLanguage
  						 * object in the initialiser object (i.e. the above
  						 * parameters). Please refer to one of the example
  						 * language files to see how this works in action.
  						 * 
  						 * @type string
  						 * @default <i>Empty string - i.e. disabled</i>
  						 * @dtopt Language
  						 * 
  						 * @example $(document).ready( function() {
  						 *          $('#example').dataTable( { "oLanguage": {
  						 *          "sUrl":
  						 *          "http://www.sprymedia.co.uk/dataTables/lang.txt" } } ); } );
  						 */
  						"sUrl" : "",
  
  						/**
  						 * Text shown inside the table records when the is no
  						 * information to be displayed after filtering.
  						 * sEmptyTable is shown when there is simply no
  						 * information in the table at all (regardless of
  						 * filtering).
  						 * 
  						 * @type string
  						 * @default No matching records found
  						 * @dtopt Language
  						 * 
  						 * @example $(document).ready( function() {
  						 *          $('#example').dataTable( { "oLanguage": {
  						 *          "sZeroRecords": "No records to display" } } ); } );
  						 */
  						"sZeroRecords" : "Aucune donnée trouvée"
  					},
  
  					/**
  					 * This parameter allows you to have define the global
  					 * filtering state at initialisation time. As an object the
  					 * "sSearch" parameter must be defined, but all other
  					 * parameters are optional. When "bRegex" is true, the
  					 * search string will be treated as a regular expression,
  					 * when false (default) it will be treated as a straight
  					 * string. When "bSmart" DataTables will use it's smart
  					 * filtering methods (to word match at any point in the
  					 * data), when false this will not be done.
  					 * 
  					 * @namespace
  					 * @extends DataTable.models.oSearch
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "oSearch":
  					 *          {"sSearch": "Initial search"} } ); } )
  					 */
  					"oSearch" : $.extend({}, DataTable.models.oSearch),
  
  					/**
  					 * By default DataTables will look for the property 'aaData'
  					 * when obtaining data from an Ajax source or for
  					 * server-side processing - this parameter allows that
  					 * property to be changed. You can use Javascript dotted
  					 * object notation to get a data source for multiple levels
  					 * of nesting.
  					 * 
  					 * @type string
  					 * @default aaData
  					 * @dtopt Options
  					 * @dtopt Server-side
  					 * 
  					 * @example // Get data from { "data": [...] }
  					 *          $(document).ready( function() { var oTable =
  					 *          $('#example').dataTable( { "sAjaxSource":
  					 *          "sources/data.txt", "sAjaxDataProp": "data" } ); } );
  					 * 
  					 * @example // Get data from { "data": { "inner": [...] } }
  					 *          $(document).ready( function() { var oTable =
  					 *          $('#example').dataTable( { "sAjaxSource":
  					 *          "sources/data.txt", "sAjaxDataProp":
  					 *          "data.inner" } ); } );
  					 */
  					"sAjaxDataProp" : "aaData",
  
  					/**
  					 * You can instruct DataTables to load data from an external
  					 * source using this parameter (use aData if you want to
  					 * pass data in you already have). Simply provide a url a
  					 * JSON object can be obtained from. This object must
  					 * include the parameter 'aaData' which is the data source
  					 * for the table.
  					 * 
  					 * @type string
  					 * @default null
  					 * @dtopt Options
  					 * @dtopt Server-side
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "sAjaxSource":
  					 *          "http://www.sprymedia.co.uk/dataTables/json.php" } ); } )
  					 */
  					"sAjaxSource" : null,
  
  					/**
  					 * This parameter can be used to override the default prefix
  					 * that DataTables assigns to a cookie when state saving is
  					 * enabled.
  					 * 
  					 * @type string
  					 * @default SpryMedia_DataTables_
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "sCookiePrefix":
  					 *          "my_datatable_", } ); } );
  					 */
  					"sCookiePrefix" : "SpryMedia_DataTables_",
  
  					/**
  					 * This initialisation variable allows you to specify
  					 * exactly where in the DOM you want DataTables to inject
  					 * the various controls it adds to the page (for example you
  					 * might want the pagination controls at the top of the
  					 * table). DIV elements (with or without a custom class) can
  					 * also be added to aid styling. The follow syntax is used:
  					 * <ul>
  					 * <li>The following options are allowed:
  					 * <ul>
  					 * <li>'l' - Length changing</li
  					 * <li>'f' - Filtering input</li>
  					 * <li>'t' - The table!</li>
  					 * <li>'i' - Information</li>
  					 * <li>'p' - Pagination</li>
  					 * <li>'r' - pRocessing</li>
  					 * </ul>
  					 * </li>
  					 * <li>The following constants are allowed:
  					 * <ul>
  					 * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar
  					 * ui-widget-header ui-corner-tl ui-corner-tr
  					 * ui-helper-clearfix')</li>
  					 * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar
  					 * ui-widget-header ui-corner-bl ui-corner-br
  					 * ui-helper-clearfix')</li>
  					 * </ul>
  					 * </li>
  					 * <li>The following syntax is expected:
  					 * <ul>
  					 * <li>'&lt;' and '&gt;' - div elements</li>
  					 * <li>'&lt;"class" and '&gt;' - div with a class</li>
  					 * <li>'&lt;"#id" and '&gt;' - div with an ID</li>
  					 * </ul>
  					 * </li>
  					 * <li>Examples:
  					 * <ul>
  					 * <li>'&lt;"wrapper"flipt&gt;'</li>
  					 * <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
  					 * </ul>
  					 * </li>
  					 * </ul>
  					 * 
  					 * @type string
  					 * @default lfrtip <i>(when bJQueryUI is false)</i> <b>or</b>
  					 *          <"H"lfr>t<"F"ip> <i>(when bJQueryUI is true)</i>
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "sDom":
  					 *          '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;' } ); } );
  					 */
  					"sDom" : "lfrtip",
  
  					/**
  					 * DataTables features two different built-in pagination
  					 * interaction methods ('two_button' or 'full_numbers')
  					 * which present different page controls to the end user.
  					 * Further methods can be added using the API (see below).
  					 * 
  					 * @type string
  					 * @default two_button
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "sPaginationType":
  					 *          "full_numbers" } ); } )
  					 */
  					"sPaginationType" : "two_button",
  
  					/**
  					 * Enable horizontal scrolling. When a table is too wide to
  					 * fit into a certain layout, or you have a large number of
  					 * columns in the table, you can enable x-scrolling to show
  					 * the table in a viewport, which can be scrolled. This
  					 * property can be any CSS unit, or a number (in which case
  					 * it will be treated as a pixel measurement).
  					 * 
  					 * @type string
  					 * @default <i>blank string - i.e. disabled</i>
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "sScrollX": "100%",
  					 *          "bScrollCollapse": true } ); } );
  					 */
  					"sScrollX" : "",
  
  					/**
  					 * This property can be used to force a DataTable to use
  					 * more width than it might otherwise do when x-scrolling is
  					 * enabled. For example if you have a table which requires
  					 * to be well spaced, this parameter is useful for
  					 * "over-sizing" the table, and thus forcing scrolling. This
  					 * property can by any CSS unit, or a number (in which case
  					 * it will be treated as a pixel measurement).
  					 * 
  					 * @type string
  					 * @default <i>blank string - i.e. disabled</i>
  					 * @dtopt Options
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "sScrollX": "100%",
  					 *          "sScrollXInner": "110%" } ); } );
  					 */
  					"sScrollXInner" : "",
  
  					/**
  					 * Enable vertical scrolling. Vertical scrolling will
  					 * constrain the DataTable to the given height, and enable
  					 * scrolling for any data which overflows the current
  					 * viewport. This can be used as an alternative to paging to
  					 * display a lot of data in a small area (although paging
  					 * and scrolling can both be enabled at the same time). This
  					 * property can be any CSS unit, or a number (in which case
  					 * it will be treated as a pixel measurement).
  					 * 
  					 * @type string
  					 * @default <i>blank string - i.e. disabled</i>
  					 * @dtopt Features
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "sScrollY": "200px",
  					 *          "bPaginate": false } ); } );
  					 */
  					"sScrollY" : "",
  
  					/**
  					 * Set the HTTP method that is used to make the Ajax call
  					 * for server-side processing or Ajax sourced data.
  					 * 
  					 * @type string
  					 * @default GET
  					 * @dtopt Options
  					 * @dtopt Server-side
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "bServerSide": true,
  					 *          "sAjaxSource": "scripts/post.php",
  					 *          "sServerMethod": "POST" } ); } );
  					 */
  					"sServerMethod" : "GET"
  				};
  
  				/**
  				 * Column options that can be given to DataTables at
  				 * initialisation time.
  				 * 
  				 * @namespace
  				 */
  				DataTable.defaults.columns = {
  					/**
  					 * Allows a column's sorting to take multiple columns into
  					 * account when doing a sort. For example first name / last
  					 * name columns make sense to do a multi-column sort over
  					 * the two columns.
  					 * 
  					 * @type array
  					 * @default null <i>Takes the value of the column index
  					 *          automatically</i>
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "aDataSort": [ 0, 1 ],
  					 *          "aTargets": [ 0 ] }, { "aDataSort": [ 1, 0 ],
  					 *          "aTargets": [ 1 ] }, { "aDataSort": [ 2, 3, 4 ],
  					 *          "aTargets": [ 2 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ {
  					 *          "aDataSort": [ 0, 1 ] }, { "aDataSort": [ 1, 0 ] }, {
  					 *          "aDataSort": [ 2, 3, 4 ] }, null, null ] } ); } );
  					 */
  					"aDataSort" : null,
  
  					/**
  					 * You can control the default sorting direction, and even
  					 * alter the behaviour of the sort handler (i.e. only allow
  					 * ascending sorting etc) using this parameter.
  					 * 
  					 * @type array
  					 * @default [ 'asc', 'desc' ]
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "asSorting": [ "asc" ],
  					 *          "aTargets": [ 1 ] }, { "asSorting": [ "desc",
  					 *          "asc", "asc" ], "aTargets": [ 2 ] }, {
  					 *          "asSorting": [ "desc" ], "aTargets": [ 3 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ null, {
  					 *          "asSorting": [ "asc" ] }, { "asSorting": [
  					 *          "desc", "asc", "asc" ] }, { "asSorting": [
  					 *          "desc" ] }, null ] } ); } );
  					 */
  					"asSorting" : [ 'asc', 'desc' ],
  
  					/**
  					 * Enable or disable filtering on the data in this column.
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "bSearchable": false,
  					 *          "aTargets": [ 0 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ {
  					 *          "bSearchable": false }, null, null, null, null ] } ); } );
  					 */
  					"bSearchable" : true,
  
  					/**
  					 * Enable or disable sorting on this column.
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "bSortable": false,
  					 *          "aTargets": [ 0 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ {
  					 *          "bSortable": false }, null, null, null, null ] } ); } );
  					 */
  					"bSortable" : true,
  
  					/**
  					 * <code>Deprecated</code> When using fnRender() for a
  					 * column, you may wish to use the original data (before
  					 * rendering) for sorting and filtering (the default is to
  					 * used the rendered data that the user can see). This may
  					 * be useful for dates etc.
  					 * 
  					 * Please note that this option has now been deprecated and
  					 * will be removed in the next version of DataTables. Please
  					 * use mRender / mData rather than fnRender.
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @dtopt Columns
  					 * @deprecated
  					 */
  					"bUseRendered" : true,
  
  					/**
  					 * Enable or disable the display of this column.
  					 * 
  					 * @type boolean
  					 * @default true
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "bVisible": false,
  					 *          "aTargets": [ 0 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ {
  					 *          "bVisible": false }, null, null, null, null ] } ); } );
  					 */
  					"bVisible" : true,
  
  					/**
  					 * Developer definable function that is called whenever a
  					 * cell is created (Ajax source, etc) or processed for input
  					 * (DOM source). This can be used as a compliment to mRender
  					 * allowing you to modify the DOM element (add background
  					 * colour for example) when the element is available.
  					 * 
  					 * @type function
  					 * @param {element}
  					 *            nTd The TD node that has been created
  					 * @param {*}
  					 *            sData The Data for the cell
  					 * @param {array|object}
  					 *            oData The data for the whole row
  					 * @param {int}
  					 *            iRow The row index for the aoData data store
  					 * @param {int}
  					 *            iCol The column index for aoColumns
  					 * @dtopt Columns
  					 * 
  					 * @example $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumnDefs": [ {
  					 *          "aTargets": [3], "fnCreatedCell": function (nTd,
  					 *          sData, oData, iRow, iCol) { if ( sData == "1.7" ) {
  					 *          $(nTd).css('color', 'blue') } } } ] }); } );
  					 */
  					"fnCreatedCell" : null,
  
  					/**
  					 * <code>Deprecated</code> Custom display function that
  					 * will be called for the display of each cell in this
  					 * column.
  					 * 
  					 * Please note that this option has now been deprecated and
  					 * will be removed in the next version of DataTables. Please
  					 * use mRender / mData rather than fnRender.
  					 * 
  					 * @type function
  					 * @param {object}
  					 *            o Object with the following parameters:
  					 * @param {int}
  					 *            o.iDataRow The row in aoData
  					 * @param {int}
  					 *            o.iDataColumn The column in question
  					 * @param {array}
  					 *            o.aData The data for the row in question
  					 * @param {object}
  					 *            o.oSettings The settings object for this
  					 *            DataTables instance
  					 * @param {object}
  					 *            o.mDataProp The data property used for this
  					 *            column
  					 * @param {*}
  					 *            val The current cell value
  					 * @returns {string} The string you which to use in the
  					 *          display
  					 * @dtopt Columns
  					 * @deprecated
  					 */
  					"fnRender" : null,
  
  					/**
  					 * The column index (starting from 0!) that you wish a sort
  					 * to be performed upon when this column is selected for
  					 * sorting. This can be used for sorting on hidden columns
  					 * for example.
  					 * 
  					 * @type int
  					 * @default -1 <i>Use automatically calculated column index</i>
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "iDataSort": 1, "aTargets": [
  					 *          0 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ {
  					 *          "iDataSort": 1 }, null, null, null, null ] } ); } );
  					 */
  					"iDataSort" : -1,
  
  					/**
  					 * This parameter has been replaced by mData in DataTables
  					 * to ensure naming consistency. mDataProp can still be
  					 * used, as there is backwards compatibility in DataTables
  					 * for this option, but it is strongly recommended that you
  					 * use mData in preference to mDataProp.
  					 * 
  					 * @name DataTable.defaults.columns.mDataProp
  					 */
  
  					/**
  					 * This property can be used to read data from any JSON data
  					 * source property, including deeply nested objects /
  					 * properties. mData can be given in a number of different
  					 * ways which effect its behaviour:
  					 * <ul>
  					 * <li>integer - treated as an array index for the data
  					 * source. This is the default that DataTables uses
  					 * (incrementally increased for each column).</li>
  					 * <li>string - read an object property from the data
  					 * source. Note that you can use Javascript dotted notation
  					 * to read deep properties / arrays from the data source.</li>
  					 * <li>null - the sDefaultContent option will be used for
  					 * the cell (null by default, so you will need to specify
  					 * the default content you want - typically an empty
  					 * string). This can be useful on generated columns such as
  					 * edit / delete action columns.</li>
  					 * <li>function - the function given will be executed
  					 * whenever DataTables needs to set or get the data for a
  					 * cell in the column. The function takes three parameters:
  					 * <ul>
  					 * <li>{array|object} The data source for the row</li>
  					 * <li>{string} The type call data requested - this will be
  					 * 'set' when setting data or 'filter', 'display', 'type',
  					 * 'sort' or undefined when gathering data. Note that when
  					 * <i>undefined</i> is given for the type DataTables
  					 * expects to get the raw data for the object back</li>
  					 * <li>{*} Data to set when the second parameter is 'set'.</li>
  					 * </ul>
  					 * The return value from the function is not required when
  					 * 'set' is the type of call, but otherwise the return is
  					 * what will be used for the data requested.</li>
  					 * </ul>
  					 * 
  					 * Note that prior to DataTables 1.9.2 mData was called
  					 * mDataProp. The name change reflects the flexibility of
  					 * this property and is consistent with the naming of
  					 * mRender. If 'mDataProp' is given, then it will still be
  					 * used by DataTables, as it automatically maps the old name
  					 * to the new if required.
  					 * 
  					 * @type string|int|function|null
  					 * @default null <i>Use automatically calculated column
  					 *          index</i>
  					 * @dtopt Columns
  					 * 
  					 * @example // Read table data from objects
  					 *          $(document).ready( function() { var oTable =
  					 *          $('#example').dataTable( { "sAjaxSource":
  					 *          "sources/deep.txt", "aoColumns": [ { "mData":
  					 *          "engine" }, { "mData": "browser" }, { "mData":
  					 *          "platform.inner" }, { "mData":
  					 *          "platform.details.0" }, { "mData":
  					 *          "platform.details.1" } ] } ); } );
  					 * 
  					 * @example // Using mData as a function to provide
  					 *          different information for // sorting, filtering
  					 *          and display. In this case, currency (price)
  					 *          $(document).ready( function() { var oTable =
  					 *          $('#example').dataTable( { "aoColumnDefs": [ {
  					 *          "aTargets": [ 0 ], "mData": function ( source,
  					 *          type, val ) { if (type === 'set') { source.price =
  					 *          val; // Store the computed dislay and filter
  					 *          values for efficiency source.price_display =
  					 *          val=="" ? "" : "$"+numberFormat(val);
  					 *          source.price_filter = val=="" ? "" :
  					 *          "$"+numberFormat(val)+" "+val; return; } else if
  					 *          (type === 'display') { return
  					 *          source.price_display; } else if (type ===
  					 *          'filter') { return source.price_filter; } //
  					 *          'sort', 'type' and undefined all just use the
  					 *          integer return source.price; } } ] } ); } );
  					 */
  					"mData" : null,
  
  					/**
  					 * This property is the rendering partner to mData and it is
  					 * suggested that when you want to manipulate data for
  					 * display (including filtering, sorting etc) but not
  					 * altering the underlying data for the table, use this
  					 * property. mData can actually do everything this property
  					 * can and more, but this parameter is easier to use since
  					 * there is no 'set' option. Like mData is can be given in a
  					 * number of different ways to effect its behaviour, with
  					 * the addition of supporting array syntax for easy
  					 * outputting of arrays (including arrays of objects):
  					 * <ul>
  					 * <li>integer - treated as an array index for the data
  					 * source. This is the default that DataTables uses
  					 * (incrementally increased for each column).</li>
  					 * <li>string - read an object property from the data
  					 * source. Note that you can use Javascript dotted notation
  					 * to read deep properties / arrays from the data source and
  					 * also array brackets to indicate that the data reader
  					 * should loop over the data source array. When characters
  					 * are given between the array brackets, these characters
  					 * are used to join the data source array together. For
  					 * example: "accounts[, ].name" would result in a comma
  					 * separated list with the 'name' value from the 'accounts'
  					 * array of objects.</li>
  					 * <li>function - the function given will be executed
  					 * whenever DataTables needs to set or get the data for a
  					 * cell in the column. The function takes three parameters:
  					 * <ul>
  					 * <li>{array|object} The data source for the row (based on
  					 * mData)</li>
  					 * <li>{string} The type call data requested - this will be
  					 * 'filter', 'display', 'type' or 'sort'.</li>
  					 * <li>{array|object} The full data source for the row (not
  					 * based on mData)</li>
  					 * </ul>
  					 * The return value from the function is what will be used
  					 * for the data requested.</li>
  					 * </ul>
  					 * 
  					 * @type string|int|function|null
  					 * @default null <i>Use mData</i>
  					 * @dtopt Columns
  					 * 
  					 * @example // Create a comma separated list from an array
  					 *          of objects $(document).ready( function() { var
  					 *          oTable = $('#example').dataTable( {
  					 *          "sAjaxSource": "sources/deep.txt", "aoColumns": [ {
  					 *          "mData": "engine" }, { "mData": "browser" }, {
  					 *          "mData": "platform", "mRender": "[, ].name" } ] } ); } );
  					 * 
  					 * @example // Use as a function to create a link from the
  					 *          data source $(document).ready( function() { var
  					 *          oTable = $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "aTargets": [ 0 ], "mData":
  					 *          "download_link", "mRender": function ( data,
  					 *          type, full ) { return '<a
  					 *          href="'+data+'">Download</a>'; } ] } ); } );
  					 */
  					"mRender" : null,
  
  					/**
  					 * Change the cell type created for the column - either TD
  					 * cells or TH cells. This can be useful as TH cells have
  					 * semantic meaning in the table body, allowing them to act
  					 * as a header for a row (you may wish to add scope='row' to
  					 * the TH elements).
  					 * 
  					 * @type string
  					 * @default td
  					 * @dtopt Columns
  					 * 
  					 * @example // Make the first column use TH cells
  					 *          $(document).ready( function() { var oTable =
  					 *          $('#example').dataTable( { "aoColumnDefs": [ {
  					 *          "aTargets": [ 0 ], "sCellType": "th" } ] } ); } );
  					 */
  					"sCellType" : "td",
  
  					/**
  					 * Class to give to each cell in this column.
  					 * 
  					 * @type string
  					 * @default <i>Empty string</i>
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "sClass": "my_class",
  					 *          "aTargets": [ 0 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ {
  					 *          "sClass": "my_class" }, null, null, null, null ] } ); } );
  					 */
  					"sClass" : "",
  
  					/**
  					 * When DataTables calculates the column widths to assign to
  					 * each column, it finds the longest string in each column
  					 * and then constructs a temporary table and reads the
  					 * widths from that. The problem with this is that "mmm" is
  					 * much wider then "iiii", but the latter is a longer string -
  					 * thus the calculation can go wrong (doing it properly and
  					 * putting it into an DOM object and measuring that is
  					 * horribly(!) slow). Thus as a "work around" we provide
  					 * this option. It will append its value to the text that is
  					 * found to be the longest string for the column - i.e.
  					 * padding. Generally you shouldn't need this, and it is not
  					 * documented on the general DataTables.net documentation
  					 * 
  					 * @type string
  					 * @default <i>Empty string<i>
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ null,
  					 *          null, null, { "sContentPadding": "mmm" } ] } ); } );
  					 */
  					"sContentPadding" : "",
  
  					/**
  					 * Allows a default value to be given for a column's data,
  					 * and will be used whenever a null data source is
  					 * encountered (this can be because mData is set to null, or
  					 * because the data source itself is null).
  					 * 
  					 * @type string
  					 * @default null
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "mData": null,
  					 *          "sDefaultContent": "Edit", "aTargets": [ -1 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ null,
  					 *          null, null, { "mData": null, "sDefaultContent":
  					 *          "Edit" } ] } ); } );
  					 */
  					"sDefaultContent" : null,
  
  					/**
  					 * This parameter is only used in DataTables' server-side
  					 * processing. It can be exceptionally useful to know what
  					 * columns are being displayed on the client side, and to
  					 * map these to database fields. When defined, the names
  					 * also allow DataTables to reorder information from the
  					 * server if it comes back in an unexpected order (i.e. if
  					 * you switch your columns around on the client-side, your
  					 * server-side code does not also need updating).
  					 * 
  					 * @type string
  					 * @default <i>Empty string</i>
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "sName": "engine",
  					 *          "aTargets": [ 0 ] }, { "sName": "browser",
  					 *          "aTargets": [ 1 ] }, { "sName": "platform",
  					 *          "aTargets": [ 2 ] }, { "sName": "version",
  					 *          "aTargets": [ 3 ] }, { "sName": "grade",
  					 *          "aTargets": [ 4 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ {
  					 *          "sName": "engine" }, { "sName": "browser" }, {
  					 *          "sName": "platform" }, { "sName": "version" }, {
  					 *          "sName": "grade" } ] } ); } );
  					 */
  					"sName" : "",
  
  					/**
  					 * Defines a data source type for the sorting which can be
  					 * used to read real-time information from the table
  					 * (updating the internally cached version) prior to
  					 * sorting. This allows sorting to occur on user editable
  					 * elements such as form inputs.
  					 * 
  					 * @type string
  					 * @default std
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "sSortDataType": "dom-text",
  					 *          "aTargets": [ 2, 3 ] }, { "sType": "numeric",
  					 *          "aTargets": [ 3 ] }, { "sSortDataType":
  					 *          "dom-select", "aTargets": [ 4 ] }, {
  					 *          "sSortDataType": "dom-checkbox", "aTargets": [ 5 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ null,
  					 *          null, { "sSortDataType": "dom-text" }, {
  					 *          "sSortDataType": "dom-text", "sType": "numeric" }, {
  					 *          "sSortDataType": "dom-select" }, {
  					 *          "sSortDataType": "dom-checkbox" } ] } ); } );
  					 */
  					"sSortDataType" : "std",
  
  					/**
  					 * The title of this column.
  					 * 
  					 * @type string
  					 * @default null <i>Derived from the 'TH' value for this
  					 *          column in the original HTML table.</i>
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "sTitle": "My column title",
  					 *          "aTargets": [ 0 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ {
  					 *          "sTitle": "My column title" }, null, null, null,
  					 *          null ] } ); } );
  					 */
  					"sTitle" : null,
  
  					/**
  					 * The type allows you to specify how the data for this
  					 * column will be sorted. Four types (string, numeric, date
  					 * and html (which will strip HTML tags before sorting)) are
  					 * currently available. Note that only date formats
  					 * understood by Javascript's Date() object will be accepted
  					 * as type date. For example: "Mar 26, 2008 5:03 PM". May
  					 * take the values: 'string', 'numeric', 'date' or 'html'
  					 * (by default). Further types can be adding through
  					 * plug-ins.
  					 * 
  					 * @type string
  					 * @default null <i>Auto-detected from raw data</i>
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "sType": "html", "aTargets": [
  					 *          0 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ {
  					 *          "sType": "html" }, null, null, null, null ] } ); } );
  					 */
  					"sType" : null,
  
  					/**
  					 * Defining the width of the column, this parameter may take
  					 * any CSS value (3em, 20px etc). DataTables apples 'smart'
  					 * widths to columns which have not been given a specific
  					 * width through this interface ensuring that the table
  					 * remains readable.
  					 * 
  					 * @type string
  					 * @default null <i>Automatic</i>
  					 * @dtopt Columns
  					 * 
  					 * @example // Using aoColumnDefs $(document).ready(
  					 *          function() { $('#example').dataTable( {
  					 *          "aoColumnDefs": [ { "sWidth": "20%", "aTargets": [
  					 *          0 ] } ] } ); } );
  					 * 
  					 * @example // Using aoColumns $(document).ready( function() {
  					 *          $('#example').dataTable( { "aoColumns": [ {
  					 *          "sWidth": "20%" }, null, null, null, null ] } ); } );
  					 */
  					"sWidth" : null
  				};
  
  				/**
  				 * DataTables settings object - this holds all the information
  				 * needed for a given table, including configuration, data and
  				 * current application of the table options. DataTables does not
  				 * have a single instance for each DataTable with the settings
  				 * attached to that instance, but rather instances of the
  				 * DataTable "class" are created on-the-fly as needed (typically
  				 * by a $().dataTable() call) and the settings object is then
  				 * applied to that instance.
  				 * 
  				 * Note that this object is related to
  				 * {@link DataTable.defaults} but this one is the internal data
  				 * store for DataTables's cache of columns. It should NOT be
  				 * manipulated outside of DataTables. Any configuration should
  				 * be done through the initialisation options.
  				 * 
  				 * @namespace
  				 * @todo Really should attach the settings object to individual
  				 *       instances so we don't need to create new instances on
  				 *       each $().dataTable() call (if the table already
  				 *       exists). It would also save passing oSettings around
  				 *       and into every single function. However, this is a very
  				 *       significant architecture change for DataTables and will
  				 *       almost certainly break backwards compatibility with
  				 *       older installations. This is something that will be
  				 *       done in 2.0.
  				 */
  				DataTable.models.oSettings = {
  					/**
  					 * Primary features of DataTables and their enablement
  					 * state.
  					 * 
  					 * @namespace
  					 */
  					"oFeatures" : {
  
  						/**
  						 * Flag to say if DataTables should automatically try to
  						 * calculate the optimum table and columns widths (true)
  						 * or not (false). Note that this parameter will be set
  						 * by the initialisation routine. To set a default use
  						 * {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bAutoWidth" : null,
  
  						/**
  						 * Delay the creation of TR and TD elements until they
  						 * are actually needed by a driven page draw. This can
  						 * give a significant speed increase for Ajax source and
  						 * Javascript source data, but makes no difference at
  						 * all fro DOM and server-side processing tables. Note
  						 * that this parameter will be set by the initialisation
  						 * routine. To set a default use
  						 * {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bDeferRender" : null,
  
  						/**
  						 * Enable filtering on the table or not. Note that if
  						 * this is disabled then there is no filtering at all on
  						 * the table, including fnFilter. To just remove the
  						 * filtering input use sDom and remove the 'f' option.
  						 * Note that this parameter will be set by the
  						 * initialisation routine. To set a default use
  						 * {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bFilter" : null,
  
  						/**
  						 * Table information element (the 'Showing x of y
  						 * records' div) enable flag. Note that this parameter
  						 * will be set by the initialisation routine. To set a
  						 * default use {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bInfo" : null,
  
  						/**
  						 * Present a user control allowing the end user to
  						 * change the page size when pagination is enabled. Note
  						 * that this parameter will be set by the initialisation
  						 * routine. To set a default use
  						 * {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bLengthChange" : null,
  
  						/**
  						 * Pagination enabled or not. Note that if this is
  						 * disabled then length changing must also be disabled.
  						 * Note that this parameter will be set by the
  						 * initialisation routine. To set a default use
  						 * {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bPaginate" : null,
  
  						/**
  						 * Processing indicator enable flag whenever DataTables
  						 * is enacting a user request - typically an Ajax
  						 * request for server-side processing. Note that this
  						 * parameter will be set by the initialisation routine.
  						 * To set a default use {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bProcessing" : null,
  
  						/**
  						 * Server-side processing enabled flag - when enabled
  						 * DataTables will get all data from the server for
  						 * every draw - there is no filtering, sorting or paging
  						 * done on the client-side. Note that this parameter
  						 * will be set by the initialisation routine. To set a
  						 * default use {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bServerSide" : null,
  
  						/**
  						 * Sorting enablement flag. Note that this parameter
  						 * will be set by the initialisation routine. To set a
  						 * default use {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bSort" : null,
  
  						/**
  						 * Apply a class to the columns which are being sorted
  						 * to provide a visual highlight or not. This can slow
  						 * things down when enabled since there is a lot of DOM
  						 * interaction. Note that this parameter will be set by
  						 * the initialisation routine. To set a default use
  						 * {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bSortClasses" : null,
  
  						/**
  						 * State saving enablement flag. Note that this
  						 * parameter will be set by the initialisation routine.
  						 * To set a default use {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bStateSave" : null
  					},
  
  					/**
  					 * Scrolling settings for a table.
  					 * 
  					 * @namespace
  					 */
  					"oScroll" : {
  						/**
  						 * Indicate if DataTables should be allowed to set the
  						 * padding / margin etc for the scrolling header
  						 * elements or not. Typically you will want this. Note
  						 * that this parameter will be set by the initialisation
  						 * routine. To set a default use
  						 * {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bAutoCss" : null,
  
  						/**
  						 * When the table is shorter in height than sScrollY,
  						 * collapse the table container down to the height of
  						 * the table (when true). Note that this parameter will
  						 * be set by the initialisation routine. To set a
  						 * default use {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bCollapse" : null,
  
  						/**
  						 * Infinite scrolling enablement flag. Now deprecated in
  						 * favour of using the Scroller plug-in. Note that this
  						 * parameter will be set by the initialisation routine.
  						 * To set a default use {@link DataTable.defaults}.
  						 * 
  						 * @type boolean
  						 */
  						"bInfinite" : null,
  
  						/**
  						 * Width of the scrollbar for the web-browser's
  						 * platform. Calculated during table initialisation.
  						 * 
  						 * @type int
  						 * @default 0
  						 */
  						"iBarWidth" : 0,
  
  						/**
  						 * Space (in pixels) between the bottom of the scrolling
  						 * container and the bottom of the scrolling viewport
  						 * before the next page is loaded when using infinite
  						 * scrolling. Note that this parameter will be set by
  						 * the initialisation routine. To set a default use
  						 * {@link DataTable.defaults}.
  						 * 
  						 * @type int
  						 */
  						"iLoadGap" : null,
  
  						/**
  						 * Viewport width for horizontal scrolling. Horizontal
  						 * scrolling is disabled if an empty string. Note that
  						 * this parameter will be set by the initialisation
  						 * routine. To set a default use
  						 * {@link DataTable.defaults}.
  						 * 
  						 * @type string
  						 */
  						"sX" : null,
  
  						/**
  						 * Width to expand the table to when using x-scrolling.
  						 * Typically you should not need to use this. Note that
  						 * this parameter will be set by the initialisation
  						 * routine. To set a default use
  						 * {@link DataTable.defaults}.
  						 * 
  						 * @type string
  						 * @deprecated
  						 */
  						"sXInner" : null,
  
  						/**
  						 * Viewport height for vertical scrolling. Vertical
  						 * scrolling is disabled if an empty string. Note that
  						 * this parameter will be set by the initialisation
  						 * routine. To set a default use
  						 * {@link DataTable.defaults}.
  						 * 
  						 * @type string
  						 */
  						"sY" : null
  					},
  
  					/**
  					 * Language information for the table.
  					 * 
  					 * @namespace
  					 * @extends DataTable.defaults.oLanguage
  					 */
  					"oLanguage" : {
  						/**
  						 * Information callback function. See
  						 * {@link DataTable.defaults.fnInfoCallback}
  						 * 
  						 * @type function
  						 * @default null
  						 */
  						"fnInfoCallback" : null
  					},
  
  					/**
  					 * Browser support parameters
  					 * 
  					 * @namespace
  					 */
  					"oBrowser" : {
  						/**
  						 * Indicate if the browser incorrectly calculates
  						 * width:100% inside a scrolling element (IE6/7)
  						 * 
  						 * @type boolean
  						 * @default false
  						 */
  						"bScrollOversize" : false
  					},
  
  					/**
  					 * Array referencing the nodes which are used for the
  					 * features. The parameters of this object match what is
  					 * allowed by sDom - i.e.
  					 * <ul>
  					 * <li>'l' - Length changing</li>
  					 * <li>'f' - Filtering input</li>
  					 * <li>'t' - The table!</li>
  					 * <li>'i' - Information</li>
  					 * <li>'p' - Pagination</li>
  					 * <li>'r' - pRocessing</li>
  					 * </ul>
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aanFeatures" : [],
  
  					/**
  					 * Store data information - see
  					 * {@link DataTable.models.oRow} for detailed information.
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoData" : [],
  
  					/**
  					 * Array of indexes which are in the current display (after
  					 * filtering etc)
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aiDisplay" : [],
  
  					/**
  					 * Array of indexes for display - no filtering
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aiDisplayMaster" : [],
  
  					/**
  					 * Store information about each column that is in use
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoColumns" : [],
  
  					/**
  					 * Store information about the table's header
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoHeader" : [],
  
  					/**
  					 * Store information about the table's footer
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoFooter" : [],
  
  					/**
  					 * Search data array for regular expression searching
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"asDataSearch" : [],
  
  					/**
  					 * Store the applied global search information in case we
  					 * want to force a research or compare the old search to a
  					 * new one. Note that this parameter will be set by the
  					 * initialisation routine. To set a default use
  					 * {@link DataTable.defaults}.
  					 * 
  					 * @namespace
  					 * @extends DataTable.models.oSearch
  					 */
  					"oPreviousSearch" : {},
  
  					/**
  					 * Store the applied search for each column - see
  					 * {@link DataTable.models.oSearch} for the format that is
  					 * used for the filtering information for each column.
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoPreSearchCols" : [],
  
  					/**
  					 * Sorting that is applied to the table. Note that the inner
  					 * arrays are used in the following manner:
  					 * <ul>
  					 * <li>Index 0 - column number</li>
  					 * <li>Index 1 - current sorting direction</li>
  					 * <li>Index 2 - index of asSorting for this column</li>
  					 * </ul>
  					 * Note that this parameter will be set by the
  					 * initialisation routine. To set a default use
  					 * {@link DataTable.defaults}.
  					 * 
  					 * @type array
  					 * @todo These inner arrays should really be objects
  					 */
  					"aaSorting" : null,
  
  					/**
  					 * Sorting that is always applied to the table (i.e.
  					 * prefixed in front of aaSorting). Note that this parameter
  					 * will be set by the initialisation routine. To set a
  					 * default use {@link DataTable.defaults}.
  					 * 
  					 * @type array|null
  					 * @default null
  					 */
  					"aaSortingFixed" : null,
  
  					/**
  					 * Classes to use for the striping of a table. Note that
  					 * this parameter will be set by the initialisation routine.
  					 * To set a default use {@link DataTable.defaults}.
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"asStripeClasses" : null,
  
  					/**
  					 * If restoring a table - we should restore its striping
  					 * classes as well
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"asDestroyStripes" : [],
  
  					/**
  					 * If restoring a table - we should restore its width
  					 * 
  					 * @type int
  					 * @default 0
  					 */
  					"sDestroyWidth" : 0,
  
  					/**
  					 * Callback functions array for every time a row is inserted
  					 * (i.e. on a draw).
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoRowCallback" : [],
  
  					/**
  					 * Callback functions for the header on each draw.
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoHeaderCallback" : [],
  
  					/**
  					 * Callback function for the footer on each draw.
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoFooterCallback" : [],
  
  					/**
  					 * Array of callback functions for draw callback functions
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoDrawCallback" : [],
  
  					/**
  					 * Array of callback functions for row created function
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoRowCreatedCallback" : [],
  
  					/**
  					 * Callback functions for just before the table is redrawn.
  					 * A return of false will be used to cancel the draw.
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoPreDrawCallback" : [],
  
  					/**
  					 * Callback functions for when the table has been
  					 * initialised.
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoInitComplete" : [],
  
  					/**
  					 * Callbacks for modifying the settings to be stored for
  					 * state saving, prior to saving state.
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoStateSaveParams" : [],
  
  					/**
  					 * Callbacks for modifying the settings that have been
  					 * stored for state saving prior to using the stored values
  					 * to restore the state.
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoStateLoadParams" : [],
  
  					/**
  					 * Callbacks for operating on the settings object once the
  					 * saved state has been loaded
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoStateLoaded" : [],
  
  					/**
  					 * Cache the table ID for quick access
  					 * 
  					 * @type string
  					 * @default <i>Empty string</i>
  					 */
  					"sTableId" : "",
  
  					/**
  					 * The TABLE node for the main table
  					 * 
  					 * @type node
  					 * @default null
  					 */
  					"nTable" : null,
  
  					/**
  					 * Permanent ref to the thead element
  					 * 
  					 * @type node
  					 * @default null
  					 */
  					"nTHead" : null,
  
  					/**
  					 * Permanent ref to the tfoot element - if it exists
  					 * 
  					 * @type node
  					 * @default null
  					 */
  					"nTFoot" : null,
  
  					/**
  					 * Permanent ref to the tbody element
  					 * 
  					 * @type node
  					 * @default null
  					 */
  					"nTBody" : null,
  
  					/**
  					 * Cache the wrapper node (contains all DataTables
  					 * controlled elements)
  					 * 
  					 * @type node
  					 * @default null
  					 */
  					"nTableWrapper" : null,
  
  					/**
  					 * Indicate if when using server-side processing the loading
  					 * of data should be deferred until the second draw. Note
  					 * that this parameter will be set by the initialisation
  					 * routine. To set a default use {@link DataTable.defaults}.
  					 * 
  					 * @type boolean
  					 * @default false
  					 */
  					"bDeferLoading" : false,
  
  					/**
  					 * Indicate if all required information has been read in
  					 * 
  					 * @type boolean
  					 * @default false
  					 */
  					"bInitialised" : false,
  
  					/**
  					 * Information about open rows. Each object in the array has
  					 * the parameters 'nTr' and 'nParent'
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoOpenRows" : [],
  
  					/**
  					 * Dictate the positioning of DataTables' control elements -
  					 * see {@link DataTable.model.oInit.sDom}. Note that this
  					 * parameter will be set by the initialisation routine. To
  					 * set a default use {@link DataTable.defaults}.
  					 * 
  					 * @type string
  					 * @default null
  					 */
  					"sDom" : null,
  
  					/**
  					 * Which type of pagination should be used. Note that this
  					 * parameter will be set by the initialisation routine. To
  					 * set a default use {@link DataTable.defaults}.
  					 * 
  					 * @type string
  					 * @default two_button
  					 */
  					"sPaginationType" : "two_button",
  
  					/**
  					 * The cookie duration (for bStateSave) in seconds. Note
  					 * that this parameter will be set by the initialisation
  					 * routine. To set a default use {@link DataTable.defaults}.
  					 * 
  					 * @type int
  					 * @default 0
  					 */
  					"iCookieDuration" : 0,
  
  					/**
  					 * The cookie name prefix. Note that this parameter will be
  					 * set by the initialisation routine. To set a default use
  					 * {@link DataTable.defaults}.
  					 * 
  					 * @type string
  					 * @default <i>Empty string</i>
  					 */
  					"sCookiePrefix" : "",
  
  					/**
  					 * Callback function for cookie creation. Note that this
  					 * parameter will be set by the initialisation routine. To
  					 * set a default use {@link DataTable.defaults}.
  					 * 
  					 * @type function
  					 * @default null
  					 */
  					"fnCookieCallback" : null,
  
  					/**
  					 * Array of callback functions for state saving. Each array
  					 * element is an object with the following parameters:
  					 * <ul>
  					 * <li>function:fn - function to call. Takes two
  					 * parameters, oSettings and the JSON string to save that
  					 * has been thus far created. Returns a JSON string to be
  					 * inserted into a json object (i.e. '"param": [ 0, 1, 2]')</li>
  					 * <li>string:sName - name of callback</li>
  					 * </ul>
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoStateSave" : [],
  
  					/**
  					 * Array of callback functions for state loading. Each array
  					 * element is an object with the following parameters:
  					 * <ul>
  					 * <li>function:fn - function to call. Takes two
  					 * parameters, oSettings and the object stored. May return
  					 * false to cancel state loading</li>
  					 * <li>string:sName - name of callback</li>
  					 * </ul>
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoStateLoad" : [],
  
  					/**
  					 * State that was loaded from the cookie. Useful for back
  					 * reference
  					 * 
  					 * @type object
  					 * @default null
  					 */
  					"oLoadedState" : null,
  
  					/**
  					 * Source url for AJAX data for the table. Note that this
  					 * parameter will be set by the initialisation routine. To
  					 * set a default use {@link DataTable.defaults}.
  					 * 
  					 * @type string
  					 * @default null
  					 */
  					"sAjaxSource" : null,
  
  					/**
  					 * Property from a given object from which to read the table
  					 * data from. This can be an empty string (when not
  					 * server-side processing), in which case it is assumed an
  					 * an array is given directly. Note that this parameter will
  					 * be set by the initialisation routine. To set a default
  					 * use {@link DataTable.defaults}.
  					 * 
  					 * @type string
  					 */
  					"sAjaxDataProp" : null,
  
  					/**
  					 * Note if draw should be blocked while getting data
  					 * 
  					 * @type boolean
  					 * @default true
  					 */
  					"bAjaxDataGet" : true,
  
  					/**
  					 * The last jQuery XHR object that was used for server-side
  					 * data gathering. This can be used for working with the XHR
  					 * information in one of the callbacks
  					 * 
  					 * @type object
  					 * @default null
  					 */
  					"jqXHR" : null,
  
  					/**
  					 * Function to get the server-side data. Note that this
  					 * parameter will be set by the initialisation routine. To
  					 * set a default use {@link DataTable.defaults}.
  					 * 
  					 * @type function
  					 */
  					"fnServerData" : null,
  
  					/**
  					 * Functions which are called prior to sending an Ajax
  					 * request so extra parameters can easily be sent to the
  					 * server
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoServerParams" : [],
  
  					/**
  					 * Send the XHR HTTP method - GET or POST (could be PUT or
  					 * DELETE if required). Note that this parameter will be set
  					 * by the initialisation routine. To set a default use
  					 * {@link DataTable.defaults}.
  					 * 
  					 * @type string
  					 */
  					"sServerMethod" : null,
  
  					/**
  					 * Format numbers for display. Note that this parameter will
  					 * be set by the initialisation routine. To set a default
  					 * use {@link DataTable.defaults}.
  					 * 
  					 * @type function
  					 */
  					"fnFormatNumber" : null,
  
  					/**
  					 * List of options that can be used for the user selectable
  					 * length menu. Note that this parameter will be set by the
  					 * initialisation routine. To set a default use
  					 * {@link DataTable.defaults}.
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aLengthMenu" : null,
  
  					/**
  					 * Counter for the draws that the table does. Also used as a
  					 * tracker for server-side processing
  					 * 
  					 * @type int
  					 * @default 0
  					 */
  					"iDraw" : 0,
  
  					/**
  					 * Indicate if a redraw is being done - useful for Ajax
  					 * 
  					 * @type boolean
  					 * @default false
  					 */
  					"bDrawing" : false,
  
  					/**
  					 * Draw index (iDraw) of the last error when parsing the
  					 * returned data
  					 * 
  					 * @type int
  					 * @default -1
  					 */
  					"iDrawError" : -1,
  
  					/**
  					 * Paging display length
  					 * 
  					 * @type int
  					 * @default 10
  					 */
  					"_iDisplayLength" : 10,
  
  					/**
  					 * Paging start point - aiDisplay index
  					 * 
  					 * @type int
  					 * @default 0
  					 */
  					"_iDisplayStart" : 0,
  
  					/**
  					 * Paging end point - aiDisplay index. Use fnDisplayEnd
  					 * rather than this property to get the end point
  					 * 
  					 * @type int
  					 * @default 10
  					 * @private
  					 */
  					"_iDisplayEnd" : 10,
  
  					/**
  					 * Server-side processing - number of records in the result
  					 * set (i.e. before filtering), Use fnRecordsTotal rather
  					 * than this property to get the value of the number of
  					 * records, regardless of the server-side processing
  					 * setting.
  					 * 
  					 * @type int
  					 * @default 0
  					 * @private
  					 */
  					"_iRecordsTotal" : 0,
  
  					/**
  					 * Server-side processing - number of records in the current
  					 * display set (i.e. after filtering). Use fnRecordsDisplay
  					 * rather than this property to get the value of the number
  					 * of records, regardless of the server-side processing
  					 * setting.
  					 * 
  					 * @type boolean
  					 * @default 0
  					 * @private
  					 */
  					"_iRecordsDisplay" : 0,
  
  					/**
  					 * Flag to indicate if jQuery UI marking and classes should
  					 * be used. Note that this parameter will be set by the
  					 * initialisation routine. To set a default use
  					 * {@link DataTable.defaults}.
  					 * 
  					 * @type boolean
  					 */
  					"bJUI" : null,
  
  					/**
  					 * The classes to use for the table
  					 * 
  					 * @type object
  					 * @default {}
  					 */
  					"oClasses" : {},
  
  					/**
  					 * Flag attached to the settings object so you can check in
  					 * the draw callback if filtering has been done in the draw.
  					 * Deprecated in favour of events.
  					 * 
  					 * @type boolean
  					 * @default false
  					 * @deprecated
  					 */
  					"bFiltered" : false,
  
  					/**
  					 * Flag attached to the settings object so you can check in
  					 * the draw callback if sorting has been done in the draw.
  					 * Deprecated in favour of events.
  					 * 
  					 * @type boolean
  					 * @default false
  					 * @deprecated
  					 */
  					"bSorted" : false,
  
  					/**
  					 * Indicate that if multiple rows are in the header and
  					 * there is more than one unique cell per column, if the top
  					 * one (true) or bottom one (false) should be used for
  					 * sorting / title by DataTables. Note that this parameter
  					 * will be set by the initialisation routine. To set a
  					 * default use {@link DataTable.defaults}.
  					 * 
  					 * @type boolean
  					 */
  					"bSortCellsTop" : null,
  
  					/**
  					 * Initialisation object that is used for the table
  					 * 
  					 * @type object
  					 * @default null
  					 */
  					"oInit" : null,
  
  					/**
  					 * Destroy callback functions - for plug-ins to attach
  					 * themselves to the destroy so they can clean up markup and
  					 * events.
  					 * 
  					 * @type array
  					 * @default []
  					 */
  					"aoDestroyCallback" : [],
  
  					/**
  					 * Get the number of records in the current record set,
  					 * before filtering
  					 * 
  					 * @type function
  					 */
  					"fnRecordsTotal" : function() {
  						if (this.oFeatures.bServerSide) {
  							return parseInt(this._iRecordsTotal, 10);
  						} else {
  							return this.aiDisplayMaster.length;
  						}
  					},
  
  					/**
  					 * Get the number of records in the current record set,
  					 * after filtering
  					 * 
  					 * @type function
  					 */
  					"fnRecordsDisplay" : function() {
  						if (this.oFeatures.bServerSide) {
  							return parseInt(this._iRecordsDisplay, 10);
  						} else {
  							return this.aiDisplay.length;
  						}
  					},
  
  					/**
  					 * Set the display end point - aiDisplay index
  					 * 
  					 * @type function
  					 * @todo Should do away with _iDisplayEnd and calculate it
  					 *       on-the-fly here
  					 */
  					"fnDisplayEnd" : function() {
  						if (this.oFeatures.bServerSide) {
  							if (this.oFeatures.bPaginate === false
  									|| this._iDisplayLength == -1) {
  								return this._iDisplayStart
  										+ this.aiDisplay.length;
  							} else {
  								return Math.min(this._iDisplayStart
  										+ this._iDisplayLength,
  										this._iRecordsDisplay);
  							}
  						} else {
  							return this._iDisplayEnd;
  						}
  					},
  
  					/**
  					 * The DataTables object for this table
  					 * 
  					 * @type object
  					 * @default null
  					 */
  					"oInstance" : null,
  
  					/**
  					 * Unique identifier for each instance of the DataTables
  					 * object. If there is an ID on the table node, then it
  					 * takes that value, otherwise an incrementing internal
  					 * counter is used.
  					 * 
  					 * @type string
  					 * @default null
  					 */
  					"sInstance" : null,
  
  					/**
  					 * tabindex attribute value that is added to DataTables
  					 * control elements, allowing keyboard navigation of the
  					 * table and its controls.
  					 */
  					"iTabIndex" : 0,
  
  					/**
  					 * DIV container for the footer scrolling table if scrolling
  					 */
  					"nScrollHead" : null,
  
  					/**
  					 * DIV container for the footer scrolling table if scrolling
  					 */
  					"nScrollFoot" : null
  				};
  
  				/**
  				 * Extension object for DataTables that is used to provide all
  				 * extension options.
  				 * 
  				 * Note that the <i>DataTable.ext</i> object is available
  				 * through <i>jQuery.fn.dataTable.ext</i> where it may be
  				 * accessed and manipulated. It is also aliased to
  				 * <i>jQuery.fn.dataTableExt</i> for historic reasons.
  				 * 
  				 * @namespace
  				 * @extends DataTable.models.ext
  				 */
  				DataTable.ext = $.extend(true, {}, DataTable.models.ext);
  
  				$
  						.extend(
  								DataTable.ext.oStdClasses,
  								{
  									"sTable" : "dataTable",
  
  									/* Two buttons buttons */
  									"sPagePrevEnabled" : "paginate_enabled_previous",
  									"sPagePrevDisabled" : "paginate_disabled_previous",
  									"sPageNextEnabled" : "paginate_enabled_next",
  									"sPageNextDisabled" : "paginate_disabled_next",
  									"sPageJUINext" : "",
  									"sPageJUIPrev" : "",
  
  									/* Full numbers paging buttons */
  									"sPageButton" : "paginate_button",
  									"sPageButtonActive" : "paginate_active",
  									"sPageButtonStaticDisabled" : "paginate_button paginate_button_disabled",
  									"sPageFirst" : "first",
  									"sPagePrevious" : "previous",
  									"sPageNext" : "next",
  									"sPageLast" : "last",
  
  									/* Striping classes */
  									"sStripeOdd" : "odd",
  									"sStripeEven" : "even",
  
  									/* Empty row */
  									"sRowEmpty" : "dataTables_empty",
  
  									/* Features */
  									"sWrapper" : "dataTables_wrapper",
  									"sFilter" : "dataTables_filter",
  									"sInfo" : "dataTables_info",
  									"sPaging" : "dataTables_paginate paging_", /*
  																				 * Note
  																				 * that
  																				 * the
  																				 * type
  																				 * is
  																				 * postfixed
  																				 */
  									"sLength" : "dataTables_length",
  									"sProcessing" : "dataTables_processing",
  
  									/* Sorting */
  									"sSortAsc" : "sorting_asc",
  									"sSortDesc" : "sorting_desc",
  									"sSortable" : "sorting", /*
  																 * Sortable in
  																 * both
  																 * directions
  																 */
  									"sSortableAsc" : "sorting_asc_disabled",
  									"sSortableDesc" : "sorting_desc_disabled",
  									"sSortableNone" : "sorting_disabled",
  									"sSortColumn" : "sorting_", /*
  																 * Note that an
  																 * int is
  																 * postfixed for
  																 * the sorting
  																 * order
  																 */
  									"sSortJUIAsc" : "",
  									"sSortJUIDesc" : "",
  									"sSortJUI" : "",
  									"sSortJUIAscAllowed" : "",
  									"sSortJUIDescAllowed" : "",
  									"sSortJUIWrapper" : "",
  									"sSortIcon" : "",
  
  									/* Scrolling */
  									"sScrollWrapper" : "dataTables_scroll",
  									"sScrollHead" : "dataTables_scrollHead",
  									"sScrollHeadInner" : "dataTables_scrollHeadInner",
  									"sScrollBody" : "dataTables_scrollBody",
  									"sScrollFoot" : "dataTables_scrollFoot",
  									"sScrollFootInner" : "dataTables_scrollFootInner",
  
  									/* Misc */
  									"sFooterTH" : "",
  									"sJUIHeader" : "",
  									"sJUIFooter" : ""
  								});
  
  				$
  						.extend(
  								DataTable.ext.oJUIClasses,
  								DataTable.ext.oStdClasses,
  								{
  									/* Two buttons buttons */
  									"sPagePrevEnabled" : "fg-button ui-button ui-state-default ui-corner-left",
  									"sPagePrevDisabled" : "fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",
  									"sPageNextEnabled" : "fg-button ui-button ui-state-default ui-corner-right",
  									"sPageNextDisabled" : "fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",
  									"sPageJUINext" : "ui-icon ui-icon-circle-arrow-e",
  									"sPageJUIPrev" : "ui-icon ui-icon-circle-arrow-w",
  
  									/* Full numbers paging buttons */
  									"sPageButton" : "fg-button ui-button ui-state-default",
  									"sPageButtonActive" : "fg-button ui-button ui-state-default ui-state-disabled",
  									"sPageButtonStaticDisabled" : "fg-button ui-button ui-state-default ui-state-disabled",
  									"sPageFirst" : "first ui-corner-tl ui-corner-bl",
  									"sPageLast" : "last ui-corner-tr ui-corner-br",
  
  									/* Features */
  									"sPaging" : "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "
  											+ "ui-buttonset-multi paging_", /*
  																			 * Note
  																			 * that
  																			 * the
  																			 * type
  																			 * is
  																			 * postfixed
  																			 */
  
  									/* Sorting */
  									"sSortAsc" : "ui-state-default",
  									"sSortDesc" : "ui-state-default",
  									"sSortable" : "ui-state-default",
  									"sSortableAsc" : "ui-state-default",
  									"sSortableDesc" : "ui-state-default",
  									"sSortableNone" : "ui-state-default",
  									"sSortJUIAsc" : "css_right ui-icon ui-icon-triangle-1-n",
  									"sSortJUIDesc" : "css_right ui-icon ui-icon-triangle-1-s",
  									"sSortJUI" : "css_right ui-icon ui-icon-carat-2-n-s",
  									"sSortJUIAscAllowed" : "css_right ui-icon ui-icon-carat-1-n",
  									"sSortJUIDescAllowed" : "css_right ui-icon ui-icon-carat-1-s",
  									"sSortJUIWrapper" : "DataTables_sort_wrapper",
  									"sSortIcon" : "DataTables_sort_icon",
  
  									/* Scrolling */
  									"sScrollHead" : "dataTables_scrollHead ui-state-default",
  									"sScrollFoot" : "dataTables_scrollFoot ui-state-default",
  
  									/* Misc */
  									"sFooterTH" : "ui-state-default",
  									"sJUIHeader" : "fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix",
  									"sJUIFooter" : "fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix"
  								});
  
  				/*
  				 * Variable: oPagination Purpose: Scope: jQuery.fn.dataTableExt
  				 */
  				$
  						.extend(
  								DataTable.ext.oPagination,
  								{
  									/*
  									 * Variable: two_button Purpose: Standard
  									 * two button (forward/back) pagination
  									 * Scope: jQuery.fn.dataTableExt.oPagination
  									 */
  									"two_button" : {
  										/*
  										 * Function:
  										 * oPagination.two_button.fnInit
  										 * Purpose: Initialise dom elements
  										 * required for pagination with
  										 * forward/back buttons only Returns: -
  										 * Inputs: object:oSettings - dataTables
  										 * settings object node:nPaging - the
  										 * DIV which contains this pagination
  										 * control function:fnCallbackDraw -
  										 * draw function which must be called on
  										 * update
  										 */
  										"fnInit" : function(oSettings, nPaging,
  												fnCallbackDraw) {
  											var oLang = oSettings.oLanguage.oPaginate;
  											var oClasses = oSettings.oClasses;
  											var fnClickHandler = function(e) {
  												if (oSettings.oApi
  														._fnPageChange(
  																oSettings,
  																e.data.action)) {
  													fnCallbackDraw(oSettings);
  												}
  											};
  
  											var sAppend = (!oSettings.bJUI) ? '<a class="'
  													+ oSettings.oClasses.sPagePrevDisabled
  													+ '" tabindex="'
  													+ oSettings.iTabIndex
  													+ '" role="button">'
  													+ oLang.sPrevious
  													+ '</a>'
  													+ '<a class="'
  													+ oSettings.oClasses.sPageNextDisabled
  													+ '" tabindex="'
  													+ oSettings.iTabIndex
  													+ '" role="button">'
  													+ oLang.sNext + '</a>'
  													: '<a class="'
  															+ oSettings.oClasses.sPagePrevDisabled
  															+ '" tabindex="'
  															+ oSettings.iTabIndex
  															+ '" role="button"><span class="'
  															+ oSettings.oClasses.sPageJUIPrev
  															+ '"></span></a>'
  															+ '<a class="'
  															+ oSettings.oClasses.sPageNextDisabled
  															+ '" tabindex="'
  															+ oSettings.iTabIndex
  															+ '" role="button"><span class="'
  															+ oSettings.oClasses.sPageJUINext
  															+ '"></span></a>';
  											$(nPaging).append(sAppend);
  
  											var els = $('a', nPaging);
  											var nPrevious = els[0], nNext = els[1];
  
  											oSettings.oApi._fnBindAction(
  													nPrevious, {
  														action : "previous"
  													}, fnClickHandler);
  											oSettings.oApi._fnBindAction(nNext,
  													{
  														action : "next"
  													}, fnClickHandler);
  
  											/* ID the first elements only */
  											if (!oSettings.aanFeatures.p) {
  												nPaging.id = oSettings.sTableId
  														+ '_paginate';
  												nPrevious.id = oSettings.sTableId
  														+ '_previous';
  												nNext.id = oSettings.sTableId
  														+ '_next';
  
  												nPrevious.setAttribute(
  														'aria-controls',
  														oSettings.sTableId);
  												nNext.setAttribute(
  														'aria-controls',
  														oSettings.sTableId);
  											}
  										},
  
  										/*
  										 * Function:
  										 * oPagination.two_button.fnUpdate
  										 * Purpose: Update the two button
  										 * pagination at the end of the draw
  										 * Returns: - Inputs: object:oSettings -
  										 * dataTables settings object
  										 * function:fnCallbackDraw - draw
  										 * function to call on page change
  										 */
  										"fnUpdate" : function(oSettings,
  												fnCallbackDraw) {
  											if (!oSettings.aanFeatures.p) {
  												return;
  											}
  
  											var oClasses = oSettings.oClasses;
  											var an = oSettings.aanFeatures.p;
  											var nNode;
  
  											/*
  											 * Loop over each instance of the
  											 * pager
  											 */
  											for (var i = 0, iLen = an.length; i < iLen; i++) {
  												nNode = an[i].firstChild;
  												if (nNode) {
  													/* Previous page */
  													nNode.className = (oSettings._iDisplayStart === 0) ? oClasses.sPagePrevDisabled
  															: oClasses.sPagePrevEnabled;
  
  													/* Next page */
  													nNode = nNode.nextSibling;
  													nNode.className = (oSettings
  															.fnDisplayEnd() == oSettings
  															.fnRecordsDisplay()) ? oClasses.sPageNextDisabled
  															: oClasses.sPageNextEnabled;
  												}
  											}
  										}
  									},
  
  									/*
  									 * Variable: iFullNumbersShowPages Purpose:
  									 * Change the number of pages which can be
  									 * seen Scope:
  									 * jQuery.fn.dataTableExt.oPagination
  									 */
  									"iFullNumbersShowPages" : 5,
  
  									/*
  									 * Variable: full_numbers Purpose: Full
  									 * numbers pagination Scope:
  									 * jQuery.fn.dataTableExt.oPagination
  									 */
  									"full_numbers" : {
  										/*
  										 * Function:
  										 * oPagination.full_numbers.fnInit
  										 * Purpose: Initialise dom elements
  										 * required for pagination with a list
  										 * of the pages Returns: - Inputs:
  										 * object:oSettings - dataTables
  										 * settings object node:nPaging - the
  										 * DIV which contains this pagination
  										 * control function:fnCallbackDraw -
  										 * draw function which must be called on
  										 * update
  										 */
  										"fnInit" : function(oSettings, nPaging,
  												fnCallbackDraw) {
  											var oLang = oSettings.oLanguage.oPaginate;
  											var oClasses = oSettings.oClasses;
  											var fnClickHandler = function(e) {
  												if (oSettings.oApi
  														._fnPageChange(
  																oSettings,
  																e.data.action)) {
  													fnCallbackDraw(oSettings);
  												}
  											};
  
  											$(nPaging)
  													.append(
  															'<a  tabindex="'
  																	+ oSettings.iTabIndex
  																	+ '" class="'
  																	+ oClasses.sPageButton
  																	+ " "
  																	+ oClasses.sPageFirst
  																	+ '">'
  																	+ oLang.sFirst
  																	+ '</a>'
  																	+ '<a  tabindex="'
  																	+ oSettings.iTabIndex
  																	+ '" class="'
  																	+ oClasses.sPageButton
  																	+ " "
  																	+ oClasses.sPagePrevious
  																	+ '">'
  																	+ oLang.sPrevious
  																	+ '</a>'
  																	+ '<span></span>'
  																	+ '<a tabindex="'
  																	+ oSettings.iTabIndex
  																	+ '" class="'
  																	+ oClasses.sPageButton
  																	+ " "
  																	+ oClasses.sPageNext
  																	+ '">'
  																	+ oLang.sNext
  																	+ '</a>'
  																	+ '<a tabindex="'
  																	+ oSettings.iTabIndex
  																	+ '" class="'
  																	+ oClasses.sPageButton
  																	+ " "
  																	+ oClasses.sPageLast
  																	+ '">'
  																	+ oLang.sLast
  																	+ '</a>');
  											var els = $('a', nPaging);
  											var nFirst = els[0], nPrev = els[1], nNext = els[2], nLast = els[3];
  
  											oSettings.oApi._fnBindAction(
  													nFirst, {
  														action : "first"
  													}, fnClickHandler);
  											oSettings.oApi._fnBindAction(nPrev,
  													{
  														action : "previous"
  													}, fnClickHandler);
  											oSettings.oApi._fnBindAction(nNext,
  													{
  														action : "next"
  													}, fnClickHandler);
  											oSettings.oApi._fnBindAction(nLast,
  													{
  														action : "last"
  													}, fnClickHandler);
  
  											/* ID the first elements only */
  											if (!oSettings.aanFeatures.p) {
  												nPaging.id = oSettings.sTableId
  														+ '_paginate';
  												nFirst.id = oSettings.sTableId
  														+ '_first';
  												nPrev.id = oSettings.sTableId
  														+ '_previous';
  												nNext.id = oSettings.sTableId
  														+ '_next';
  												nLast.id = oSettings.sTableId
  														+ '_last';
  											}
  										},
  
  										/*
  										 * Function:
  										 * oPagination.full_numbers.fnUpdate
  										 * Purpose: Update the list of page
  										 * buttons shows Returns: - Inputs:
  										 * object:oSettings - dataTables
  										 * settings object
  										 * function:fnCallbackDraw - draw
  										 * function to call on page change
  										 */
  										"fnUpdate" : function(oSettings,
  												fnCallbackDraw) {
  											if (!oSettings.aanFeatures.p) {
  												return;
  											}
  
  											var iPageCount = DataTable.ext.oPagination.iFullNumbersShowPages;
  											var iPageCountHalf = Math
  													.floor(iPageCount / 2);
  											var iPages = Math
  													.ceil((oSettings
  															.fnRecordsDisplay())
  															/ oSettings._iDisplayLength);
  											var iCurrentPage = Math
  													.ceil(oSettings._iDisplayStart
  															/ oSettings._iDisplayLength) + 1;
  											var sList = "";
  											var iStartButton, iEndButton, i, iLen;
  											var oClasses = oSettings.oClasses;
  											var anButtons, anStatic, nPaginateList, nNode;
  											var an = oSettings.aanFeatures.p;
  											var fnBind = function(j) {
  												oSettings.oApi
  														._fnBindAction(
  																this,
  																{
  																	"page" : j
  																			+ iStartButton
  																			- 1
  																},
  																function(e) {
  																	/*
  																	 * Use the
  																	 * information
  																	 * in the
  																	 * element
  																	 * to jump
  																	 * to the
  																	 * required
  																	 * page
  																	 */
  																	oSettings.oApi
  																			._fnPageChange(
  																					oSettings,
  																					e.data.page);
  																	fnCallbackDraw(oSettings);
  																	e
  																			.preventDefault();
  																});
  											};
  
  											/* Pages calculation */
  											if (oSettings._iDisplayLength === -1) {
  												iStartButton = 1;
  												iEndButton = 1;
  												iCurrentPage = 1;
  											} else if (iPages < iPageCount) {
  												iStartButton = 1;
  												iEndButton = iPages;
  											} else if (iCurrentPage <= iPageCountHalf) {
  												iStartButton = 1;
  												iEndButton = iPageCount;
  											} else if (iCurrentPage >= (iPages - iPageCountHalf)) {
  												iStartButton = iPages
  														- iPageCount + 1;
  												iEndButton = iPages;
  											} else {
  												iStartButton = iCurrentPage
  														- Math
  																.ceil(iPageCount / 2)
  														+ 1;
  												iEndButton = iStartButton
  														+ iPageCount - 1;
  											}
  
  											/* Build the dynamic list */
  											for (i = iStartButton; i <= iEndButton; i++) {
  												sList += (iCurrentPage !== i) ? '<a tabindex="'
  														+ oSettings.iTabIndex
  														+ '" class="'
  														+ oClasses.sPageButton
  														+ '">'
  														+ oSettings
  																.fnFormatNumber(i)
  														+ '</a>'
  														: '<a tabindex="'
  																+ oSettings.iTabIndex
  																+ '" class="'
  																+ oClasses.sPageButtonActive
  																+ '">'
  																+ oSettings
  																		.fnFormatNumber(i)
  																+ '</a>';
  											}
  
  											/*
  											 * Loop over each instance of the
  											 * pager
  											 */
  											for (i = 0, iLen = an.length; i < iLen; i++) {
  												nNode = an[i];
  												if (!nNode.hasChildNodes()) {
  													continue;
  												}
  
  												/*
  												 * Build up the dynamic list
  												 * first - html and listeners
  												 */
  												$('span:eq(0)', nNode).html(
  														sList).children('a')
  														.each(fnBind);
  
  												/*
  												 * Update the permanent button's
  												 * classes
  												 */
  												anButtons = nNode
  														.getElementsByTagName('a');
  												anStatic = [
  														anButtons[0],
  														anButtons[1],
  														anButtons[anButtons.length - 2],
  														anButtons[anButtons.length - 1] ];
  
  												$(anStatic)
  														.removeClass(
  																oClasses.sPageButton
  																		+ " "
  																		+ oClasses.sPageButtonActive
  																		+ " "
  																		+ oClasses.sPageButtonStaticDisabled);
  												$([ anStatic[0], anStatic[1] ])
  														.addClass(
  																(iCurrentPage == 1) ? oClasses.sPageButtonStaticDisabled
  																		: oClasses.sPageButton);
  												$([ anStatic[2], anStatic[3] ])
  														.addClass(
  																(iPages === 0
  																		|| iCurrentPage === iPages || oSettings._iDisplayLength === -1) ? oClasses.sPageButtonStaticDisabled
  																		: oClasses.sPageButton);
  											}
  										}
  									}
  								});
  
  				$.extend(DataTable.ext.oSort, {
  					/*
  					 * text sorting
  					 */
  					"string-pre" : function(a) {
  						if (typeof a != 'string') {
  							a = (a !== null && a.toString) ? a.toString() : '';
  						}
  						return a.toLowerCase();
  					},
  
  					"string-asc" : function(x, y) {
  						return ((x < y) ? -1 : ((x > y) ? 1 : 0));
  					},
  
  					"string-desc" : function(x, y) {
  						return ((x < y) ? 1 : ((x > y) ? -1 : 0));
  					},
  
  					/*
  					 * html sorting (ignore html tags)
  					 */
  					"html-pre" : function(a) {
  						return a.replace(/<.*?>/g, "").toLowerCase();
  					},
  
  					"html-asc" : function(x, y) {
  						return ((x < y) ? -1 : ((x > y) ? 1 : 0));
  					},
  
  					"html-desc" : function(x, y) {
  						return ((x < y) ? 1 : ((x > y) ? -1 : 0));
  					},
  
  					/*
  					 * date sorting
  					 */
  					"date-pre" : function(a) {
  						var x = Date.parse(a);
  
  						if (isNaN(x) || x === "") {
  							x = Date.parse("01/01/1970 00:00:00");
  						}
  						return x;
  					},
  
  					"date-asc" : function(x, y) {
  						return x - y;
  					},
  
  					"date-desc" : function(x, y) {
  						return y - x;
  					},
  
  					/*
  					 * numerical sorting
  					 */
  					"numeric-pre" : function(a) {
  						return (a == "-" || a === "") ? 0 : a * 1;
  					},
  
  					"numeric-asc" : function(x, y) {
  						return x - y;
  					},
  
  					"numeric-desc" : function(x, y) {
  						return y - x;
  					}
  				});
  
  				$
  						.extend(
  								DataTable.ext.aTypes,
  								[
  										/*
  										 * Function: - Purpose: Check to see if
  										 * a string is numeric Returns:
  										 * string:'numeric' or null Inputs:
  										 * mixed:sText - string to check
  										 */
  										function(sData) {
  											/*
  											 * Allow zero length strings as a
  											 * number
  											 */
  											if (typeof sData === 'number') {
  												return 'numeric';
  											} else if (typeof sData !== 'string') {
  												return null;
  											}
  
  											var sValidFirstChars = "0123456789-";
  											var sValidChars = "0123456789.";
  											var Char;
  											var bDecimal = false;
  
  											/*
  											 * Check for a valid first char (no
  											 * period and allow negatives)
  											 */
  											Char = sData.charAt(0);
  											if (sValidFirstChars.indexOf(Char) == -1) {
  												return null;
  											}
  
  											/*
  											 * Check all the other characters
  											 * are valid
  											 */
  											for (var i = 1; i < sData.length; i++) {
  												Char = sData.charAt(i);
  												if (sValidChars.indexOf(Char) == -1) {
  													return null;
  												}
  
  												/*
  												 * Only allowed one decimal
  												 * place...
  												 */
  												if (Char == ".") {
  													if (bDecimal) {
  														return null;
  													}
  													bDecimal = true;
  												}
  											}
  
  											return 'numeric';
  										},
  
  										/*
  										 * Function: - Purpose: Check to see if
  										 * a string is actually a formatted date
  										 * Returns: string:'date' or null
  										 * Inputs: string:sText - string to
  										 * check
  										 */
  										function(sData) {
  											var iParse = Date.parse(sData);
  											if ((iParse !== null && !isNaN(iParse))
  													|| (typeof sData === 'string' && sData.length === 0)) {
  												return 'date';
  											}
  											return null;
  										},
  
  										/*
  										 * Function: - Purpose: Check to see if
  										 * a string should be treated as an HTML
  										 * string Returns: string:'html' or null
  										 * Inputs: string:sText - string to
  										 * check
  										 */
  										function(sData) {
  											if (typeof sData === 'string'
  													&& sData.indexOf('<') != -1
  													&& sData.indexOf('>') != -1) {
  												return 'html';
  											}
  											return null;
  										} ]);
  
  				// jQuery aliases
  				$.fn.DataTable = DataTable;
  				$.fn.dataTable = DataTable;
  				$.fn.dataTableSettings = DataTable.settings;
  				$.fn.dataTableExt = DataTable.ext;
  
  				// Information about events fired by DataTables - for
  				// documentation.
  				/**
  				 * Draw event, fired whenever the table is redrawn on the page,
  				 * at the same point as fnDrawCallback. This may be useful for
  				 * binding events or performing calculations when the table is
  				 * altered at all.
  				 * 
  				 * @name DataTable#draw
  				 * @event
  				 * @param {event}
  				 *            e jQuery event object
  				 * @param {object}
  				 *            o DataTables settings object
  				 *            {@link DataTable.models.oSettings}
  				 */
  
  				/**
  				 * Filter event, fired when the filtering applied to the table
  				 * (using the build in global global filter, or column filters)
  				 * is altered.
  				 * 
  				 * @name DataTable#filter
  				 * @event
  				 * @param {event}
  				 *            e jQuery event object
  				 * @param {object}
  				 *            o DataTables settings object
  				 *            {@link DataTable.models.oSettings}
  				 */
  
  				/**
  				 * Page change event, fired when the paging of the table is
  				 * altered.
  				 * 
  				 * @name DataTable#page
  				 * @event
  				 * @param {event}
  				 *            e jQuery event object
  				 * @param {object}
  				 *            o DataTables settings object
  				 *            {@link DataTable.models.oSettings}
  				 */
  
  				/**
  				 * Sort event, fired when the sorting applied to the table is
  				 * altered.
  				 * 
  				 * @name DataTable#sort
  				 * @event
  				 * @param {event}
  				 *            e jQuery event object
  				 * @param {object}
  				 *            o DataTables settings object
  				 *            {@link DataTable.models.oSettings}
  				 */
  
  				/**
  				 * DataTables initialisation complete event, fired when the
  				 * table is fully drawn, including Ajax data loaded, if Ajax
  				 * data is required.
  				 * 
  				 * @name DataTable#init
  				 * @event
  				 * @param {event}
  				 *            e jQuery event object
  				 * @param {object}
  				 *            oSettings DataTables settings object
  				 * @param {object}
  				 *            json The JSON object request from the server -
  				 *            only present if client-side Ajax sourced data is
  				 *            used</li>
  				 *            </ol>
  				 */
  
  				/**
  				 * State save event, fired when the table has changed state a
  				 * new state save is required. This method allows modification
  				 * of the state saving object prior to actually doing the save,
  				 * including addition or other state properties (for plug-ins)
  				 * or modification of a DataTables core property.
  				 * 
  				 * @name DataTable#stateSaveParams
  				 * @event
  				 * @param {event}
  				 *            e jQuery event object
  				 * @param {object}
  				 *            oSettings DataTables settings object
  				 * @param {object}
  				 *            json The state information to be saved
  				 */
  
  				/**
  				 * State load event, fired when the table is loading state from
  				 * the stored data, but prior to the settings object being
  				 * modified by the saved state - allowing modification of the
  				 * saved state is required or loading of state for a plug-in.
  				 * 
  				 * @name DataTable#stateLoadParams
  				 * @event
  				 * @param {event}
  				 *            e jQuery event object
  				 * @param {object}
  				 *            oSettings DataTables settings object
  				 * @param {object}
  				 *            json The saved state information
  				 */
  
  				/**
  				 * State loaded event, fired when state has been loaded from
  				 * stored data and the settings object has been modified by the
  				 * loaded data.
  				 * 
  				 * @name DataTable#stateLoaded
  				 * @event
  				 * @param {event}
  				 *            e jQuery event object
  				 * @param {object}
  				 *            oSettings DataTables settings object
  				 * @param {object}
  				 *            json The saved state information
  				 */
  
  				/**
  				 * Processing event, fired when DataTables is doing some kind of
  				 * processing (be it, sort, filter or anything else). Can be
  				 * used to indicate to the end user that there is something
  				 * happening, or that something has finished.
  				 * 
  				 * @name DataTable#processing
  				 * @event
  				 * @param {event}
  				 *            e jQuery event object
  				 * @param {object}
  				 *            oSettings DataTables settings object
  				 * @param {boolean}
  				 *            bShow Flag for if DataTables is doing processing
  				 *            or not
  				 */
  
  				/**
  				 * Ajax (XHR) event, fired whenever an Ajax request is completed
  				 * from a request to made to the server for new data (note that
  				 * this trigger is called in fnServerData, if you override
  				 * fnServerData and which to use this event, you need to trigger
  				 * it in you success function).
  				 * 
  				 * @name DataTable#xhr
  				 * @event
  				 * @param {event}
  				 *            e jQuery event object
  				 * @param {object}
  				 *            o DataTables settings object
  				 *            {@link DataTable.models.oSettings}
  				 * @param {object}
  				 *            json JSON returned from the server
  				 */
  
  				/**
  				 * Destroy event, fired when the DataTable is destroyed by
  				 * calling fnDestroy or passing the bDestroy:true parameter in
  				 * the initialisation object. This can be used to remove bound
  				 * events, added DOM nodes, etc.
  				 * 
  				 * @name DataTable#destroy
  				 * @event
  				 * @param {event}
  				 *            e jQuery event object
  				 * @param {object}
  				 *            o DataTables settings object
  				 *            {@link DataTable.models.oSettings}
  				 */
  			}));
  
  }(window, document));
  