You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2012/10/25 21:17:39 UTC
svn commit: r1402281 [3/7] - in /incubator/ambari/branches/AMBARI-666: ./
ambari-web/ ambari-web/app/ ambari-web/app/controllers/installer/
ambari-web/app/controllers/main/ ambari-web/app/controllers/main/apps/
ambari-web/app/controllers/main/apps/runs...
Added: incubator/ambari/branches/AMBARI-666/ambari-web/vendor/scripts/jquery.dataTables.js
URL: http://svn.apache.org/viewvc/incubator/ambari/branches/AMBARI-666/ambari-web/vendor/scripts/jquery.dataTables.js?rev=1402281&view=auto
==============================================================================
--- incubator/ambari/branches/AMBARI-666/ambari-web/vendor/scripts/jquery.dataTables.js (added)
+++ incubator/ambari/branches/AMBARI-666/ambari-web/vendor/scripts/jquery.dataTables.js Thu Oct 25 19:17:36 2012
@@ -0,0 +1,11272 @@
+/**
+ * @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,_fnEscap
eRegex,_fnDeleteIndex,_fnReOrderIndex,_fnColumnOrdering,_fnLog,_fnClearTable,_fnSaveState,_fnLoadState,_fnCreateCookie,_fnReadCookie,_fnDetectHeader,_fnGetUniqueThs,_fnScrollBarWidth,_fnApplyToChildren,_fnMap,_fnGetRowData,_fnGetCellData,_fnSetCellData,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnApplyColumnDefs,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnJsonString,_fnRender,_fnNodeToColumnIndex,_fnInfoMacros,_fnBrowserDetect,_fnGetColumns*/
+
+(/** @lends <global> */function (window, document, undefined) {
+
+ (function (factory) {
+ "use strict";
+
+ // Define as an AMD module if possible
+ if (typeof define === 'function' && define.amd) {
+ define(['jquery'], factory);
+ }
+ /* Define using browser globals otherwise
+ * Prevent multiple instantiations if the script is loaded twice
+ */
+ else if (jQuery && !jQuery.fn.dataTable) {
+ factory(jQuery);
+ }
+ }
+ (/** @lends <global> */function ($) {
+ "use strict";
+ /**
+ * DataTables is a plug-in for the jQuery Javascript library. It is a
+ * highly flexible tool, based upon the foundations of progressive
+ * enhancement, which will add advanced interaction controls to any
+ * HTML table. For a full list of features please refer to
+ * <a href="http://datatables.net">DataTables.net</a>.
+ *
+ * Note that the <i>DataTable</i> object is not a global variable but is
+ * aliased to <i>jQuery.fn.DataTable</i> and <i>jQuery.fn.dataTable</i> through which
+ * it may be accessed.
+ *
+ * @class
+ * @param {object} [oInit={}] Configuration object for DataTables. Options
+ * are defined by {@link DataTable.defaults}
+ * @requires jQuery 1.3+
+ *
+ * @example
+ * // Basic initialisation
+ * $(document).ready( function {
+ * $('#example').dataTable();
+ * } );
+ *
+ * @example
+ * // Initialisation with configuration options - in this case, disable
+ * // pagination and sorting.
+ * $(document).ready( function {
+ * $('#example').dataTable( {
+ * "bPaginate": false,
+ * "bSort": false
+ * } );
+ * } );
+ */
+ var DataTable = function (oInit) {
+
+
+ /**
+ * Add a column to the list used for the table with default values
+ * @param {object} oSettings dataTables settings object
+ * @param {node} nTh The th element for this column
+ * @memberof DataTable#oApi
+ */
+ function _fnAddColumn(oSettings, nTh) {
+ var oDefaults = DataTable.defaults.columns;
+ var iCol = oSettings.aoColumns.length;
+ var oCol = $.extend({}, DataTable.models.oColumn, oDefaults, {
+ "sSortingClass":oSettings.oClasses.sSortable,
+ "sSortingClassJUI":oSettings.oClasses.sSortJUI,
+ "nTh":nTh ? nTh : document.createElement('th'),
+ "sTitle":oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
+ "aDataSort":oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
+ "mData":oDefaults.mData ? oDefaults.oDefaults : iCol
+ });
+ oSettings.aoColumns.push(oCol);
+
+ /* Add a column specific filter */
+ if (oSettings.aoPreSearchCols[ iCol ] === undefined || oSettings.aoPreSearchCols[ iCol ] === null) {
+ oSettings.aoPreSearchCols[ iCol ] = $.extend({}, DataTable.models.oSearch);
+ }
+ else {
+ var oPre = oSettings.aoPreSearchCols[ iCol ];
+
+ /* Don't require that the user must specify bRegex, bSmart or bCaseInsensitive */
+ if (oPre.bRegex === undefined) {
+ oPre.bRegex = true;
+ }
+
+ if (oPre.bSmart === undefined) {
+ oPre.bSmart = true;
+ }
+
+ if (oPre.bCaseInsensitive === undefined) {
+ oPre.bCaseInsensitive = true;
+ }
+ }
+
+ /* Use the column options function to initialise classes etc */
+ _fnColumnOptions(oSettings, iCol, null);
+ }
+
+
+ /**
+ * Apply options for a column
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iCol column index to consider
+ * @param {object} oOptions object with sType, bVisible and bSearchable etc
+ * @memberof DataTable#oApi
+ */
+ function _fnColumnOptions(oSettings, iCol, oOptions) {
+ var oCol = oSettings.aoColumns[ iCol ];
+
+ /* User specified column options */
+ if (oOptions !== undefined && oOptions !== null) {
+ /* Backwards compatibility for mDataProp */
+ if (oOptions.mDataProp && !oOptions.mData) {
+ oOptions.mData = oOptions.mDataProp;
+ }
+
+ if (oOptions.sType !== undefined) {
+ oCol.sType = oOptions.sType;
+ oCol._bAutoType = false;
+ }
+
+ $.extend(oCol, oOptions);
+ _fnMap(oCol, oOptions, "sWidth", "sWidthOrig");
+
+ /* iDataSort to be applied (backwards compatibility), but aDataSort will take
+ * priority if defined
+ */
+ if (oOptions.iDataSort !== undefined) {
+ oCol.aDataSort = [ oOptions.iDataSort ];
+ }
+ _fnMap(oCol, oOptions, "aDataSort");
+ }
+
+ /* Cache the data get and set functions for speed */
+ var mRender = oCol.mRender ? _fnGetObjectDataFn(oCol.mRender) : null;
+ var mData = _fnGetObjectDataFn(oCol.mData);
+
+ oCol.fnGetData = function (oData, sSpecific) {
+ var innerData = mData(oData, sSpecific);
+
+ if (oCol.mRender && (sSpecific && sSpecific !== '')) {
+ return mRender(innerData, sSpecific, oData);
+ }
+ return innerData;
+ };
+ oCol.fnSetData = _fnSetObjectDataFn(oCol.mData);
+
+ /* Feature sorting overrides column specific when off */
+ if (!oSettings.oFeatures.bSort) {
+ oCol.bSortable = false;
+ }
+
+ /* Check that the class assignment is correct for sorting */
+ if (!oCol.bSortable ||
+ ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1)) {
+ oCol.sSortingClass = oSettings.oClasses.sSortableNone;
+ oCol.sSortingClassJUI = "";
+ }
+ else if ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) == -1) {
+ oCol.sSortingClass = oSettings.oClasses.sSortable;
+ oCol.sSortingClassJUI = oSettings.oClasses.sSortJUI;
+ }
+ else if ($.inArray('asc', oCol.asSorting) != -1 && $.inArray('desc', oCol.asSorting) == -1) {
+ oCol.sSortingClass = oSettings.oClasses.sSortableAsc;
+ oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIAscAllowed;
+ }
+ else if ($.inArray('asc', oCol.asSorting) == -1 && $.inArray('desc', oCol.asSorting) != -1) {
+ oCol.sSortingClass = oSettings.oClasses.sSortableDesc;
+ oCol.sSortingClassJUI = oSettings.oClasses.sSortJUIDescAllowed;
+ }
+ }
+
+
+ /**
+ * Adjust the table column widths for new data. Note: you would probably want to
+ * do a redraw after calling this function!
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnAdjustColumnSizing(oSettings) {
+ /* Not interested in doing column width calculation if auto-width is disabled */
+ if (oSettings.oFeatures.bAutoWidth === false) {
+ return false;
+ }
+
+ _fnCalculateColumnWidths(oSettings);
+ for (var i = 0 , iLen = oSettings.aoColumns.length; i < iLen; i++) {
+ oSettings.aoColumns[i].nTh.style.width = oSettings.aoColumns[i].sWidth;
+ }
+ }
+
+
+ /**
+ * Covert the index of a visible column to the index in the data array (take account
+ * of hidden columns)
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iMatch Visible column index to lookup
+ * @returns {int} i the data index
+ * @memberof DataTable#oApi
+ */
+ function _fnVisibleToColumnIndex(oSettings, iMatch) {
+ var aiVis = _fnGetColumns(oSettings, 'bVisible');
+
+ return typeof aiVis[iMatch] === 'number' ?
+ aiVis[iMatch] :
+ null;
+ }
+
+
+ /**
+ * Covert the index of an index in the data array and convert it to the visible
+ * column index (take account of hidden columns)
+ * @param {int} iMatch Column index to lookup
+ * @param {object} oSettings dataTables settings object
+ * @returns {int} i the data index
+ * @memberof DataTable#oApi
+ */
+ function _fnColumnIndexToVisible(oSettings, iMatch) {
+ var aiVis = _fnGetColumns(oSettings, 'bVisible');
+ var iPos = $.inArray(iMatch, aiVis);
+
+ return iPos !== -1 ? iPos : null;
+ }
+
+
+ /**
+ * Get the number of visible columns
+ * @param {object} oSettings dataTables settings object
+ * @returns {int} i the number of visible columns
+ * @memberof DataTable#oApi
+ */
+ function _fnVisbleColumns(oSettings) {
+ return _fnGetColumns(oSettings, 'bVisible').length;
+ }
+
+
+ /**
+ * Get an array of column indexes that match a given property
+ * @param {object} oSettings dataTables settings object
+ * @param {string} sParam Parameter in aoColumns to look for - typically
+ * bVisible or bSearchable
+ * @returns {array} Array of indexes with matched properties
+ * @memberof DataTable#oApi
+ */
+ function _fnGetColumns(oSettings, sParam) {
+ var a = [];
+
+ $.map(oSettings.aoColumns, function (val, i) {
+ if (val[sParam]) {
+ a.push(i);
+ }
+ });
+
+ return a;
+ }
+
+
+ /**
+ * Get the sort type based on an input string
+ * @param {string} sData data we wish to know the type of
+ * @returns {string} type (defaults to 'string' if no type can be detected)
+ * @memberof DataTable#oApi
+ */
+ function _fnDetectType(sData) {
+ var aTypes = DataTable.ext.aTypes;
+ var iLen = aTypes.length;
+
+ for (var i = 0; i < iLen; i++) {
+ var sType = aTypes[i](sData);
+ if (sType !== null) {
+ return sType;
+ }
+ }
+
+ return 'string';
+ }
+
+
+ /**
+ * Figure out how to reorder a display list
+ * @param {object} oSettings dataTables settings object
+ * @returns array {int} aiReturn index list for reordering
+ * @memberof DataTable#oApi
+ */
+ function _fnReOrderIndex(oSettings, sColumns) {
+ var aColumns = sColumns.split(',');
+ var aiReturn = [];
+
+ for (var i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
+ for (var j = 0; j < iLen; j++) {
+ if (oSettings.aoColumns[i].sName == aColumns[j]) {
+ aiReturn.push(j);
+ break;
+ }
+ }
+ }
+
+ return aiReturn;
+ }
+
+
+ /**
+ * Get the column ordering that DataTables expects
+ * @param {object} oSettings dataTables settings object
+ * @returns {string} comma separated list of names
+ * @memberof DataTable#oApi
+ */
+ function _fnColumnOrdering(oSettings) {
+ var sNames = '';
+ for (var i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
+ sNames += oSettings.aoColumns[i].sName + ',';
+ }
+ if (sNames.length == iLen) {
+ return "";
+ }
+ return sNames.slice(0, -1);
+ }
+
+
+ /**
+ * Take the column definitions and static columns arrays and calculate how
+ * they relate to column indexes. The callback function will then apply the
+ * definition found for a column to a suitable configuration object.
+ * @param {object} oSettings dataTables settings object
+ * @param {array} aoColDefs The aoColumnDefs array that is to be applied
+ * @param {array} aoCols The aoColumns array that defines columns individually
+ * @param {function} fn Callback function - takes two parameters, the calculated
+ * column index and the definition for that column.
+ * @memberof DataTable#oApi
+ */
+ function _fnApplyColumnDefs(oSettings, aoColDefs, aoCols, fn) {
+ var i, iLen, j, jLen, k, kLen;
+
+ // Column definitions with aTargets
+ if (aoColDefs) {
+ /* Loop over the definitions array - loop in reverse so first instance has priority */
+ for (i = aoColDefs.length - 1; i >= 0; i--) {
+ /* Each definition can target multiple columns, as it is an array */
+ var aTargets = aoColDefs[i].aTargets;
+ if (!$.isArray(aTargets)) {
+ _fnLog(oSettings, 1, 'aTargets must be an array of targets, not a ' + (typeof aTargets));
+ }
+
+ for (j = 0, jLen = aTargets.length; j < jLen; j++) {
+ if (typeof aTargets[j] === 'number' && aTargets[j] >= 0) {
+ /* Add columns that we don't yet know about */
+ while (oSettings.aoColumns.length <= aTargets[j]) {
+ _fnAddColumn(oSettings);
+ }
+
+ /* Integer, basic index */
+ fn(aTargets[j], aoColDefs[i]);
+ }
+ else if (typeof aTargets[j] === 'number' && aTargets[j] < 0) {
+ /* Negative integer, right to left column counting */
+ fn(oSettings.aoColumns.length + aTargets[j], aoColDefs[i]);
+ }
+ else if (typeof aTargets[j] === 'string') {
+ /* Class name matching on TH element */
+ for (k = 0, kLen = oSettings.aoColumns.length; k < kLen; k++) {
+ if (aTargets[j] == "_all" ||
+ $(oSettings.aoColumns[k].nTh).hasClass(aTargets[j])) {
+ fn(k, aoColDefs[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Statically defined columns array
+ if (aoCols) {
+ for (i = 0, iLen = aoCols.length; i < iLen; i++) {
+ fn(i, aoCols[i]);
+ }
+ }
+ }
+
+ /**
+ * Add a data array to the table, creating DOM node etc. This is the parallel to
+ * _fnGatherData, but for adding rows from a Javascript source, rather than a
+ * DOM source.
+ * @param {object} oSettings dataTables settings object
+ * @param {array} aData data array to be added
+ * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
+ * @memberof DataTable#oApi
+ */
+ function _fnAddData(oSettings, aDataSupplied) {
+ var oCol;
+
+ /* Take an independent copy of the data source so we can bash it about as we wish */
+ var aDataIn = ($.isArray(aDataSupplied)) ?
+ aDataSupplied.slice() :
+ $.extend(true, {}, aDataSupplied);
+
+ /* Create the object for storing information about this new row */
+ var iRow = oSettings.aoData.length;
+ var oData = $.extend(true, {}, DataTable.models.oRow);
+ oData._aData = aDataIn;
+ oSettings.aoData.push(oData);
+
+ /* Create the cells */
+ var nTd, sThisType;
+ for (var i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
+ oCol = oSettings.aoColumns[i];
+
+ /* Use rendered data for filtering / sorting */
+ if (typeof oCol.fnRender === 'function' && oCol.bUseRendered && oCol.mData !== null) {
+ _fnSetCellData(oSettings, iRow, i, _fnRender(oSettings, iRow, i));
+ }
+ else {
+ _fnSetCellData(oSettings, iRow, i, _fnGetCellData(oSettings, iRow, i));
+ }
+
+ /* See if we should auto-detect the column type */
+ if (oCol._bAutoType && oCol.sType != 'string') {
+ /* Attempt to auto detect the type - same as _fnGatherData() */
+ var sVarType = _fnGetCellData(oSettings, iRow, i, 'type');
+ if (sVarType !== null && sVarType !== '') {
+ sThisType = _fnDetectType(sVarType);
+ if (oCol.sType === null) {
+ oCol.sType = sThisType;
+ }
+ else if (oCol.sType != sThisType && oCol.sType != "html") {
+ /* String is always the 'fallback' option */
+ oCol.sType = 'string';
+ }
+ }
+ }
+ }
+
+ /* Add to the display array */
+ oSettings.aiDisplayMaster.push(iRow);
+
+ /* Create the DOM information */
+ if (!oSettings.oFeatures.bDeferRender) {
+ _fnCreateTr(oSettings, iRow);
+ }
+
+ return iRow;
+ }
+
+
+ /**
+ * Read in the data from the target table from the DOM
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnGatherData(oSettings) {
+ var iLoop, i, iLen, j, jLen, jInner,
+ nTds, nTrs, nTd, nTr, aLocalData, iThisIndex,
+ iRow, iRows, iColumn, iColumns, sNodeName,
+ oCol, oData;
+
+ /*
+ * Process by row first
+ * Add the data object for the whole table - storing the tr node. Note - no point in getting
+ * DOM based data if we are going to go and replace it with Ajax source data.
+ */
+ if (oSettings.bDeferLoading || oSettings.sAjaxSource === null) {
+ nTr = oSettings.nTBody.firstChild;
+ while (nTr) {
+ if (nTr.nodeName.toUpperCase() == "TR") {
+ iThisIndex = oSettings.aoData.length;
+ nTr._DT_RowIndex = iThisIndex;
+ oSettings.aoData.push($.extend(true, {}, DataTable.models.oRow, {
+ "nTr":nTr
+ }));
+
+ oSettings.aiDisplayMaster.push(iThisIndex);
+ nTd = nTr.firstChild;
+ jInner = 0;
+ while (nTd) {
+ sNodeName = nTd.nodeName.toUpperCase();
+ if (sNodeName == "TD" || sNodeName == "TH") {
+ _fnSetCellData(oSettings, iThisIndex, jInner, $.trim(nTd.innerHTML));
+ jInner++;
+ }
+ nTd = nTd.nextSibling;
+ }
+ }
+ nTr = nTr.nextSibling;
+ }
+ }
+
+ /* Gather in the TD elements of the Table - note that this is basically the same as
+ * fnGetTdNodes, but that function takes account of hidden columns, which we haven't yet
+ * setup!
+ */
+ nTrs = _fnGetTrNodes(oSettings);
+ nTds = [];
+ for (i = 0, iLen = nTrs.length; i < iLen; i++) {
+ nTd = nTrs[i].firstChild;
+ while (nTd) {
+ sNodeName = nTd.nodeName.toUpperCase();
+ if (sNodeName == "TD" || sNodeName == "TH") {
+ nTds.push(nTd);
+ }
+ nTd = nTd.nextSibling;
+ }
+ }
+
+ /* Now process by column */
+ for (iColumn = 0, iColumns = oSettings.aoColumns.length; iColumn < iColumns; iColumn++) {
+ oCol = oSettings.aoColumns[iColumn];
+
+ /* Get the title of the column - unless there is a user set one */
+ if (oCol.sTitle === null) {
+ oCol.sTitle = oCol.nTh.innerHTML;
+ }
+
+ var
+ bAutoType = oCol._bAutoType,
+ bRender = typeof oCol.fnRender === 'function',
+ bClass = oCol.sClass !== null,
+ bVisible = oCol.bVisible,
+ nCell, sThisType, sRendered, sValType;
+
+ /* A single loop to rule them all (and be more efficient) */
+ if (bAutoType || bRender || bClass || !bVisible) {
+ for (iRow = 0, iRows = oSettings.aoData.length; iRow < iRows; iRow++) {
+ oData = oSettings.aoData[iRow];
+ nCell = nTds[ (iRow * iColumns) + iColumn ];
+
+ /* Type detection */
+ if (bAutoType && oCol.sType != 'string') {
+ sValType = _fnGetCellData(oSettings, iRow, iColumn, 'type');
+ if (sValType !== '') {
+ sThisType = _fnDetectType(sValType);
+ if (oCol.sType === null) {
+ oCol.sType = sThisType;
+ }
+ else if (oCol.sType != sThisType &&
+ oCol.sType != "html") {
+ /* String is always the 'fallback' option */
+ oCol.sType = 'string';
+ }
+ }
+ }
+
+ if (oCol.mRender) {
+ // mRender has been defined, so we need to get the value and set it
+ nCell.innerHTML = _fnGetCellData(oSettings, iRow, iColumn, 'display');
+ }
+ else if (oCol.mData !== iColumn) {
+ // If mData is not the same as the column number, then we need to
+ // get the dev set value. If it is the column, no point in wasting
+ // time setting the value that is already there!
+ nCell.innerHTML = _fnGetCellData(oSettings, iRow, iColumn, 'display');
+ }
+
+ /* Rendering */
+ if (bRender) {
+ sRendered = _fnRender(oSettings, iRow, iColumn);
+ nCell.innerHTML = sRendered;
+ if (oCol.bUseRendered) {
+ /* Use the rendered data for filtering / sorting */
+ _fnSetCellData(oSettings, iRow, iColumn, sRendered);
+ }
+ }
+
+ /* Classes */
+ if (bClass) {
+ nCell.className += ' ' + oCol.sClass;
+ }
+
+ /* Column visibility */
+ if (!bVisible) {
+ oData._anHidden[iColumn] = nCell;
+ nCell.parentNode.removeChild(nCell);
+ }
+ else {
+ oData._anHidden[iColumn] = null;
+ }
+
+ if (oCol.fnCreatedCell) {
+ oCol.fnCreatedCell.call(oSettings.oInstance,
+ nCell, _fnGetCellData(oSettings, iRow, iColumn, 'display'), oData._aData, iRow, iColumn
+ );
+ }
+ }
+ }
+ }
+
+ /* Row created callbacks */
+ if (oSettings.aoRowCreatedCallback.length !== 0) {
+ for (i = 0, iLen = oSettings.aoData.length; i < iLen; i++) {
+ oData = oSettings.aoData[i];
+ _fnCallbackFire(oSettings, 'aoRowCreatedCallback', null, [oData.nTr, oData._aData, i]);
+ }
+ }
+ }
+
+
+ /**
+ * Take a TR element and convert it to an index in aoData
+ * @param {object} oSettings dataTables settings object
+ * @param {node} n the TR element to find
+ * @returns {int} index if the node is found, null if not
+ * @memberof DataTable#oApi
+ */
+ function _fnNodeToDataIndex(oSettings, n) {
+ return (n._DT_RowIndex !== undefined) ? n._DT_RowIndex : null;
+ }
+
+
+ /**
+ * Take a TD element and convert it into a column data index (not the visible index)
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iRow The row number the TD/TH can be found in
+ * @param {node} n The TD/TH element to find
+ * @returns {int} index if the node is found, -1 if not
+ * @memberof DataTable#oApi
+ */
+ function _fnNodeToColumnIndex(oSettings, iRow, n) {
+ var anCells = _fnGetTdNodes(oSettings, iRow);
+
+ for (var i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
+ if (anCells[i] === n) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+
+ /**
+ * Get an array of data for a given row from the internal data cache
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iRow aoData row id
+ * @param {string} sSpecific data get type ('type' 'filter' 'sort')
+ * @param {array} aiColumns Array of column indexes to get data from
+ * @returns {array} Data array
+ * @memberof DataTable#oApi
+ */
+ function _fnGetRowData(oSettings, iRow, sSpecific, aiColumns) {
+ var out = [];
+ for (var i = 0, iLen = aiColumns.length; i < iLen; i++) {
+ out.push(_fnGetCellData(oSettings, iRow, aiColumns[i], sSpecific));
+ }
+ return out;
+ }
+
+
+ /**
+ * Get the data for a given cell from the internal cache, taking into account data mapping
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iRow aoData row id
+ * @param {int} iCol Column index
+ * @param {string} sSpecific data get type ('display', 'type' 'filter' 'sort')
+ * @returns {*} Cell data
+ * @memberof DataTable#oApi
+ */
+ function _fnGetCellData(oSettings, iRow, iCol, sSpecific) {
+ var sData;
+ var oCol = oSettings.aoColumns[iCol];
+ var oData = oSettings.aoData[iRow]._aData;
+
+ if ((sData = oCol.fnGetData(oData, sSpecific)) === undefined) {
+ if (oSettings.iDrawError != oSettings.iDraw && oCol.sDefaultContent === null) {
+ _fnLog(oSettings, 0, "Requested unknown parameter " +
+ (typeof oCol.mData == 'function' ? '{mData function}' : "'" + oCol.mData + "'") +
+ " from the data source for row " + iRow);
+ oSettings.iDrawError = oSettings.iDraw;
+ }
+ return oCol.sDefaultContent;
+ }
+
+ /* When the data source is null, we can use default column data */
+ if (sData === null && oCol.sDefaultContent !== null) {
+ sData = oCol.sDefaultContent;
+ }
+ else if (typeof sData === 'function') {
+ /* If the data source is a function, then we run it and use the return */
+ return sData();
+ }
+
+ if (sSpecific == 'display' && sData === null) {
+ return '';
+ }
+ return sData;
+ }
+
+
+ /**
+ * Set the value for a specific cell, into the internal data cache
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iRow aoData row id
+ * @param {int} iCol Column index
+ * @param {*} val Value to set
+ * @memberof DataTable#oApi
+ */
+ function _fnSetCellData(oSettings, iRow, iCol, val) {
+ var oCol = oSettings.aoColumns[iCol];
+ var oData = oSettings.aoData[iRow]._aData;
+
+ oCol.fnSetData(oData, val);
+ }
+
+
+ // Private variable that is used to match array syntax in the data property object
+ var __reArray = /\[.*?\]$/;
+
+ /**
+ * Return a function that can be used to get data from a source object, taking
+ * into account the ability to use nested objects as a source
+ * @param {string|int|function} mSource The data source for the object
+ * @returns {function} Data get function
+ * @memberof DataTable#oApi
+ */
+ function _fnGetObjectDataFn(mSource) {
+ if (mSource === null) {
+ /* Give an empty string for rendering / sorting etc */
+ return function (data, type) {
+ return null;
+ };
+ }
+ else if (typeof mSource === 'function') {
+ return function (data, type, extra) {
+ return mSource(data, type, extra);
+ };
+ }
+ else if (typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || mSource.indexOf('[') !== -1)) {
+ /* If there is a . in the source string then the data source is in a
+ * nested object so we loop over the data for each level to get the next
+ * level down. On each loop we test for undefined, and if found immediately
+ * return. This allows entire objects to be missing and sDefaultContent to
+ * be used if defined, rather than throwing an error
+ */
+ var fetchData = function (data, type, src) {
+ var a = src.split('.');
+ var arrayNotation, out, innerSrc;
+
+ if (src !== "") {
+ for (var i = 0, iLen = a.length; i < iLen; i++) {
+ // Check if we are dealing with an array notation request
+ arrayNotation = a[i].match(__reArray);
+
+ if (arrayNotation) {
+ a[i] = a[i].replace(__reArray, '');
+
+ // Condition allows simply [] to be passed in
+ if (a[i] !== "") {
+ data = data[ a[i] ];
+ }
+ out = [];
+
+ // Get the remainder of the nested object to get
+ a.splice(0, i + 1);
+ innerSrc = a.join('.');
+
+ // Traverse each entry in the array getting the properties requested
+ for (var j = 0, jLen = data.length; j < jLen; j++) {
+ out.push(fetchData(data[j], type, innerSrc));
+ }
+
+ // If a string is given in between the array notation indicators, that
+ // is used to join the strings together, otherwise an array is returned
+ var join = arrayNotation[0].substring(1, arrayNotation[0].length - 1);
+ data = (join === "") ? out : out.join(join);
+
+ // The inner call to fetchData has already traversed through the remainder
+ // of the source requested, so we exit from the loop
+ break;
+ }
+
+ if (data === null || data[ a[i] ] === undefined) {
+ return undefined;
+ }
+ data = data[ a[i] ];
+ }
+ }
+
+ return data;
+ };
+
+ return function (data, type) {
+ return fetchData(data, type, mSource);
+ };
+ }
+ else {
+ /* Array or flat object mapping */
+ return function (data, type) {
+ return data[mSource];
+ };
+ }
+ }
+
+
+ /**
+ * Return a function that can be used to set data from a source object, taking
+ * into account the ability to use nested objects as a source
+ * @param {string|int|function} mSource The data source for the object
+ * @returns {function} Data set function
+ * @memberof DataTable#oApi
+ */
+ function _fnSetObjectDataFn(mSource) {
+ if (mSource === null) {
+ /* Nothing to do when the data source is null */
+ return function (data, val) {
+ };
+ }
+ else if (typeof mSource === 'function') {
+ return function (data, val) {
+ mSource(data, 'set', val);
+ };
+ }
+ else if (typeof mSource === 'string' && (mSource.indexOf('.') !== -1 || mSource.indexOf('[') !== -1)) {
+ /* Like the get, we need to get data from a nested object */
+ var setData = function (data, val, src) {
+ var a = src.split('.'), b;
+ var arrayNotation, o, innerSrc;
+
+ for (var i = 0, iLen = a.length - 1; i < iLen; i++) {
+ // Check if we are dealing with an array notation request
+ arrayNotation = a[i].match(__reArray);
+
+ if (arrayNotation) {
+ a[i] = a[i].replace(__reArray, '');
+ data[ a[i] ] = [];
+
+ // Get the remainder of the nested object to set so we can recurse
+ b = a.slice();
+ b.splice(0, i + 1);
+ innerSrc = b.join('.');
+
+ // Traverse each entry in the array setting the properties requested
+ for (var j = 0, jLen = val.length; j < jLen; j++) {
+ o = {};
+ setData(o, val[j], innerSrc);
+ data[ a[i] ].push(o);
+ }
+
+ // The inner call to setData has already traversed through the remainder
+ // of the source and has set the data, thus we can exit here
+ return;
+ }
+
+ // If the nested object doesn't currently exist - since we are
+ // trying to set the value - create it
+ if (data[ a[i] ] === null || data[ a[i] ] === undefined) {
+ data[ a[i] ] = {};
+ }
+ data = data[ a[i] ];
+ }
+
+ // If array notation is used, we just want to strip it and use the property name
+ // and assign the value. If it isn't used, then we get the result we want anyway
+ data[ a[a.length - 1].replace(__reArray, '') ] = val;
+ };
+
+ return function (data, val) {
+ return setData(data, val, mSource);
+ };
+ }
+ else {
+ /* Array or flat object mapping */
+ return function (data, val) {
+ data[mSource] = val;
+ };
+ }
+ }
+
+
+ /**
+ * Return an array with the full table data
+ * @param {object} oSettings dataTables settings object
+ * @returns array {array} aData Master data array
+ * @memberof DataTable#oApi
+ */
+ function _fnGetDataMaster(oSettings) {
+ var aData = [];
+ var iLen = oSettings.aoData.length;
+ for (var i = 0; i < iLen; i++) {
+ aData.push(oSettings.aoData[i]._aData);
+ }
+ return aData;
+ }
+
+
+ /**
+ * Nuke the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnClearTable(oSettings) {
+ oSettings.aoData.splice(0, oSettings.aoData.length);
+ oSettings.aiDisplayMaster.splice(0, oSettings.aiDisplayMaster.length);
+ oSettings.aiDisplay.splice(0, oSettings.aiDisplay.length);
+ _fnCalculateEnd(oSettings);
+ }
+
+
+ /**
+ * Take an array of integers (index array) and remove a target integer (value - not
+ * the key!)
+ * @param {array} a Index array to target
+ * @param {int} iTarget value to find
+ * @memberof DataTable#oApi
+ */
+ function _fnDeleteIndex(a, iTarget) {
+ var iTargetIndex = -1;
+
+ for (var i = 0, iLen = a.length; i < iLen; i++) {
+ if (a[i] == iTarget) {
+ iTargetIndex = i;
+ }
+ else if (a[i] > iTarget) {
+ a[i]--;
+ }
+ }
+
+ if (iTargetIndex != -1) {
+ a.splice(iTargetIndex, 1);
+ }
+ }
+
+
+ /**
+ * Call the developer defined fnRender function for a given cell (row/column) with
+ * the required parameters and return the result.
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iRow aoData index for the row
+ * @param {int} iCol aoColumns index for the column
+ * @returns {*} Return of the developer's fnRender function
+ * @memberof DataTable#oApi
+ */
+ function _fnRender(oSettings, iRow, iCol) {
+ var oCol = oSettings.aoColumns[iCol];
+
+ return oCol.fnRender({
+ "iDataRow":iRow,
+ "iDataColumn":iCol,
+ "oSettings":oSettings,
+ "aData":oSettings.aoData[iRow]._aData,
+ "mDataProp":oCol.mData
+ }, _fnGetCellData(oSettings, iRow, iCol, 'display'));
+ }
+
+ /**
+ * Create a new TR element (and it's TD children) for a row
+ * @param {object} oSettings dataTables settings object
+ * @param {int} iRow Row to consider
+ * @memberof DataTable#oApi
+ */
+ function _fnCreateTr(oSettings, iRow) {
+ var oData = oSettings.aoData[iRow];
+ var nTd;
+
+ if (oData.nTr === null) {
+ oData.nTr = document.createElement('tr');
+
+ /* Use a private property on the node to allow reserve mapping from the node
+ * to the aoData array for fast look up
+ */
+ oData.nTr._DT_RowIndex = iRow;
+
+ /* Special parameters can be given by the data source to be used on the row */
+ if (oData._aData.DT_RowId) {
+ oData.nTr.id = oData._aData.DT_RowId;
+ }
+
+ if (oData._aData.DT_RowClass) {
+ oData.nTr.className = oData._aData.DT_RowClass;
+ }
+
+ /* Process each column */
+ for (var i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
+ var oCol = oSettings.aoColumns[i];
+ nTd = document.createElement(oCol.sCellType);
+
+ /* Render if needed - if bUseRendered is true then we already have the rendered
+ * value in the data source - so can just use that
+ */
+ nTd.innerHTML = (typeof oCol.fnRender === 'function' && (!oCol.bUseRendered || oCol.mData === null)) ?
+ _fnRender(oSettings, iRow, i) :
+ _fnGetCellData(oSettings, iRow, i, 'display');
+
+ /* Add user defined class */
+ if (oCol.sClass !== null) {
+ nTd.className = oCol.sClass;
+ }
+
+ if (oCol.bVisible) {
+ oData.nTr.appendChild(nTd);
+ oData._anHidden[i] = null;
+ }
+ else {
+ oData._anHidden[i] = nTd;
+ }
+
+ if (oCol.fnCreatedCell) {
+ oCol.fnCreatedCell.call(oSettings.oInstance,
+ nTd, _fnGetCellData(oSettings, iRow, i, 'display'), oData._aData, iRow, i
+ );
+ }
+ }
+
+ _fnCallbackFire(oSettings, 'aoRowCreatedCallback', null, [oData.nTr, oData._aData, iRow]);
+ }
+ }
+
+
+ /**
+ * Create the HTML header for the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnBuildHead(oSettings) {
+ var i, nTh, iLen, j, jLen;
+ var iThs = $('th, td', oSettings.nTHead).length;
+ var iCorrector = 0;
+ var jqChildren;
+
+ /* If there is a header in place - then use it - otherwise it's going to get nuked... */
+ if (iThs !== 0) {
+ /* We've got a thead from the DOM, so remove hidden columns and apply width to vis cols */
+ for (i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
+ nTh = oSettings.aoColumns[i].nTh;
+ nTh.setAttribute('role', 'columnheader');
+ if (oSettings.aoColumns[i].bSortable) {
+ nTh.setAttribute('tabindex', oSettings.iTabIndex);
+ nTh.setAttribute('aria-controls', oSettings.sTableId);
+ }
+
+ if (oSettings.aoColumns[i].sClass !== null) {
+ $(nTh).addClass(oSettings.aoColumns[i].sClass);
+ }
+
+ /* Set the title of the column if it is user defined (not what was auto detected) */
+ if (oSettings.aoColumns[i].sTitle != nTh.innerHTML) {
+ nTh.innerHTML = oSettings.aoColumns[i].sTitle;
+ }
+ }
+ }
+ else {
+ /* We don't have a header in the DOM - so we are going to have to create one */
+ var nTr = document.createElement("tr");
+
+ for (i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
+ nTh = oSettings.aoColumns[i].nTh;
+ nTh.innerHTML = oSettings.aoColumns[i].sTitle;
+ nTh.setAttribute('tabindex', '0');
+
+ if (oSettings.aoColumns[i].sClass !== null) {
+ $(nTh).addClass(oSettings.aoColumns[i].sClass);
+ }
+
+ nTr.appendChild(nTh);
+ }
+ $(oSettings.nTHead).html('')[0].appendChild(nTr);
+ _fnDetectHeader(oSettings.aoHeader, oSettings.nTHead);
+ }
+
+ /* ARIA role for the rows */
+ $(oSettings.nTHead).children('tr').attr('role', 'row');
+
+ /* Add the extra markup needed by jQuery UI's themes */
+ if (oSettings.bJUI) {
+ for (i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
+ nTh = oSettings.aoColumns[i].nTh;
+
+ var nDiv = document.createElement('div');
+ nDiv.className = oSettings.oClasses.sSortJUIWrapper;
+ $(nTh).contents().appendTo(nDiv);
+
+ var nSpan = document.createElement('span');
+ nSpan.className = oSettings.oClasses.sSortIcon;
+ nDiv.appendChild(nSpan);
+ nTh.appendChild(nDiv);
+ }
+ }
+
+ if (oSettings.oFeatures.bSort) {
+ for (i = 0; i < oSettings.aoColumns.length; i++) {
+ if (oSettings.aoColumns[i].bSortable !== false) {
+ _fnSortAttachListener(oSettings, oSettings.aoColumns[i].nTh, i);
+ }
+ else {
+ $(oSettings.aoColumns[i].nTh).addClass(oSettings.oClasses.sSortableNone);
+ }
+ }
+ }
+
+ /* Deal with the footer - add classes if required */
+ if (oSettings.oClasses.sFooterTH !== "") {
+ $(oSettings.nTFoot).children('tr').children('th').addClass(oSettings.oClasses.sFooterTH);
+ }
+
+ /* Cache the footer elements */
+ if (oSettings.nTFoot !== null) {
+ var anCells = _fnGetUniqueThs(oSettings, null, oSettings.aoFooter);
+ for (i = 0, iLen = oSettings.aoColumns.length; i < iLen; i++) {
+ if (anCells[i]) {
+ oSettings.aoColumns[i].nTf = anCells[i];
+ if (oSettings.aoColumns[i].sClass) {
+ $(anCells[i]).addClass(oSettings.aoColumns[i].sClass);
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Draw the header (or footer) element based on the column visibility states. The
+ * methodology here is to use the layout array from _fnDetectHeader, modified for
+ * the instantaneous column visibility, to construct the new layout. The grid is
+ * traversed over cell at a time in a rows x columns grid fashion, although each
+ * cell insert can cover multiple elements in the grid - which is tracks using the
+ * aApplied array. Cell inserts in the grid will only occur where there isn't
+ * already a cell in that position.
+ * @param {object} oSettings dataTables settings object
+ * @param array {objects} aoSource Layout array from _fnDetectHeader
+ * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
+ * @memberof DataTable#oApi
+ */
+ function _fnDrawHead(oSettings, aoSource, bIncludeHidden) {
+ var i, iLen, j, jLen, k, kLen, n, nLocalTr;
+ var aoLocal = [];
+ var aApplied = [];
+ var iColumns = oSettings.aoColumns.length;
+ var iRowspan, iColspan;
+
+ if (bIncludeHidden === undefined) {
+ bIncludeHidden = false;
+ }
+
+ /* Make a copy of the master layout array, but without the visible columns in it */
+ for (i = 0, iLen = aoSource.length; i < iLen; i++) {
+ aoLocal[i] = aoSource[i].slice();
+ aoLocal[i].nTr = aoSource[i].nTr;
+
+ /* Remove any columns which are currently hidden */
+ for (j = iColumns - 1; j >= 0; j--) {
+ if (!oSettings.aoColumns[j].bVisible && !bIncludeHidden) {
+ aoLocal[i].splice(j, 1);
+ }
+ }
+
+ /* Prep the applied array - it needs an element for each row */
+ aApplied.push([]);
+ }
+
+ for (i = 0, iLen = aoLocal.length; i < iLen; i++) {
+ nLocalTr = aoLocal[i].nTr;
+
+ /* All cells are going to be replaced, so empty out the row */
+ if (nLocalTr) {
+ while ((n = nLocalTr.firstChild)) {
+ nLocalTr.removeChild(n);
+ }
+ }
+
+ for (j = 0, jLen = aoLocal[i].length; j < jLen; j++) {
+ iRowspan = 1;
+ iColspan = 1;
+
+ /* Check to see if there is already a cell (row/colspan) covering our target
+ * insert point. If there is, then there is nothing to do.
+ */
+ if (aApplied[i][j] === undefined) {
+ nLocalTr.appendChild(aoLocal[i][j].cell);
+ aApplied[i][j] = 1;
+
+ /* Expand the cell to cover as many rows as needed */
+ while (aoLocal[i + iRowspan] !== undefined &&
+ aoLocal[i][j].cell == aoLocal[i + iRowspan][j].cell) {
+ aApplied[i + iRowspan][j] = 1;
+ iRowspan++;
+ }
+
+ /* Expand the cell to cover as many columns as needed */
+ while (aoLocal[i][j + iColspan] !== undefined &&
+ aoLocal[i][j].cell == aoLocal[i][j + iColspan].cell) {
+ /* Must update the applied array over the rows for the columns */
+ for (k = 0; k < iRowspan; k++) {
+ aApplied[i + k][j + iColspan] = 1;
+ }
+ iColspan++;
+ }
+
+ /* Do the actual expansion in the DOM */
+ aoLocal[i][j].cell.rowSpan = iRowspan;
+ aoLocal[i][j].cell.colSpan = iColspan;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Insert the required TR nodes into the table for display
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnDraw(oSettings) {
+ /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
+ var aPreDraw = _fnCallbackFire(oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings]);
+ if ($.inArray(false, aPreDraw) !== -1) {
+ _fnProcessingDisplay(oSettings, false);
+ return;
+ }
+
+ var i, iLen, n;
+ var anRows = [];
+ var iRowCount = 0;
+ var iStripes = oSettings.asStripeClasses.length;
+ var iOpenRows = oSettings.aoOpenRows.length;
+
+ oSettings.bDrawing = true;
+
+ /* Check and see if we have an initial draw position from state saving */
+ if (oSettings.iInitDisplayStart !== undefined && oSettings.iInitDisplayStart != -1) {
+ if (oSettings.oFeatures.bServerSide) {
+ oSettings._iDisplayStart = oSettings.iInitDisplayStart;
+ }
+ else {
+ oSettings._iDisplayStart = (oSettings.iInitDisplayStart >= oSettings.fnRecordsDisplay()) ?
+ 0 : oSettings.iInitDisplayStart;
+ }
+ oSettings.iInitDisplayStart = -1;
+ _fnCalculateEnd(oSettings);
+ }
+
+ /* Server-side processing draw intercept */
+ if (oSettings.bDeferLoading) {
+ oSettings.bDeferLoading = false;
+ oSettings.iDraw++;
+ }
+ else if (!oSettings.oFeatures.bServerSide) {
+ oSettings.iDraw++;
+ }
+ else if (!oSettings.bDestroying && !_fnAjaxUpdate(oSettings)) {
+ return;
+ }
+
+ if (oSettings.aiDisplay.length !== 0) {
+ var iStart = oSettings._iDisplayStart;
+ var iEnd = oSettings._iDisplayEnd;
+
+ if (oSettings.oFeatures.bServerSide) {
+ iStart = 0;
+ iEnd = oSettings.aoData.length;
+ }
+
+ for (var j = iStart; j < iEnd; j++) {
+ var aoData = oSettings.aoData[ oSettings.aiDisplay[j] ];
+ if (aoData.nTr === null) {
+ _fnCreateTr(oSettings, oSettings.aiDisplay[j]);
+ }
+
+ var nRow = aoData.nTr;
+
+ /* Remove the old striping classes and then add the new one */
+ if (iStripes !== 0) {
+ var sStripe = oSettings.asStripeClasses[ iRowCount % iStripes ];
+ if (aoData._sRowStripe != sStripe) {
+ $(nRow).removeClass(aoData._sRowStripe).addClass(sStripe);
+ aoData._sRowStripe = sStripe;
+ }
+ }
+
+ /* Row callback functions - might want to manipulate the row */
+ _fnCallbackFire(oSettings, 'aoRowCallback', null,
+ [nRow, oSettings.aoData[ oSettings.aiDisplay[j] ]._aData, iRowCount, j]);
+
+ anRows.push(nRow);
+ iRowCount++;
+
+ /* If there is an open row - and it is attached to this parent - attach it on redraw */
+ if (iOpenRows !== 0) {
+ for (var k = 0; k < iOpenRows; k++) {
+ if (nRow == oSettings.aoOpenRows[k].nParent) {
+ anRows.push(oSettings.aoOpenRows[k].nTr);
+ break;
+ }
+ }
+ }
+ }
+ }
+ else {
+ /* Table is empty - create a row with an empty message in it */
+ anRows[ 0 ] = document.createElement('tr');
+
+ if (oSettings.asStripeClasses[0]) {
+ anRows[ 0 ].className = oSettings.asStripeClasses[0];
+ }
+
+ var oLang = oSettings.oLanguage;
+ var sZero = oLang.sZeroRecords;
+ if (oSettings.iDraw == 1 && oSettings.sAjaxSource !== null && !oSettings.oFeatures.bServerSide) {
+ sZero = oLang.sLoadingRecords;
+ }
+ else if (oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0) {
+ sZero = oLang.sEmptyTable;
+ }
+
+ var nTd = document.createElement('td');
+ nTd.setAttribute('valign', "top");
+ nTd.colSpan = _fnVisbleColumns(oSettings);
+ nTd.className = oSettings.oClasses.sRowEmpty;
+ nTd.innerHTML = _fnInfoMacros(oSettings, sZero);
+
+ anRows[ iRowCount ].appendChild(nTd);
+ }
+
+ /* Header and footer callbacks */
+ _fnCallbackFire(oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
+ _fnGetDataMaster(oSettings), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), oSettings.aiDisplay ]);
+
+ _fnCallbackFire(oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
+ _fnGetDataMaster(oSettings), oSettings._iDisplayStart, oSettings.fnDisplayEnd(), oSettings.aiDisplay ]);
+
+ /*
+ * Need to remove any old row from the display - note we can't just empty the tbody using
+ * $().html('') since this will unbind the jQuery event handlers (even although the node
+ * still exists!) - equally we can't use innerHTML, since IE throws an exception.
+ */
+ var
+ nAddFrag = document.createDocumentFragment(),
+ nRemoveFrag = document.createDocumentFragment(),
+ nBodyPar, nTrs;
+
+ if (oSettings.nTBody) {
+ nBodyPar = oSettings.nTBody.parentNode;
+ nRemoveFrag.appendChild(oSettings.nTBody);
+
+ /* When doing infinite scrolling, only remove child rows when sorting, filtering or start
+ * up. When not infinite scroll, always do it.
+ */
+ if (!oSettings.oScroll.bInfinite || !oSettings._bInitComplete ||
+ oSettings.bSorted || oSettings.bFiltered) {
+ while ((n = oSettings.nTBody.firstChild)) {
+ oSettings.nTBody.removeChild(n);
+ }
+ }
+
+ /* Put the draw table into the dom */
+ for (i = 0, iLen = anRows.length; i < iLen; i++) {
+ nAddFrag.appendChild(anRows[i]);
+ }
+
+ oSettings.nTBody.appendChild(nAddFrag);
+ if (nBodyPar !== null) {
+ nBodyPar.appendChild(oSettings.nTBody);
+ }
+ }
+
+ /* Call all required callback functions for the end of a draw */
+ _fnCallbackFire(oSettings, 'aoDrawCallback', 'draw', [oSettings]);
+
+ /* Draw is complete, sorting and filtering must be as well */
+ oSettings.bSorted = false;
+ oSettings.bFiltered = false;
+ oSettings.bDrawing = false;
+
+ if (oSettings.oFeatures.bServerSide) {
+ _fnProcessingDisplay(oSettings, false);
+ if (!oSettings._bInitComplete) {
+ _fnInitComplete(oSettings);
+ }
+ }
+ }
+
+
+ /**
+ * Redraw the table - taking account of the various features which are enabled
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnReDraw(oSettings) {
+ if (oSettings.oFeatures.bSort) {
+ /* Sorting will refilter and draw for us */
+ _fnSort(oSettings, oSettings.oPreviousSearch);
+ }
+ else if (oSettings.oFeatures.bFilter) {
+ /* Filtering will redraw for us */
+ _fnFilterComplete(oSettings, oSettings.oPreviousSearch);
+ }
+ else {
+ _fnCalculateEnd(oSettings);
+ _fnDraw(oSettings);
+ }
+ }
+
+
+ /**
+ * Add the options to the page HTML for the table
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnAddOptionsHtml(oSettings) {
+ /*
+ * Create a temporary, empty, div which we can later on replace with what we have generated
+ * we do it this way to rendering the 'options' html offline - speed :-)
+ */
+ var nHolding = $('<div></div>')[0];
+ oSettings.nTable.parentNode.insertBefore(nHolding, oSettings.nTable);
+
+ /*
+ * All DataTables are wrapped in a div
+ */
+ oSettings.nTableWrapper = $('<div id="' + oSettings.sTableId + '_wrapper" class="' + oSettings.oClasses.sWrapper + '" role="grid"></div>')[0];
+ oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
+
+ /* Track where we want to insert the option */
+ var nInsertNode = oSettings.nTableWrapper;
+
+ /* Loop over the user set positioning and place the elements as needed */
+ var aDom = oSettings.sDom.split('');
+ var nTmp, iPushFeature, cOption, nNewNode, cNext, sAttr, j;
+ for (var i = 0; i < aDom.length; i++) {
+ iPushFeature = 0;
+ cOption = aDom[i];
+
+ if (cOption == '<') {
+ /* New container div */
+ nNewNode = $('<div></div>')[0];
+
+ /* Check to see if we should append an id and/or a class name to the container */
+ cNext = aDom[i + 1];
+ if (cNext == "'" || cNext == '"') {
+ sAttr = "";
+ j = 2;
+ while (aDom[i + j] != cNext) {
+ sAttr += aDom[i + j];
+ j++;
+ }
+
+ /* Replace jQuery UI constants */
+ if (sAttr == "H") {
+ sAttr = oSettings.oClasses.sJUIHeader;
+ }
+ else if (sAttr == "F") {
+ sAttr = oSettings.oClasses.sJUIFooter;
+ }
+
+ /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
+ * breaks the string into parts and applies them as needed
+ */
+ if (sAttr.indexOf('.') != -1) {
+ var aSplit = sAttr.split('.');
+ nNewNode.id = aSplit[0].substr(1, aSplit[0].length - 1);
+ nNewNode.className = aSplit[1];
+ }
+ else if (sAttr.charAt(0) == "#") {
+ nNewNode.id = sAttr.substr(1, sAttr.length - 1);
+ }
+ else {
+ nNewNode.className = sAttr;
+ }
+
+ i += j;
+ /* Move along the position array */
+ }
+
+ nInsertNode.appendChild(nNewNode);
+ nInsertNode = nNewNode;
+ }
+ else if (cOption == '>') {
+ /* End container div */
+ nInsertNode = nInsertNode.parentNode;
+ }
+ else if (cOption == 'l' && oSettings.oFeatures.bPaginate && oSettings.oFeatures.bLengthChange) {
+ /* Length */
+ nTmp = _fnFeatureHtmlLength(oSettings);
+ iPushFeature = 1;
+ }
+ else if (cOption == 'f' && oSettings.oFeatures.bFilter) {
+ /* Filter */
+ nTmp = _fnFeatureHtmlFilter(oSettings);
+ iPushFeature = 1;
+ }
+ else if (cOption == 'r' && oSettings.oFeatures.bProcessing) {
+ /* pRocessing */
+ nTmp = _fnFeatureHtmlProcessing(oSettings);
+ iPushFeature = 1;
+ }
+ else if (cOption == 't') {
+ /* Table */
+ nTmp = _fnFeatureHtmlTable(oSettings);
+ iPushFeature = 1;
+ }
+ else if (cOption == 'i' && oSettings.oFeatures.bInfo) {
+ /* Info */
+ nTmp = _fnFeatureHtmlInfo(oSettings);
+ iPushFeature = 1;
+ }
+ else if (cOption == 'p' && oSettings.oFeatures.bPaginate) {
+ /* Pagination */
+ nTmp = _fnFeatureHtmlPaginate(oSettings);
+ iPushFeature = 1;
+ }
+ else if (DataTable.ext.aoFeatures.length !== 0) {
+ /* Plug-in features */
+ var aoFeatures = DataTable.ext.aoFeatures;
+ for (var k = 0, kLen = aoFeatures.length; k < kLen; k++) {
+ if (cOption == aoFeatures[k].cFeature) {
+ nTmp = aoFeatures[k].fnInit(oSettings);
+ if (nTmp) {
+ iPushFeature = 1;
+ }
+ break;
+ }
+ }
+ }
+
+ /* Add to the 2D features array */
+ if (iPushFeature == 1 && nTmp !== null) {
+ if (typeof oSettings.aanFeatures[cOption] !== 'object') {
+ oSettings.aanFeatures[cOption] = [];
+ }
+ oSettings.aanFeatures[cOption].push(nTmp);
+ nInsertNode.appendChild(nTmp);
+ }
+ }
+
+ /* Built our DOM structure - replace the holding div with what we want */
+ nHolding.parentNode.replaceChild(oSettings.nTableWrapper, nHolding);
+ }
+
+
+ /**
+ * Use the DOM source to create up an array of header cells. The idea here is to
+ * create a layout grid (array) of rows x columns, which contains a reference
+ * to the cell that that point in the grid (regardless of col/rowspan), such that
+ * any column / row could be removed and the new grid constructed
+ * @param array {object} aLayout Array to store the calculated layout in
+ * @param {node} nThead The header/footer element for the table
+ * @memberof DataTable#oApi
+ */
+ function _fnDetectHeader(aLayout, nThead) {
+ var nTrs = $(nThead).children('tr');
+ var nTr, nCell;
+ var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
+ var bUnique;
+ var fnShiftCol = function (a, i, j) {
+ var k = a[i];
+ while (k[j]) {
+ j++;
+ }
+ return j;
+ };
+
+ aLayout.splice(0, aLayout.length);
+
+ /* We know how many rows there are in the layout - so prep it */
+ for (i = 0, iLen = nTrs.length; i < iLen; i++) {
+ aLayout.push([]);
+ }
+
+ /* Calculate a layout array */
+ for (i = 0, iLen = nTrs.length; i < iLen; i++) {
+ nTr = nTrs[i];
+ iColumn = 0;
+
+ /* For every cell in the row... */
+ nCell = nTr.firstChild;
+ while (nCell) {
+ if (nCell.nodeName.toUpperCase() == "TD" ||
+ nCell.nodeName.toUpperCase() == "TH") {
+ /* Get the col and rowspan attributes from the DOM and sanitise them */
+ iColspan = nCell.getAttribute('colspan') * 1;
+ iRowspan = nCell.getAttribute('rowspan') * 1;
+ iColspan = (!iColspan || iColspan === 0 || iColspan === 1) ? 1 : iColspan;
+ iRowspan = (!iRowspan || iRowspan === 0 || iRowspan === 1) ? 1 : iRowspan;
+
+ /* There might be colspan cells already in this row, so shift our target
+ * accordingly
+ */
+ iColShifted = fnShiftCol(aLayout, i, iColumn);
+
+ /* Cache calculation for unique columns */
+ bUnique = iColspan === 1 ? true : false;
+
+ /* If there is col / rowspan, copy the information into the layout grid */
+ for (l = 0; l < iColspan; l++) {
+ for (k = 0; k < iRowspan; k++) {
+ aLayout[i + k][iColShifted + l] = {
+ "cell":nCell,
+ "unique":bUnique
+ };
+ aLayout[i + k].nTr = nTr;
+ }
+ }
+ }
+ nCell = nCell.nextSibling;
+ }
+ }
+ }
+
+
+ /**
+ * Get an array of unique th elements, one for each column
+ * @param {object} oSettings dataTables settings object
+ * @param {node} nHeader automatically detect the layout from this node - optional
+ * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
+ * @returns array {node} aReturn list of unique th's
+ * @memberof DataTable#oApi
+ */
+ function _fnGetUniqueThs(oSettings, nHeader, aLayout) {
+ var aReturn = [];
+ if (!aLayout) {
+ aLayout = oSettings.aoHeader;
+ if (nHeader) {
+ aLayout = [];
+ _fnDetectHeader(aLayout, nHeader);
+ }
+ }
+
+ for (var i = 0, iLen = aLayout.length; i < iLen; i++) {
+ for (var j = 0, jLen = aLayout[i].length; j < jLen; j++) {
+ if (aLayout[i][j].unique &&
+ (!aReturn[j] || !oSettings.bSortCellsTop)) {
+ aReturn[j] = aLayout[i][j].cell;
+ }
+ }
+ }
+
+ return aReturn;
+ }
+
+
+ /**
+ * Update the table using an Ajax call
+ * @param {object} oSettings dataTables settings object
+ * @returns {boolean} Block the table drawing or not
+ * @memberof DataTable#oApi
+ */
+ function _fnAjaxUpdate(oSettings) {
+ if (oSettings.bAjaxDataGet) {
+ oSettings.iDraw++;
+ _fnProcessingDisplay(oSettings, true);
+ var iColumns = oSettings.aoColumns.length;
+ var aoData = _fnAjaxParameters(oSettings);
+ _fnServerParams(oSettings, aoData);
+
+ oSettings.fnServerData.call(oSettings.oInstance, oSettings.sAjaxSource, aoData,
+ function (json) {
+ _fnAjaxUpdateDraw(oSettings, json);
+ }, oSettings);
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+
+
+ /**
+ * Build up the parameters in an object needed for a server-side processing request
+ * @param {object} oSettings dataTables settings object
+ * @returns {bool} block the table drawing or not
+ * @memberof DataTable#oApi
+ */
+ function _fnAjaxParameters(oSettings) {
+ var iColumns = oSettings.aoColumns.length;
+ var aoData = [], mDataProp, aaSort, aDataSort;
+ var i, j;
+
+ aoData.push({ "name":"sEcho", "value":oSettings.iDraw });
+ aoData.push({ "name":"iColumns", "value":iColumns });
+ aoData.push({ "name":"sColumns", "value":_fnColumnOrdering(oSettings) });
+ aoData.push({ "name":"iDisplayStart", "value":oSettings._iDisplayStart });
+ aoData.push({ "name":"iDisplayLength", "value":oSettings.oFeatures.bPaginate !== false ?
+ oSettings._iDisplayLength : -1 });
+
+ for (i = 0; i < iColumns; i++) {
+ mDataProp = oSettings.aoColumns[i].mData;
+ aoData.push({ "name":"mDataProp_" + i, "value":typeof(mDataProp) === "function" ? 'function' : mDataProp });
+ }
+
+ /* Filtering */
+ if (oSettings.oFeatures.bFilter !== false) {
+ aoData.push({ "name":"sSearch", "value":oSettings.oPreviousSearch.sSearch });
+ aoData.push({ "name":"bRegex", "value":oSettings.oPreviousSearch.bRegex });
+ for (i = 0; i < iColumns; i++) {
+ aoData.push({ "name":"sSearch_" + i, "value":oSettings.aoPreSearchCols[i].sSearch });
+ aoData.push({ "name":"bRegex_" + i, "value":oSettings.aoPreSearchCols[i].bRegex });
+ aoData.push({ "name":"bSearchable_" + i, "value":oSettings.aoColumns[i].bSearchable });
+ }
+ }
+
+ /* Sorting */
+ if (oSettings.oFeatures.bSort !== false) {
+ var iCounter = 0;
+
+ aaSort = ( oSettings.aaSortingFixed !== null ) ?
+ oSettings.aaSortingFixed.concat(oSettings.aaSorting) :
+ oSettings.aaSorting.slice();
+
+ for (i = 0; i < aaSort.length; i++) {
+ aDataSort = oSettings.aoColumns[ aaSort[i][0] ].aDataSort;
+
+ for (j = 0; j < aDataSort.length; j++) {
+ aoData.push({ "name":"iSortCol_" + iCounter, "value":aDataSort[j] });
+ aoData.push({ "name":"sSortDir_" + iCounter, "value":aaSort[i][1] });
+ iCounter++;
+ }
+ }
+ aoData.push({ "name":"iSortingCols", "value":iCounter });
+
+ for (i = 0; i < iColumns; i++) {
+ aoData.push({ "name":"bSortable_" + i, "value":oSettings.aoColumns[i].bSortable });
+ }
+ }
+
+ return aoData;
+ }
+
+
+ /**
+ * Add Ajax parameters from plug-ins
+ * @param {object} oSettings dataTables settings object
+ * @param array {objects} aoData name/value pairs to send to the server
+ * @memberof DataTable#oApi
+ */
+ function _fnServerParams(oSettings, aoData) {
+ _fnCallbackFire(oSettings, 'aoServerParams', 'serverParams', [aoData]);
+ }
+
+
+ /**
+ * Data the data from the server (nuking the old) and redraw the table
+ * @param {object} oSettings dataTables settings object
+ * @param {object} json json data return from the server.
+ * @param {string} json.sEcho Tracking flag for DataTables to match requests
+ * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
+ * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
+ * @param {array} json.aaData The data to display on this page
+ * @param {string} [json.sColumns] Column ordering (sName, comma separated)
+ * @memberof DataTable#oApi
+ */
+ function _fnAjaxUpdateDraw(oSettings, json) {
+ if (json.sEcho !== undefined) {
+ /* Protect against old returns over-writing a new one. Possible when you get
+ * very fast interaction, and later queries are completed much faster
+ */
+ if (json.sEcho * 1 < oSettings.iDraw) {
+ return;
+ }
+ else {
+ oSettings.iDraw = json.sEcho * 1;
+ }
+ }
+
+ if (!oSettings.oScroll.bInfinite ||
+ (oSettings.oScroll.bInfinite && (oSettings.bSorted || oSettings.bFiltered))) {
+ _fnClearTable(oSettings);
+ }
+ oSettings._iRecordsTotal = parseInt(json.iTotalRecords, 10);
+ oSettings._iRecordsDisplay = parseInt(json.iTotalDisplayRecords, 10);
+
+ /* Determine if reordering is required */
+ var sOrdering = _fnColumnOrdering(oSettings);
+ var bReOrder = (json.sColumns !== undefined && sOrdering !== "" && json.sColumns != sOrdering );
+ var aiIndex;
+ if (bReOrder) {
+ aiIndex = _fnReOrderIndex(oSettings, json.sColumns);
+ }
+
+ var aData = _fnGetObjectDataFn(oSettings.sAjaxDataProp)(json);
+ for (var i = 0, iLen = aData.length; i < iLen; i++) {
+ if (bReOrder) {
+ /* If we need to re-order, then create a new array with the correct order and add it */
+ var aDataSorted = [];
+ for (var j = 0, jLen = oSettings.aoColumns.length; j < jLen; j++) {
+ aDataSorted.push(aData[i][ aiIndex[j] ]);
+ }
+ _fnAddData(oSettings, aDataSorted);
+ }
+ else {
+ /* No re-order required, sever got it "right" - just straight add */
+ _fnAddData(oSettings, aData[i]);
+ }
+ }
+ oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
+
+ oSettings.bAjaxDataGet = false;
+ _fnDraw(oSettings);
+ oSettings.bAjaxDataGet = true;
+ _fnProcessingDisplay(oSettings, false);
+ }
+
+
+ /**
+ * Generate the node required for filtering text
+ * @returns {node} Filter control element
+ * @param {object} oSettings dataTables settings object
+ * @memberof DataTable#oApi
+ */
+ function _fnFeatureHtmlFilter(oSettings) {
+ var oPreviousSearch = oSettings.oPreviousSearch;
+
+ var sSearchStr = oSettings.oLanguage.sSearch;
+ sSearchStr = (sSearchStr.indexOf('_INPUT_') !== -1) ?
+ sSearchStr.replace('_INPUT_', '<input type="text" />') :
+ sSearchStr === "" ? '<input type="text" />' : sSearchStr + ' <input type="text" />';
+
+ var nFilter = document.createElement('div');
+ nFilter.className = oSettings.oClasses.sFilter;
+ nFilter.innerHTML = '<label>' + sSearchStr + '</label>';
+ if (!oSettings.aanFeatures.f) {
+ nFilter.id = oSettings.sTableId + '_filter';
+ }
+
+ var jqFilter = $('input[type="text"]', nFilter);
+
+ // Store a reference to the input element, so other input elements could be
+ // added to the filter wrapper if needed (submit button for example)
+ nFilter._DT_Input = jqFilter[0];
+
+ jqFilter.val(oPreviousSearch.sSearch.replace('"', '"'));
+ 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 */
[... 9074 lines stripped ...]