scriptbox
Version:
Script box is a full VAS application
1,085 lines (937 loc) • 484 kB
JavaScript
/**
* @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