UNPKG

scriptbox

Version:

Script box is a full VAS application

1,085 lines (937 loc) 484 kB
/** * @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