OpenSencillo  2015.009
Long live the simplicity of PHP
 All Data Structures Namespaces Files Functions Pages
jquery.dataTables.js
Go to the documentation of this file.
1 
24 /*jslint evil: true, undef: true, browser: true */
25 /*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
26 
27 (function( factory ) {
28  "use strict";
29 
30  if ( typeof define === 'function' && define.amd ) {
31  // AMD
32  define( ['jquery'], function ( $ ) {
33  return factory( $, window, document );
34  } );
35  }
36  else if ( typeof exports === 'object' ) {
37  // CommonJS
38  module.exports = function (root, $) {
39  if ( ! root ) {
40  // CommonJS environments without a window global must pass a
41  // root. This will give an error otherwise
42  root = window;
43  }
44 
45  if ( ! $ ) {
46  $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
47  require('jquery') :
48  require('jquery')( root );
49  }
50 
51  return factory( $, root, root.document );
52  };
53  }
54  else {
55  // Browser
56  factory( jQuery, window, document );
57  }
58 }
59 (function( $, window, document, undefined ) {
60  "use strict";
61 
94  var DataTable;
95 
96 
97  /*
98  * It is useful to have variables which are scoped locally so only the
99  * DataTables functions can access them and they don't leak into global space.
100  * At the same time these functions are often useful over multiple files in the
101  * core and API, so we list, or at least document, all variables which are used
102  * by DataTables as private variables here. This also ensures that there is no
103  * clashing of variable names and that they can easily referenced for reuse.
104  */
105 
106 
107  // Defined else where
108  // _selector_run
109  // _selector_opts
110  // _selector_first
111  // _selector_row_indexes
112 
113  var _ext; // DataTable.ext
114  var _Api; // DataTable.Api
115  var _api_register; // DataTable.Api.register
116  var _api_registerPlural; // DataTable.Api.registerPlural
117 
118  var _re_dic = {};
119  var _re_new_lines = /[\r\n]/g;
120  var _re_html = /<.*?>/g;
121  var _re_date_start = /^[\w\+\-]/;
122  var _re_date_end = /[\w\+\-]$/;
123 
124  // Escape regular expression special characters
125  var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
126 
127  // http://en.wikipedia.org/wiki/Foreign_exchange_market
128  // - \u20BD - Russian ruble.
129  // - \u20a9 - South Korean Won
130  // - \u20BA - Turkish Lira
131  // - \u20B9 - Indian Rupee
132  // - R - Brazil (R$) and South Africa
133  // - fr - Swiss Franc
134  // - kr - Swedish krona, Norwegian krone and Danish krone
135  // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
136  // standards as thousands separators.
137  var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi;
138 
139 
140  var _empty = function ( d ) {
141  return !d || d === true || d === '-' ? true : false;
142  };
143 
144 
145  var _intVal = function ( s ) {
146  var integer = parseInt( s, 10 );
147  return !isNaN(integer) && isFinite(s) ? integer : null;
148  };
149 
150  // Convert from a formatted number with characters other than `.` as the
151  // decimal place, to a Javascript number
152  var _numToDecimal = function ( num, decimalPoint ) {
153  // Cache created regular expressions for speed as this function is called often
154  if ( ! _re_dic[ decimalPoint ] ) {
155  _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
156  }
157  return typeof num === 'string' && decimalPoint !== '.' ?
158  num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
159  num;
160  };
161 
162 
163  var _isNumber = function ( d, decimalPoint, formatted ) {
164  var strType = typeof d === 'string';
165 
166  // If empty return immediately so there must be a number if it is a
167  // formatted string (this stops the string "k", or "kr", etc being detected
168  // as a formatted number for currency
169  if ( _empty( d ) ) {
170  return true;
171  }
172 
173  if ( decimalPoint && strType ) {
174  d = _numToDecimal( d, decimalPoint );
175  }
176 
177  if ( formatted && strType ) {
178  d = d.replace( _re_formatted_numeric, '' );
179  }
180 
181  return !isNaN( parseFloat(d) ) && isFinite( d );
182  };
183 
184 
185  // A string without HTML in it can be considered to be HTML still
186  var _isHtml = function ( d ) {
187  return _empty( d ) || typeof d === 'string';
188  };
189 
190 
191  var _htmlNumeric = function ( d, decimalPoint, formatted ) {
192  if ( _empty( d ) ) {
193  return true;
194  }
195 
196  var html = _isHtml( d );
197  return ! html ?
198  null :
199  _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
200  true :
201  null;
202  };
203 
204 
205  var _pluck = function ( a, prop, prop2 ) {
206  var out = [];
207  var i=0, ien=a.length;
208 
209  // Could have the test in the loop for slightly smaller code, but speed
210  // is essential here
211  if ( prop2 !== undefined ) {
212  for ( ; i<ien ; i++ ) {
213  if ( a[i] && a[i][ prop ] ) {
214  out.push( a[i][ prop ][ prop2 ] );
215  }
216  }
217  }
218  else {
219  for ( ; i<ien ; i++ ) {
220  if ( a[i] ) {
221  out.push( a[i][ prop ] );
222  }
223  }
224  }
225 
226  return out;
227  };
228 
229 
230  // Basically the same as _pluck, but rather than looping over `a` we use `order`
231  // as the indexes to pick from `a`
232  var _pluck_order = function ( a, order, prop, prop2 )
233  {
234  var out = [];
235  var i=0, ien=order.length;
236 
237  // Could have the test in the loop for slightly smaller code, but speed
238  // is essential here
239  if ( prop2 !== undefined ) {
240  for ( ; i<ien ; i++ ) {
241  if ( a[ order[i] ][ prop ] ) {
242  out.push( a[ order[i] ][ prop ][ prop2 ] );
243  }
244  }
245  }
246  else {
247  for ( ; i<ien ; i++ ) {
248  out.push( a[ order[i] ][ prop ] );
249  }
250  }
251 
252  return out;
253  };
254 
255 
256  var _range = function ( len, start )
257  {
258  var out = [];
259  var end;
260 
261  if ( start === undefined ) {
262  start = 0;
263  end = len;
264  }
265  else {
266  end = start;
267  start = len;
268  }
269 
270  for ( var i=start ; i<end ; i++ ) {
271  out.push( i );
272  }
273 
274  return out;
275  };
276 
277 
278  var _removeEmpty = function ( a )
279  {
280  var out = [];
281 
282  for ( var i=0, ien=a.length ; i<ien ; i++ ) {
283  if ( a[i] ) { // careful - will remove all falsy values!
284  out.push( a[i] );
285  }
286  }
287 
288  return out;
289  };
290 
291 
292  var _stripHtml = function ( d ) {
293  return d.replace( _re_html, '' );
294  };
295 
296 
304  var _unique = function ( src )
305  {
306  // A faster unique method is to use object keys to identify used values,
307  // but this doesn't work with arrays or objects, which we must also
308  // consider. See jsperf.com/compare-array-unique-versions/4 for more
309  // information.
310  var
311  out = [],
312  val,
313  i, ien=src.length,
314  j, k=0;
315 
316  again: for ( i=0 ; i<ien ; i++ ) {
317  val = src[i];
318 
319  for ( j=0 ; j<k ; j++ ) {
320  if ( out[j] === val ) {
321  continue again;
322  }
323  }
324 
325  out.push( val );
326  k++;
327  }
328 
329  return out;
330  };
331 
332 
333 
341  function _fnHungarianMap ( o )
342  {
343  var
344  hungarian = 'a aa ai ao as b fn i m o s ',
345  match,
346  newKey,
347  map = {};
348 
349  $.each( o, function (key, val) {
350  match = key.match(/^([^A-Z]+?)([A-Z])/);
351 
352  if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
353  {
354  newKey = key.replace( match[0], match[2].toLowerCase() );
355  map[ newKey ] = key;
356 
357  if ( match[1] === 'o' )
358  {
359  _fnHungarianMap( o[key] );
360  }
361  }
362  } );
363 
364  o._hungarianMap = map;
365  }
366 
367 
379  function _fnCamelToHungarian ( src, user, force )
380  {
381  if ( ! src._hungarianMap ) {
382  _fnHungarianMap( src );
383  }
384 
385  var hungarianKey;
386 
387  $.each( user, function (key, val) {
388  hungarianKey = src._hungarianMap[ key ];
389 
390  if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
391  {
392  // For objects, we need to buzz down into the object to copy parameters
393  if ( hungarianKey.charAt(0) === 'o' )
394  {
395  // Copy the camelCase options over to the hungarian
396  if ( ! user[ hungarianKey ] ) {
397  user[ hungarianKey ] = {};
398  }
399  $.extend( true, user[hungarianKey], user[key] );
400 
401  _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
402  }
403  else {
404  user[hungarianKey] = user[ key ];
405  }
406  }
407  } );
408  }
409 
410 
418  function _fnLanguageCompat( lang )
419  {
420  var defaults = DataTable.defaults.oLanguage;
421  var zeroRecords = lang.sZeroRecords;
422 
423  /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
424  * sZeroRecords - assuming that is given.
425  */
426  if ( ! lang.sEmptyTable && zeroRecords &&
427  defaults.sEmptyTable === "No data available in table" )
428  {
429  _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
430  }
431 
432  /* Likewise with loading records */
433  if ( ! lang.sLoadingRecords && zeroRecords &&
434  defaults.sLoadingRecords === "Loading..." )
435  {
436  _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
437  }
438 
439  // Old parameter name of the thousands separator mapped onto the new
440  if ( lang.sInfoThousands ) {
441  lang.sThousands = lang.sInfoThousands;
442  }
443 
444  var decimal = lang.sDecimal;
445  if ( decimal ) {
446  _addNumericSort( decimal );
447  }
448  }
449 
450 
457  var _fnCompatMap = function ( o, knew, old ) {
458  if ( o[ knew ] !== undefined ) {
459  o[ old ] = o[ knew ];
460  }
461  };
462 
463 
470  function _fnCompatOpts ( init )
471  {
472  _fnCompatMap( init, 'ordering', 'bSort' );
473  _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
474  _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
475  _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
476  _fnCompatMap( init, 'order', 'aaSorting' );
477  _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
478  _fnCompatMap( init, 'paging', 'bPaginate' );
479  _fnCompatMap( init, 'pagingType', 'sPaginationType' );
480  _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
481  _fnCompatMap( init, 'searching', 'bFilter' );
482 
483  // Boolean initialisation of x-scrolling
484  if ( typeof init.sScrollX === 'boolean' ) {
485  init.sScrollX = init.sScrollX ? '100%' : '';
486  }
487  if ( typeof init.scrollX === 'boolean' ) {
488  init.scrollX = init.scrollX ? '100%' : '';
489  }
490 
491  // Column search objects are in an array, so it needs to be converted
492  // element by element
493  var searchCols = init.aoSearchCols;
494 
495  if ( searchCols ) {
496  for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
497  if ( searchCols[i] ) {
498  _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
499  }
500  }
501  }
502  }
503 
504 
511  function _fnCompatCols ( init )
512  {
513  _fnCompatMap( init, 'orderable', 'bSortable' );
514  _fnCompatMap( init, 'orderData', 'aDataSort' );
515  _fnCompatMap( init, 'orderSequence', 'asSorting' );
516  _fnCompatMap( init, 'orderDataType', 'sortDataType' );
517 
518  // orderData can be given as an integer
519  var dataSort = init.aDataSort;
520  if ( dataSort && ! $.isArray( dataSort ) ) {
521  init.aDataSort = [ dataSort ];
522  }
523  }
524 
525 
531  function _fnBrowserDetect( settings )
532  {
533  // We don't need to do this every time DataTables is constructed, the values
534  // calculated are specific to the browser and OS configuration which we
535  // don't expect to change between initialisations
536  if ( ! DataTable.__browser ) {
537  var browser = {};
538  DataTable.__browser = browser;
539 
540  // Scrolling feature / quirks detection
541  var n = $('<div/>')
542  .css( {
543  position: 'fixed',
544  top: 0,
545  left: 0,
546  height: 1,
547  width: 1,
548  overflow: 'hidden'
549  } )
550  .append(
551  $('<div/>')
552  .css( {
553  position: 'absolute',
554  top: 1,
555  left: 1,
556  width: 100,
557  overflow: 'scroll'
558  } )
559  .append(
560  $('<div/>')
561  .css( {
562  width: '100%',
563  height: 10
564  } )
565  )
566  )
567  .appendTo( 'body' );
568 
569  var outer = n.children();
570  var inner = outer.children();
571 
572  // Numbers below, in order, are:
573  // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
574  //
575  // IE6 XP: 100 100 100 83
576  // IE7 Vista: 100 100 100 83
577  // IE 8+ Windows: 83 83 100 83
578  // Evergreen Windows: 83 83 100 83
579  // Evergreen Mac with scrollbars: 85 85 100 85
580  // Evergreen Mac without scrollbars: 100 100 100 100
581 
582  // Get scrollbar width
583  browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
584 
585  // IE6/7 will oversize a width 100% element inside a scrolling element, to
586  // include the width of the scrollbar, while other browsers ensure the inner
587  // element is contained without forcing scrolling
588  browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
589 
590  // In rtl text layout, some browsers (most, but not all) will place the
591  // scrollbar on the left, rather than the right.
592  browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
593 
594  // IE8- don't provide height and width for getBoundingClientRect
595  browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
596 
597  n.remove();
598  }
599 
600  $.extend( settings.oBrowser, DataTable.__browser );
601  settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
602  }
603 
604 
611  function _fnReduce ( that, fn, init, start, end, inc )
612  {
613  var
614  i = start,
615  value,
616  isSet = false;
617 
618  if ( init !== undefined ) {
619  value = init;
620  isSet = true;
621  }
622 
623  while ( i !== end ) {
624  if ( ! that.hasOwnProperty(i) ) {
625  continue;
626  }
627 
628  value = isSet ?
629  fn( value, that[i], i, that ) :
630  that[i];
631 
632  isSet = true;
633  i += inc;
634  }
635 
636  return value;
637  }
638 
645  function _fnAddColumn( oSettings, nTh )
646  {
647  // Add column to aoColumns array
648  var oDefaults = DataTable.defaults.column;
649  var iCol = oSettings.aoColumns.length;
650  var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
651  "nTh": nTh ? nTh : document.createElement('th'),
652  "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
653  "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
654  "mData": oDefaults.mData ? oDefaults.mData : iCol,
655  idx: iCol
656  } );
657  oSettings.aoColumns.push( oCol );
658 
659  // Add search object for column specific search. Note that the `searchCols[ iCol ]`
660  // passed into extend can be undefined. This allows the user to give a default
661  // with only some of the parameters defined, and also not give a default
662  var searchCols = oSettings.aoPreSearchCols;
663  searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
664 
665  // Use the default column options function to initialise classes etc
666  _fnColumnOptions( oSettings, iCol, $(nTh).data() );
667  }
668 
669 
677  function _fnColumnOptions( oSettings, iCol, oOptions )
678  {
679  var oCol = oSettings.aoColumns[ iCol ];
680  var oClasses = oSettings.oClasses;
681  var th = $(oCol.nTh);
682 
683  // Try to get width information from the DOM. We can't get it from CSS
684  // as we'd need to parse the CSS stylesheet. `width` option can override
685  if ( ! oCol.sWidthOrig ) {
686  // Width attribute
687  oCol.sWidthOrig = th.attr('width') || null;
688 
689  // Style attribute
690  var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
691  if ( t ) {
692  oCol.sWidthOrig = t[1];
693  }
694  }
695 
696  /* User specified column options */
697  if ( oOptions !== undefined && oOptions !== null )
698  {
699  // Backwards compatibility
700  _fnCompatCols( oOptions );
701 
702  // Map camel case parameters to their Hungarian counterparts
703  _fnCamelToHungarian( DataTable.defaults.column, oOptions );
704 
705  /* Backwards compatibility for mDataProp */
706  if ( oOptions.mDataProp !== undefined && !oOptions.mData )
707  {
708  oOptions.mData = oOptions.mDataProp;
709  }
710 
711  if ( oOptions.sType )
712  {
713  oCol._sManualType = oOptions.sType;
714  }
715 
716  // `class` is a reserved word in Javascript, so we need to provide
717  // the ability to use a valid name for the camel case input
718  if ( oOptions.className && ! oOptions.sClass )
719  {
720  oOptions.sClass = oOptions.className;
721  }
722 
723  $.extend( oCol, oOptions );
724  _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
725 
726  /* iDataSort to be applied (backwards compatibility), but aDataSort will take
727  * priority if defined
728  */
729  if ( oOptions.iDataSort !== undefined )
730  {
731  oCol.aDataSort = [ oOptions.iDataSort ];
732  }
733  _fnMap( oCol, oOptions, "aDataSort" );
734  }
735 
736  /* Cache the data get and set functions for speed */
737  var mDataSrc = oCol.mData;
738  var mData = _fnGetObjectDataFn( mDataSrc );
739  var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
740 
741  var attrTest = function( src ) {
742  return typeof src === 'string' && src.indexOf('@') !== -1;
743  };
744  oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
745  attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
746  );
747  oCol._setter = null;
748 
749  oCol.fnGetData = function (rowData, type, meta) {
750  var innerData = mData( rowData, type, undefined, meta );
751 
752  return mRender && type ?
753  mRender( innerData, type, rowData, meta ) :
754  innerData;
755  };
756  oCol.fnSetData = function ( rowData, val, meta ) {
757  return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
758  };
759 
760  // Indicate if DataTables should read DOM data as an object or array
761  // Used in _fnGetRowElements
762  if ( typeof mDataSrc !== 'number' ) {
763  oSettings._rowReadObject = true;
764  }
765 
766  /* Feature sorting overrides column specific when off */
767  if ( !oSettings.oFeatures.bSort )
768  {
769  oCol.bSortable = false;
770  th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
771  }
772 
773  /* Check that the class assignment is correct for sorting */
774  var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
775  var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
776  if ( !oCol.bSortable || (!bAsc && !bDesc) )
777  {
778  oCol.sSortingClass = oClasses.sSortableNone;
779  oCol.sSortingClassJUI = "";
780  }
781  else if ( bAsc && !bDesc )
782  {
783  oCol.sSortingClass = oClasses.sSortableAsc;
784  oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
785  }
786  else if ( !bAsc && bDesc )
787  {
788  oCol.sSortingClass = oClasses.sSortableDesc;
789  oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
790  }
791  else
792  {
793  oCol.sSortingClass = oClasses.sSortable;
794  oCol.sSortingClassJUI = oClasses.sSortJUI;
795  }
796  }
797 
798 
805  function _fnAdjustColumnSizing ( settings )
806  {
807  /* Not interested in doing column width calculation if auto-width is disabled */
808  if ( settings.oFeatures.bAutoWidth !== false )
809  {
810  var columns = settings.aoColumns;
811 
812  _fnCalculateColumnWidths( settings );
813  for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
814  {
815  columns[i].nTh.style.width = columns[i].sWidth;
816  }
817  }
818 
819  var scroll = settings.oScroll;
820  if ( scroll.sY !== '' || scroll.sX !== '')
821  {
822  _fnScrollDraw( settings );
823  }
824 
825  _fnCallbackFire( settings, null, 'column-sizing', [settings] );
826  }
827 
828 
837  function _fnVisibleToColumnIndex( oSettings, iMatch )
838  {
839  var aiVis = _fnGetColumns( oSettings, 'bVisible' );
840 
841  return typeof aiVis[iMatch] === 'number' ?
842  aiVis[iMatch] :
843  null;
844  }
845 
846 
855  function _fnColumnIndexToVisible( oSettings, iMatch )
856  {
857  var aiVis = _fnGetColumns( oSettings, 'bVisible' );
858  var iPos = $.inArray( iMatch, aiVis );
859 
860  return iPos !== -1 ? iPos : null;
861  }
862 
863 
870  function _fnVisbleColumns( oSettings )
871  {
872  var vis = 0;
873 
874  // No reduce in IE8, use a loop for now
875  $.each( oSettings.aoColumns, function ( i, col ) {
876  if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
877  vis++;
878  }
879  } );
880 
881  return vis;
882  }
883 
884 
893  function _fnGetColumns( oSettings, sParam )
894  {
895  var a = [];
896 
897  $.map( oSettings.aoColumns, function(val, i) {
898  if ( val[sParam] ) {
899  a.push( i );
900  }
901  } );
902 
903  return a;
904  }
905 
906 
912  function _fnColumnTypes ( settings )
913  {
914  var columns = settings.aoColumns;
915  var data = settings.aoData;
916  var types = DataTable.ext.type.detect;
917  var i, ien, j, jen, k, ken;
918  var col, cell, detectedType, cache;
919 
920  // For each column, spin over the
921  for ( i=0, ien=columns.length ; i<ien ; i++ ) {
922  col = columns[i];
923  cache = [];
924 
925  if ( ! col.sType && col._sManualType ) {
926  col.sType = col._sManualType;
927  }
928  else if ( ! col.sType ) {
929  for ( j=0, jen=types.length ; j<jen ; j++ ) {
930  for ( k=0, ken=data.length ; k<ken ; k++ ) {
931  // Use a cache array so we only need to get the type data
932  // from the formatter once (when using multiple detectors)
933  if ( cache[k] === undefined ) {
934  cache[k] = _fnGetCellData( settings, k, i, 'type' );
935  }
936 
937  detectedType = types[j]( cache[k], settings );
938 
939  // If null, then this type can't apply to this column, so
940  // rather than testing all cells, break out. There is an
941  // exception for the last type which is `html`. We need to
942  // scan all rows since it is possible to mix string and HTML
943  // types
944  if ( ! detectedType && j !== types.length-1 ) {
945  break;
946  }
947 
948  // Only a single match is needed for html type since it is
949  // bottom of the pile and very similar to string
950  if ( detectedType === 'html' ) {
951  break;
952  }
953  }
954 
955  // Type is valid for all data points in the column - use this
956  // type
957  if ( detectedType ) {
958  col.sType = detectedType;
959  break;
960  }
961  }
962 
963  // Fall back - if no type was detected, always use string
964  if ( ! col.sType ) {
965  col.sType = 'string';
966  }
967  }
968  }
969  }
970 
971 
983  function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
984  {
985  var i, iLen, j, jLen, k, kLen, def;
986  var columns = oSettings.aoColumns;
987 
988  // Column definitions with aTargets
989  if ( aoColDefs )
990  {
991  /* Loop over the definitions array - loop in reverse so first instance has priority */
992  for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
993  {
994  def = aoColDefs[i];
995 
996  /* Each definition can target multiple columns, as it is an array */
997  var aTargets = def.targets !== undefined ?
998  def.targets :
999  def.aTargets;
1000 
1001  if ( ! $.isArray( aTargets ) )
1002  {
1003  aTargets = [ aTargets ];
1004  }
1005 
1006  for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
1007  {
1008  if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
1009  {
1010  /* Add columns that we don't yet know about */
1011  while( columns.length <= aTargets[j] )
1012  {
1013  _fnAddColumn( oSettings );
1014  }
1015 
1016  /* Integer, basic index */
1017  fn( aTargets[j], def );
1018  }
1019  else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
1020  {
1021  /* Negative integer, right to left column counting */
1022  fn( columns.length+aTargets[j], def );
1023  }
1024  else if ( typeof aTargets[j] === 'string' )
1025  {
1026  /* Class name matching on TH element */
1027  for ( k=0, kLen=columns.length ; k<kLen ; k++ )
1028  {
1029  if ( aTargets[j] == "_all" ||
1030  $(columns[k].nTh).hasClass( aTargets[j] ) )
1031  {
1032  fn( k, def );
1033  }
1034  }
1035  }
1036  }
1037  }
1038  }
1039 
1040  // Statically defined columns array
1041  if ( aoCols )
1042  {
1043  for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
1044  {
1045  fn( i, aoCols[i] );
1046  }
1047  }
1048  }
1049 
1063  function _fnAddData ( oSettings, aDataIn, nTr, anTds )
1064  {
1065  /* Create the object for storing information about this new row */
1066  var iRow = oSettings.aoData.length;
1067  var oData = $.extend( true, {}, DataTable.models.oRow, {
1068  src: nTr ? 'dom' : 'data',
1069  idx: iRow
1070  } );
1071 
1072  oData._aData = aDataIn;
1073  oSettings.aoData.push( oData );
1074 
1075  /* Create the cells */
1076  var nTd, sThisType;
1077  var columns = oSettings.aoColumns;
1078 
1079  // Invalidate the column types as the new data needs to be revalidated
1080  for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
1081  {
1082  columns[i].sType = null;
1083  }
1084 
1085  /* Add to the display array */
1086  oSettings.aiDisplayMaster.push( iRow );
1087 
1088  var id = oSettings.rowIdFn( aDataIn );
1089  if ( id !== undefined ) {
1090  oSettings.aIds[ id ] = oData;
1091  }
1092 
1093  /* Create the DOM information, or register it if already present */
1094  if ( nTr || ! oSettings.oFeatures.bDeferRender )
1095  {
1096  _fnCreateTr( oSettings, iRow, nTr, anTds );
1097  }
1098 
1099  return iRow;
1100  }
1101 
1102 
1113  function _fnAddTr( settings, trs )
1114  {
1115  var row;
1116 
1117  // Allow an individual node to be passed in
1118  if ( ! (trs instanceof $) ) {
1119  trs = $(trs);
1120  }
1121 
1122  return trs.map( function (i, el) {
1123  row = _fnGetRowElements( settings, el );
1124  return _fnAddData( settings, row.data, el, row.cells );
1125  } );
1126  }
1127 
1128 
1136  function _fnNodeToDataIndex( oSettings, n )
1137  {
1138  return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
1139  }
1140 
1141 
1150  function _fnNodeToColumnIndex( oSettings, iRow, n )
1151  {
1152  return $.inArray( n, oSettings.aoData[ iRow ].anCells );
1153  }
1154 
1155 
1165  function _fnGetCellData( settings, rowIdx, colIdx, type )
1166  {
1167  var draw = settings.iDraw;
1168  var col = settings.aoColumns[colIdx];
1169  var rowData = settings.aoData[rowIdx]._aData;
1170  var defaultContent = col.sDefaultContent;
1171  var cellData = col.fnGetData( rowData, type, {
1172  settings: settings,
1173  row: rowIdx,
1174  col: colIdx
1175  } );
1176 
1177  if ( cellData === undefined ) {
1178  if ( settings.iDrawError != draw && defaultContent === null ) {
1179  _fnLog( settings, 0, "Requested unknown parameter "+
1180  (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
1181  " for row "+rowIdx+", column "+colIdx, 4 );
1182  settings.iDrawError = draw;
1183  }
1184  return defaultContent;
1185  }
1186 
1187  // When the data source is null and a specific data type is requested (i.e.
1188  // not the original data), we can use default column data
1189  if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
1190  cellData = defaultContent;
1191  }
1192  else if ( typeof cellData === 'function' ) {
1193  // If the data source is a function, then we run it and use the return,
1194  // executing in the scope of the data object (for instances)
1195  return cellData.call( rowData );
1196  }
1197 
1198  if ( cellData === null && type == 'display' ) {
1199  return '';
1200  }
1201  return cellData;
1202  }
1203 
1204 
1213  function _fnSetCellData( settings, rowIdx, colIdx, val )
1214  {
1215  var col = settings.aoColumns[colIdx];
1216  var rowData = settings.aoData[rowIdx]._aData;
1217 
1218  col.fnSetData( rowData, val, {
1219  settings: settings,
1220  row: rowIdx,
1221  col: colIdx
1222  } );
1223  }
1224 
1225 
1226  // Private variable that is used to match action syntax in the data property object
1227  var __reArray = /\[.*?\]$/;
1228  var __reFn = /\(\)$/;
1229 
1235  function _fnSplitObjNotation( str )
1236  {
1237  return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
1238  return s.replace(/\\./g, '.');
1239  } );
1240  }
1241 
1242 
1250  function _fnGetObjectDataFn( mSource )
1251  {
1252  if ( $.isPlainObject( mSource ) )
1253  {
1254  /* Build an object of get functions, and wrap them in a single call */
1255  var o = {};
1256  $.each( mSource, function (key, val) {
1257  if ( val ) {
1258  o[key] = _fnGetObjectDataFn( val );
1259  }
1260  } );
1261 
1262  return function (data, type, row, meta) {
1263  var t = o[type] || o._;
1264  return t !== undefined ?
1265  t(data, type, row, meta) :
1266  data;
1267  };
1268  }
1269  else if ( mSource === null )
1270  {
1271  /* Give an empty string for rendering / sorting etc */
1272  return function (data) { // type, row and meta also passed, but not used
1273  return data;
1274  };
1275  }
1276  else if ( typeof mSource === 'function' )
1277  {
1278  return function (data, type, row, meta) {
1279  return mSource( data, type, row, meta );
1280  };
1281  }
1282  else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
1283  mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
1284  {
1285  /* If there is a . in the source string then the data source is in a
1286  * nested object so we loop over the data for each level to get the next
1287  * level down. On each loop we test for undefined, and if found immediately
1288  * return. This allows entire objects to be missing and sDefaultContent to
1289  * be used if defined, rather than throwing an error
1290  */
1291  var fetchData = function (data, type, src) {
1292  var arrayNotation, funcNotation, out, innerSrc;
1293 
1294  if ( src !== "" )
1295  {
1296  var a = _fnSplitObjNotation( src );
1297 
1298  for ( var i=0, iLen=a.length ; i<iLen ; i++ )
1299  {
1300  // Check if we are dealing with special notation
1301  arrayNotation = a[i].match(__reArray);
1302  funcNotation = a[i].match(__reFn);
1303 
1304  if ( arrayNotation )
1305  {
1306  // Array notation
1307  a[i] = a[i].replace(__reArray, '');
1308 
1309  // Condition allows simply [] to be passed in
1310  if ( a[i] !== "" ) {
1311  data = data[ a[i] ];
1312  }
1313  out = [];
1314 
1315  // Get the remainder of the nested object to get
1316  a.splice( 0, i+1 );
1317  innerSrc = a.join('.');
1318 
1319  // Traverse each entry in the array getting the properties requested
1320  if ( $.isArray( data ) ) {
1321  for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
1322  out.push( fetchData( data[j], type, innerSrc ) );
1323  }
1324  }
1325 
1326  // If a string is given in between the array notation indicators, that
1327  // is used to join the strings together, otherwise an array is returned
1328  var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
1329  data = (join==="") ? out : out.join(join);
1330 
1331  // The inner call to fetchData has already traversed through the remainder
1332  // of the source requested, so we exit from the loop
1333  break;
1334  }
1335  else if ( funcNotation )
1336  {
1337  // Function call
1338  a[i] = a[i].replace(__reFn, '');
1339  data = data[ a[i] ]();
1340  continue;
1341  }
1342 
1343  if ( data === null || data[ a[i] ] === undefined )
1344  {
1345  return undefined;
1346  }
1347  data = data[ a[i] ];
1348  }
1349  }
1350 
1351  return data;
1352  };
1353 
1354  return function (data, type) { // row and meta also passed, but not used
1355  return fetchData( data, type, mSource );
1356  };
1357  }
1358  else
1359  {
1360  /* Array or flat object mapping */
1361  return function (data, type) { // row and meta also passed, but not used
1362  return data[mSource];
1363  };
1364  }
1365  }
1366 
1367 
1375  function _fnSetObjectDataFn( mSource )
1376  {
1377  if ( $.isPlainObject( mSource ) )
1378  {
1379  /* Unlike get, only the underscore (global) option is used for for
1380  * setting data since we don't know the type here. This is why an object
1381  * option is not documented for `mData` (which is read/write), but it is
1382  * for `mRender` which is read only.
1383  */
1384  return _fnSetObjectDataFn( mSource._ );
1385  }
1386  else if ( mSource === null )
1387  {
1388  /* Nothing to do when the data source is null */
1389  return function () {};
1390  }
1391  else if ( typeof mSource === 'function' )
1392  {
1393  return function (data, val, meta) {
1394  mSource( data, 'set', val, meta );
1395  };
1396  }
1397  else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
1398  mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
1399  {
1400  /* Like the get, we need to get data from a nested object */
1401  var setData = function (data, val, src) {
1402  var a = _fnSplitObjNotation( src ), b;
1403  var aLast = a[a.length-1];
1404  var arrayNotation, funcNotation, o, innerSrc;
1405 
1406  for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
1407  {
1408  // Check if we are dealing with an array notation request
1409  arrayNotation = a[i].match(__reArray);
1410  funcNotation = a[i].match(__reFn);
1411 
1412  if ( arrayNotation )
1413  {
1414  a[i] = a[i].replace(__reArray, '');
1415  data[ a[i] ] = [];
1416 
1417  // Get the remainder of the nested object to set so we can recurse
1418  b = a.slice();
1419  b.splice( 0, i+1 );
1420  innerSrc = b.join('.');
1421 
1422  // Traverse each entry in the array setting the properties requested
1423  if ( $.isArray( val ) )
1424  {
1425  for ( var j=0, jLen=val.length ; j<jLen ; j++ )
1426  {
1427  o = {};
1428  setData( o, val[j], innerSrc );
1429  data[ a[i] ].push( o );
1430  }
1431  }
1432  else
1433  {
1434  // We've been asked to save data to an array, but it
1435  // isn't array data to be saved. Best that can be done
1436  // is to just save the value.
1437  data[ a[i] ] = val;
1438  }
1439 
1440  // The inner call to setData has already traversed through the remainder
1441  // of the source and has set the data, thus we can exit here
1442  return;
1443  }
1444  else if ( funcNotation )
1445  {
1446  // Function call
1447  a[i] = a[i].replace(__reFn, '');
1448  data = data[ a[i] ]( val );
1449  }
1450 
1451  // If the nested object doesn't currently exist - since we are
1452  // trying to set the value - create it
1453  if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
1454  {
1455  data[ a[i] ] = {};
1456  }
1457  data = data[ a[i] ];
1458  }
1459 
1460  // Last item in the input - i.e, the actual set
1461  if ( aLast.match(__reFn ) )
1462  {
1463  // Function call
1464  data = data[ aLast.replace(__reFn, '') ]( val );
1465  }
1466  else
1467  {
1468  // If array notation is used, we just want to strip it and use the property name
1469  // and assign the value. If it isn't used, then we get the result we want anyway
1470  data[ aLast.replace(__reArray, '') ] = val;
1471  }
1472  };
1473 
1474  return function (data, val) { // meta is also passed in, but not used
1475  return setData( data, val, mSource );
1476  };
1477  }
1478  else
1479  {
1480  /* Array or flat object mapping */
1481  return function (data, val) { // meta is also passed in, but not used
1482  data[mSource] = val;
1483  };
1484  }
1485  }
1486 
1487 
1494  function _fnGetDataMaster ( settings )
1495  {
1496  return _pluck( settings.aoData, '_aData' );
1497  }
1498 
1499 
1505  function _fnClearTable( settings )
1506  {
1507  settings.aoData.length = 0;
1508  settings.aiDisplayMaster.length = 0;
1509  settings.aiDisplay.length = 0;
1510  settings.aIds = {};
1511  }
1512 
1513 
1521  function _fnDeleteIndex( a, iTarget, splice )
1522  {
1523  var iTargetIndex = -1;
1524 
1525  for ( var i=0, iLen=a.length ; i<iLen ; i++ )
1526  {
1527  if ( a[i] == iTarget )
1528  {
1529  iTargetIndex = i;
1530  }
1531  else if ( a[i] > iTarget )
1532  {
1533  a[i]--;
1534  }
1535  }
1536 
1537  if ( iTargetIndex != -1 && splice === undefined )
1538  {
1539  a.splice( iTargetIndex, 1 );
1540  }
1541  }
1542 
1543 
1560  function _fnInvalidate( settings, rowIdx, src, colIdx )
1561  {
1562  var row = settings.aoData[ rowIdx ];
1563  var i, ien;
1564  var cellWrite = function ( cell, col ) {
1565  // This is very frustrating, but in IE if you just write directly
1566  // to innerHTML, and elements that are overwritten are GC'ed,
1567  // even if there is a reference to them elsewhere
1568  while ( cell.childNodes.length ) {
1569  cell.removeChild( cell.firstChild );
1570  }
1571 
1572  cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
1573  };
1574 
1575  // Are we reading last data from DOM or the data object?
1576  if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
1577  // Read the data from the DOM
1578  row._aData = _fnGetRowElements(
1579  settings, row, colIdx, colIdx === undefined ? undefined : row._aData
1580  )
1581  .data;
1582  }
1583  else {
1584  // Reading from data object, update the DOM
1585  var cells = row.anCells;
1586 
1587  if ( cells ) {
1588  if ( colIdx !== undefined ) {
1589  cellWrite( cells[colIdx], colIdx );
1590  }
1591  else {
1592  for ( i=0, ien=cells.length ; i<ien ; i++ ) {
1593  cellWrite( cells[i], i );
1594  }
1595  }
1596  }
1597  }
1598 
1599  // For both row and cell invalidation, the cached data for sorting and
1600  // filtering is nulled out
1601  row._aSortData = null;
1602  row._aFilterData = null;
1603 
1604  // Invalidate the type for a specific column (if given) or all columns since
1605  // the data might have changed
1606  var cols = settings.aoColumns;
1607  if ( colIdx !== undefined ) {
1608  cols[ colIdx ].sType = null;
1609  }
1610  else {
1611  for ( i=0, ien=cols.length ; i<ien ; i++ ) {
1612  cols[i].sType = null;
1613  }
1614 
1615  // Update DataTables special `DT_*` attributes for the row
1616  _fnRowAttributes( settings, row );
1617  }
1618  }
1619 
1620 
1638  function _fnGetRowElements( settings, row, colIdx, d )
1639  {
1640  var
1641  tds = [],
1642  td = row.firstChild,
1643  name, col, o, i=0, contents,
1644  columns = settings.aoColumns,
1645  objectRead = settings._rowReadObject;
1646 
1647  // Allow the data object to be passed in, or construct
1648  d = d !== undefined ?
1649  d :
1650  objectRead ?
1651  {} :
1652  [];
1653 
1654  var attr = function ( str, td ) {
1655  if ( typeof str === 'string' ) {
1656  var idx = str.indexOf('@');
1657 
1658  if ( idx !== -1 ) {
1659  var attr = str.substring( idx+1 );
1660  var setter = _fnSetObjectDataFn( str );
1661  setter( d, td.getAttribute( attr ) );
1662  }
1663  }
1664  };
1665 
1666  // Read data from a cell and store into the data object
1667  var cellProcess = function ( cell ) {
1668  if ( colIdx === undefined || colIdx === i ) {
1669  col = columns[i];
1670  contents = $.trim(cell.innerHTML);
1671 
1672  if ( col && col._bAttrSrc ) {
1673  var setter = _fnSetObjectDataFn( col.mData._ );
1674  setter( d, contents );
1675 
1676  attr( col.mData.sort, cell );
1677  attr( col.mData.type, cell );
1678  attr( col.mData.filter, cell );
1679  }
1680  else {
1681  // Depending on the `data` option for the columns the data can
1682  // be read to either an object or an array.
1683  if ( objectRead ) {
1684  if ( ! col._setter ) {
1685  // Cache the setter function
1686  col._setter = _fnSetObjectDataFn( col.mData );
1687  }
1688  col._setter( d, contents );
1689  }
1690  else {
1691  d[i] = contents;
1692  }
1693  }
1694  }
1695 
1696  i++;
1697  };
1698 
1699  if ( td ) {
1700  // `tr` element was passed in
1701  while ( td ) {
1702  name = td.nodeName.toUpperCase();
1703 
1704  if ( name == "TD" || name == "TH" ) {
1705  cellProcess( td );
1706  tds.push( td );
1707  }
1708 
1709  td = td.nextSibling;
1710  }
1711  }
1712  else {
1713  // Existing row object passed in
1714  tds = row.anCells;
1715 
1716  for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
1717  cellProcess( tds[j] );
1718  }
1719  }
1720 
1721  // Read the ID from the DOM if present
1722  var rowNode = row.firstChild ? row : row.nTr;
1723 
1724  if ( rowNode ) {
1725  var id = rowNode.getAttribute( 'id' );
1726 
1727  if ( id ) {
1728  _fnSetObjectDataFn( settings.rowId )( d, id );
1729  }
1730  }
1731 
1732  return {
1733  data: d,
1734  cells: tds
1735  };
1736  }
1747  function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
1748  {
1749  var
1750  row = oSettings.aoData[iRow],
1751  rowData = row._aData,
1752  cells = [],
1753  nTr, nTd, oCol,
1754  i, iLen;
1755 
1756  if ( row.nTr === null )
1757  {
1758  nTr = nTrIn || document.createElement('tr');
1759 
1760  row.nTr = nTr;
1761  row.anCells = cells;
1762 
1763  /* Use a private property on the node to allow reserve mapping from the node
1764  * to the aoData array for fast look up
1765  */
1766  nTr._DT_RowIndex = iRow;
1767 
1768  /* Special parameters can be given by the data source to be used on the row */
1769  _fnRowAttributes( oSettings, row );
1770 
1771  /* Process each column */
1772  for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
1773  {
1774  oCol = oSettings.aoColumns[i];
1775 
1776  nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
1777  nTd._DT_CellIndex = {
1778  row: iRow,
1779  column: i
1780  };
1781 
1782  cells.push( nTd );
1783 
1784  // Need to create the HTML if new, or if a rendering function is defined
1785  if ( (!nTrIn || oCol.mRender || oCol.mData !== i) &&
1786  (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
1787  ) {
1788  nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
1789  }
1790 
1791  /* Add user defined class */
1792  if ( oCol.sClass )
1793  {
1794  nTd.className += ' '+oCol.sClass;
1795  }
1796 
1797  // Visibility - add or remove as required
1798  if ( oCol.bVisible && ! nTrIn )
1799  {
1800  nTr.appendChild( nTd );
1801  }
1802  else if ( ! oCol.bVisible && nTrIn )
1803  {
1804  nTd.parentNode.removeChild( nTd );
1805  }
1806 
1807  if ( oCol.fnCreatedCell )
1808  {
1809  oCol.fnCreatedCell.call( oSettings.oInstance,
1810  nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
1811  );
1812  }
1813  }
1814 
1815  _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] );
1816  }
1817 
1818  // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
1819  // and deployed
1820  row.nTr.setAttribute( 'role', 'row' );
1821  }
1822 
1823 
1831  function _fnRowAttributes( settings, row )
1832  {
1833  var tr = row.nTr;
1834  var data = row._aData;
1835 
1836  if ( tr ) {
1837  var id = settings.rowIdFn( data );
1838 
1839  if ( id ) {
1840  tr.id = id;
1841  }
1842 
1843  if ( data.DT_RowClass ) {
1844  // Remove any classes added by DT_RowClass before
1845  var a = data.DT_RowClass.split(' ');
1846  row.__rowc = row.__rowc ?
1847  _unique( row.__rowc.concat( a ) ) :
1848  a;
1849 
1850  $(tr)
1851  .removeClass( row.__rowc.join(' ') )
1852  .addClass( data.DT_RowClass );
1853  }
1854 
1855  if ( data.DT_RowAttr ) {
1856  $(tr).attr( data.DT_RowAttr );
1857  }
1858 
1859  if ( data.DT_RowData ) {
1860  $(tr).data( data.DT_RowData );
1861  }
1862  }
1863  }
1864 
1865 
1871  function _fnBuildHead( oSettings )
1872  {
1873  var i, ien, cell, row, column;
1874  var thead = oSettings.nTHead;
1875  var tfoot = oSettings.nTFoot;
1876  var createHeader = $('th, td', thead).length === 0;
1877  var classes = oSettings.oClasses;
1878  var columns = oSettings.aoColumns;
1879 
1880  if ( createHeader ) {
1881  row = $('<tr/>').appendTo( thead );
1882  }
1883 
1884  for ( i=0, ien=columns.length ; i<ien ; i++ ) {
1885  column = columns[i];
1886  cell = $( column.nTh ).addClass( column.sClass );
1887 
1888  if ( createHeader ) {
1889  cell.appendTo( row );
1890  }
1891 
1892  // 1.11 move into sorting
1893  if ( oSettings.oFeatures.bSort ) {
1894  cell.addClass( column.sSortingClass );
1895 
1896  if ( column.bSortable !== false ) {
1897  cell
1898  .attr( 'tabindex', oSettings.iTabIndex )
1899  .attr( 'aria-controls', oSettings.sTableId );
1900 
1901  _fnSortAttachListener( oSettings, column.nTh, i );
1902  }
1903  }
1904 
1905  if ( column.sTitle != cell[0].innerHTML ) {
1906  cell.html( column.sTitle );
1907  }
1908 
1909  _fnRenderer( oSettings, 'header' )(
1910  oSettings, cell, column, classes
1911  );
1912  }
1913 
1914  if ( createHeader ) {
1915  _fnDetectHeader( oSettings.aoHeader, thead );
1916  }
1917 
1918  /* ARIA role for the rows */
1919  $(thead).find('>tr').attr('role', 'row');
1920 
1921  /* Deal with the footer - add classes if required */
1922  $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );
1923  $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH );
1924 
1925  // Cache the footer cells. Note that we only take the cells from the first
1926  // row in the footer. If there is more than one row the user wants to
1927  // interact with, they need to use the table().foot() method. Note also this
1928  // allows cells to be used for multiple columns using colspan
1929  if ( tfoot !== null ) {
1930  var cells = oSettings.aoFooter[0];
1931 
1932  for ( i=0, ien=cells.length ; i<ien ; i++ ) {
1933  column = columns[i];
1934  column.nTf = cells[i].cell;
1935 
1936  if ( column.sClass ) {
1937  $(column.nTf).addClass( column.sClass );
1938  }
1939  }
1940  }
1941  }
1942 
1943 
1957  function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
1958  {
1959  var i, iLen, j, jLen, k, kLen, n, nLocalTr;
1960  var aoLocal = [];
1961  var aApplied = [];
1962  var iColumns = oSettings.aoColumns.length;
1963  var iRowspan, iColspan;
1964 
1965  if ( ! aoSource )
1966  {
1967  return;
1968  }
1969 
1970  if ( bIncludeHidden === undefined )
1971  {
1972  bIncludeHidden = false;
1973  }
1974 
1975  /* Make a copy of the master layout array, but without the visible columns in it */
1976  for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
1977  {
1978  aoLocal[i] = aoSource[i].slice();
1979  aoLocal[i].nTr = aoSource[i].nTr;
1980 
1981  /* Remove any columns which are currently hidden */
1982  for ( j=iColumns-1 ; j>=0 ; j-- )
1983  {
1984  if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
1985  {
1986  aoLocal[i].splice( j, 1 );
1987  }
1988  }
1989 
1990  /* Prep the applied array - it needs an element for each row */
1991  aApplied.push( [] );
1992  }
1993 
1994  for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
1995  {
1996  nLocalTr = aoLocal[i].nTr;
1997 
1998  /* All cells are going to be replaced, so empty out the row */
1999  if ( nLocalTr )
2000  {
2001  while( (n = nLocalTr.firstChild) )
2002  {
2003  nLocalTr.removeChild( n );
2004  }
2005  }
2006 
2007  for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
2008  {
2009  iRowspan = 1;
2010  iColspan = 1;
2011 
2012  /* Check to see if there is already a cell (row/colspan) covering our target
2013  * insert point. If there is, then there is nothing to do.
2014  */
2015  if ( aApplied[i][j] === undefined )
2016  {
2017  nLocalTr.appendChild( aoLocal[i][j].cell );
2018  aApplied[i][j] = 1;
2019 
2020  /* Expand the cell to cover as many rows as needed */
2021  while ( aoLocal[i+iRowspan] !== undefined &&
2022  aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
2023  {
2024  aApplied[i+iRowspan][j] = 1;
2025  iRowspan++;
2026  }
2027 
2028  /* Expand the cell to cover as many columns as needed */
2029  while ( aoLocal[i][j+iColspan] !== undefined &&
2030  aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
2031  {
2032  /* Must update the applied array over the rows for the columns */
2033  for ( k=0 ; k<iRowspan ; k++ )
2034  {
2035  aApplied[i+k][j+iColspan] = 1;
2036  }
2037  iColspan++;
2038  }
2039 
2040  /* Do the actual expansion in the DOM */
2041  $(aoLocal[i][j].cell)
2042  .attr('rowspan', iRowspan)
2043  .attr('colspan', iColspan);
2044  }
2045  }
2046  }
2047  }
2048 
2049 
2055  function _fnDraw( oSettings )
2056  {
2057  /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
2058  var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
2059  if ( $.inArray( false, aPreDraw ) !== -1 )
2060  {
2061  _fnProcessingDisplay( oSettings, false );
2062  return;
2063  }
2064 
2065  var i, iLen, n;
2066  var anRows = [];
2067  var iRowCount = 0;
2068  var asStripeClasses = oSettings.asStripeClasses;
2069  var iStripes = asStripeClasses.length;
2070  var iOpenRows = oSettings.aoOpenRows.length;
2071  var oLang = oSettings.oLanguage;
2072  var iInitDisplayStart = oSettings.iInitDisplayStart;
2073  var bServerSide = _fnDataSource( oSettings ) == 'ssp';
2074  var aiDisplay = oSettings.aiDisplay;
2075 
2076  oSettings.bDrawing = true;
2077 
2078  /* Check and see if we have an initial draw position from state saving */
2079  if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
2080  {
2081  oSettings._iDisplayStart = bServerSide ?
2082  iInitDisplayStart :
2083  iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
2084  0 :
2085  iInitDisplayStart;
2086 
2087  oSettings.iInitDisplayStart = -1;
2088  }
2089 
2090  var iDisplayStart = oSettings._iDisplayStart;
2091  var iDisplayEnd = oSettings.fnDisplayEnd();
2092 
2093  /* Server-side processing draw intercept */
2094  if ( oSettings.bDeferLoading )
2095  {
2096  oSettings.bDeferLoading = false;
2097  oSettings.iDraw++;
2098  _fnProcessingDisplay( oSettings, false );
2099  }
2100  else if ( !bServerSide )
2101  {
2102  oSettings.iDraw++;
2103  }
2104  else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
2105  {
2106  return;
2107  }
2108 
2109  if ( aiDisplay.length !== 0 )
2110  {
2111  var iStart = bServerSide ? 0 : iDisplayStart;
2112  var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
2113 
2114  for ( var j=iStart ; j<iEnd ; j++ )
2115  {
2116  var iDataIndex = aiDisplay[j];
2117  var aoData = oSettings.aoData[ iDataIndex ];
2118  if ( aoData.nTr === null )
2119  {
2120  _fnCreateTr( oSettings, iDataIndex );
2121  }
2122 
2123  var nRow = aoData.nTr;
2124 
2125  /* Remove the old striping classes and then add the new one */
2126  if ( iStripes !== 0 )
2127  {
2128  var sStripe = asStripeClasses[ iRowCount % iStripes ];
2129  if ( aoData._sRowStripe != sStripe )
2130  {
2131  $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
2132  aoData._sRowStripe = sStripe;
2133  }
2134  }
2135 
2136  // Row callback functions - might want to manipulate the row
2137  // iRowCount and j are not currently documented. Are they at all
2138  // useful?
2139  _fnCallbackFire( oSettings, 'aoRowCallback', null,
2140  [nRow, aoData._aData, iRowCount, j] );
2141 
2142  anRows.push( nRow );
2143  iRowCount++;
2144  }
2145  }
2146  else
2147  {
2148  /* Table is empty - create a row with an empty message in it */
2149  var sZero = oLang.sZeroRecords;
2150  if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
2151  {
2152  sZero = oLang.sLoadingRecords;
2153  }
2154  else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
2155  {
2156  sZero = oLang.sEmptyTable;
2157  }
2158 
2159  anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
2160  .append( $('<td />', {
2161  'valign': 'top',
2162  'colSpan': _fnVisbleColumns( oSettings ),
2163  'class': oSettings.oClasses.sRowEmpty
2164  } ).html( sZero ) )[0];
2165  }
2166 
2167  /* Header and footer callbacks */
2168  _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
2169  _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
2170 
2171  _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
2172  _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
2173 
2174  var body = $(oSettings.nTBody);
2175 
2176  body.children().detach();
2177  body.append( $(anRows) );
2178 
2179  /* Call all required callback functions for the end of a draw */
2180  _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
2181 
2182  /* Draw is complete, sorting and filtering must be as well */
2183  oSettings.bSorted = false;
2184  oSettings.bFiltered = false;
2185  oSettings.bDrawing = false;
2186  }
2187 
2188 
2196  function _fnReDraw( settings, holdPosition )
2197  {
2198  var
2199  features = settings.oFeatures,
2200  sort = features.bSort,
2201  filter = features.bFilter;
2202 
2203  if ( sort ) {
2204  _fnSort( settings );
2205  }
2206 
2207  if ( filter ) {
2208  _fnFilterComplete( settings, settings.oPreviousSearch );
2209  }
2210  else {
2211  // No filtering, so we want to just use the display master
2212  settings.aiDisplay = settings.aiDisplayMaster.slice();
2213  }
2214 
2215  if ( holdPosition !== true ) {
2216  settings._iDisplayStart = 0;
2217  }
2218 
2219  // Let any modules know about the draw hold position state (used by
2220  // scrolling internally)
2221  settings._drawHold = holdPosition;
2222 
2223  _fnDraw( settings );
2224 
2225  settings._drawHold = false;
2226  }
2227 
2228 
2234  function _fnAddOptionsHtml ( oSettings )
2235  {
2236  var classes = oSettings.oClasses;
2237  var table = $(oSettings.nTable);
2238  var holding = $('<div/>').insertBefore( table ); // Holding element for speed
2239  var features = oSettings.oFeatures;
2240 
2241  // All DataTables are wrapped in a div
2242  var insert = $('<div/>', {
2243  id: oSettings.sTableId+'_wrapper',
2244  'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
2245  } );
2246 
2247  oSettings.nHolding = holding[0];
2248  oSettings.nTableWrapper = insert[0];
2249  oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
2250 
2251  /* Loop over the user set positioning and place the elements as needed */
2252  var aDom = oSettings.sDom.split('');
2253  var featureNode, cOption, nNewNode, cNext, sAttr, j;
2254  for ( var i=0 ; i<aDom.length ; i++ )
2255  {
2256  featureNode = null;
2257  cOption = aDom[i];
2258 
2259  if ( cOption == '<' )
2260  {
2261  /* New container div */
2262  nNewNode = $('<div/>')[0];
2263 
2264  /* Check to see if we should append an id and/or a class name to the container */
2265  cNext = aDom[i+1];
2266  if ( cNext == "'" || cNext == '"' )
2267  {
2268  sAttr = "";
2269  j = 2;
2270  while ( aDom[i+j] != cNext )
2271  {
2272  sAttr += aDom[i+j];
2273  j++;
2274  }
2275 
2276  /* Replace jQuery UI constants @todo depreciated */
2277  if ( sAttr == "H" )
2278  {
2279  sAttr = classes.sJUIHeader;
2280  }
2281  else if ( sAttr == "F" )
2282  {
2283  sAttr = classes.sJUIFooter;
2284  }
2285 
2286  /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
2287  * breaks the string into parts and applies them as needed
2288  */
2289  if ( sAttr.indexOf('.') != -1 )
2290  {
2291  var aSplit = sAttr.split('.');
2292  nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
2293  nNewNode.className = aSplit[1];
2294  }
2295  else if ( sAttr.charAt(0) == "#" )
2296  {
2297  nNewNode.id = sAttr.substr(1, sAttr.length-1);
2298  }
2299  else
2300  {
2301  nNewNode.className = sAttr;
2302  }
2303 
2304  i += j; /* Move along the position array */
2305  }
2306 
2307  insert.append( nNewNode );
2308  insert = $(nNewNode);
2309  }
2310  else if ( cOption == '>' )
2311  {
2312  /* End container div */
2313  insert = insert.parent();
2314  }
2315  // @todo Move options into their own plugins?
2316  else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
2317  {
2318  /* Length */
2319  featureNode = _fnFeatureHtmlLength( oSettings );
2320  }
2321  else if ( cOption == 'f' && features.bFilter )
2322  {
2323  /* Filter */
2324  featureNode = _fnFeatureHtmlFilter( oSettings );
2325  }
2326  else if ( cOption == 'r' && features.bProcessing )
2327  {
2328  /* pRocessing */
2329  featureNode = _fnFeatureHtmlProcessing( oSettings );
2330  }
2331  else if ( cOption == 't' )
2332  {
2333  /* Table */
2334  featureNode = _fnFeatureHtmlTable( oSettings );
2335  }
2336  else if ( cOption == 'i' && features.bInfo )
2337  {
2338  /* Info */
2339  featureNode = _fnFeatureHtmlInfo( oSettings );
2340  }
2341  else if ( cOption == 'p' && features.bPaginate )
2342  {
2343  /* Pagination */
2344  featureNode = _fnFeatureHtmlPaginate( oSettings );
2345  }
2346  else if ( DataTable.ext.feature.length !== 0 )
2347  {
2348  /* Plug-in features */
2349  var aoFeatures = DataTable.ext.feature;
2350  for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
2351  {
2352  if ( cOption == aoFeatures[k].cFeature )
2353  {
2354  featureNode = aoFeatures[k].fnInit( oSettings );
2355  break;
2356  }
2357  }
2358  }
2359 
2360  /* Add to the 2D features array */
2361  if ( featureNode )
2362  {
2363  var aanFeatures = oSettings.aanFeatures;
2364 
2365  if ( ! aanFeatures[cOption] )
2366  {
2367  aanFeatures[cOption] = [];
2368  }
2369 
2370  aanFeatures[cOption].push( featureNode );
2371  insert.append( featureNode );
2372  }
2373  }
2374 
2375  /* Built our DOM structure - replace the holding div with what we want */
2376  holding.replaceWith( insert );
2377  oSettings.nHolding = null;
2378  }
2379 
2380 
2390  function _fnDetectHeader ( aLayout, nThead )
2391  {
2392  var nTrs = $(nThead).children('tr');
2393  var nTr, nCell;
2394  var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
2395  var bUnique;
2396  var fnShiftCol = function ( a, i, j ) {
2397  var k = a[i];
2398  while ( k[j] ) {
2399  j++;
2400  }
2401  return j;
2402  };
2403 
2404  aLayout.splice( 0, aLayout.length );
2405 
2406  /* We know how many rows there are in the layout - so prep it */
2407  for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
2408  {
2409  aLayout.push( [] );
2410  }
2411 
2412  /* Calculate a layout array */
2413  for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
2414  {
2415  nTr = nTrs[i];
2416  iColumn = 0;
2417 
2418  /* For every cell in the row... */
2419  nCell = nTr.firstChild;
2420  while ( nCell ) {
2421  if ( nCell.nodeName.toUpperCase() == "TD" ||
2422  nCell.nodeName.toUpperCase() == "TH" )
2423  {
2424  /* Get the col and rowspan attributes from the DOM and sanitise them */
2425  iColspan = nCell.getAttribute('colspan') * 1;
2426  iRowspan = nCell.getAttribute('rowspan') * 1;
2427  iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
2428  iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
2429 
2430  /* There might be colspan cells already in this row, so shift our target
2431  * accordingly
2432  */
2433  iColShifted = fnShiftCol( aLayout, i, iColumn );
2434 
2435  /* Cache calculation for unique columns */
2436  bUnique = iColspan === 1 ? true : false;
2437 
2438  /* If there is col / rowspan, copy the information into the layout grid */
2439  for ( l=0 ; l<iColspan ; l++ )
2440  {
2441  for ( k=0 ; k<iRowspan ; k++ )
2442  {
2443  aLayout[i+k][iColShifted+l] = {
2444  "cell": nCell,
2445  "unique": bUnique
2446  };
2447  aLayout[i+k].nTr = nTr;
2448  }
2449  }
2450  }
2451  nCell = nCell.nextSibling;
2452  }
2453  }
2454  }
2455 
2456 
2465  function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
2466  {
2467  var aReturn = [];
2468  if ( !aLayout )
2469  {
2470  aLayout = oSettings.aoHeader;
2471  if ( nHeader )
2472  {
2473  aLayout = [];
2474  _fnDetectHeader( aLayout, nHeader );
2475  }
2476  }
2477 
2478  for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
2479  {
2480  for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
2481  {
2482  if ( aLayout[i][j].unique &&
2483  (!aReturn[j] || !oSettings.bSortCellsTop) )
2484  {
2485  aReturn[j] = aLayout[i][j].cell;
2486  }
2487  }
2488  }
2489 
2490  return aReturn;
2491  }
2492 
2502  function _fnBuildAjax( oSettings, data, fn )
2503  {
2504  // Compatibility with 1.9-, allow fnServerData and event to manipulate
2505  _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
2506 
2507  // Convert to object based for 1.10+ if using the old array scheme which can
2508  // come from server-side processing or serverParams
2509  if ( data && $.isArray(data) ) {
2510  var tmp = {};
2511  var rbracket = /(.*?)\[\]$/;
2512 
2513  $.each( data, function (key, val) {
2514  var match = val.name.match(rbracket);
2515 
2516  if ( match ) {
2517  // Support for arrays
2518  var name = match[0];
2519 
2520  if ( ! tmp[ name ] ) {
2521  tmp[ name ] = [];
2522  }
2523  tmp[ name ].push( val.value );
2524  }
2525  else {
2526  tmp[val.name] = val.value;
2527  }
2528  } );
2529  data = tmp;
2530  }
2531 
2532  var ajaxData;
2533  var ajax = oSettings.ajax;
2534  var instance = oSettings.oInstance;
2535  var callback = function ( json ) {
2536  _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
2537  fn( json );
2538  };
2539 
2540  if ( $.isPlainObject( ajax ) && ajax.data )
2541  {
2542  ajaxData = ajax.data;
2543 
2544  var newData = $.isFunction( ajaxData ) ?
2545  ajaxData( data, oSettings ) : // fn can manipulate data or return
2546  ajaxData; // an object object or array to merge
2547 
2548  // If the function returned something, use that alone
2549  data = $.isFunction( ajaxData ) && newData ?
2550  newData :
2551  $.extend( true, data, newData );
2552 
2553  // Remove the data property as we've resolved it already and don't want
2554  // jQuery to do it again (it is restored at the end of the function)
2555  delete ajax.data;
2556  }
2557 
2558  var baseAjax = {
2559  "data": data,
2560  "success": function (json) {
2561  var error = json.error || json.sError;
2562  if ( error ) {
2563  _fnLog( oSettings, 0, error );
2564  }
2565 
2566  oSettings.json = json;
2567  callback( json );
2568  },
2569  "dataType": "json",
2570  "cache": false,
2571  "type": oSettings.sServerMethod,
2572  "error": function (xhr, error, thrown) {
2573  var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
2574 
2575  if ( $.inArray( true, ret ) === -1 ) {
2576  if ( error == "parsererror" ) {
2577  _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
2578  }
2579  else if ( xhr.readyState === 4 ) {
2580  _fnLog( oSettings, 0, 'Ajax error', 7 );
2581  }
2582  }
2583 
2584  _fnProcessingDisplay( oSettings, false );
2585  }
2586  };
2587 
2588  // Store the data submitted for the API
2589  oSettings.oAjaxData = data;
2590 
2591  // Allow plug-ins and external processes to modify the data
2592  _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
2593 
2594  if ( oSettings.fnServerData )
2595  {
2596  // DataTables 1.9- compatibility
2597  oSettings.fnServerData.call( instance,
2598  oSettings.sAjaxSource,
2599  $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
2600  return { name: key, value: val };
2601  } ),
2602  callback,
2603  oSettings
2604  );
2605  }
2606  else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
2607  {
2608  // DataTables 1.9- compatibility
2609  oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
2610  url: ajax || oSettings.sAjaxSource
2611  } ) );
2612  }
2613  else if ( $.isFunction( ajax ) )
2614  {
2615  // Is a function - let the caller define what needs to be done
2616  oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
2617  }
2618  else
2619  {
2620  // Object to extend the base settings
2621  oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
2622 
2623  // Restore for next time around
2624  ajax.data = ajaxData;
2625  }
2626  }
2627 
2628 
2635  function _fnAjaxUpdate( settings )
2636  {
2637  if ( settings.bAjaxDataGet ) {
2638  settings.iDraw++;
2639  _fnProcessingDisplay( settings, true );
2640 
2641  _fnBuildAjax(
2642  settings,
2643  _fnAjaxParameters( settings ),
2644  function(json) {
2645  _fnAjaxUpdateDraw( settings, json );
2646  }
2647  );
2648 
2649  return false;
2650  }
2651  return true;
2652  }
2653 
2654 
2666  function _fnAjaxParameters( settings )
2667  {
2668  var
2669  columns = settings.aoColumns,
2670  columnCount = columns.length,
2671  features = settings.oFeatures,
2672  preSearch = settings.oPreviousSearch,
2673  preColSearch = settings.aoPreSearchCols,
2674  i, data = [], dataProp, column, columnSearch,
2675  sort = _fnSortFlatten( settings ),
2676  displayStart = settings._iDisplayStart,
2677  displayLength = features.bPaginate !== false ?
2678  settings._iDisplayLength :
2679  -1;
2680 
2681  var param = function ( name, value ) {
2682  data.push( { 'name': name, 'value': value } );
2683  };
2684 
2685  // DataTables 1.9- compatible method
2686  param( 'sEcho', settings.iDraw );
2687  param( 'iColumns', columnCount );
2688  param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
2689  param( 'iDisplayStart', displayStart );
2690  param( 'iDisplayLength', displayLength );
2691 
2692  // DataTables 1.10+ method
2693  var d = {
2694  draw: settings.iDraw,
2695  columns: [],
2696  order: [],
2697  start: displayStart,
2698  length: displayLength,
2699  search: {
2700  value: preSearch.sSearch,
2701  regex: preSearch.bRegex
2702  }
2703  };
2704 
2705  for ( i=0 ; i<columnCount ; i++ ) {
2706  column = columns[i];
2707  columnSearch = preColSearch[i];
2708  dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
2709 
2710  d.columns.push( {
2711  data: dataProp,
2712  name: column.sName,
2713  searchable: column.bSearchable,
2714  orderable: column.bSortable,
2715  search: {
2716  value: columnSearch.sSearch,
2717  regex: columnSearch.bRegex
2718  }
2719  } );
2720 
2721  param( "mDataProp_"+i, dataProp );
2722 
2723  if ( features.bFilter ) {
2724  param( 'sSearch_'+i, columnSearch.sSearch );
2725  param( 'bRegex_'+i, columnSearch.bRegex );
2726  param( 'bSearchable_'+i, column.bSearchable );
2727  }
2728 
2729  if ( features.bSort ) {
2730  param( 'bSortable_'+i, column.bSortable );
2731  }
2732  }
2733 
2734  if ( features.bFilter ) {
2735  param( 'sSearch', preSearch.sSearch );
2736  param( 'bRegex', preSearch.bRegex );
2737  }
2738 
2739  if ( features.bSort ) {
2740  $.each( sort, function ( i, val ) {
2741  d.order.push( { column: val.col, dir: val.dir } );
2742 
2743  param( 'iSortCol_'+i, val.col );
2744  param( 'sSortDir_'+i, val.dir );
2745  } );
2746 
2747  param( 'iSortingCols', sort.length );
2748  }
2749 
2750  // If the legacy.ajax parameter is null, then we automatically decide which
2751  // form to use, based on sAjaxSource
2752  var legacy = DataTable.ext.legacy.ajax;
2753  if ( legacy === null ) {
2754  return settings.sAjaxSource ? data : d;
2755  }
2756 
2757  // Otherwise, if legacy has been specified then we use that to decide on the
2758  // form
2759  return legacy ? data : d;
2760  }
2761 
2762 
2774  function _fnAjaxUpdateDraw ( settings, json )
2775  {
2776  // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
2777  // Support both
2778  var compat = function ( old, modern ) {
2779  return json[old] !== undefined ? json[old] : json[modern];
2780  };
2781 
2782  var data = _fnAjaxDataSrc( settings, json );
2783  var draw = compat( 'sEcho', 'draw' );
2784  var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
2785  var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
2786 
2787  if ( draw ) {
2788  // Protect against out of sequence returns
2789  if ( draw*1 < settings.iDraw ) {
2790  return;
2791  }
2792  settings.iDraw = draw * 1;
2793  }
2794 
2795  _fnClearTable( settings );
2796  settings._iRecordsTotal = parseInt(recordsTotal, 10);
2797  settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
2798 
2799  for ( var i=0, ien=data.length ; i<ien ; i++ ) {
2800  _fnAddData( settings, data[i] );
2801  }
2802  settings.aiDisplay = settings.aiDisplayMaster.slice();
2803 
2804  settings.bAjaxDataGet = false;
2805  _fnDraw( settings );
2806 
2807  if ( ! settings._bInitComplete ) {
2808  _fnInitComplete( settings, json );
2809  }
2810 
2811  settings.bAjaxDataGet = true;
2812  _fnProcessingDisplay( settings, false );
2813  }
2814 
2815 
2824  function _fnAjaxDataSrc ( oSettings, json )
2825  {
2826  var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
2827  oSettings.ajax.dataSrc :
2828  oSettings.sAjaxDataProp; // Compatibility with 1.9-.
2829 
2830  // Compatibility with 1.9-. In order to read from aaData, check if the
2831  // default has been changed, if not, check for aaData
2832  if ( dataSrc === 'data' ) {
2833  return json.aaData || json[dataSrc];
2834  }
2835 
2836  return dataSrc !== "" ?
2837  _fnGetObjectDataFn( dataSrc )( json ) :
2838  json;
2839  }
2840 
2847  function _fnFeatureHtmlFilter ( settings )
2848  {
2849  var classes = settings.oClasses;
2850  var tableId = settings.sTableId;
2851  var language = settings.oLanguage;
2852  var previousSearch = settings.oPreviousSearch;
2853  var features = settings.aanFeatures;
2854  var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
2855 
2856  var str = language.sSearch;
2857  str = str.match(/_INPUT_/) ?
2858  str.replace('_INPUT_', input) :
2859  str+input;
2860 
2861  var filter = $('<div/>', {
2862  'id': ! features.f ? tableId+'_filter' : null,
2863  'class': classes.sFilter
2864  } )
2865  .append( $('<label/>' ).append( str ) );
2866 
2867  var searchFn = function() {
2868  /* Update all other filter input elements for the new display */
2869  var n = features.f;
2870  var val = !this.value ? "" : this.value; // mental IE8 fix :-(
2871 
2872  /* Now do the filter */
2873  if ( val != previousSearch.sSearch ) {
2874  _fnFilterComplete( settings, {
2875  "sSearch": val,
2876  "bRegex": previousSearch.bRegex,
2877  "bSmart": previousSearch.bSmart ,
2878  "bCaseInsensitive": previousSearch.bCaseInsensitive
2879  } );
2880 
2881  // Need to redraw, without resorting
2882  settings._iDisplayStart = 0;
2883  _fnDraw( settings );
2884  }
2885  };
2886 
2887  var searchDelay = settings.searchDelay !== null ?
2888  settings.searchDelay :
2889  _fnDataSource( settings ) === 'ssp' ?
2890  400 :
2891  0;
2892 
2893  var jqFilter = $('input', filter)
2894  .val( previousSearch.sSearch )
2895  .attr( 'placeholder', language.sSearchPlaceholder )
2896  .bind(
2897  'keyup.DT search.DT input.DT paste.DT cut.DT',
2898  searchDelay ?
2899  _fnThrottle( searchFn, searchDelay ) :
2900  searchFn
2901  )
2902  .bind( 'keypress.DT', function(e) {
2903  /* Prevent form submission */
2904  if ( e.keyCode == 13 ) {
2905  return false;
2906  }
2907  } )
2908  .attr('aria-controls', tableId);
2909 
2910  // Update the input elements whenever the table is filtered
2911  $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
2912  if ( settings === s ) {
2913  // IE9 throws an 'unknown error' if document.activeElement is used
2914  // inside an iframe or frame...
2915  try {
2916  if ( jqFilter[0] !== document.activeElement ) {
2917  jqFilter.val( previousSearch.sSearch );
2918  }
2919  }
2920  catch ( e ) {}
2921  }
2922  } );
2923 
2924  return filter[0];
2925  }
2926 
2927 
2935  function _fnFilterComplete ( oSettings, oInput, iForce )
2936  {
2937  var oPrevSearch = oSettings.oPreviousSearch;
2938  var aoPrevSearch = oSettings.aoPreSearchCols;
2939  var fnSaveFilter = function ( oFilter ) {
2940  /* Save the filtering values */
2941  oPrevSearch.sSearch = oFilter.sSearch;
2942  oPrevSearch.bRegex = oFilter.bRegex;
2943  oPrevSearch.bSmart = oFilter.bSmart;
2944  oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
2945  };
2946  var fnRegex = function ( o ) {
2947  // Backwards compatibility with the bEscapeRegex option
2948  return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
2949  };
2950 
2951  // Resolve any column types that are unknown due to addition or invalidation
2952  // @todo As per sort - can this be moved into an event handler?
2953  _fnColumnTypes( oSettings );
2954 
2955  /* In server-side processing all filtering is done by the server, so no point hanging around here */
2956  if ( _fnDataSource( oSettings ) != 'ssp' )
2957  {
2958  /* Global filter */
2959  _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
2960  fnSaveFilter( oInput );
2961 
2962  /* Now do the individual column filter */
2963  for ( var i=0 ; i<aoPrevSearch.length ; i++ )
2964  {
2965  _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
2966  aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
2967  }
2968 
2969  /* Custom filtering */
2970  _fnFilterCustom( oSettings );
2971  }
2972  else
2973  {
2974  fnSaveFilter( oInput );
2975  }
2976 
2977  /* Tell the draw function we have been filtering */
2978  oSettings.bFiltered = true;
2979  _fnCallbackFire( oSettings, null, 'search', [oSettings] );
2980  }
2981 
2982 
2988  function _fnFilterCustom( settings )
2989  {
2990  var filters = DataTable.ext.search;
2991  var displayRows = settings.aiDisplay;
2992  var row, rowIdx;
2993 
2994  for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
2995  var rows = [];
2996 
2997  // Loop over each row and see if it should be included
2998  for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
2999  rowIdx = displayRows[ j ];
3000  row = settings.aoData[ rowIdx ];
3001 
3002  if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
3003  rows.push( rowIdx );
3004  }
3005  }
3006 
3007  // So the array reference doesn't break set the results into the
3008  // existing array
3009  displayRows.length = 0;
3010  $.merge( displayRows, rows );
3011  }
3012  }
3013 
3014 
3025  function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
3026  {
3027  if ( searchStr === '' ) {
3028  return;
3029  }
3030 
3031  var data;
3032  var display = settings.aiDisplay;
3033  var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
3034 
3035  for ( var i=display.length-1 ; i>=0 ; i-- ) {
3036  data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
3037 
3038  if ( ! rpSearch.test( data ) ) {
3039  display.splice( i, 1 );
3040  }
3041  }
3042  }
3043 
3044 
3055  function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
3056  {
3057  var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
3058  var prevSearch = settings.oPreviousSearch.sSearch;
3059  var displayMaster = settings.aiDisplayMaster;
3060  var display, invalidated, i;
3061 
3062  // Need to take account of custom filtering functions - always filter
3063  if ( DataTable.ext.search.length !== 0 ) {
3064  force = true;
3065  }
3066 
3067  // Check if any of the rows were invalidated
3068  invalidated = _fnFilterData( settings );
3069 
3070  // If the input is blank - we just want the full data set
3071  if ( input.length <= 0 ) {
3072  settings.aiDisplay = displayMaster.slice();
3073  }
3074  else {
3075  // New search - start from the master array
3076  if ( invalidated ||
3077  force ||
3078  prevSearch.length > input.length ||
3079  input.indexOf(prevSearch) !== 0 ||
3080  settings.bSorted // On resort, the display master needs to be
3081  // re-filtered since indexes will have changed
3082  ) {
3083  settings.aiDisplay = displayMaster.slice();
3084  }
3085 
3086  // Search the display array
3087  display = settings.aiDisplay;
3088 
3089  for ( i=display.length-1 ; i>=0 ; i-- ) {
3090  if ( ! rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
3091  display.splice( i, 1 );
3092  }
3093  }
3094  }
3095  }
3096 
3097 
3107  function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
3108  {
3109  search = regex ?
3110  search :
3111  _fnEscapeRegex( search );
3112 
3113  if ( smart ) {
3114  /* For smart filtering we want to allow the search to work regardless of
3115  * word order. We also want double quoted text to be preserved, so word
3116  * order is important - a la google. So this is what we want to
3117  * generate:
3118  *
3119  * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
3120  */
3121  var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
3122  if ( word.charAt(0) === '"' ) {
3123  var m = word.match( /^"(.*)"$/ );
3124  word = m ? m[1] : word;
3125  }
3126 
3127  return word.replace('"', '');
3128  } );
3129 
3130  search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
3131  }
3132 
3133  return new RegExp( search, caseInsensitive ? 'i' : '' );
3134  }
3135 
3136 
3143  function _fnEscapeRegex ( sVal )
3144  {
3145  return sVal.replace( _re_escape_regex, '\\$1' );
3146  }
3147 
3148 
3149 
3150  var __filter_div = $('<div>')[0];
3151  var __filter_div_textContent = __filter_div.textContent !== undefined;
3152 
3153  // Update the filtering data for each row if needed (by invalidation or first run)
3154  function _fnFilterData ( settings )
3155  {
3156  var columns = settings.aoColumns;
3157  var column;
3158  var i, j, ien, jen, filterData, cellData, row;
3159  var fomatters = DataTable.ext.type.search;
3160  var wasInvalidated = false;
3161 
3162  for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
3163  row = settings.aoData[i];
3164 
3165  if ( ! row._aFilterData ) {
3166  filterData = [];
3167 
3168  for ( j=0, jen=columns.length ; j<jen ; j++ ) {
3169  column = columns[j];
3170 
3171  if ( column.bSearchable ) {
3172  cellData = _fnGetCellData( settings, i, j, 'filter' );
3173 
3174  if ( fomatters[ column.sType ] ) {
3175  cellData = fomatters[ column.sType ]( cellData );
3176  }
3177 
3178  // Search in DataTables 1.10 is string based. In 1.11 this
3179  // should be altered to also allow strict type checking.
3180  if ( cellData === null ) {
3181  cellData = '';
3182  }
3183 
3184  if ( typeof cellData !== 'string' && cellData.toString ) {
3185  cellData = cellData.toString();
3186  }
3187  }
3188  else {
3189  cellData = '';
3190  }
3191 
3192  // If it looks like there is an HTML entity in the string,
3193  // attempt to decode it so sorting works as expected. Note that
3194  // we could use a single line of jQuery to do this, but the DOM
3195  // method used here is much faster http://jsperf.com/html-decode
3196  if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
3197  __filter_div.innerHTML = cellData;
3198  cellData = __filter_div_textContent ?
3199  __filter_div.textContent :
3200  __filter_div.innerText;
3201  }
3202 
3203  if ( cellData.replace ) {
3204  cellData = cellData.replace(/[\r\n]/g, '');
3205  }
3206 
3207  filterData.push( cellData );
3208  }
3209 
3210  row._aFilterData = filterData;
3211  row._sFilterRow = filterData.join(' ');
3212  wasInvalidated = true;
3213  }
3214  }
3215 
3216  return wasInvalidated;
3217  }
3218 
3219 
3227  function _fnSearchToCamel ( obj )
3228  {
3229  return {
3230  search: obj.sSearch,
3231  smart: obj.bSmart,
3232  regex: obj.bRegex,
3233  caseInsensitive: obj.bCaseInsensitive
3234  };
3235  }
3236 
3237 
3238 
3246  function _fnSearchToHung ( obj )
3247  {
3248  return {
3249  sSearch: obj.search,
3250  bSmart: obj.smart,
3251  bRegex: obj.regex,
3252  bCaseInsensitive: obj.caseInsensitive
3253  };
3254  }
3255 
3262  function _fnFeatureHtmlInfo ( settings )
3263  {
3264  var
3265  tid = settings.sTableId,
3266  nodes = settings.aanFeatures.i,
3267  n = $('<div/>', {
3268  'class': settings.oClasses.sInfo,
3269  'id': ! nodes ? tid+'_info' : null
3270  } );
3271 
3272  if ( ! nodes ) {
3273  // Update display on each draw
3274  settings.aoDrawCallback.push( {
3275  "fn": _fnUpdateInfo,
3276  "sName": "information"
3277  } );
3278 
3279  n
3280  .attr( 'role', 'status' )
3281  .attr( 'aria-live', 'polite' );
3282 
3283  // Table is described by our info div
3284  $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
3285  }
3286 
3287  return n[0];
3288  }
3289 
3290 
3296  function _fnUpdateInfo ( settings )
3297  {
3298  /* Show information about the table */
3299  var nodes = settings.aanFeatures.i;
3300  if ( nodes.length === 0 ) {
3301  return;
3302  }
3303 
3304  var
3305  lang = settings.oLanguage,
3306  start = settings._iDisplayStart+1,
3307  end = settings.fnDisplayEnd(),
3308  max = settings.fnRecordsTotal(),
3309  total = settings.fnRecordsDisplay(),
3310  out = total ?
3311  lang.sInfo :
3312  lang.sInfoEmpty;
3313 
3314  if ( total !== max ) {
3315  /* Record set after filtering */
3316  out += ' ' + lang.sInfoFiltered;
3317  }
3318 
3319  // Convert the macros
3320  out += lang.sInfoPostFix;
3321  out = _fnInfoMacros( settings, out );
3322 
3323  var callback = lang.fnInfoCallback;
3324  if ( callback !== null ) {
3325  out = callback.call( settings.oInstance,
3326  settings, start, end, max, total, out
3327  );
3328  }
3329 
3330  $(nodes).html( out );
3331  }
3332 
3333 
3334  function _fnInfoMacros ( settings, str )
3335  {
3336  // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
3337  // internally
3338  var
3339  formatter = settings.fnFormatNumber,
3340  start = settings._iDisplayStart+1,
3341  len = settings._iDisplayLength,
3342  vis = settings.fnRecordsDisplay(),
3343  all = len === -1;
3344 
3345  return str.
3346  replace(/_START_/g, formatter.call( settings, start ) ).
3347  replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
3348  replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
3349  replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
3350  replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
3351  replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
3352  }
3353 
3354 
3355 
3361  function _fnInitialise ( settings )
3362  {
3363  var i, iLen, iAjaxStart=settings.iInitDisplayStart;
3364  var columns = settings.aoColumns, column;
3365  var features = settings.oFeatures;
3366  var deferLoading = settings.bDeferLoading; // value modified by the draw
3367 
3368  /* Ensure that the table data is fully initialised */
3369  if ( ! settings.bInitialised ) {
3370  setTimeout( function(){ _fnInitialise( settings ); }, 200 );
3371  return;
3372  }
3373 
3374  /* Show the display HTML options */
3375  _fnAddOptionsHtml( settings );
3376 
3377  /* Build and draw the header / footer for the table */
3378  _fnBuildHead( settings );
3379  _fnDrawHead( settings, settings.aoHeader );
3380  _fnDrawHead( settings, settings.aoFooter );
3381 
3382  /* Okay to show that something is going on now */
3383  _fnProcessingDisplay( settings, true );
3384 
3385  /* Calculate sizes for columns */
3386  if ( features.bAutoWidth ) {
3387  _fnCalculateColumnWidths( settings );
3388  }
3389 
3390  for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
3391  column = columns[i];
3392 
3393  if ( column.sWidth ) {
3394  column.nTh.style.width = _fnStringToCss( column.sWidth );
3395  }
3396  }
3397 
3398  _fnCallbackFire( settings, null, 'preInit', [settings] );
3399 
3400  // If there is default sorting required - let's do it. The sort function
3401  // will do the drawing for us. Otherwise we draw the table regardless of the
3402  // Ajax source - this allows the table to look initialised for Ajax sourcing
3403  // data (show 'loading' message possibly)
3404  _fnReDraw( settings );
3405 
3406  // Server-side processing init complete is done by _fnAjaxUpdateDraw
3407  var dataSrc = _fnDataSource( settings );
3408  if ( dataSrc != 'ssp' || deferLoading ) {
3409  // if there is an ajax source load the data
3410  if ( dataSrc == 'ajax' ) {
3411  _fnBuildAjax( settings, [], function(json) {
3412  var aData = _fnAjaxDataSrc( settings, json );
3413 
3414  // Got the data - add it to the table
3415  for ( i=0 ; i<aData.length ; i++ ) {
3416  _fnAddData( settings, aData[i] );
3417  }
3418 
3419  // Reset the init display for cookie saving. We've already done
3420  // a filter, and therefore cleared it before. So we need to make
3421  // it appear 'fresh'
3422  settings.iInitDisplayStart = iAjaxStart;
3423 
3424  _fnReDraw( settings );
3425 
3426  _fnProcessingDisplay( settings, false );
3427  _fnInitComplete( settings, json );
3428  }, settings );
3429  }
3430  else {
3431  _fnProcessingDisplay( settings, false );
3432  _fnInitComplete( settings );
3433  }
3434  }
3435  }
3436 
3437 
3445  function _fnInitComplete ( settings, json )
3446  {
3447  settings._bInitComplete = true;
3448 
3449  // When data was added after the initialisation (data or Ajax) we need to
3450  // calculate the column sizing
3451  if ( json || settings.oInit.aaData ) {
3452  _fnAdjustColumnSizing( settings );
3453  }
3454 
3455  _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
3456  _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
3457  }
3458 
3459 
3460  function _fnLengthChange ( settings, val )
3461  {
3462  var len = parseInt( val, 10 );
3463  settings._iDisplayLength = len;
3464 
3465  _fnLengthOverflow( settings );
3466 
3467  // Fire length change event
3468  _fnCallbackFire( settings, null, 'length', [settings, len] );
3469  }
3470 
3471 
3478  function _fnFeatureHtmlLength ( settings )
3479  {
3480  var
3481  classes = settings.oClasses,
3482  tableId = settings.sTableId,
3483  menu = settings.aLengthMenu,
3484  d2 = $.isArray( menu[0] ),
3485  lengths = d2 ? menu[0] : menu,
3486  language = d2 ? menu[1] : menu;
3487 
3488  var select = $('<select/>', {
3489  'name': tableId+'_length',
3490  'aria-controls': tableId,
3491  'class': classes.sLengthSelect
3492  } );
3493 
3494  for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
3495  select[0][ i ] = new Option( language[i], lengths[i] );
3496  }
3497 
3498  var div = $('<div><label/></div>').addClass( classes.sLength );
3499  if ( ! settings.aanFeatures.l ) {
3500  div[0].id = tableId+'_length';
3501  }
3502 
3503  div.children().append(
3504  settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
3505  );
3506 
3507  // Can't use `select` variable as user might provide their own and the
3508  // reference is broken by the use of outerHTML
3509  $('select', div)
3510  .val( settings._iDisplayLength )
3511  .bind( 'change.DT', function(e) {
3512  _fnLengthChange( settings, $(this).val() );
3513  _fnDraw( settings );
3514  } );
3515 
3516  // Update node value whenever anything changes the table's length
3517  $(settings.nTable).bind( 'length.dt.DT', function (e, s, len) {
3518  if ( settings === s ) {
3519  $('select', div).val( len );
3520  }
3521  } );
3522 
3523  return div[0];
3524  }
3525 
3526 
3527 
3528  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3529  * Note that most of the paging logic is done in
3530  * DataTable.ext.pager
3531  */
3532 
3539  function _fnFeatureHtmlPaginate ( settings )
3540  {
3541  var
3542  type = settings.sPaginationType,
3543  plugin = DataTable.ext.pager[ type ],
3544  modern = typeof plugin === 'function',
3545  redraw = function( settings ) {
3546  _fnDraw( settings );
3547  },
3548  node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
3549  features = settings.aanFeatures;
3550 
3551  if ( ! modern ) {
3552  plugin.fnInit( settings, node, redraw );
3553  }
3554 
3555  /* Add a draw callback for the pagination on first instance, to update the paging display */
3556  if ( ! features.p )
3557  {
3558  node.id = settings.sTableId+'_paginate';
3559 
3560  settings.aoDrawCallback.push( {
3561  "fn": function( settings ) {
3562  if ( modern ) {
3563  var
3564  start = settings._iDisplayStart,
3565  len = settings._iDisplayLength,
3566  visRecords = settings.fnRecordsDisplay(),
3567  all = len === -1,
3568  page = all ? 0 : Math.ceil( start / len ),
3569  pages = all ? 1 : Math.ceil( visRecords / len ),
3570  buttons = plugin(page, pages),
3571  i, ien;
3572 
3573  for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
3574  _fnRenderer( settings, 'pageButton' )(
3575  settings, features.p[i], i, buttons, page, pages
3576  );
3577  }
3578  }
3579  else {
3580  plugin.fnUpdate( settings, redraw );
3581  }
3582  },
3583  "sName": "pagination"
3584  } );
3585  }
3586 
3587  return node;
3588  }
3589 
3590 
3600  function _fnPageChange ( settings, action, redraw )
3601  {
3602  var
3603  start = settings._iDisplayStart,
3604  len = settings._iDisplayLength,
3605  records = settings.fnRecordsDisplay();
3606 
3607  if ( records === 0 || len === -1 )
3608  {
3609  start = 0;
3610  }
3611  else if ( typeof action === "number" )
3612  {
3613  start = action * len;
3614 
3615  if ( start > records )
3616  {
3617  start = 0;
3618  }
3619  }
3620  else if ( action == "first" )
3621  {
3622  start = 0;
3623  }
3624  else if ( action == "previous" )
3625  {
3626  start = len >= 0 ?
3627  start - len :
3628  0;
3629 
3630  if ( start < 0 )
3631  {
3632  start = 0;
3633  }
3634  }
3635  else if ( action == "next" )
3636  {
3637  if ( start + len < records )
3638  {
3639  start += len;
3640  }
3641  }
3642  else if ( action == "last" )
3643  {
3644  start = Math.floor( (records-1) / len) * len;
3645  }
3646  else
3647  {
3648  _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
3649  }
3650 
3651  var changed = settings._iDisplayStart !== start;
3652  settings._iDisplayStart = start;
3653 
3654  if ( changed ) {
3655  _fnCallbackFire( settings, null, 'page', [settings] );
3656 
3657  if ( redraw ) {
3658  _fnDraw( settings );
3659  }
3660  }
3661 
3662  return changed;
3663  }
3664 
3665 
3666 
3673  function _fnFeatureHtmlProcessing ( settings )
3674  {
3675  return $('<div/>', {
3676  'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
3677  'class': settings.oClasses.sProcessing
3678  } )
3679  .html( settings.oLanguage.sProcessing )
3680  .insertBefore( settings.nTable )[0];
3681  }
3682 
3683 
3690  function _fnProcessingDisplay ( settings, show )
3691  {
3692  if ( settings.oFeatures.bProcessing ) {
3693  $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
3694  }
3695 
3696  _fnCallbackFire( settings, null, 'processing', [settings, show] );
3697  }
3698 
3705  function _fnFeatureHtmlTable ( settings )
3706  {
3707  var table = $(settings.nTable);
3708 
3709  // Add the ARIA grid role to the table
3710  table.attr( 'role', 'grid' );
3711 
3712  // Scrolling from here on in
3713  var scroll = settings.oScroll;
3714 
3715  if ( scroll.sX === '' && scroll.sY === '' ) {
3716  return settings.nTable;
3717  }
3718 
3719  var scrollX = scroll.sX;
3720  var scrollY = scroll.sY;
3721  var classes = settings.oClasses;
3722  var caption = table.children('caption');
3723  var captionSide = caption.length ? caption[0]._captionSide : null;
3724  var headerClone = $( table[0].cloneNode(false) );
3725  var footerClone = $( table[0].cloneNode(false) );
3726  var footer = table.children('tfoot');
3727  var _div = '<div/>';
3728  var size = function ( s ) {
3729  return !s ? null : _fnStringToCss( s );
3730  };
3731 
3732  if ( ! footer.length ) {
3733  footer = null;
3734  }
3735 
3736  /*
3737  * The HTML structure that we want to generate in this function is:
3738  * div - scroller
3739  * div - scroll head
3740  * div - scroll head inner
3741  * table - scroll head table
3742  * thead - thead
3743  * div - scroll body
3744  * table - table (master table)
3745  * thead - thead clone for sizing
3746  * tbody - tbody
3747  * div - scroll foot
3748  * div - scroll foot inner
3749  * table - scroll foot table
3750  * tfoot - tfoot
3751  */
3752  var scroller = $( _div, { 'class': classes.sScrollWrapper } )
3753  .append(
3754  $(_div, { 'class': classes.sScrollHead } )
3755  .css( {
3756  overflow: 'hidden',
3757  position: 'relative',
3758  border: 0,
3759  width: scrollX ? size(scrollX) : '100%'
3760  } )
3761  .append(
3762  $(_div, { 'class': classes.sScrollHeadInner } )
3763  .css( {
3764  'box-sizing': 'content-box',
3765  width: scroll.sXInner || '100%'
3766  } )
3767  .append(
3768  headerClone
3769  .removeAttr('id')
3770  .css( 'margin-left', 0 )
3771  .append( captionSide === 'top' ? caption : null )
3772  .append(
3773  table.children('thead')
3774  )
3775  )
3776  )
3777  )
3778  .append(
3779  $(_div, { 'class': classes.sScrollBody } )
3780  .css( {
3781  position: 'relative',
3782  overflow: 'auto',
3783  width: size( scrollX )
3784  } )
3785  .append( table )
3786  );
3787 
3788  if ( footer ) {
3789  scroller.append(
3790  $(_div, { 'class': classes.sScrollFoot } )
3791  .css( {
3792  overflow: 'hidden',
3793  border: 0,
3794  width: scrollX ? size(scrollX) : '100%'
3795  } )
3796  .append(
3797  $(_div, { 'class': classes.sScrollFootInner } )
3798  .append(
3799  footerClone
3800  .removeAttr('id')
3801  .css( 'margin-left', 0 )
3802  .append( captionSide === 'bottom' ? caption : null )
3803  .append(
3804  table.children('tfoot')
3805  )
3806  )
3807  )
3808  );
3809  }
3810 
3811  var children = scroller.children();
3812  var scrollHead = children[0];
3813  var scrollBody = children[1];
3814  var scrollFoot = footer ? children[2] : null;
3815 
3816  // When the body is scrolled, then we also want to scroll the headers
3817  if ( scrollX ) {
3818  $(scrollBody).on( 'scroll.DT', function (e) {
3819  var scrollLeft = this.scrollLeft;
3820 
3821  scrollHead.scrollLeft = scrollLeft;
3822 
3823  if ( footer ) {
3824  scrollFoot.scrollLeft = scrollLeft;
3825  }
3826  } );
3827  }
3828 
3829  $(scrollBody).css(
3830  scrollY && scroll.bCollapse ? 'max-height' : 'height',
3831  scrollY
3832  );
3833 
3834  settings.nScrollHead = scrollHead;
3835  settings.nScrollBody = scrollBody;
3836  settings.nScrollFoot = scrollFoot;
3837 
3838  // On redraw - align columns
3839  settings.aoDrawCallback.push( {
3840  "fn": _fnScrollDraw,
3841  "sName": "scrolling"
3842  } );
3843 
3844  return scroller[0];
3845  }
3846 
3847 
3848 
3863  function _fnScrollDraw ( settings )
3864  {
3865  // Given that this is such a monster function, a lot of variables are use
3866  // to try and keep the minimised size as small as possible
3867  var
3868  scroll = settings.oScroll,
3869  scrollX = scroll.sX,
3870  scrollXInner = scroll.sXInner,
3871  scrollY = scroll.sY,
3872  barWidth = scroll.iBarWidth,
3873  divHeader = $(settings.nScrollHead),
3874  divHeaderStyle = divHeader[0].style,
3875  divHeaderInner = divHeader.children('div'),
3876  divHeaderInnerStyle = divHeaderInner[0].style,
3877  divHeaderTable = divHeaderInner.children('table'),
3878  divBodyEl = settings.nScrollBody,
3879  divBody = $(divBodyEl),
3880  divBodyStyle = divBodyEl.style,
3881  divFooter = $(settings.nScrollFoot),
3882  divFooterInner = divFooter.children('div'),
3883  divFooterTable = divFooterInner.children('table'),
3884  header = $(settings.nTHead),
3885  table = $(settings.nTable),
3886  tableEl = table[0],
3887  tableStyle = tableEl.style,
3888  footer = settings.nTFoot ? $(settings.nTFoot) : null,
3889  browser = settings.oBrowser,
3890  ie67 = browser.bScrollOversize,
3891  dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
3892  headerTrgEls, footerTrgEls,
3893  headerSrcEls, footerSrcEls,
3894  headerCopy, footerCopy,
3895  headerWidths=[], footerWidths=[],
3896  headerContent=[], footerContent=[],
3897  idx, correction, sanityWidth,
3898  zeroOut = function(nSizer) {
3899  var style = nSizer.style;
3900  style.paddingTop = "0";
3901  style.paddingBottom = "0";
3902  style.borderTopWidth = "0";
3903  style.borderBottomWidth = "0";
3904  style.height = 0;
3905  };
3906 
3907  // If the scrollbar visibility has changed from the last draw, we need to
3908  // adjust the column sizes as the table width will have changed to account
3909  // for the scrollbar
3910  var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
3911 
3912  if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
3913  settings.scrollBarVis = scrollBarVis;
3914  _fnAdjustColumnSizing( settings );
3915  return; // adjust column sizing will call this function again
3916  }
3917  else {
3918  settings.scrollBarVis = scrollBarVis;
3919  }
3920 
3921  /*
3922  * 1. Re-create the table inside the scrolling div
3923  */
3924 
3925  // Remove the old minimised thead and tfoot elements in the inner table
3926  table.children('thead, tfoot').remove();
3927 
3928  if ( footer ) {
3929  footerCopy = footer.clone().prependTo( table );
3930  footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
3931  footerSrcEls = footerCopy.find('tr');
3932  }
3933 
3934  // Clone the current header and footer elements and then place it into the inner table
3935  headerCopy = header.clone().prependTo( table );
3936  headerTrgEls = header.find('tr'); // original header is in its own table
3937  headerSrcEls = headerCopy.find('tr');
3938  headerCopy.find('th, td').removeAttr('tabindex');
3939 
3940 
3941  /*
3942  * 2. Take live measurements from the DOM - do not alter the DOM itself!
3943  */
3944 
3945  // Remove old sizing and apply the calculated column widths
3946  // Get the unique column headers in the newly created (cloned) header. We want to apply the
3947  // calculated sizes to this header
3948  if ( ! scrollX )
3949  {
3950  divBodyStyle.width = '100%';
3951  divHeader[0].style.width = '100%';
3952  }
3953 
3954  $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
3955  idx = _fnVisibleToColumnIndex( settings, i );
3956  el.style.width = settings.aoColumns[idx].sWidth;
3957  } );
3958 
3959  if ( footer ) {
3960  _fnApplyToChildren( function(n) {
3961  n.style.width = "";
3962  }, footerSrcEls );
3963  }
3964 
3965  // Size the table as a whole
3966  sanityWidth = table.outerWidth();
3967  if ( scrollX === "" ) {
3968  // No x scrolling
3969  tableStyle.width = "100%";
3970 
3971  // IE7 will make the width of the table when 100% include the scrollbar
3972  // - which is shouldn't. When there is a scrollbar we need to take this
3973  // into account.
3974  if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
3975  divBody.css('overflow-y') == "scroll")
3976  ) {
3977  tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
3978  }
3979 
3980  // Recalculate the sanity width
3981  sanityWidth = table.outerWidth();
3982  }
3983  else if ( scrollXInner !== "" ) {
3984  // legacy x scroll inner has been given - use it
3985  tableStyle.width = _fnStringToCss(scrollXInner);
3986 
3987  // Recalculate the sanity width
3988  sanityWidth = table.outerWidth();
3989  }
3990 
3991  // Hidden header should have zero height, so remove padding and borders. Then
3992  // set the width based on the real headers
3993 
3994  // Apply all styles in one pass
3995  _fnApplyToChildren( zeroOut, headerSrcEls );
3996 
3997  // Read all widths in next pass
3998  _fnApplyToChildren( function(nSizer) {
3999  headerContent.push( nSizer.innerHTML );
4000  headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
4001  }, headerSrcEls );
4002 
4003  // Apply all widths in final pass
4004  _fnApplyToChildren( function(nToSize, i) {
4005  // Only apply widths to the DataTables detected header cells - this
4006  // prevents complex headers from having contradictory sizes applied
4007  if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) {
4008  nToSize.style.width = headerWidths[i];
4009  }
4010  }, headerTrgEls );
4011 
4012  $(headerSrcEls).height(0);
4013 
4014  /* Same again with the footer if we have one */
4015  if ( footer )
4016  {
4017  _fnApplyToChildren( zeroOut, footerSrcEls );
4018 
4019  _fnApplyToChildren( function(nSizer) {
4020  footerContent.push( nSizer.innerHTML );
4021  footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
4022  }, footerSrcEls );
4023 
4024  _fnApplyToChildren( function(nToSize, i) {
4025  nToSize.style.width = footerWidths[i];
4026  }, footerTrgEls );
4027 
4028  $(footerSrcEls).height(0);
4029  }
4030 
4031 
4032  /*
4033  * 3. Apply the measurements
4034  */
4035 
4036  // "Hide" the header and footer that we used for the sizing. We need to keep
4037  // the content of the cell so that the width applied to the header and body
4038  // both match, but we want to hide it completely. We want to also fix their
4039  // width to what they currently are
4040  _fnApplyToChildren( function(nSizer, i) {
4041  nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+headerContent[i]+'</div>';
4042  nSizer.style.width = headerWidths[i];
4043  }, headerSrcEls );
4044 
4045  if ( footer )
4046  {
4047  _fnApplyToChildren( function(nSizer, i) {
4048  nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+footerContent[i]+'</div>';
4049  nSizer.style.width = footerWidths[i];
4050  }, footerSrcEls );
4051  }
4052 
4053  // Sanity check that the table is of a sensible width. If not then we are going to get
4054  // misalignment - try to prevent this by not allowing the table to shrink below its min width
4055  if ( table.outerWidth() < sanityWidth )
4056  {
4057  // The min width depends upon if we have a vertical scrollbar visible or not */
4058  correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
4059  divBody.css('overflow-y') == "scroll")) ?
4060  sanityWidth+barWidth :
4061  sanityWidth;
4062 
4063  // IE6/7 are a law unto themselves...
4064  if ( ie67 && (divBodyEl.scrollHeight >
4065  divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
4066  ) {
4067  tableStyle.width = _fnStringToCss( correction-barWidth );
4068  }
4069 
4070  // And give the user a warning that we've stopped the table getting too small
4071  if ( scrollX === "" || scrollXInner !== "" ) {
4072  _fnLog( settings, 1, 'Possible column misalignment', 6 );
4073  }
4074  }
4075  else
4076  {
4077  correction = '100%';
4078  }
4079 
4080  // Apply to the container elements
4081  divBodyStyle.width = _fnStringToCss( correction );
4082  divHeaderStyle.width = _fnStringToCss( correction );
4083 
4084  if ( footer ) {
4085  settings.nScrollFoot.style.width = _fnStringToCss( correction );
4086  }
4087 
4088 
4089  /*
4090  * 4. Clean up
4091  */
4092  if ( ! scrollY ) {
4093  /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
4094  * the scrollbar height from the visible display, rather than adding it on. We need to
4095  * set the height in order to sort this. Don't want to do it in any other browsers.
4096  */
4097  if ( ie67 ) {
4098  divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
4099  }
4100  }
4101 
4102  /* Finally set the width's of the header and footer tables */
4103  var iOuterWidth = table.outerWidth();
4104  divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
4105  divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
4106 
4107  // Figure out if there are scrollbar present - if so then we need a the header and footer to
4108  // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
4109  var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
4110  var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
4111  divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
4112 
4113  if ( footer ) {
4114  divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
4115  divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
4116  divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
4117  }
4118 
4119  // Correct DOM ordering for colgroup - comes before the thead
4120  table.children('colgroup').insertBefore( table.children('thead') );
4121 
4122  /* Adjust the position of the header in case we loose the y-scrollbar */
4123  divBody.scroll();
4124 
4125  // If sorting or filtering has occurred, jump the scrolling back to the top
4126  // only if we aren't holding the position
4127  if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
4128  divBodyEl.scrollTop = 0;
4129  }
4130  }
4131 
4132 
4133 
4142  function _fnApplyToChildren( fn, an1, an2 )
4143  {
4144  var index=0, i=0, iLen=an1.length;
4145  var nNode1, nNode2;
4146 
4147  while ( i < iLen ) {
4148  nNode1 = an1[i].firstChild;
4149  nNode2 = an2 ? an2[i].firstChild : null;
4150 
4151  while ( nNode1 ) {
4152  if ( nNode1.nodeType === 1 ) {
4153  if ( an2 ) {
4154  fn( nNode1, nNode2, index );
4155  }
4156  else {
4157  fn( nNode1, index );
4158  }
4159 
4160  index++;
4161  }
4162 
4163  nNode1 = nNode1.nextSibling;
4164  nNode2 = an2 ? nNode2.nextSibling : null;
4165  }
4166 
4167  i++;
4168  }
4169  }
4170 
4171 
4172 
4173  var __re_html_remove = /<.*?>/g;
4174 
4175 
4181  function _fnCalculateColumnWidths ( oSettings )
4182  {
4183  var
4184  table = oSettings.nTable,
4185  columns = oSettings.aoColumns,
4186  scroll = oSettings.oScroll,
4187  scrollY = scroll.sY,
4188  scrollX = scroll.sX,
4189  scrollXInner = scroll.sXInner,
4190  columnCount = columns.length,
4191  visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
4192  headerCells = $('th', oSettings.nTHead),
4193  tableWidthAttr = table.getAttribute('width'), // from DOM element
4194  tableContainer = table.parentNode,
4195  userInputs = false,
4196  i, column, columnIdx, width, outerWidth,
4197  browser = oSettings.oBrowser,
4198  ie67 = browser.bScrollOversize;
4199 
4200  var styleWidth = table.style.width;
4201  if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
4202  tableWidthAttr = styleWidth;
4203  }
4204 
4205  /* Convert any user input sizes into pixel sizes */
4206  for ( i=0 ; i<visibleColumns.length ; i++ ) {
4207  column = columns[ visibleColumns[i] ];
4208 
4209  if ( column.sWidth !== null ) {
4210  column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
4211 
4212  userInputs = true;
4213  }
4214  }
4215 
4216  /* If the number of columns in the DOM equals the number that we have to
4217  * process in DataTables, then we can use the offsets that are created by
4218  * the web- browser. No custom sizes can be set in order for this to happen,
4219  * nor scrolling used
4220  */
4221  if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
4222  columnCount == _fnVisbleColumns( oSettings ) &&
4223  columnCount == headerCells.length
4224  ) {
4225  for ( i=0 ; i<columnCount ; i++ ) {
4226  var colIdx = _fnVisibleToColumnIndex( oSettings, i );
4227 
4228  if ( colIdx !== null ) {
4229  columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
4230  }
4231  }
4232  }
4233  else
4234  {
4235  // Otherwise construct a single row, worst case, table with the widest
4236  // node in the data, assign any user defined widths, then insert it into
4237  // the DOM and allow the browser to do all the hard work of calculating
4238  // table widths
4239  var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
4240  .css( 'visibility', 'hidden' )
4241  .removeAttr( 'id' );
4242 
4243  // Clean up the table body
4244  tmpTable.find('tbody tr').remove();
4245  var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
4246 
4247  // Clone the table header and footer - we can't use the header / footer
4248  // from the cloned table, since if scrolling is active, the table's
4249  // real header and footer are contained in different table tags
4250  tmpTable.find('thead, tfoot').remove();
4251  tmpTable
4252  .append( $(oSettings.nTHead).clone() )
4253  .append( $(oSettings.nTFoot).clone() );
4254 
4255  // Remove any assigned widths from the footer (from scrolling)
4256  tmpTable.find('tfoot th, tfoot td').css('width', '');
4257 
4258  // Apply custom sizing to the cloned header
4259  headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
4260 
4261  for ( i=0 ; i<visibleColumns.length ; i++ ) {
4262  column = columns[ visibleColumns[i] ];
4263 
4264  headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
4265  _fnStringToCss( column.sWidthOrig ) :
4266  '';
4267 
4268  // For scrollX we need to force the column width otherwise the
4269  // browser will collapse it. If this width is smaller than the
4270  // width the column requires, then it will have no effect
4271  if ( column.sWidthOrig && scrollX ) {
4272  $( headerCells[i] ).append( $('<div/>').css( {
4273  width: column.sWidthOrig,
4274  margin: 0,
4275  padding: 0,
4276  border: 0,
4277  height: 1
4278  } ) );
4279  }
4280  }
4281 
4282  // Find the widest cell for each column and put it into the table
4283  if ( oSettings.aoData.length ) {
4284  for ( i=0 ; i<visibleColumns.length ; i++ ) {
4285  columnIdx = visibleColumns[i];
4286  column = columns[ columnIdx ];
4287 
4288  $( _fnGetWidestNode( oSettings, columnIdx ) )
4289  .clone( false )
4290  .append( column.sContentPadding )
4291  .appendTo( tr );
4292  }
4293  }
4294 
4295  // Tidy the temporary table - remove name attributes so there aren't
4296  // duplicated in the dom (radio elements for example)
4297  $('[name]', tmpTable).removeAttr('name');
4298 
4299  // Table has been built, attach to the document so we can work with it.
4300  // A holding element is used, positioned at the top of the container
4301  // with minimal height, so it has no effect on if the container scrolls
4302  // or not. Otherwise it might trigger scrolling when it actually isn't
4303  // needed
4304  var holder = $('<div/>').css( scrollX || scrollY ?
4305  {
4306  position: 'absolute',
4307  top: 0,
4308  left: 0,
4309  height: 1,
4310  right: 0,
4311  overflow: 'hidden'
4312  } :
4313  {}
4314  )
4315  .append( tmpTable )
4316  .appendTo( tableContainer );
4317 
4318  // When scrolling (X or Y) we want to set the width of the table as
4319  // appropriate. However, when not scrolling leave the table width as it
4320  // is. This results in slightly different, but I think correct behaviour
4321  if ( scrollX && scrollXInner ) {
4322  tmpTable.width( scrollXInner );
4323  }
4324  else if ( scrollX ) {
4325  tmpTable.css( 'width', 'auto' );
4326  tmpTable.removeAttr('width');
4327 
4328  // If there is no width attribute or style, then allow the table to
4329  // collapse
4330  if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
4331  tmpTable.width( tableContainer.clientWidth );
4332  }
4333  }
4334  else if ( scrollY ) {
4335  tmpTable.width( tableContainer.clientWidth );
4336  }
4337  else if ( tableWidthAttr ) {
4338  tmpTable.width( tableWidthAttr );
4339  }
4340 
4341  // Get the width of each column in the constructed table - we need to
4342  // know the inner width (so it can be assigned to the other table's
4343  // cells) and the outer width so we can calculate the full width of the
4344  // table. This is safe since DataTables requires a unique cell for each
4345  // column, but if ever a header can span multiple columns, this will
4346  // need to be modified.
4347  var total = 0;
4348  for ( i=0 ; i<visibleColumns.length ; i++ ) {
4349  var cell = $(headerCells[i]);
4350  var border = cell.outerWidth() - cell.width();
4351 
4352  // Use getBounding... where possible (not IE8-) because it can give
4353  // sub-pixel accuracy, which we then want to round up!
4354  var bounding = browser.bBounding ?
4355  Math.ceil( headerCells[i].getBoundingClientRect().width ) :
4356  cell.outerWidth();
4357 
4358  // Total is tracked to remove any sub-pixel errors as the outerWidth
4359  // of the table might not equal the total given here (IE!).
4360  total += bounding;
4361 
4362  // Width for each column to use
4363  columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
4364  }
4365 
4366  table.style.width = _fnStringToCss( total );
4367 
4368  // Finished with the table - ditch it
4369  holder.remove();
4370  }
4371 
4372  // If there is a width attr, we want to attach an event listener which
4373  // allows the table sizing to automatically adjust when the window is
4374  // resized. Use the width attr rather than CSS, since we can't know if the
4375  // CSS is a relative value or absolute - DOM read is always px.
4376  if ( tableWidthAttr ) {
4377  table.style.width = _fnStringToCss( tableWidthAttr );
4378  }
4379 
4380  if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
4381  var bindResize = function () {
4382  $(window).bind('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
4383  _fnAdjustColumnSizing( oSettings );
4384  } ) );
4385  };
4386 
4387  // IE6/7 will crash if we bind a resize event handler on page load.
4388  // To be removed in 1.11 which drops IE6/7 support
4389  if ( ie67 ) {
4390  setTimeout( bindResize, 1000 );
4391  }
4392  else {
4393  bindResize();
4394  }
4395 
4396  oSettings._reszEvt = true;
4397  }
4398  }
4399 
4400 
4409  function _fnThrottle( fn, freq ) {
4410  var
4411  frequency = freq !== undefined ? freq : 200,
4412  last,
4413  timer;
4414 
4415  return function () {
4416  var
4417  that = this,
4418  now = +new Date(),
4419  args = arguments;
4420 
4421  if ( last && now < last + frequency ) {
4422  clearTimeout( timer );
4423 
4424  timer = setTimeout( function () {
4425  last = undefined;
4426  fn.apply( that, args );
4427  }, frequency );
4428  }
4429  else {
4430  last = now;
4431  fn.apply( that, args );
4432  }
4433  };
4434  }
4435 
4436 
4444  function _fnConvertToWidth ( width, parent )
4445  {
4446  if ( ! width ) {
4447  return 0;
4448  }
4449 
4450  var n = $('<div/>')
4451  .css( 'width', _fnStringToCss( width ) )
4452  .appendTo( parent || document.body );
4453 
4454  var val = n[0].offsetWidth;
4455  n.remove();
4456 
4457  return val;
4458  }
4459 
4460 
4468  function _fnGetWidestNode( settings, colIdx )
4469  {
4470  var idx = _fnGetMaxLenString( settings, colIdx );
4471  if ( idx < 0 ) {
4472  return null;
4473  }
4474 
4475  var data = settings.aoData[ idx ];
4476  return ! data.nTr ? // Might not have been created when deferred rendering
4477  $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
4478  data.anCells[ colIdx ];
4479  }
4480 
4481 
4489  function _fnGetMaxLenString( settings, colIdx )
4490  {
4491  var s, max=-1, maxIdx = -1;
4492 
4493  for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4494  s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
4495  s = s.replace( __re_html_remove, '' );
4496  s = s.replace( /&nbsp;/g, ' ' );
4497 
4498  if ( s.length > max ) {
4499  max = s.length;
4500  maxIdx = i;
4501  }
4502  }
4503 
4504  return maxIdx;
4505  }
4506 
4507 
4514  function _fnStringToCss( s )
4515  {
4516  if ( s === null ) {
4517  return '0px';
4518  }
4519 
4520  if ( typeof s == 'number' ) {
4521  return s < 0 ?
4522  '0px' :
4523  s+'px';
4524  }
4525 
4526  // Check it has a unit character already
4527  return s.match(/\d$/) ?
4528  s+'px' :
4529  s;
4530  }
4531 
4532 
4533 
4534  function _fnSortFlatten ( settings )
4535  {
4536  var
4537  i, iLen, k, kLen,
4538  aSort = [],
4539  aiOrig = [],
4540  aoColumns = settings.aoColumns,
4541  aDataSort, iCol, sType, srcCol,
4542  fixed = settings.aaSortingFixed,
4543  fixedObj = $.isPlainObject( fixed ),
4544  nestedSort = [],
4545  add = function ( a ) {
4546  if ( a.length && ! $.isArray( a[0] ) ) {
4547  // 1D array
4548  nestedSort.push( a );
4549  }
4550  else {
4551  // 2D array
4552  $.merge( nestedSort, a );
4553  }
4554  };
4555 
4556  // Build the sort array, with pre-fix and post-fix options if they have been
4557  // specified
4558  if ( $.isArray( fixed ) ) {
4559  add( fixed );
4560  }
4561 
4562  if ( fixedObj && fixed.pre ) {
4563  add( fixed.pre );
4564  }
4565 
4566  add( settings.aaSorting );
4567 
4568  if (fixedObj && fixed.post ) {
4569  add( fixed.post );
4570  }
4571 
4572  for ( i=0 ; i<nestedSort.length ; i++ )
4573  {
4574  srcCol = nestedSort[i][0];
4575  aDataSort = aoColumns[ srcCol ].aDataSort;
4576 
4577  for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
4578  {
4579  iCol = aDataSort[k];
4580  sType = aoColumns[ iCol ].sType || 'string';
4581 
4582  if ( nestedSort[i]._idx === undefined ) {
4583  nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
4584  }
4585 
4586  aSort.push( {
4587  src: srcCol,
4588  col: iCol,
4589  dir: nestedSort[i][1],
4590  index: nestedSort[i]._idx,
4591  type: sType,
4592  formatter: DataTable.ext.type.order[ sType+"-pre" ]
4593  } );
4594  }
4595  }
4596 
4597  return aSort;
4598  }
4599 
4606  function _fnSort ( oSettings )
4607  {
4608  var
4609  i, ien, iLen, j, jLen, k, kLen,
4610  sDataType, nTh,
4611  aiOrig = [],
4612  oExtSort = DataTable.ext.type.order,
4613  aoData = oSettings.aoData,
4614  aoColumns = oSettings.aoColumns,
4615  aDataSort, data, iCol, sType, oSort,
4616  formatters = 0,
4617  sortCol,
4618  displayMaster = oSettings.aiDisplayMaster,
4619  aSort;
4620 
4621  // Resolve any column types that are unknown due to addition or invalidation
4622  // @todo Can this be moved into a 'data-ready' handler which is called when
4623  // data is going to be used in the table?
4624  _fnColumnTypes( oSettings );
4625 
4626  aSort = _fnSortFlatten( oSettings );
4627 
4628  for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
4629  sortCol = aSort[i];
4630 
4631  // Track if we can use the fast sort algorithm
4632  if ( sortCol.formatter ) {
4633  formatters++;
4634  }
4635 
4636  // Load the data needed for the sort, for each cell
4637  _fnSortData( oSettings, sortCol.col );
4638  }
4639 
4640  /* No sorting required if server-side or no sorting array */
4641  if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
4642  {
4643  // Create a value - key array of the current row positions such that we can use their
4644  // current position during the sort, if values match, in order to perform stable sorting
4645  for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
4646  aiOrig[ displayMaster[i] ] = i;
4647  }
4648 
4649  /* Do the sort - here we want multi-column sorting based on a given data source (column)
4650  * and sorting function (from oSort) in a certain direction. It's reasonably complex to
4651  * follow on it's own, but this is what we want (example two column sorting):
4652  * fnLocalSorting = function(a,b){
4653  * var iTest;
4654  * iTest = oSort['string-asc']('data11', 'data12');
4655  * if (iTest !== 0)
4656  * return iTest;
4657  * iTest = oSort['numeric-desc']('data21', 'data22');
4658  * if (iTest !== 0)
4659  * return iTest;
4660  * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
4661  * }
4662  * Basically we have a test for each sorting column, if the data in that column is equal,
4663  * test the next column. If all columns match, then we use a numeric sort on the row
4664  * positions in the original data array to provide a stable sort.
4665  *
4666  * Note - I know it seems excessive to have two sorting methods, but the first is around
4667  * 15% faster, so the second is only maintained for backwards compatibility with sorting
4668  * methods which do not have a pre-sort formatting function.
4669  */
4670  if ( formatters === aSort.length ) {
4671  // All sort types have formatting functions
4672  displayMaster.sort( function ( a, b ) {
4673  var
4674  x, y, k, test, sort,
4675  len=aSort.length,
4676  dataA = aoData[a]._aSortData,
4677  dataB = aoData[b]._aSortData;
4678 
4679  for ( k=0 ; k<len ; k++ ) {
4680  sort = aSort[k];
4681 
4682  x = dataA[ sort.col ];
4683  y = dataB[ sort.col ];
4684 
4685  test = x<y ? -1 : x>y ? 1 : 0;
4686  if ( test !== 0 ) {
4687  return sort.dir === 'asc' ? test : -test;
4688  }
4689  }
4690 
4691  x = aiOrig[a];
4692  y = aiOrig[b];
4693  return x<y ? -1 : x>y ? 1 : 0;
4694  } );
4695  }
4696  else {
4697  // Depreciated - remove in 1.11 (providing a plug-in option)
4698  // Not all sort types have formatting methods, so we have to call their sorting
4699  // methods.
4700  displayMaster.sort( function ( a, b ) {
4701  var
4702  x, y, k, l, test, sort, fn,
4703  len=aSort.length,
4704  dataA = aoData[a]._aSortData,
4705  dataB = aoData[b]._aSortData;
4706 
4707  for ( k=0 ; k<len ; k++ ) {
4708  sort = aSort[k];
4709 
4710  x = dataA[ sort.col ];
4711  y = dataB[ sort.col ];
4712 
4713  fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
4714  test = fn( x, y );
4715  if ( test !== 0 ) {
4716  return test;
4717  }
4718  }
4719 
4720  x = aiOrig[a];
4721  y = aiOrig[b];
4722  return x<y ? -1 : x>y ? 1 : 0;
4723  } );
4724  }
4725  }
4726 
4727  /* Tell the draw function that we have sorted the data */
4728  oSettings.bSorted = true;
4729  }
4730 
4731 
4732  function _fnSortAria ( settings )
4733  {
4734  var label;
4735  var nextSort;
4736  var columns = settings.aoColumns;
4737  var aSort = _fnSortFlatten( settings );
4738  var oAria = settings.oLanguage.oAria;
4739 
4740  // ARIA attributes - need to loop all columns, to update all (removing old
4741  // attributes as needed)
4742  for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
4743  {
4744  var col = columns[i];
4745  var asSorting = col.asSorting;
4746  var sTitle = col.sTitle.replace( /<.*?>/g, "" );
4747  var th = col.nTh;
4748 
4749  // IE7 is throwing an error when setting these properties with jQuery's
4750  // attr() and removeAttr() methods...
4751  th.removeAttribute('aria-sort');
4752 
4753  /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
4754  if ( col.bSortable ) {
4755  if ( aSort.length > 0 && aSort[0].col == i ) {
4756  th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
4757  nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
4758  }
4759  else {
4760  nextSort = asSorting[0];
4761  }
4762 
4763  label = sTitle + ( nextSort === "asc" ?
4764  oAria.sSortAscending :
4765  oAria.sSortDescending
4766  );
4767  }
4768  else {
4769  label = sTitle;
4770  }
4771 
4772  th.setAttribute('aria-label', label);
4773  }
4774  }
4775 
4776 
4787  function _fnSortListener ( settings, colIdx, append, callback )
4788  {
4789  var col = settings.aoColumns[ colIdx ];
4790  var sorting = settings.aaSorting;
4791  var asSorting = col.asSorting;
4792  var nextSortIdx;
4793  var next = function ( a, overflow ) {
4794  var idx = a._idx;
4795  if ( idx === undefined ) {
4796  idx = $.inArray( a[1], asSorting );
4797  }
4798 
4799  return idx+1 < asSorting.length ?
4800  idx+1 :
4801  overflow ?
4802  null :
4803  0;
4804  };
4805 
4806  // Convert to 2D array if needed
4807  if ( typeof sorting[0] === 'number' ) {
4808  sorting = settings.aaSorting = [ sorting ];
4809  }
4810 
4811  // If appending the sort then we are multi-column sorting
4812  if ( append && settings.oFeatures.bSortMulti ) {
4813  // Are we already doing some kind of sort on this column?
4814  var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
4815 
4816  if ( sortIdx !== -1 ) {
4817  // Yes, modify the sort
4818  nextSortIdx = next( sorting[sortIdx], true );
4819 
4820  if ( nextSortIdx === null && sorting.length === 1 ) {
4821  nextSortIdx = 0; // can't remove sorting completely
4822  }
4823 
4824  if ( nextSortIdx === null ) {
4825  sorting.splice( sortIdx, 1 );
4826  }
4827  else {
4828  sorting[sortIdx][1] = asSorting[ nextSortIdx ];
4829  sorting[sortIdx]._idx = nextSortIdx;
4830  }
4831  }
4832  else {
4833  // No sort on this column yet
4834  sorting.push( [ colIdx, asSorting[0], 0 ] );
4835  sorting[sorting.length-1]._idx = 0;
4836  }
4837  }
4838  else if ( sorting.length && sorting[0][0] == colIdx ) {
4839  // Single column - already sorting on this column, modify the sort
4840  nextSortIdx = next( sorting[0] );
4841 
4842  sorting.length = 1;
4843  sorting[0][1] = asSorting[ nextSortIdx ];
4844  sorting[0]._idx = nextSortIdx;
4845  }
4846  else {
4847  // Single column - sort only on this column
4848  sorting.length = 0;
4849  sorting.push( [ colIdx, asSorting[0] ] );
4850  sorting[0]._idx = 0;
4851  }
4852 
4853  // Run the sort by calling a full redraw
4854  _fnReDraw( settings );
4855 
4856  // callback used for async user interaction
4857  if ( typeof callback == 'function' ) {
4858  callback( settings );
4859  }
4860  }
4861 
4862 
4871  function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
4872  {
4873  var col = settings.aoColumns[ colIdx ];
4874 
4875  _fnBindAction( attachTo, {}, function (e) {
4876  /* If the column is not sortable - don't to anything */
4877  if ( col.bSortable === false ) {
4878  return;
4879  }
4880 
4881  // If processing is enabled use a timeout to allow the processing
4882  // display to be shown - otherwise to it synchronously
4883  if ( settings.oFeatures.bProcessing ) {
4884  _fnProcessingDisplay( settings, true );
4885 
4886  setTimeout( function() {
4887  _fnSortListener( settings, colIdx, e.shiftKey, callback );
4888 
4889  // In server-side processing, the draw callback will remove the
4890  // processing display
4891  if ( _fnDataSource( settings ) !== 'ssp' ) {
4892  _fnProcessingDisplay( settings, false );
4893  }
4894  }, 0 );
4895  }
4896  else {
4897  _fnSortListener( settings, colIdx, e.shiftKey, callback );
4898  }
4899  } );
4900  }
4901 
4902 
4909  function _fnSortingClasses( settings )
4910  {
4911  var oldSort = settings.aLastSort;
4912  var sortClass = settings.oClasses.sSortColumn;
4913  var sort = _fnSortFlatten( settings );
4914  var features = settings.oFeatures;
4915  var i, ien, colIdx;
4916 
4917  if ( features.bSort && features.bSortClasses ) {
4918  // Remove old sorting classes
4919  for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
4920  colIdx = oldSort[i].src;
4921 
4922  // Remove column sorting
4923  $( _pluck( settings.aoData, 'anCells', colIdx ) )
4924  .removeClass( sortClass + (i<2 ? i+1 : 3) );
4925  }
4926 
4927  // Add new column sorting
4928  for ( i=0, ien=sort.length ; i<ien ; i++ ) {
4929  colIdx = sort[i].src;
4930 
4931  $( _pluck( settings.aoData, 'anCells', colIdx ) )
4932  .addClass( sortClass + (i<2 ? i+1 : 3) );
4933  }
4934  }
4935 
4936  settings.aLastSort = sort;
4937  }
4938 
4939 
4940  // Get the data to sort a column, be it from cache, fresh (populating the
4941  // cache), or from a sort formatter
4942  function _fnSortData( settings, idx )
4943  {
4944  // Custom sorting function - provided by the sort data type
4945  var column = settings.aoColumns[ idx ];
4946  var customSort = DataTable.ext.order[ column.sSortDataType ];
4947  var customData;
4948 
4949  if ( customSort ) {
4950  customData = customSort.call( settings.oInstance, settings, idx,
4951  _fnColumnIndexToVisible( settings, idx )
4952  );
4953  }
4954 
4955  // Use / populate cache
4956  var row, cellData;
4957  var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
4958 
4959  for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4960  row = settings.aoData[i];
4961 
4962  if ( ! row._aSortData ) {
4963  row._aSortData = [];
4964  }
4965 
4966  if ( ! row._aSortData[idx] || customSort ) {
4967  cellData = customSort ?
4968  customData[i] : // If there was a custom sort function, use data from there
4969  _fnGetCellData( settings, i, idx, 'sort' );
4970 
4971  row._aSortData[ idx ] = formatter ?
4972  formatter( cellData ) :
4973  cellData;
4974  }
4975  }
4976  }
4977 
4978 
4979 
4985  function _fnSaveState ( settings )
4986  {
4987  if ( !settings.oFeatures.bStateSave || settings.bDestroying )
4988  {
4989  return;
4990  }
4991 
4992  /* Store the interesting variables */
4993  var state = {
4994  time: +new Date(),
4995  start: settings._iDisplayStart,
4996  length: settings._iDisplayLength,
4997  order: $.extend( true, [], settings.aaSorting ),
4998  search: _fnSearchToCamel( settings.oPreviousSearch ),
4999  columns: $.map( settings.aoColumns, function ( col, i ) {
5000  return {
5001  visible: col.bVisible,
5002  search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
5003  };
5004  } )
5005  };
5006 
5007  _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
5008 
5009  settings.oSavedState = state;
5010  settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
5011  }
5012 
5013 
5020  function _fnLoadState ( settings, oInit )
5021  {
5022  var i, ien;
5023  var columns = settings.aoColumns;
5024 
5025  if ( ! settings.oFeatures.bStateSave ) {
5026  return;
5027  }
5028 
5029  var state = settings.fnStateLoadCallback.call( settings.oInstance, settings );
5030  if ( ! state || ! state.time ) {
5031  return;
5032  }
5033 
5034  /* Allow custom and plug-in manipulation functions to alter the saved data set and
5035  * cancelling of loading by returning false
5036  */
5037  var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] );
5038  if ( $.inArray( false, abStateLoad ) !== -1 ) {
5039  return;
5040  }
5041 
5042  /* Reject old data */
5043  var duration = settings.iStateDuration;
5044  if ( duration > 0 && state.time < +new Date() - (duration*1000) ) {
5045  return;
5046  }
5047 
5048  // Number of columns have changed - all bets are off, no restore of settings
5049  if ( columns.length !== state.columns.length ) {
5050  return;
5051  }
5052 
5053  // Store the saved state so it might be accessed at any time
5054  settings.oLoadedState = $.extend( true, {}, state );
5055 
5056  // Restore key features - todo - for 1.11 this needs to be done by
5057  // subscribed events
5058  if ( state.start !== undefined ) {
5059  settings._iDisplayStart = state.start;
5060  settings.iInitDisplayStart = state.start;
5061  }
5062  if ( state.length !== undefined ) {
5063  settings._iDisplayLength = state.length;
5064  }
5065 
5066  // Order
5067  if ( state.order !== undefined ) {
5068  settings.aaSorting = [];
5069  $.each( state.order, function ( i, col ) {
5070  settings.aaSorting.push( col[0] >= columns.length ?
5071  [ 0, col[1] ] :
5072  col
5073  );
5074  } );
5075  }
5076 
5077  // Search
5078  if ( state.search !== undefined ) {
5079  $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) );
5080  }
5081 
5082  // Columns
5083  for ( i=0, ien=state.columns.length ; i<ien ; i++ ) {
5084  var col = state.columns[i];
5085 
5086  // Visibility
5087  if ( col.visible !== undefined ) {
5088  columns[i].bVisible = col.visible;
5089  }
5090 
5091  // Search
5092  if ( col.search !== undefined ) {
5093  $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
5094  }
5095  }
5096 
5097  _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] );
5098  }
5099 
5100 
5107  function _fnSettingsFromNode ( table )
5108  {
5109  var settings = DataTable.settings;
5110  var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
5111 
5112  return idx !== -1 ?
5113  settings[ idx ] :
5114  null;
5115  }
5116 
5117 
5126  function _fnLog( settings, level, msg, tn )
5127  {
5128  msg = 'DataTables warning: '+
5129  (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
5130 
5131  if ( tn ) {
5132  msg += '. For more information about this error, please see '+
5133  'http://datatables.net/tn/'+tn;
5134  }
5135 
5136  if ( ! level ) {
5137  // Backwards compatibility pre 1.10
5138  var ext = DataTable.ext;
5139  var type = ext.sErrMode || ext.errMode;
5140 
5141  if ( settings ) {
5142  _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
5143  }
5144 
5145  if ( type == 'alert' ) {
5146  alert( msg );
5147  }
5148  else if ( type == 'throw' ) {
5149  throw new Error(msg);
5150  }
5151  else if ( typeof type == 'function' ) {
5152  type( settings, tn, msg );
5153  }
5154  }
5155  else if ( window.console && console.log ) {
5156  console.log( msg );
5157  }
5158  }
5159 
5160 
5169  function _fnMap( ret, src, name, mappedName )
5170  {
5171  if ( $.isArray( name ) ) {
5172  $.each( name, function (i, val) {
5173  if ( $.isArray( val ) ) {
5174  _fnMap( ret, src, val[0], val[1] );
5175  }
5176  else {
5177  _fnMap( ret, src, val );
5178  }
5179  } );
5180 
5181  return;
5182  }
5183 
5184  if ( mappedName === undefined ) {
5185  mappedName = name;
5186  }
5187 
5188  if ( src[name] !== undefined ) {
5189  ret[mappedName] = src[name];
5190  }
5191  }
5192 
5193 
5211  function _fnExtend( out, extender, breakRefs )
5212  {
5213  var val;
5214 
5215  for ( var prop in extender ) {
5216  if ( extender.hasOwnProperty(prop) ) {
5217  val = extender[prop];
5218 
5219  if ( $.isPlainObject( val ) ) {
5220  if ( ! $.isPlainObject( out[prop] ) ) {
5221  out[prop] = {};
5222  }
5223  $.extend( true, out[prop], val );
5224  }
5225  else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
5226  out[prop] = val.slice();
5227  }
5228  else {
5229  out[prop] = val;
5230  }
5231  }
5232  }
5233 
5234  return out;
5235  }
5236 
5237 
5247  function _fnBindAction( n, oData, fn )
5248  {
5249  $(n)
5250  .bind( 'click.DT', oData, function (e) {
5251  n.blur(); // Remove focus outline for mouse users
5252  fn(e);
5253  } )
5254  .bind( 'keypress.DT', oData, function (e){
5255  if ( e.which === 13 ) {
5256  e.preventDefault();
5257  fn(e);
5258  }
5259  } )
5260  .bind( 'selectstart.DT', function () {
5261  /* Take the brutal approach to cancelling text selection */
5262  return false;
5263  } );
5264  }
5265 
5266 
5276  function _fnCallbackReg( oSettings, sStore, fn, sName )
5277  {
5278  if ( fn )
5279  {
5280  oSettings[sStore].push( {
5281  "fn": fn,
5282  "sName": sName
5283  } );
5284  }
5285  }
5286 
5287 
5302  function _fnCallbackFire( settings, callbackArr, eventName, args )
5303  {
5304  var ret = [];
5305 
5306  if ( callbackArr ) {
5307  ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
5308  return val.fn.apply( settings.oInstance, args );
5309  } );
5310  }
5311 
5312  if ( eventName !== null ) {
5313  var e = $.Event( eventName+'.dt' );
5314 
5315  $(settings.nTable).trigger( e, args );
5316 
5317  ret.push( e.result );
5318  }
5319 
5320  return ret;
5321  }
5322 
5323 
5324  function _fnLengthOverflow ( settings )
5325  {
5326  var
5327  start = settings._iDisplayStart,
5328  end = settings.fnDisplayEnd(),
5329  len = settings._iDisplayLength;
5330 
5331  /* If we have space to show extra rows (backing up from the end point - then do so */
5332  if ( start >= end )
5333  {
5334  start = end - len;
5335  }
5336 
5337  // Keep the start record on the current page
5338  start -= (start % len);
5339 
5340  if ( len === -1 || start < 0 )
5341  {
5342  start = 0;
5343  }
5344 
5345  settings._iDisplayStart = start;
5346  }
5347 
5348 
5349  function _fnRenderer( settings, type )
5350  {
5351  var renderer = settings.renderer;
5352  var host = DataTable.ext.renderer[type];
5353 
5354  if ( $.isPlainObject( renderer ) && renderer[type] ) {
5355  // Specific renderer for this type. If available use it, otherwise use
5356  // the default.
5357  return host[renderer[type]] || host._;
5358  }
5359  else if ( typeof renderer === 'string' ) {
5360  // Common renderer - if there is one available for this type use it,
5361  // otherwise use the default
5362  return host[renderer] || host._;
5363  }
5364 
5365  // Use the default
5366  return host._;
5367  }
5368 
5369 
5378  function _fnDataSource ( settings )
5379  {
5380  if ( settings.oFeatures.bServerSide ) {
5381  return 'ssp';
5382  }
5383  else if ( settings.ajax || settings.sAjaxSource ) {
5384  return 'ajax';
5385  }
5386  return 'dom';
5387  }
5388 
5389 
5390  DataTable = function( options )
5391  {
5428  this.$ = function ( sSelector, oOpts )
5429  {
5430  return this.api(true).$( sSelector, oOpts );
5431  };
5432 
5433 
5482  this._ = function ( sSelector, oOpts )
5483  {
5484  return this.api(true).rows( sSelector, oOpts ).data();
5485  };
5486 
5487 
5497  this.api = function ( traditional )
5498  {
5499  return traditional ?
5500  new _Api(
5501  _fnSettingsFromNode( this[ _ext.iApiIndex ] )
5502  ) :
5503  new _Api( this );
5504  };
5505 
5506 
5545  this.fnAddData = function( data, redraw )
5546  {
5547  var api = this.api( true );
5548 
5549  /* Check if we want to add multiple rows or not */
5550  var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
5551  api.rows.add( data ) :
5552  api.row.add( data );
5553 
5554  if ( redraw === undefined || redraw ) {
5555  api.draw();
5556  }
5557 
5558  return rows.flatten().toArray();
5559  };
5560 
5561 
5583  this.fnAdjustColumnSizing = function ( bRedraw )
5584  {
5585  var api = this.api( true ).columns.adjust();
5586  var settings = api.settings()[0];
5587  var scroll = settings.oScroll;
5588 
5589  if ( bRedraw === undefined || bRedraw ) {
5590  api.draw( false );
5591  }
5592  else if ( scroll.sX !== "" || scroll.sY !== "" ) {
5593  /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
5594  _fnScrollDraw( settings );
5595  }
5596  };
5597 
5598 
5613  this.fnClearTable = function( bRedraw )
5614  {
5615  var api = this.api( true ).clear();
5616 
5617  if ( bRedraw === undefined || bRedraw ) {
5618  api.draw();
5619  }
5620  };
5621 
5622 
5647  this.fnClose = function( nTr )
5648  {
5649  this.api( true ).row( nTr ).child.hide();
5650  };
5651 
5652 
5671  this.fnDeleteRow = function( target, callback, redraw )
5672  {
5673  var api = this.api( true );
5674  var rows = api.rows( target );
5675  var settings = rows.settings()[0];
5676  var data = settings.aoData[ rows[0][0] ];
5677 
5678  rows.remove();
5679 
5680  if ( callback ) {
5681  callback.call( this, settings, data );
5682  }
5683 
5684  if ( redraw === undefined || redraw ) {
5685  api.draw();
5686  }
5687 
5688  return data;
5689  };
5690 
5691 
5706  this.fnDestroy = function ( remove )
5707  {
5708  this.api( true ).destroy( remove );
5709  };
5710 
5711 
5726  this.fnDraw = function( complete )
5727  {
5728  // Note that this isn't an exact match to the old call to _fnDraw - it takes
5729  // into account the new data, but can hold position.
5730  this.api( true ).draw( complete );
5731  };
5732 
5733 
5753  this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
5754  {
5755  var api = this.api( true );
5756 
5757  if ( iColumn === null || iColumn === undefined ) {
5758  api.search( sInput, bRegex, bSmart, bCaseInsensitive );
5759  }
5760  else {
5761  api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
5762  }
5763 
5764  api.draw();
5765  };
5766 
5767 
5805  this.fnGetData = function( src, col )
5806  {
5807  var api = this.api( true );
5808 
5809  if ( src !== undefined ) {
5810  var type = src.nodeName ? src.nodeName.toLowerCase() : '';
5811 
5812  return col !== undefined || type == 'td' || type == 'th' ?
5813  api.cell( src, col ).data() :
5814  api.row( src ).data() || null;
5815  }
5816 
5817  return api.data().toArray();
5818  };
5819 
5820 
5839  this.fnGetNodes = function( iRow )
5840  {
5841  var api = this.api( true );
5842 
5843  return iRow !== undefined ?
5844  api.row( iRow ).node() :
5845  api.rows().nodes().flatten().toArray();
5846  };
5847 
5848 
5877  this.fnGetPosition = function( node )
5878  {
5879  var api = this.api( true );
5880  var nodeName = node.nodeName.toUpperCase();
5881 
5882  if ( nodeName == 'TR' ) {
5883  return api.row( node ).index();
5884  }
5885  else if ( nodeName == 'TD' || nodeName == 'TH' ) {
5886  var cell = api.cell( node ).index();
5887 
5888  return [
5889  cell.row,
5890  cell.columnVisible,
5891  cell.column
5892  ];
5893  }
5894  return null;
5895  };
5896 
5897 
5921  this.fnIsOpen = function( nTr )
5922  {
5923  return this.api( true ).row( nTr ).child.isShown();
5924  };
5925 
5926 
5957  this.fnOpen = function( nTr, mHtml, sClass )
5958  {
5959  return this.api( true )
5960  .row( nTr )
5961  .child( mHtml, sClass )
5962  .show()
5963  .child()[0];
5964  };
5965 
5966 
5983  this.fnPageChange = function ( mAction, bRedraw )
5984  {
5985  var api = this.api( true ).page( mAction );
5986 
5987  if ( bRedraw === undefined || bRedraw ) {
5988  api.draw(false);
5989  }
5990  };
5991 
5992 
6009  this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
6010  {
6011  var api = this.api( true ).column( iCol ).visible( bShow );
6012 
6013  if ( bRedraw === undefined || bRedraw ) {
6014  api.columns.adjust().draw();
6015  }
6016  };
6017 
6018 
6035  this.fnSettings = function()
6036  {
6037  return _fnSettingsFromNode( this[_ext.iApiIndex] );
6038  };
6039 
6040 
6056  this.fnSort = function( aaSort )
6057  {
6058  this.api( true ).order( aaSort ).draw();
6059  };
6060 
6061 
6078  this.fnSortListener = function( nNode, iColumn, fnCallback )
6079  {
6080  this.api( true ).order.listener( nNode, iColumn, fnCallback );
6081  };
6082 
6083 
6106  this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
6107  {
6108  var api = this.api( true );
6109 
6110  if ( iColumn === undefined || iColumn === null ) {
6111  api.row( mRow ).data( mData );
6112  }
6113  else {
6114  api.cell( mRow, iColumn ).data( mData );
6115  }
6116 
6117  if ( bAction === undefined || bAction ) {
6118  api.columns.adjust();
6119  }
6120 
6121  if ( bRedraw === undefined || bRedraw ) {
6122  api.draw();
6123  }
6124  return 0;
6125  };
6126 
6127 
6145  this.fnVersionCheck = _ext.fnVersionCheck;
6146 
6147 
6148  var _that = this;
6149  var emptyInit = options === undefined;
6150  var len = this.length;
6151 
6152  if ( emptyInit ) {
6153  options = {};
6154  }
6155 
6156  this.oApi = this.internal = _ext.internal;
6157 
6158  // Extend with old style plug-in API methods
6159  for ( var fn in DataTable.ext.internal ) {
6160  if ( fn ) {
6161  this[fn] = _fnExternApiFunc(fn);
6162  }
6163  }
6164 
6165  this.each(function() {
6166  // For each initialisation we want to give it a clean initialisation
6167  // object that can be bashed around
6168  var o = {};
6169  var oInit = len > 1 ? // optimisation for single table case
6170  _fnExtend( o, options, true ) :
6171  options;
6172 
6173  /*global oInit,_that,emptyInit*/
6174  var i=0, iLen, j, jLen, k, kLen;
6175  var sId = this.getAttribute( 'id' );
6176  var bInitHandedOff = false;
6177  var defaults = DataTable.defaults;
6178  var $this = $(this);
6179 
6180 
6181  /* Sanity check */
6182  if ( this.nodeName.toLowerCase() != 'table' )
6183  {
6184  _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
6185  return;
6186  }
6187 
6188  /* Backwards compatibility for the defaults */
6189  _fnCompatOpts( defaults );
6190  _fnCompatCols( defaults.column );
6191 
6192  /* Convert the camel-case defaults to Hungarian */
6193  _fnCamelToHungarian( defaults, defaults, true );
6194  _fnCamelToHungarian( defaults.column, defaults.column, true );
6195 
6196  /* Setting up the initialisation object */
6197  _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) );
6198 
6199 
6200 
6201  /* Check to see if we are re-initialising a table */
6202  var allSettings = DataTable.settings;
6203  for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
6204  {
6205  var s = allSettings[i];
6206 
6207  /* Base check on table node */
6208  if ( s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this) )
6209  {
6210  var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
6211  var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
6212 
6213  if ( emptyInit || bRetrieve )
6214  {
6215  return s.oInstance;
6216  }
6217  else if ( bDestroy )
6218  {
6219  s.oInstance.fnDestroy();
6220  break;
6221  }
6222  else
6223  {
6224  _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
6225  return;
6226  }
6227  }
6228 
6229  /* If the element we are initialising has the same ID as a table which was previously
6230  * initialised, but the table nodes don't match (from before) then we destroy the old
6231  * instance by simply deleting it. This is under the assumption that the table has been
6232  * destroyed by other methods. Anyone using non-id selectors will need to do this manually
6233  */
6234  if ( s.sTableId == this.id )
6235  {
6236  allSettings.splice( i, 1 );
6237  break;
6238  }
6239  }
6240 
6241  /* Ensure the table has an ID - required for accessibility */
6242  if ( sId === null || sId === "" )
6243  {
6244  sId = "DataTables_Table_"+(DataTable.ext._unique++);
6245  this.id = sId;
6246  }
6247 
6248  /* Create the settings object for this table and set some of the default parameters */
6249  var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
6250  "sDestroyWidth": $this[0].style.width,
6251  "sInstance": sId,
6252  "sTableId": sId
6253  } );
6254  oSettings.nTable = this;
6255  oSettings.oApi = _that.internal;
6256  oSettings.oInit = oInit;
6257 
6258  allSettings.push( oSettings );
6259 
6260  // Need to add the instance after the instance after the settings object has been added
6261  // to the settings array, so we can self reference the table instance if more than one
6262  oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
6263 
6264  // Backwards compatibility, before we apply all the defaults
6265  _fnCompatOpts( oInit );
6266 
6267  if ( oInit.oLanguage )
6268  {
6269  _fnLanguageCompat( oInit.oLanguage );
6270  }
6271 
6272  // If the length menu is given, but the init display length is not, use the length menu
6273  if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
6274  {
6275  oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
6276  oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
6277  }
6278 
6279  // Apply the defaults and init options to make a single init object will all
6280  // options defined from defaults and instance options.
6281  oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
6282 
6283 
6284  // Map the initialisation options onto the settings object
6285  _fnMap( oSettings.oFeatures, oInit, [
6286  "bPaginate",
6287  "bLengthChange",
6288  "bFilter",
6289  "bSort",
6290  "bSortMulti",
6291  "bInfo",
6292  "bProcessing",
6293  "bAutoWidth",
6294  "bSortClasses",
6295  "bServerSide",
6296  "bDeferRender"
6297  ] );
6298  _fnMap( oSettings, oInit, [
6299  "asStripeClasses",
6300  "ajax",
6301  "fnServerData",
6302  "fnFormatNumber",
6303  "sServerMethod",
6304  "aaSorting",
6305  "aaSortingFixed",
6306  "aLengthMenu",
6307  "sPaginationType",
6308  "sAjaxSource",
6309  "sAjaxDataProp",
6310  "iStateDuration",
6311  "sDom",
6312  "bSortCellsTop",
6313  "iTabIndex",
6314  "fnStateLoadCallback",
6315  "fnStateSaveCallback",
6316  "renderer",
6317  "searchDelay",
6318  "rowId",
6319  [ "iCookieDuration", "iStateDuration" ], // backwards compat
6320  [ "oSearch", "oPreviousSearch" ],
6321  [ "aoSearchCols", "aoPreSearchCols" ],
6322  [ "iDisplayLength", "_iDisplayLength" ],
6323  [ "bJQueryUI", "bJUI" ]
6324  ] );
6325  _fnMap( oSettings.oScroll, oInit, [
6326  [ "sScrollX", "sX" ],
6327  [ "sScrollXInner", "sXInner" ],
6328  [ "sScrollY", "sY" ],
6329  [ "bScrollCollapse", "bCollapse" ]
6330  ] );
6331  _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
6332 
6333  /* Callback functions which are array driven */
6334  _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
6335  _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
6336  _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
6337  _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
6338  _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
6339  _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
6340  _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
6341  _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
6342  _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
6343  _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
6344  _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
6345 
6346  oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
6347 
6348  /* Browser support detection */
6349  _fnBrowserDetect( oSettings );
6350 
6351  var oClasses = oSettings.oClasses;
6352 
6353  // @todo Remove in 1.11
6354  if ( oInit.bJQueryUI )
6355  {
6356  /* Use the JUI classes object for display. You could clone the oStdClasses object if
6357  * you want to have multiple tables with multiple independent classes
6358  */
6359  $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses );
6360 
6361  if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" )
6362  {
6363  /* Set the DOM to use a layout suitable for jQuery UI's theming */
6364  oSettings.sDom = '<"H"lfr>t<"F"ip>';
6365  }
6366 
6367  if ( ! oSettings.renderer ) {
6368  oSettings.renderer = 'jqueryui';
6369  }
6370  else if ( $.isPlainObject( oSettings.renderer ) && ! oSettings.renderer.header ) {
6371  oSettings.renderer.header = 'jqueryui';
6372  }
6373  }
6374  else
6375  {
6376  $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
6377  }
6378  $this.addClass( oClasses.sTable );
6379 
6380 
6381  if ( oSettings.iInitDisplayStart === undefined )
6382  {
6383  /* Display start point, taking into account the save saving */
6384  oSettings.iInitDisplayStart = oInit.iDisplayStart;
6385  oSettings._iDisplayStart = oInit.iDisplayStart;
6386  }
6387 
6388  if ( oInit.iDeferLoading !== null )
6389  {
6390  oSettings.bDeferLoading = true;
6391  var tmp = $.isArray( oInit.iDeferLoading );
6392  oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
6393  oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
6394  }
6395 
6396  /* Language definitions */
6397  var oLanguage = oSettings.oLanguage;
6398  $.extend( true, oLanguage, oInit.oLanguage );
6399 
6400  if ( oLanguage.sUrl !== "" )
6401  {
6402  /* Get the language definitions from a file - because this Ajax call makes the language
6403  * get async to the remainder of this function we use bInitHandedOff to indicate that
6404  * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
6405  */
6406  $.ajax( {
6407  dataType: 'json',
6408  url: oLanguage.sUrl,
6409  success: function ( json ) {
6410  _fnLanguageCompat( json );
6411  _fnCamelToHungarian( defaults.oLanguage, json );
6412  $.extend( true, oLanguage, json );
6413  _fnInitialise( oSettings );
6414  },
6415  error: function () {
6416  // Error occurred loading language file, continue on as best we can
6417  _fnInitialise( oSettings );
6418  }
6419  } );
6420  bInitHandedOff = true;
6421  }
6422 
6423  /*
6424  * Stripes
6425  */
6426  if ( oInit.asStripeClasses === null )
6427  {
6428  oSettings.asStripeClasses =[
6429  oClasses.sStripeOdd,
6430  oClasses.sStripeEven
6431  ];
6432  }
6433 
6434  /* Remove row stripe classes if they are already on the table row */
6435  var stripeClasses = oSettings.asStripeClasses;
6436  var rowOne = $this.children('tbody').find('tr').eq(0);
6437  if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
6438  return rowOne.hasClass(el);
6439  } ) ) !== -1 ) {
6440  $('tbody tr', this).removeClass( stripeClasses.join(' ') );
6441  oSettings.asDestroyStripes = stripeClasses.slice();
6442  }
6443 
6444  /*
6445  * Columns
6446  * See if we should load columns automatically or use defined ones
6447  */
6448  var anThs = [];
6449  var aoColumnsInit;
6450  var nThead = this.getElementsByTagName('thead');
6451  if ( nThead.length !== 0 )
6452  {
6453  _fnDetectHeader( oSettings.aoHeader, nThead[0] );
6454  anThs = _fnGetUniqueThs( oSettings );
6455  }
6456 
6457  /* If not given a column array, generate one with nulls */
6458  if ( oInit.aoColumns === null )
6459  {
6460  aoColumnsInit = [];
6461  for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
6462  {
6463  aoColumnsInit.push( null );
6464  }
6465  }
6466  else
6467  {
6468  aoColumnsInit = oInit.aoColumns;
6469  }
6470 
6471  /* Add the columns */
6472  for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
6473  {
6474  _fnAddColumn( oSettings, anThs ? anThs[i] : null );
6475  }
6476 
6477  /* Apply the column definitions */
6478  _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
6479  _fnColumnOptions( oSettings, iCol, oDef );
6480  } );
6481 
6482  /* HTML5 attribute detection - build an mData object automatically if the
6483  * attributes are found
6484  */
6485  if ( rowOne.length ) {
6486  var a = function ( cell, name ) {
6487  return cell.getAttribute( 'data-'+name ) !== null ? name : null;
6488  };
6489 
6490  $( rowOne[0] ).children('th, td').each( function (i, cell) {
6491  var col = oSettings.aoColumns[i];
6492 
6493  if ( col.mData === i ) {
6494  var sort = a( cell, 'sort' ) || a( cell, 'order' );
6495  var filter = a( cell, 'filter' ) || a( cell, 'search' );
6496 
6497  if ( sort !== null || filter !== null ) {
6498  col.mData = {
6499  _: i+'.display',
6500  sort: sort !== null ? i+'.@data-'+sort : undefined,
6501  type: sort !== null ? i+'.@data-'+sort : undefined,
6502  filter: filter !== null ? i+'.@data-'+filter : undefined
6503  };
6504 
6505  _fnColumnOptions( oSettings, i );
6506  }
6507  }
6508  } );
6509  }
6510 
6511  var features = oSettings.oFeatures;
6512 
6513  /* Must be done after everything which can be overridden by the state saving! */
6514  if ( oInit.bStateSave )
6515  {
6516  features.bStateSave = true;
6517  _fnLoadState( oSettings, oInit );
6518  _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
6519  }
6520 
6521 
6522  /*
6523  * Sorting
6524  * @todo For modularisation (1.11) this needs to do into a sort start up handler
6525  */
6526 
6527  // If aaSorting is not defined, then we use the first indicator in asSorting
6528  // in case that has been altered, so the default sort reflects that option
6529  if ( oInit.aaSorting === undefined )
6530  {
6531  var sorting = oSettings.aaSorting;
6532  for ( i=0, iLen=sorting.length ; i<iLen ; i++ )
6533  {
6534  sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
6535  }
6536  }
6537 
6538  /* Do a first pass on the sorting classes (allows any size changes to be taken into
6539  * account, and also will apply sorting disabled classes if disabled
6540  */
6541  _fnSortingClasses( oSettings );
6542 
6543  if ( features.bSort )
6544  {
6545  _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
6546  if ( oSettings.bSorted ) {
6547  var aSort = _fnSortFlatten( oSettings );
6548  var sortedColumns = {};
6549 
6550  $.each( aSort, function (i, val) {
6551  sortedColumns[ val.src ] = val.dir;
6552  } );
6553 
6554  _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
6555  _fnSortAria( oSettings );
6556  }
6557  } );
6558  }
6559 
6560  _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
6561  if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
6562  _fnSortingClasses( oSettings );
6563  }
6564  }, 'sc' );
6565 
6566 
6567  /*
6568  * Final init
6569  * Cache the header, body and footer as required, creating them if needed
6570  */
6571 
6572  // Work around for Webkit bug 83867 - store the caption-side before removing from doc
6573  var captions = $this.children('caption').each( function () {
6574  this._captionSide = $this.css('caption-side');
6575  } );
6576 
6577  var thead = $this.children('thead');
6578  if ( thead.length === 0 )
6579  {
6580  thead = $('<thead/>').appendTo(this);
6581  }
6582  oSettings.nTHead = thead[0];
6583 
6584  var tbody = $this.children('tbody');
6585  if ( tbody.length === 0 )
6586  {
6587  tbody = $('<tbody/>').appendTo(this);
6588  }
6589  oSettings.nTBody = tbody[0];
6590 
6591  var tfoot = $this.children('tfoot');
6592  if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
6593  {
6594  // If we are a scrolling table, and no footer has been given, then we need to create
6595  // a tfoot element for the caption element to be appended to
6596  tfoot = $('<tfoot/>').appendTo(this);
6597  }
6598 
6599  if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
6600  $this.addClass( oClasses.sNoFooter );
6601  }
6602  else if ( tfoot.length > 0 ) {
6603  oSettings.nTFoot = tfoot[0];
6604  _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
6605  }
6606 
6607  /* Check if there is data passing into the constructor */
6608  if ( oInit.aaData )
6609  {
6610  for ( i=0 ; i<oInit.aaData.length ; i++ )
6611  {
6612  _fnAddData( oSettings, oInit.aaData[ i ] );
6613  }
6614  }
6615  else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' )
6616  {
6617  /* Grab the data from the page - only do this when deferred loading or no Ajax
6618  * source since there is no point in reading the DOM data if we are then going
6619  * to replace it with Ajax data
6620  */
6621  _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
6622  }
6623 
6624  /* Copy the data index array */
6625  oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
6626 
6627  /* Initialisation complete - table can be drawn */
6628  oSettings.bInitialised = true;
6629 
6630  /* Check if we need to initialise the table (it might not have been handed off to the
6631  * language processor)
6632  */
6633  if ( bInitHandedOff === false )
6634  {
6635  _fnInitialise( oSettings );
6636  }
6637  } );
6638  _that = null;
6639  return this;
6640  };
6641 
6642 
6643 
6680  var __apiStruct = [];
6681 
6682 
6689  var __arrayProto = Array.prototype;
6690 
6691 
6712  var _toSettings = function ( mixed )
6713  {
6714  var idx, jq;
6715  var settings = DataTable.settings;
6716  var tables = $.map( settings, function (el, i) {
6717  return el.nTable;
6718  } );
6719 
6720  if ( ! mixed ) {
6721  return [];
6722  }
6723  else if ( mixed.nTable && mixed.oApi ) {
6724  // DataTables settings object
6725  return [ mixed ];
6726  }
6727  else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
6728  // Table node
6729  idx = $.inArray( mixed, tables );
6730  return idx !== -1 ? [ settings[idx] ] : null;
6731  }
6732  else if ( mixed && typeof mixed.settings === 'function' ) {
6733  return mixed.settings().toArray();
6734  }
6735  else if ( typeof mixed === 'string' ) {
6736  // jQuery selector
6737  jq = $(mixed);
6738  }
6739  else if ( mixed instanceof $ ) {
6740  // jQuery object (also DataTables instance)
6741  jq = mixed;
6742  }
6743 
6744  if ( jq ) {
6745  return jq.map( function(i) {
6746  idx = $.inArray( this, tables );
6747  return idx !== -1 ? settings[idx] : null;
6748  } ).toArray();
6749  }
6750  };
6751 
6752 
6807  _Api = function ( context, data )
6808  {
6809  if ( ! (this instanceof _Api) ) {
6810  return new _Api( context, data );
6811  }
6812 
6813  var settings = [];
6814  var ctxSettings = function ( o ) {
6815  var a = _toSettings( o );
6816  if ( a ) {
6817  settings = settings.concat( a );
6818  }
6819  };
6820 
6821  if ( $.isArray( context ) ) {
6822  for ( var i=0, ien=context.length ; i<ien ; i++ ) {
6823  ctxSettings( context[i] );
6824  }
6825  }
6826  else {
6827  ctxSettings( context );
6828  }
6829 
6830  // Remove duplicates
6831  this.context = _unique( settings );
6832 
6833  // Initial data
6834  if ( data ) {
6835  $.merge( this, data );
6836  }
6837 
6838  // selector
6839  this.selector = {
6840  rows: null,
6841  cols: null,
6842  opts: null
6843  };
6844 
6845  _Api.extend( this, this, __apiStruct );
6846  };
6847 
6848  DataTable.Api = _Api;
6849 
6850  // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
6851  // isPlainObject.
6852  $.extend( _Api.prototype, {
6853  any: function ()
6854  {
6855  return this.count() !== 0;
6856  },
6857 
6858 
6859  concat: __arrayProto.concat,
6860 
6861 
6862  context: [], // array of table settings objects
6863 
6864 
6865  count: function ()
6866  {
6867  return this.flatten().length;
6868  },
6869 
6870 
6871  each: function ( fn )
6872  {
6873  for ( var i=0, ien=this.length ; i<ien; i++ ) {
6874  fn.call( this, this[i], i, this );
6875  }
6876 
6877  return this;
6878  },
6879 
6880 
6881  eq: function ( idx )
6882  {
6883  var ctx = this.context;
6884 
6885  return ctx.length > idx ?
6886  new _Api( ctx[idx], this[idx] ) :
6887  null;
6888  },
6889 
6890 
6891  filter: function ( fn )
6892  {
6893  var a = [];
6894 
6895  if ( __arrayProto.filter ) {
6896  a = __arrayProto.filter.call( this, fn, this );
6897  }
6898  else {
6899  // Compatibility for browsers without EMCA-252-5 (JS 1.6)
6900  for ( var i=0, ien=this.length ; i<ien ; i++ ) {
6901  if ( fn.call( this, this[i], i, this ) ) {
6902  a.push( this[i] );
6903  }
6904  }
6905  }
6906 
6907  return new _Api( this.context, a );
6908  },
6909 
6910 
6911  flatten: function ()
6912  {
6913  var a = [];
6914  return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
6915  },
6916 
6917 
6918  join: __arrayProto.join,
6919 
6920 
6921  indexOf: __arrayProto.indexOf || function (obj, start)
6922  {
6923  for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
6924  if ( this[i] === obj ) {
6925  return i;
6926  }
6927  }
6928  return -1;
6929  },
6930 
6931  iterator: function ( flatten, type, fn, alwaysNew ) {
6932  var
6933  a = [], ret,
6934  i, ien, j, jen,
6935  context = this.context,
6936  rows, items, item,
6937  selector = this.selector;
6938 
6939  // Argument shifting
6940  if ( typeof flatten === 'string' ) {
6941  alwaysNew = fn;
6942  fn = type;
6943  type = flatten;
6944  flatten = false;
6945  }
6946 
6947  for ( i=0, ien=context.length ; i<ien ; i++ ) {
6948  var apiInst = new _Api( context[i] );
6949 
6950  if ( type === 'table' ) {
6951  ret = fn.call( apiInst, context[i], i );
6952 
6953  if ( ret !== undefined ) {
6954  a.push( ret );
6955  }
6956  }
6957  else if ( type === 'columns' || type === 'rows' ) {
6958  // this has same length as context - one entry for each table
6959  ret = fn.call( apiInst, context[i], this[i], i );
6960 
6961  if ( ret !== undefined ) {
6962  a.push( ret );
6963  }
6964  }
6965  else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
6966  // columns and rows share the same structure.
6967  // 'this' is an array of column indexes for each context
6968  items = this[i];
6969 
6970  if ( type === 'column-rows' ) {
6971  rows = _selector_row_indexes( context[i], selector.opts );
6972  }
6973 
6974  for ( j=0, jen=items.length ; j<jen ; j++ ) {
6975  item = items[j];
6976 
6977  if ( type === 'cell' ) {
6978  ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
6979  }
6980  else {
6981  ret = fn.call( apiInst, context[i], item, i, j, rows );
6982  }
6983 
6984  if ( ret !== undefined ) {
6985  a.push( ret );
6986  }
6987  }
6988  }
6989  }
6990 
6991  if ( a.length || alwaysNew ) {
6992  var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
6993  var apiSelector = api.selector;
6994  apiSelector.rows = selector.rows;
6995  apiSelector.cols = selector.cols;
6996  apiSelector.opts = selector.opts;
6997  return api;
6998  }
6999  return this;
7000  },
7001 
7002 
7003  lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
7004  {
7005  // Bit cheeky...
7006  return this.indexOf.apply( this.toArray.reverse(), arguments );
7007  },
7008 
7009 
7010  length: 0,
7011 
7012 
7013  map: function ( fn )
7014  {
7015  var a = [];
7016 
7017  if ( __arrayProto.map ) {
7018  a = __arrayProto.map.call( this, fn, this );
7019  }
7020  else {
7021  // Compatibility for browsers without EMCA-252-5 (JS 1.6)
7022  for ( var i=0, ien=this.length ; i<ien ; i++ ) {
7023  a.push( fn.call( this, this[i], i ) );
7024  }
7025  }
7026 
7027  return new _Api( this.context, a );
7028  },
7029 
7030 
7031  pluck: function ( prop )
7032  {
7033  return this.map( function ( el ) {
7034  return el[ prop ];
7035  } );
7036  },
7037 
7038  pop: __arrayProto.pop,
7039 
7040 
7041  push: __arrayProto.push,
7042 
7043 
7044  // Does not return an API instance
7045  reduce: __arrayProto.reduce || function ( fn, init )
7046  {
7047  return _fnReduce( this, fn, init, 0, this.length, 1 );
7048  },
7049 
7050 
7051  reduceRight: __arrayProto.reduceRight || function ( fn, init )
7052  {
7053  return _fnReduce( this, fn, init, this.length-1, -1, -1 );
7054  },
7055 
7056 
7057  reverse: __arrayProto.reverse,
7058 
7059 
7060  // Object with rows, columns and opts
7061  selector: null,
7062 
7063 
7064  shift: __arrayProto.shift,
7065 
7066 
7067  sort: __arrayProto.sort, // ? name - order?
7068 
7069 
7070  splice: __arrayProto.splice,
7071 
7072 
7073  toArray: function ()
7074  {
7075  return __arrayProto.slice.call( this );
7076  },
7077 
7078 
7079  to$: function ()
7080  {
7081  return $( this );
7082  },
7083 
7084 
7085  toJQuery: function ()
7086  {
7087  return $( this );
7088  },
7089 
7090 
7091  unique: function ()
7092  {
7093  return new _Api( this.context, _unique(this) );
7094  },
7095 
7096 
7097  unshift: __arrayProto.unshift
7098  } );
7099 
7100 
7101  _Api.extend = function ( scope, obj, ext )
7102  {
7103  // Only extend API instances and static properties of the API
7104  if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
7105  return;
7106  }
7107 
7108  var
7109  i, ien,
7110  j, jen,
7111  struct, inner,
7112  methodScoping = function ( scope, fn, struc ) {
7113  return function () {
7114  var ret = fn.apply( scope, arguments );
7115 
7116  // Method extension
7117  _Api.extend( ret, ret, struc.methodExt );
7118  return ret;
7119  };
7120  };
7121 
7122  for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7123  struct = ext[i];
7124 
7125  // Value
7126  obj[ struct.name ] = typeof struct.val === 'function' ?
7127  methodScoping( scope, struct.val, struct ) :
7128  $.isPlainObject( struct.val ) ?
7129  {} :
7130  struct.val;
7131 
7132  obj[ struct.name ].__dt_wrapper = true;
7133 
7134  // Property extension
7135  _Api.extend( scope, obj[ struct.name ], struct.propExt );
7136  }
7137  };
7138 
7139 
7140  // @todo - Is there need for an augment function?
7141  // _Api.augment = function ( inst, name )
7142  // {
7143  // // Find src object in the structure from the name
7144  // var parts = name.split('.');
7145 
7146  // _Api.extend( inst, obj );
7147  // };
7148 
7149 
7150  // [
7151  // {
7152  // name: 'data' -- string - Property name
7153  // val: function () {}, -- function - Api method (or undefined if just an object
7154  // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
7155  // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
7156  // },
7157  // {
7158  // name: 'row'
7159  // val: {},
7160  // methodExt: [ ... ],
7161  // propExt: [
7162  // {
7163  // name: 'data'
7164  // val: function () {},
7165  // methodExt: [ ... ],
7166  // propExt: [ ... ]
7167  // },
7168  // ...
7169  // ]
7170  // }
7171  // ]
7172 
7173  _Api.register = _api_register = function ( name, val )
7174  {
7175  if ( $.isArray( name ) ) {
7176  for ( var j=0, jen=name.length ; j<jen ; j++ ) {
7177  _Api.register( name[j], val );
7178  }
7179  return;
7180  }
7181 
7182  var
7183  i, ien,
7184  heir = name.split('.'),
7185  struct = __apiStruct,
7186  key, method;
7187 
7188  var find = function ( src, name ) {
7189  for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7190  if ( src[i].name === name ) {
7191  return src[i];
7192  }
7193  }
7194  return null;
7195  };
7196 
7197  for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7198  method = heir[i].indexOf('()') !== -1;
7199  key = method ?
7200  heir[i].replace('()', '') :
7201  heir[i];
7202 
7203  var src = find( struct, key );
7204  if ( ! src ) {
7205  src = {
7206  name: key,
7207  val: {},
7208  methodExt: [],
7209  propExt: []
7210  };
7211  struct.push( src );
7212  }
7213 
7214  if ( i === ien-1 ) {
7215  src.val = val;
7216  }
7217  else {
7218  struct = method ?
7219  src.methodExt :
7220  src.propExt;
7221  }
7222  }
7223  };
7224 
7225 
7226  _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7227  _Api.register( pluralName, val );
7228 
7229  _Api.register( singularName, function () {
7230  var ret = val.apply( this, arguments );
7231 
7232  if ( ret === this ) {
7233  // Returned item is the API instance that was passed in, return it
7234  return this;
7235  }
7236  else if ( ret instanceof _Api ) {
7237  // New API instance returned, want the value from the first item
7238  // in the returned array for the singular result.
7239  return ret.length ?
7240  $.isArray( ret[0] ) ?
7241  new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
7242  ret[0] :
7243  undefined;
7244  }
7245 
7246  // Non-API return - just fire it back
7247  return ret;
7248  } );
7249  };
7250 
7251 
7261  var __table_selector = function ( selector, a )
7262  {
7263  // Integer is used to pick out a table by index
7264  if ( typeof selector === 'number' ) {
7265  return [ a[ selector ] ];
7266  }
7267 
7268  // Perform a jQuery selector on the table nodes
7269  var nodes = $.map( a, function (el, i) {
7270  return el.nTable;
7271  } );
7272 
7273  return $(nodes)
7274  .filter( selector )
7275  .map( function (i) {
7276  // Need to translate back from the table node to the settings
7277  var idx = $.inArray( this, nodes );
7278  return a[ idx ];
7279  } )
7280  .toArray();
7281  };
7282 
7283 
7284 
7296  _api_register( 'tables()', function ( selector ) {
7297  // A new instance is created if there was a selector specified
7298  return selector ?
7299  new _Api( __table_selector( selector, this.context ) ) :
7300  this;
7301  } );
7302 
7303 
7304  _api_register( 'table()', function ( selector ) {
7305  var tables = this.tables( selector );
7306  var ctx = tables.context;
7307 
7308  // Truncate to the first matched table
7309  return ctx.length ?
7310  new _Api( ctx[0] ) :
7311  tables;
7312  } );
7313 
7314 
7315  _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
7316  return this.iterator( 'table', function ( ctx ) {
7317  return ctx.nTable;
7318  }, 1 );
7319  } );
7320 
7321 
7322  _api_registerPlural( 'tables().body()', 'table().body()' , function () {
7323  return this.iterator( 'table', function ( ctx ) {
7324  return ctx.nTBody;
7325  }, 1 );
7326  } );
7327 
7328 
7329  _api_registerPlural( 'tables().header()', 'table().header()' , function () {
7330  return this.iterator( 'table', function ( ctx ) {
7331  return ctx.nTHead;
7332  }, 1 );
7333  } );
7334 
7335 
7336  _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
7337  return this.iterator( 'table', function ( ctx ) {
7338  return ctx.nTFoot;
7339  }, 1 );
7340  } );
7341 
7342 
7343  _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7344  return this.iterator( 'table', function ( ctx ) {
7345  return ctx.nTableWrapper;
7346  }, 1 );
7347  } );
7348 
7349 
7350 
7354  _api_register( 'draw()', function ( paging ) {
7355  return this.iterator( 'table', function ( settings ) {
7356  if ( paging === 'page' ) {
7357  _fnDraw( settings );
7358  }
7359  else {
7360  if ( typeof paging === 'string' ) {
7361  paging = paging === 'full-hold' ?
7362  false :
7363  true;
7364  }
7365 
7366  _fnReDraw( settings, paging===false );
7367  }
7368  } );
7369  } );
7370 
7371 
7372  /**
7378  * Set the current page.
7379  *
7380  * Note that if you attempt to show a page which does not exist, DataTables will
7381  * not throw an error, but rather reset the paging.
7382  *
7383  * @param {integer|string} action The paging action to take. This can be one of:
7384  * * `integer` - The page index to jump to
7385  * * `string` - An action to take:
7386  * * `first` - Jump to first page.
7387  * * `next` - Jump to the next page
7388  * * `previous` - Jump to previous page
7389  * * `last` - Jump to the last page.
7390  * @returns {DataTables.Api} this
7391  */
7392  _api_register( 'page()', function ( action ) {
7393  if ( action === undefined ) {
7394  return this.page.info().page; // not an expensive call
7395  }
7396 
7397  // else, have an action to take on all tables
7398  return this.iterator( 'table', function ( settings ) {
7399  _fnPageChange( settings, action );
7400  } );
7401  } );
7402 
7403 
7422  _api_register( 'page.info()', function ( action ) {
7423  if ( this.context.length === 0 ) {
7424  return undefined;
7425  }
7426 
7427  var
7428  settings = this.context[0],
7429  start = settings._iDisplayStart,
7430  len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
7431  visRecords = settings.fnRecordsDisplay(),
7432  all = len === -1;
7433 
7434  return {
7435  "page": all ? 0 : Math.floor( start / len ),
7436  "pages": all ? 1 : Math.ceil( visRecords / len ),
7437  "start": start,
7438  "end": settings.fnDisplayEnd(),
7439  "length": len,
7440  "recordsTotal": settings.fnRecordsTotal(),
7441  "recordsDisplay": visRecords,
7442  "serverSide": _fnDataSource( settings ) === 'ssp'
7443  };
7444  } );
7445 
7446  /**
7453  * Set the current page length.
7454  *
7455  * @param {integer} Page length to set. Use `-1` to show all records.
7456  * @returns {DataTables.Api} this
7457  */
7458  _api_register( 'page.len()', function ( len ) {
7459  // Note that we can't call this function 'length()' because `length`
7460  // is a Javascript property of functions which defines how many arguments
7461  // the function expects.
7462  if ( len === undefined ) {
7463  return this.context.length !== 0 ?
7464  this.context[0]._iDisplayLength :
7465  undefined;
7466  }
7467 
7468  // else, set the page length
7469  return this.iterator( 'table', function ( settings ) {
7470  _fnLengthChange( settings, len );
7471  } );
7472  } );
7473 
7474 
7475 
7476  var __reload = function ( settings, holdPosition, callback ) {
7477  // Use the draw event to trigger a callback
7478  if ( callback ) {
7479  var api = new _Api( settings );
7480 
7481  api.one( 'draw', function () {
7482  callback( api.ajax.json() );
7483  } );
7484  }
7485 
7486  if ( _fnDataSource( settings ) == 'ssp' ) {
7487  _fnReDraw( settings, holdPosition );
7488  }
7489  else {
7490  _fnProcessingDisplay( settings, true );
7491 
7492  // Cancel an existing request
7493  var xhr = settings.jqXHR;
7494  if ( xhr && xhr.readyState !== 4 ) {
7495  xhr.abort();
7496  }
7497 
7498  // Trigger xhr
7499  _fnBuildAjax( settings, [], function( json ) {
7500  _fnClearTable( settings );
7501 
7502  var data = _fnAjaxDataSrc( settings, json );
7503  for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7504  _fnAddData( settings, data[i] );
7505  }
7506 
7507  _fnReDraw( settings, holdPosition );
7508  _fnProcessingDisplay( settings, false );
7509  } );
7510  }
7511  };
7512 
7513 
7521  _api_register( 'ajax.json()', function () {
7522  var ctx = this.context;
7523 
7524  if ( ctx.length > 0 ) {
7525  return ctx[0].json;
7526  }
7527 
7528  // else return undefined;
7529  } );
7530 
7531 
7535  _api_register( 'ajax.params()', function () {
7536  var ctx = this.context;
7537 
7538  if ( ctx.length > 0 ) {
7539  return ctx[0].oAjaxData;
7540  }
7541 
7542  // else return undefined;
7543  } );
7544 
7545 
7555  _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
7556  return this.iterator( 'table', function (settings) {
7557  __reload( settings, resetPaging===false, callback );
7558  } );
7559  } );
7560 
7561  /**
7568  * Set the Ajax URL. Note that this will set the URL for all tables in the
7569  * current context.
7570  *
7571  * @param {string} url URL to set.
7572  * @returns {DataTables.Api} this
7573  */
7574  _api_register( 'ajax.url()', function ( url ) {
7575  var ctx = this.context;
7576 
7577  if ( url === undefined ) {
7578  // get
7579  if ( ctx.length === 0 ) {
7580  return undefined;
7581  }
7582  ctx = ctx[0];
7583 
7584  return ctx.ajax ?
7585  $.isPlainObject( ctx.ajax ) ?
7586  ctx.ajax.url :
7587  ctx.ajax :
7588  ctx.sAjaxSource;
7589  }
7590 
7591  // set
7592  return this.iterator( 'table', function ( settings ) {
7593  if ( $.isPlainObject( settings.ajax ) ) {
7594  settings.ajax.url = url;
7595  }
7596  else {
7597  settings.ajax = url;
7598  }
7599  // No need to consider sAjaxSource here since DataTables gives priority
7600  // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
7601  // value of `sAjaxSource` redundant.
7602  } );
7603  } );
7604 
7605 
7615  _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
7616  // Same as a reload, but makes sense to present it for easy access after a
7617  // url change
7618  return this.iterator( 'table', function ( ctx ) {
7619  __reload( ctx, resetPaging===false, callback );
7620  } );
7621  } );
7622 
7623 
7624 
7625 
7626  var _selector_run = function ( type, selector, selectFn, settings, opts )
7627  {
7628  var
7629  out = [], res,
7630  a, i, ien, j, jen,
7631  selectorType = typeof selector;
7632 
7633  // Can't just check for isArray here, as an API or jQuery instance might be
7634  // given with their array like look
7635  if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7636  selector = [ selector ];
7637  }
7638 
7639  for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7640  a = selector[i] && selector[i].split ?
7641  selector[i].split(',') :
7642  [ selector[i] ];
7643 
7644  for ( j=0, jen=a.length ; j<jen ; j++ ) {
7645  res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
7646 
7647  if ( res && res.length ) {
7648  out = out.concat( res );
7649  }
7650  }
7651  }
7652 
7653  // selector extensions
7654  var ext = _ext.selector[ type ];
7655  if ( ext.length ) {
7656  for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7657  out = ext[i]( settings, opts, out );
7658  }
7659  }
7660 
7661  return _unique( out );
7662  };
7663 
7664 
7665  var _selector_opts = function ( opts )
7666  {
7667  if ( ! opts ) {
7668  opts = {};
7669  }
7670 
7671  // Backwards compatibility for 1.9- which used the terminology filter rather
7672  // than search
7673  if ( opts.filter && opts.search === undefined ) {
7674  opts.search = opts.filter;
7675  }
7676 
7677  return $.extend( {
7678  search: 'none',
7679  order: 'current',
7680  page: 'all'
7681  }, opts );
7682  };
7683 
7684 
7685  var _selector_first = function ( inst )
7686  {
7687  // Reduce the API instance to the first item found
7688  for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
7689  if ( inst[i].length > 0 ) {
7690  // Assign the first element to the first item in the instance
7691  // and truncate the instance and context
7692  inst[0] = inst[i];
7693  inst[0].length = 1;
7694  inst.length = 1;
7695  inst.context = [ inst.context[i] ];
7696 
7697  return inst;
7698  }
7699  }
7700 
7701  // Not found - return an empty instance
7702  inst.length = 0;
7703  return inst;
7704  };
7705 
7706 
7707  var _selector_row_indexes = function ( settings, opts )
7708  {
7709  var
7710  i, ien, tmp, a=[],
7711  displayFiltered = settings.aiDisplay,
7712  displayMaster = settings.aiDisplayMaster;
7713 
7714  var
7715  search = opts.search, // none, applied, removed
7716  order = opts.order, // applied, current, index (original - compatibility with 1.9)
7717  page = opts.page; // all, current
7718 
7719  if ( _fnDataSource( settings ) == 'ssp' ) {
7720  // In server-side processing mode, most options are irrelevant since
7721  // rows not shown don't exist and the index order is the applied order
7722  // Removed is a special case - for consistency just return an empty
7723  // array
7724  return search === 'removed' ?
7725  [] :
7726  _range( 0, displayMaster.length );
7727  }
7728  else if ( page == 'current' ) {
7729  // Current page implies that order=current and fitler=applied, since it is
7730  // fairly senseless otherwise, regardless of what order and search actually
7731  // are
7732  for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
7733  a.push( displayFiltered[i] );
7734  }
7735  }
7736  else if ( order == 'current' || order == 'applied' ) {
7737  a = search == 'none' ?
7738  displayMaster.slice() : // no search
7739  search == 'applied' ?
7740  displayFiltered.slice() : // applied search
7741  $.map( displayMaster, function (el, i) { // removed search
7742  return $.inArray( el, displayFiltered ) === -1 ? el : null;
7743  } );
7744  }
7745  else if ( order == 'index' || order == 'original' ) {
7746  for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
7747  if ( search == 'none' ) {
7748  a.push( i );
7749  }
7750  else { // applied | removed
7751  tmp = $.inArray( i, displayFiltered );
7752 
7753  if ((tmp === -1 && search == 'removed') ||
7754  (tmp >= 0 && search == 'applied') )
7755  {
7756  a.push( i );
7757  }
7758  }
7759  }
7760  }
7761 
7762  return a;
7763  };
7764 
7765 
7766  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7767  * Rows
7768  *
7769  * {} - no selector - use all available rows
7770  * {integer} - row aoData index
7771  * {node} - TR node
7772  * {string} - jQuery selector to apply to the TR elements
7773  * {array} - jQuery array of nodes, or simply an array of TR nodes
7774  *
7775  */
7776 
7777 
7778  var __row_selector = function ( settings, selector, opts )
7779  {
7780  var run = function ( sel ) {
7781  var selInt = _intVal( sel );
7782  var i, ien;
7783 
7784  // Short cut - selector is a number and no options provided (default is
7785  // all records, so no need to check if the index is in there, since it
7786  // must be - dev error if the index doesn't exist).
7787  if ( selInt !== null && ! opts ) {
7788  return [ selInt ];
7789  }
7790 
7791  var rows = _selector_row_indexes( settings, opts );
7792 
7793  if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
7794  // Selector - integer
7795  return [ selInt ];
7796  }
7797  else if ( ! sel ) {
7798  // Selector - none
7799  return rows;
7800  }
7801 
7802  // Selector - function
7803  if ( typeof sel === 'function' ) {
7804  return $.map( rows, function (idx) {
7805  var row = settings.aoData[ idx ];
7806  return sel( idx, row._aData, row.nTr ) ? idx : null;
7807  } );
7808  }
7809 
7810  // Get nodes in the order from the `rows` array with null values removed
7811  var nodes = _removeEmpty(
7812  _pluck_order( settings.aoData, rows, 'nTr' )
7813  );
7814 
7815  // Selector - node
7816  if ( sel.nodeName ) {
7817  if ( sel._DT_RowIndex !== undefined ) {
7818  return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup
7819  }
7820  else if ( sel._DT_CellIndex ) {
7821  return [ sel._DT_CellIndex.row ];
7822  }
7823  else {
7824  var host = $(sel).closest('*[data-dt-row]');
7825  return host.length ?
7826  [ host.data('dt-row') ] :
7827  [];
7828  }
7829  }
7830 
7831  // ID selector. Want to always be able to select rows by id, regardless
7832  // of if the tr element has been created or not, so can't rely upon
7833  // jQuery here - hence a custom implementation. This does not match
7834  // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
7835  // but to select it using a CSS selector engine (like Sizzle or
7836  // querySelect) it would need to need to be escaped for some characters.
7837  // DataTables simplifies this for row selectors since you can select
7838  // only a row. A # indicates an id any anything that follows is the id -
7839  // unescaped.
7840  if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
7841  // get row index from id
7842  var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
7843  if ( rowObj !== undefined ) {
7844  return [ rowObj.idx ];
7845  }
7846 
7847  // need to fall through to jQuery in case there is DOM id that
7848  // matches
7849  }
7850 
7851  // Selector - jQuery selector string, array of nodes or jQuery object/
7852  // As jQuery's .filter() allows jQuery objects to be passed in filter,
7853  // it also allows arrays, so this will cope with all three options
7854  return $(nodes)
7855  .filter( sel )
7856  .map( function () {
7857  return this._DT_RowIndex;
7858  } )
7859  .toArray();
7860  };
7861 
7862  return _selector_run( 'row', selector, run, settings, opts );
7863  };
7864 
7865 
7866  _api_register( 'rows()', function ( selector, opts ) {
7867  // argument shifting
7868  if ( selector === undefined ) {
7869  selector = '';
7870  }
7871  else if ( $.isPlainObject( selector ) ) {
7872  opts = selector;
7873  selector = '';
7874  }
7875 
7876  opts = _selector_opts( opts );
7877 
7878  var inst = this.iterator( 'table', function ( settings ) {
7879  return __row_selector( settings, selector, opts );
7880  }, 1 );
7881 
7882  // Want argument shifting here and in __row_selector?
7883  inst.selector.rows = selector;
7884  inst.selector.opts = opts;
7885 
7886  return inst;
7887  } );
7888 
7889  _api_register( 'rows().nodes()', function () {
7890  return this.iterator( 'row', function ( settings, row ) {
7891  return settings.aoData[ row ].nTr || undefined;
7892  }, 1 );
7893  } );
7894 
7895  _api_register( 'rows().data()', function () {
7896  return this.iterator( true, 'rows', function ( settings, rows ) {
7897  return _pluck_order( settings.aoData, rows, '_aData' );
7898  }, 1 );
7899  } );
7900 
7901  _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
7902  return this.iterator( 'row', function ( settings, row ) {
7903  var r = settings.aoData[ row ];
7904  return type === 'search' ? r._aFilterData : r._aSortData;
7905  }, 1 );
7906  } );
7907 
7908  _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
7909  return this.iterator( 'row', function ( settings, row ) {
7910  _fnInvalidate( settings, row, src );
7911  } );
7912  } );
7913 
7914  _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
7915  return this.iterator( 'row', function ( settings, row ) {
7916  return row;
7917  }, 1 );
7918  } );
7919 
7920  _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
7921  var a = [];
7922  var context = this.context;
7923 
7924  // `iterator` will drop undefined values, but in this case we want them
7925  for ( var i=0, ien=context.length ; i<ien ; i++ ) {
7926  for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
7927  var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
7928  a.push( (hash === true ? '#' : '' )+ id );
7929  }
7930  }
7931 
7932  return new _Api( context, a );
7933  } );
7934 
7935  _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
7936  var that = this;
7937 
7938  this.iterator( 'row', function ( settings, row, thatIdx ) {
7939  var data = settings.aoData;
7940  var rowData = data[ row ];
7941  var i, ien, j, jen;
7942  var loopRow, loopCells;
7943 
7944  data.splice( row, 1 );
7945 
7946  // Update the cached indexes
7947  for ( i=0, ien=data.length ; i<ien ; i++ ) {
7948  loopRow = data[i];
7949  loopCells = loopRow.anCells;
7950 
7951  // Rows
7952  if ( loopRow.nTr !== null ) {
7953  loopRow.nTr._DT_RowIndex = i;
7954  }
7955 
7956  // Cells
7957  if ( loopCells !== null ) {
7958  for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
7959  loopCells[j]._DT_CellIndex.row = i;
7960  }
7961  }
7962  }
7963 
7964  // Delete from the display arrays
7965  _fnDeleteIndex( settings.aiDisplayMaster, row );
7966  _fnDeleteIndex( settings.aiDisplay, row );
7967  _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
7968 
7969  // Check for an 'overflow' they case for displaying the table
7970  _fnLengthOverflow( settings );
7971 
7972  // Remove the row's ID reference if there is one
7973  var id = settings.rowIdFn( rowData._aData );
7974  if ( id !== undefined ) {
7975  delete settings.aIds[ id ];
7976  }
7977  } );
7978 
7979  this.iterator( 'table', function ( settings ) {
7980  for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
7981  settings.aoData[i].idx = i;
7982  }
7983  } );
7984 
7985  return this;
7986  } );
7987 
7988 
7989  _api_register( 'rows.add()', function ( rows ) {
7990  var newRows = this.iterator( 'table', function ( settings ) {
7991  var row, i, ien;
7992  var out = [];
7993 
7994  for ( i=0, ien=rows.length ; i<ien ; i++ ) {
7995  row = rows[i];
7996 
7997  if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
7998  out.push( _fnAddTr( settings, row )[0] );
7999  }
8000  else {
8001  out.push( _fnAddData( settings, row ) );
8002  }
8003  }
8004 
8005  return out;
8006  }, 1 );
8007 
8008  // Return an Api.rows() extended instance, so rows().nodes() etc can be used
8009  var modRows = this.rows( -1 );
8010  modRows.pop();
8011  $.merge( modRows, newRows );
8012 
8013  return modRows;
8014  } );
8015 
8016 
8017 
8018 
8019 
8023  _api_register( 'row()', function ( selector, opts ) {
8024  return _selector_first( this.rows( selector, opts ) );
8025  } );
8026 
8027 
8028  _api_register( 'row().data()', function ( data ) {
8029  var ctx = this.context;
8030 
8031  if ( data === undefined ) {
8032  // Get
8033  return ctx.length && this.length ?
8034  ctx[0].aoData[ this[0] ]._aData :
8035  undefined;
8036  }
8037 
8038  // Set
8039  ctx[0].aoData[ this[0] ]._aData = data;
8040 
8041  // Automatically invalidate
8042  _fnInvalidate( ctx[0], this[0], 'data' );
8043 
8044  return this;
8045  } );
8046 
8047 
8048  _api_register( 'row().node()', function () {
8049  var ctx = this.context;
8050 
8051  return ctx.length && this.length ?
8052  ctx[0].aoData[ this[0] ].nTr || null :
8053  null;
8054  } );
8055 
8056 
8057  _api_register( 'row.add()', function ( row ) {
8058  // Allow a jQuery object to be passed in - only a single row is added from
8059  // it though - the first element in the set
8060  if ( row instanceof $ && row.length ) {
8061  row = row[0];
8062  }
8063 
8064  var rows = this.iterator( 'table', function ( settings ) {
8065  if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8066  return _fnAddTr( settings, row )[0];
8067  }
8068  return _fnAddData( settings, row );
8069  } );
8070 
8071  // Return an Api.rows() extended instance, with the newly added row selected
8072  return this.row( rows[0] );
8073  } );
8074 
8075 
8076 
8077  var __details_add = function ( ctx, row, data, klass )
8078  {
8079  // Convert to array of TR elements
8080  var rows = [];
8081  var addRow = function ( r, k ) {
8082  // Recursion to allow for arrays of jQuery objects
8083  if ( $.isArray( r ) || r instanceof $ ) {
8084  for ( var i=0, ien=r.length ; i<ien ; i++ ) {
8085  addRow( r[i], k );
8086  }
8087  return;
8088  }
8089 
8090  // If we get a TR element, then just add it directly - up to the dev
8091  // to add the correct number of columns etc
8092  if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
8093  rows.push( r );
8094  }
8095  else {
8096  // Otherwise create a row with a wrapper
8097  var created = $('<tr><td/></tr>').addClass( k );
8098  $('td', created)
8099  .addClass( k )
8100  .html( r )
8101  [0].colSpan = _fnVisbleColumns( ctx );
8102 
8103  rows.push( created[0] );
8104  }
8105  };
8106 
8107  addRow( data, klass );
8108 
8109  if ( row._details ) {
8110  row._details.remove();
8111  }
8112 
8113  row._details = $(rows);
8114 
8115  // If the children were already shown, that state should be retained
8116  if ( row._detailsShow ) {
8117  row._details.insertAfter( row.nTr );
8118  }
8119  };
8120 
8121 
8122  var __details_remove = function ( api, idx )
8123  {
8124  var ctx = api.context;
8125 
8126  if ( ctx.length ) {
8127  var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
8128 
8129  if ( row && row._details ) {
8130  row._details.remove();
8131 
8132  row._detailsShow = undefined;
8133  row._details = undefined;
8134  }
8135  }
8136  };
8137 
8138 
8139  var __details_display = function ( api, show ) {
8140  var ctx = api.context;
8141 
8142  if ( ctx.length && api.length ) {
8143  var row = ctx[0].aoData[ api[0] ];
8144 
8145  if ( row._details ) {
8146  row._detailsShow = show;
8147 
8148  if ( show ) {
8149  row._details.insertAfter( row.nTr );
8150  }
8151  else {
8152  row._details.detach();
8153  }
8154 
8155  __details_events( ctx[0] );
8156  }
8157  }
8158  };
8159 
8160 
8161  var __details_events = function ( settings )
8162  {
8163  var api = new _Api( settings );
8164  var namespace = '.dt.DT_details';
8165  var drawEvent = 'draw'+namespace;
8166  var colvisEvent = 'column-visibility'+namespace;
8167  var destroyEvent = 'destroy'+namespace;
8168  var data = settings.aoData;
8169 
8170  api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
8171 
8172  if ( _pluck( data, '_details' ).length > 0 ) {
8173  // On each draw, insert the required elements into the document
8174  api.on( drawEvent, function ( e, ctx ) {
8175  if ( settings !== ctx ) {
8176  return;
8177  }
8178 
8179  api.rows( {page:'current'} ).eq(0).each( function (idx) {
8180  // Internal data grab
8181  var row = data[ idx ];
8182 
8183  if ( row._detailsShow ) {
8184  row._details.insertAfter( row.nTr );
8185  }
8186  } );
8187  } );
8188 
8189  // Column visibility change - update the colspan
8190  api.on( colvisEvent, function ( e, ctx, idx, vis ) {
8191  if ( settings !== ctx ) {
8192  return;
8193  }
8194 
8195  // Update the colspan for the details rows (note, only if it already has
8196  // a colspan)
8197  var row, visible = _fnVisbleColumns( ctx );
8198 
8199  for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8200  row = data[i];
8201 
8202  if ( row._details ) {
8203  row._details.children('td[colspan]').attr('colspan', visible );
8204  }
8205  }
8206  } );
8207 
8208  // Table destroyed - nuke any child rows
8209  api.on( destroyEvent, function ( e, ctx ) {
8210  if ( settings !== ctx ) {
8211  return;
8212  }
8213 
8214  for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8215  if ( data[i]._details ) {
8216  __details_remove( api, i );
8217  }
8218  }
8219  } );
8220  }
8221  };
8222 
8223  // Strings for the method names to help minification
8224  var _emp = '';
8225  var _child_obj = _emp+'row().child';
8226  var _child_mth = _child_obj+'()';
8227 
8228  // data can be:
8229  // tr
8230  // string
8231  // jQuery or array of any of the above
8232  _api_register( _child_mth, function ( data, klass ) {
8233  var ctx = this.context;
8234 
8235  if ( data === undefined ) {
8236  // get
8237  return ctx.length && this.length ?
8238  ctx[0].aoData[ this[0] ]._details :
8239  undefined;
8240  }
8241  else if ( data === true ) {
8242  // show
8243  this.child.show();
8244  }
8245  else if ( data === false ) {
8246  // remove
8247  __details_remove( this );
8248  }
8249  else if ( ctx.length && this.length ) {
8250  // set
8251  __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8252  }
8253 
8254  return this;
8255  } );
8256 
8257 
8258  _api_register( [
8259  _child_obj+'.show()',
8260  _child_mth+'.show()' // only when `child()` was called with parameters (without
8261  ], function ( show ) { // it returns an object and this method is not executed)
8262  __details_display( this, true );
8263  return this;
8264  } );
8265 
8266 
8267  _api_register( [
8268  _child_obj+'.hide()',
8269  _child_mth+'.hide()' // only when `child()` was called with parameters (without
8270  ], function () { // it returns an object and this method is not executed)
8271  __details_display( this, false );
8272  return this;
8273  } );
8274 
8275 
8276  _api_register( [
8277  _child_obj+'.remove()',
8278  _child_mth+'.remove()' // only when `child()` was called with parameters (without
8279  ], function () { // it returns an object and this method is not executed)
8280  __details_remove( this );
8281  return this;
8282  } );
8283 
8284 
8285  _api_register( _child_obj+'.isShown()', function () {
8286  var ctx = this.context;
8287 
8288  if ( ctx.length && this.length ) {
8289  // _detailsShown as false or undefined will fall through to return false
8290  return ctx[0].aoData[ this[0] ]._detailsShow || false;
8291  }
8292  return false;
8293  } );
8294 
8295 
8296 
8297  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8298  * Columns
8299  *
8300  * {integer} - column index (>=0 count from left, <0 count from right)
8301  * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
8302  * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
8303  * "{string}:name" - column name
8304  * "{string}" - jQuery selector on column header nodes
8305  *
8306  */
8307 
8308  // can be an array of these items, comma separated list, or an array of comma
8309  // separated lists
8310 
8311  var __re_column_selector = /^(.+):(name|visIdx|visible)$/;
8312 
8313 
8314  // r1 and r2 are redundant - but it means that the parameters match for the
8315  // iterator callback in columns().data()
8316  var __columnData = function ( settings, column, r1, r2, rows ) {
8317  var a = [];
8318  for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
8319  a.push( _fnGetCellData( settings, rows[row], column ) );
8320  }
8321  return a;
8322  };
8323 
8324 
8325  var __column_selector = function ( settings, selector, opts )
8326  {
8327  var
8328  columns = settings.aoColumns,
8329  names = _pluck( columns, 'sName' ),
8330  nodes = _pluck( columns, 'nTh' );
8331 
8332  var run = function ( s ) {
8333  var selInt = _intVal( s );
8334 
8335  // Selector - all
8336  if ( s === '' ) {
8337  return _range( columns.length );
8338  }
8339 
8340  // Selector - index
8341  if ( selInt !== null ) {
8342  return [ selInt >= 0 ?
8343  selInt : // Count from left
8344  columns.length + selInt // Count from right (+ because its a negative value)
8345  ];
8346  }
8347 
8348  // Selector = function
8349  if ( typeof s === 'function' ) {
8350  var rows = _selector_row_indexes( settings, opts );
8351 
8352  return $.map( columns, function (col, idx) {
8353  return s(
8354  idx,
8355  __columnData( settings, idx, 0, 0, rows ),
8356  nodes[ idx ]
8357  ) ? idx : null;
8358  } );
8359  }
8360 
8361  // jQuery or string selector
8362  var match = typeof s === 'string' ?
8363  s.match( __re_column_selector ) :
8364  '';
8365 
8366  if ( match ) {
8367  switch( match[2] ) {
8368  case 'visIdx':
8369  case 'visible':
8370  var idx = parseInt( match[1], 10 );
8371  // Visible index given, convert to column index
8372  if ( idx < 0 ) {
8373  // Counting from the right
8374  var visColumns = $.map( columns, function (col,i) {
8375  return col.bVisible ? i : null;
8376  } );
8377  return [ visColumns[ visColumns.length + idx ] ];
8378  }
8379  // Counting from the left
8380  return [ _fnVisibleToColumnIndex( settings, idx ) ];
8381 
8382  case 'name':
8383  // match by name. `names` is column index complete and in order
8384  return $.map( names, function (name, i) {
8385  return name === match[1] ? i : null;
8386  } );
8387 
8388  default:
8389  return [];
8390  }
8391  }
8392 
8393  // Cell in the table body
8394  if ( s.nodeName && s._DT_CellIndex ) {
8395  return [ s._DT_CellIndex.column ];
8396  }
8397 
8398  // jQuery selector on the TH elements for the columns
8399  var jqResult = $( nodes )
8400  .filter( s )
8401  .map( function () {
8402  return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8403  } )
8404  .toArray();
8405 
8406  if ( jqResult.length || ! s.nodeName ) {
8407  return jqResult;
8408  }
8409 
8410  // Otherwise a node which might have a `dt-column` data attribute, or be
8411  // a child or such an element
8412  var host = $(s).closest('*[data-dt-column]');
8413  return host.length ?
8414  [ host.data('dt-column') ] :
8415  [];
8416  };
8417 
8418  return _selector_run( 'column', selector, run, settings, opts );
8419  };
8420 
8421 
8422  var __setColumnVis = function ( settings, column, vis, recalc ) {
8423  var
8424  cols = settings.aoColumns,
8425  col = cols[ column ],
8426  data = settings.aoData,
8427  row, cells, i, ien, tr;
8428 
8429  // Get
8430  if ( vis === undefined ) {
8431  return col.bVisible;
8432  }
8433 
8434  // Set
8435  // No change
8436  if ( col.bVisible === vis ) {
8437  return;
8438  }
8439 
8440  if ( vis ) {
8441  // Insert column
8442  // Need to decide if we should use appendChild or insertBefore
8443  var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
8444 
8445  for ( i=0, ien=data.length ; i<ien ; i++ ) {
8446  tr = data[i].nTr;
8447  cells = data[i].anCells;
8448 
8449  if ( tr ) {
8450  // insertBefore can act like appendChild if 2nd arg is null
8451  tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
8452  }
8453  }
8454  }
8455  else {
8456  // Remove column
8457  $( _pluck( settings.aoData, 'anCells', column ) ).detach();
8458  }
8459 
8460  // Common actions
8461  col.bVisible = vis;
8462  _fnDrawHead( settings, settings.aoHeader );
8463  _fnDrawHead( settings, settings.aoFooter );
8464 
8465  if ( recalc === undefined || recalc ) {
8466  // Automatically adjust column sizing
8467  _fnAdjustColumnSizing( settings );
8468  }
8469 
8470  _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, recalc] );
8471 
8472  _fnSaveState( settings );
8473  };
8474 
8475 
8476  _api_register( 'columns()', function ( selector, opts ) {
8477  // argument shifting
8478  if ( selector === undefined ) {
8479  selector = '';
8480  }
8481  else if ( $.isPlainObject( selector ) ) {
8482  opts = selector;
8483  selector = '';
8484  }
8485 
8486  opts = _selector_opts( opts );
8487 
8488  var inst = this.iterator( 'table', function ( settings ) {
8489  return __column_selector( settings, selector, opts );
8490  }, 1 );
8491 
8492  // Want argument shifting here and in _row_selector?
8493  inst.selector.cols = selector;
8494  inst.selector.opts = opts;
8495 
8496  return inst;
8497  } );
8498 
8499  _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
8500  return this.iterator( 'column', function ( settings, column ) {
8501  return settings.aoColumns[column].nTh;
8502  }, 1 );
8503  } );
8504 
8505  _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
8506  return this.iterator( 'column', function ( settings, column ) {
8507  return settings.aoColumns[column].nTf;
8508  }, 1 );
8509  } );
8510 
8511  _api_registerPlural( 'columns().data()', 'column().data()', function () {
8512  return this.iterator( 'column-rows', __columnData, 1 );
8513  } );
8514 
8515  _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8516  return this.iterator( 'column', function ( settings, column ) {
8517  return settings.aoColumns[column].mData;
8518  }, 1 );
8519  } );
8520 
8521  _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8522  return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8523  return _pluck_order( settings.aoData, rows,
8524  type === 'search' ? '_aFilterData' : '_aSortData', column
8525  );
8526  }, 1 );
8527  } );
8528 
8529  _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8530  return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8531  return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8532  }, 1 );
8533  } );
8534 
8535  _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
8536  return this.iterator( 'column', function ( settings, column ) {
8537  if ( vis === undefined ) {
8538  return settings.aoColumns[ column ].bVisible;
8539  } // else
8540  __setColumnVis( settings, column, vis, calc );
8541  } );
8542  } );
8543 
8544  _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8545  return this.iterator( 'column', function ( settings, column ) {
8546  return type === 'visible' ?
8547  _fnColumnIndexToVisible( settings, column ) :
8548  column;
8549  }, 1 );
8550  } );
8551 
8552  _api_register( 'columns.adjust()', function () {
8553  return this.iterator( 'table', function ( settings ) {
8554  _fnAdjustColumnSizing( settings );
8555  }, 1 );
8556  } );
8557 
8558  _api_register( 'column.index()', function ( type, idx ) {
8559  if ( this.context.length !== 0 ) {
8560  var ctx = this.context[0];
8561 
8562  if ( type === 'fromVisible' || type === 'toData' ) {
8563  return _fnVisibleToColumnIndex( ctx, idx );
8564  }
8565  else if ( type === 'fromData' || type === 'toVisible' ) {
8566  return _fnColumnIndexToVisible( ctx, idx );
8567  }
8568  }
8569  } );
8570 
8571  _api_register( 'column()', function ( selector, opts ) {
8572  return _selector_first( this.columns( selector, opts ) );
8573  } );
8574 
8575 
8576 
8577 
8578  var __cell_selector = function ( settings, selector, opts )
8579  {
8580  var data = settings.aoData;
8581  var rows = _selector_row_indexes( settings, opts );
8582  var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
8583  var allCells = $( [].concat.apply([], cells) );
8584  var row;
8585  var columns = settings.aoColumns.length;
8586  var a, i, ien, j, o, host;
8587 
8588  var run = function ( s ) {
8589  var fnSelector = typeof s === 'function';
8590 
8591  if ( s === null || s === undefined || fnSelector ) {
8592  // All cells and function selectors
8593  a = [];
8594 
8595  for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8596  row = rows[i];
8597 
8598  for ( j=0 ; j<columns ; j++ ) {
8599  o = {
8600  row: row,
8601  column: j
8602  };
8603 
8604  if ( fnSelector ) {
8605  // Selector - function
8606  host = data[ row ];
8607 
8608  if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
8609  a.push( o );
8610  }
8611  }
8612  else {
8613  // Selector - all
8614  a.push( o );
8615  }
8616  }
8617  }
8618 
8619  return a;
8620  }
8621 
8622  // Selector - index
8623  if ( $.isPlainObject( s ) ) {
8624  return [s];
8625  }
8626 
8627  // Selector - jQuery filtered cells
8628  var jqResult = allCells
8629  .filter( s )
8630  .map( function (i, el) {
8631  return { // use a new object, in case someone changes the values
8632  row: el._DT_CellIndex.row,
8633  column: el._DT_CellIndex.column
8634  };
8635  } )
8636  .toArray();
8637 
8638  if ( jqResult.length || ! s.nodeName ) {
8639  return jqResult;
8640  }
8641 
8642  // Otherwise the selector is a node, and there is one last option - the
8643  // element might be a child of an element which has dt-row and dt-column
8644  // data attributes
8645  host = $(s).closest('*[data-dt-row]');
8646  return host.length ?
8647  [ {
8648  row: host.data('dt-row'),
8649  column: host.data('dt-column')
8650  } ] :
8651  [];
8652  };
8653 
8654  return _selector_run( 'cell', selector, run, settings, opts );
8655  };
8656 
8657 
8658 
8659 
8660  _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
8661  // Argument shifting
8662  if ( $.isPlainObject( rowSelector ) ) {
8663  // Indexes
8664  if ( rowSelector.row === undefined ) {
8665  // Selector options in first parameter
8666  opts = rowSelector;
8667  rowSelector = null;
8668  }
8669  else {
8670  // Cell index objects in first parameter
8671  opts = columnSelector;
8672  columnSelector = null;
8673  }
8674  }
8675  if ( $.isPlainObject( columnSelector ) ) {
8676  opts = columnSelector;
8677  columnSelector = null;
8678  }
8679 
8680  // Cell selector
8681  if ( columnSelector === null || columnSelector === undefined ) {
8682  return this.iterator( 'table', function ( settings ) {
8683  return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
8684  } );
8685  }
8686 
8687  // Row + column selector
8688  var columns = this.columns( columnSelector, opts );
8689  var rows = this.rows( rowSelector, opts );
8690  var a, i, ien, j, jen;
8691 
8692  var cells = this.iterator( 'table', function ( settings, idx ) {
8693  a = [];
8694 
8695  for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
8696  for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
8697  a.push( {
8698  row: rows[idx][i],
8699  column: columns[idx][j]
8700  } );
8701  }
8702  }
8703 
8704  return a;
8705  }, 1 );
8706 
8707  $.extend( cells.selector, {
8708  cols: columnSelector,
8709  rows: rowSelector,
8710  opts: opts
8711  } );
8712 
8713  return cells;
8714  } );
8715 
8716 
8717  _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
8718  return this.iterator( 'cell', function ( settings, row, column ) {
8719  var data = settings.aoData[ row ];
8720 
8721  return data && data.anCells ?
8722  data.anCells[ column ] :
8723  undefined;
8724  }, 1 );
8725  } );
8726 
8727 
8728  _api_register( 'cells().data()', function () {
8729  return this.iterator( 'cell', function ( settings, row, column ) {
8730  return _fnGetCellData( settings, row, column );
8731  }, 1 );
8732  } );
8733 
8734 
8735  _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
8736  type = type === 'search' ? '_aFilterData' : '_aSortData';
8737 
8738  return this.iterator( 'cell', function ( settings, row, column ) {
8739  return settings.aoData[ row ][ type ][ column ];
8740  }, 1 );
8741  } );
8742 
8743 
8744  _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
8745  return this.iterator( 'cell', function ( settings, row, column ) {
8746  return _fnGetCellData( settings, row, column, type );
8747  }, 1 );
8748  } );
8749 
8750 
8751  _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
8752  return this.iterator( 'cell', function ( settings, row, column ) {
8753  return {
8754  row: row,
8755  column: column,
8756  columnVisible: _fnColumnIndexToVisible( settings, column )
8757  };
8758  }, 1 );
8759  } );
8760 
8761 
8762  _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
8763  return this.iterator( 'cell', function ( settings, row, column ) {
8764  _fnInvalidate( settings, row, src, column );
8765  } );
8766  } );
8767 
8768 
8769 
8770  _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
8771  return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
8772  } );
8773 
8774 
8775  _api_register( 'cell().data()', function ( data ) {
8776  var ctx = this.context;
8777  var cell = this[0];
8778 
8779  if ( data === undefined ) {
8780  // Get
8781  return ctx.length && cell.length ?
8782  _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
8783  undefined;
8784  }
8785 
8786  // Set
8787  _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
8788  _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
8789 
8790  return this;
8791  } );
8792 
8793 
8794  /**
8806  * Set the ordering for the table.
8807  *
8808  * @param {integer} order Column index to sort upon.
8809  * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
8810  * @returns {DataTables.Api} this
8811  *//**
8812  * Set the ordering for the table.
8813  *
8814  * @param {array} order 1D array of sorting information to be applied.
8815  * @param {array} [...] Optional additional sorting conditions
8816  * @returns {DataTables.Api} this
8817  *//**
8818  * Set the ordering for the table.
8819  *
8820  * @param {array} order 2D array of sorting information to be applied.
8821  * @returns {DataTables.Api} this
8822  */
8823  _api_register( 'order()', function ( order, dir ) {
8824  var ctx = this.context;
8825 
8826  if ( order === undefined ) {
8827  // get
8828  return ctx.length !== 0 ?
8829  ctx[0].aaSorting :
8830  undefined;
8831  }
8832 
8833  // set
8834  if ( typeof order === 'number' ) {
8835  // Simple column / direction passed in
8836  order = [ [ order, dir ] ];
8837  }
8838  else if ( ! $.isArray( order[0] ) ) {
8839  // Arguments passed in (list of 1D arrays)
8840  order = Array.prototype.slice.call( arguments );
8841  }
8842  // otherwise a 2D array was passed in
8843 
8844  return this.iterator( 'table', function ( settings ) {
8845  settings.aaSorting = order.slice();
8846  } );
8847  } );
8848 
8849 
8860  _api_register( 'order.listener()', function ( node, column, callback ) {
8861  return this.iterator( 'table', function ( settings ) {
8862  _fnSortAttachListener( settings, node, column, callback );
8863  } );
8864  } );
8865 
8866 
8867  _api_register( 'order.fixed()', function ( set ) {
8868  if ( ! set ) {
8869  var ctx = this.context;
8870  var fixed = ctx.length ?
8871  ctx[0].aaSortingFixed :
8872  undefined;
8873 
8874  return $.isArray( fixed ) ?
8875  { pre: fixed } :
8876  fixed;
8877  }
8878 
8879  return this.iterator( 'table', function ( settings ) {
8880  settings.aaSortingFixed = $.extend( true, {}, set );
8881  } );
8882  } );
8883 
8884 
8885  // Order by the selected column(s)
8886  _api_register( [
8887  'columns().order()',
8888  'column().order()'
8889  ], function ( dir ) {
8890  var that = this;
8891 
8892  return this.iterator( 'table', function ( settings, i ) {
8893  var sort = [];
8894 
8895  $.each( that[i], function (j, col) {
8896  sort.push( [ col, dir ] );
8897  } );
8898 
8899  settings.aaSorting = sort;
8900  } );
8901  } );
8902 
8903 
8904 
8905  _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
8906  var ctx = this.context;
8907 
8908  if ( input === undefined ) {
8909  // get
8910  return ctx.length !== 0 ?
8911  ctx[0].oPreviousSearch.sSearch :
8912  undefined;
8913  }
8914 
8915  // set
8916  return this.iterator( 'table', function ( settings ) {
8917  if ( ! settings.oFeatures.bFilter ) {
8918  return;
8919  }
8920 
8921  _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
8922  "sSearch": input+"",
8923  "bRegex": regex === null ? false : regex,
8924  "bSmart": smart === null ? true : smart,
8925  "bCaseInsensitive": caseInsen === null ? true : caseInsen
8926  } ), 1 );
8927  } );
8928  } );
8929 
8930 
8931  _api_registerPlural(
8932  'columns().search()',
8933  'column().search()',
8934  function ( input, regex, smart, caseInsen ) {
8935  return this.iterator( 'column', function ( settings, column ) {
8936  var preSearch = settings.aoPreSearchCols;
8937 
8938  if ( input === undefined ) {
8939  // get
8940  return preSearch[ column ].sSearch;
8941  }
8942 
8943  // set
8944  if ( ! settings.oFeatures.bFilter ) {
8945  return;
8946  }
8947 
8948  $.extend( preSearch[ column ], {
8949  "sSearch": input+"",
8950  "bRegex": regex === null ? false : regex,
8951  "bSmart": smart === null ? true : smart,
8952  "bCaseInsensitive": caseInsen === null ? true : caseInsen
8953  } );
8954 
8955  _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
8956  } );
8957  }
8958  );
8959 
8960  /*
8961  * State API methods
8962  */
8963 
8964  _api_register( 'state()', function () {
8965  return this.context.length ?
8966  this.context[0].oSavedState :
8967  null;
8968  } );
8969 
8970 
8971  _api_register( 'state.clear()', function () {
8972  return this.iterator( 'table', function ( settings ) {
8973  // Save an empty object
8974  settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
8975  } );
8976  } );
8977 
8978 
8979  _api_register( 'state.loaded()', function () {
8980  return this.context.length ?
8981  this.context[0].oLoadedState :
8982  null;
8983  } );
8984 
8985 
8986  _api_register( 'state.save()', function () {
8987  return this.iterator( 'table', function ( settings ) {
8988  _fnSaveState( settings );
8989  } );
8990  } );
8991 
8992 
8993 
9009  DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
9010  {
9011  var aThis = DataTable.version.split('.');
9012  var aThat = version.split('.');
9013  var iThis, iThat;
9014 
9015  for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
9016  iThis = parseInt( aThis[i], 10 ) || 0;
9017  iThat = parseInt( aThat[i], 10 ) || 0;
9018 
9019  // Parts are the same, keep comparing
9020  if (iThis === iThat) {
9021  continue;
9022  }
9023 
9024  // Parts are different, return immediately
9025  return iThis > iThat;
9026  }
9027 
9028  return true;
9029  };
9030 
9031 
9047  DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
9048  {
9049  var t = $(table).get(0);
9050  var is = false;
9051 
9052  $.each( DataTable.settings, function (i, o) {
9053  var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
9054  var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
9055 
9056  if ( o.nTable === t || head === t || foot === t ) {
9057  is = true;
9058  }
9059  } );
9060 
9061  return is;
9062  };
9063 
9064 
9081  DataTable.tables = DataTable.fnTables = function ( visible )
9082  {
9083  var api = false;
9084 
9085  if ( $.isPlainObject( visible ) ) {
9086  api = visible.api;
9087  visible = visible.visible;
9088  }
9089 
9090  var a = $.map( DataTable.settings, function (o) {
9091  if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
9092  return o.nTable;
9093  }
9094  } );
9095 
9096  return api ?
9097  new _Api( a ) :
9098  a;
9099  };
9100 
9101 
9112  DataTable.util = {
9121  throttle: _fnThrottle,
9122 
9123 
9130  escapeRegex: _fnEscapeRegex
9131  };
9132 
9133 
9147  DataTable.camelToHungarian = _fnCamelToHungarian;
9148 
9149 
9150 
9154  _api_register( '$()', function ( selector, opts ) {
9155  var
9156  rows = this.rows( opts ).nodes(), // Get all rows
9157  jqRows = $(rows);
9158 
9159  return $( [].concat(
9160  jqRows.filter( selector ).toArray(),
9161  jqRows.find( selector ).toArray()
9162  ) );
9163  } );
9164 
9165 
9166  // jQuery functions to operate on the tables
9167  $.each( [ 'on', 'one', 'off' ], function (i, key) {
9168  _api_register( key+'()', function ( /* event, handler */ ) {
9169  var args = Array.prototype.slice.call(arguments);
9170 
9171  // Add the `dt` namespace automatically if it isn't already present
9172  if ( ! args[0].match(/\.dt\b/) ) {
9173  args[0] += '.dt';
9174  }
9175 
9176  var inst = $( this.tables().nodes() );
9177  inst[key].apply( inst, args );
9178  return this;
9179  } );
9180  } );
9181 
9182 
9183  _api_register( 'clear()', function () {
9184  return this.iterator( 'table', function ( settings ) {
9185  _fnClearTable( settings );
9186  } );
9187  } );
9188 
9189 
9190  _api_register( 'settings()', function () {
9191  return new _Api( this.context, this.context );
9192  } );
9193 
9194 
9195  _api_register( 'init()', function () {
9196  var ctx = this.context;
9197  return ctx.length ? ctx[0].oInit : null;
9198  } );
9199 
9200 
9201  _api_register( 'data()', function () {
9202  return this.iterator( 'table', function ( settings ) {
9203  return _pluck( settings.aoData, '_aData' );
9204  } ).flatten();
9205  } );
9206 
9207 
9208  _api_register( 'destroy()', function ( remove ) {
9209  remove = remove || false;
9210 
9211  return this.iterator( 'table', function ( settings ) {
9212  var orig = settings.nTableWrapper.parentNode;
9213  var classes = settings.oClasses;
9214  var table = settings.nTable;
9215  var tbody = settings.nTBody;
9216  var thead = settings.nTHead;
9217  var tfoot = settings.nTFoot;
9218  var jqTable = $(table);
9219  var jqTbody = $(tbody);
9220  var jqWrapper = $(settings.nTableWrapper);
9221  var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
9222  var i, ien;
9223 
9224  // Flag to note that the table is currently being destroyed - no action
9225  // should be taken
9226  settings.bDestroying = true;
9227 
9228  // Fire off the destroy callbacks for plug-ins etc
9229  _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
9230 
9231  // If not being removed from the document, make all columns visible
9232  if ( ! remove ) {
9233  new _Api( settings ).columns().visible( true );
9234  }
9235 
9236  // Blitz all `DT` namespaced events (these are internal events, the
9237  // lowercase, `dt` events are user subscribed and they are responsible
9238  // for removing them
9239  jqWrapper.unbind('.DT').find(':not(tbody *)').unbind('.DT');
9240  $(window).unbind('.DT-'+settings.sInstance);
9241 
9242  // When scrolling we had to break the table up - restore it
9243  if ( table != thead.parentNode ) {
9244  jqTable.children('thead').detach();
9245  jqTable.append( thead );
9246  }
9247 
9248  if ( tfoot && table != tfoot.parentNode ) {
9249  jqTable.children('tfoot').detach();
9250  jqTable.append( tfoot );
9251  }
9252 
9253  settings.aaSorting = [];
9254  settings.aaSortingFixed = [];
9255  _fnSortingClasses( settings );
9256 
9257  $( rows ).removeClass( settings.asStripeClasses.join(' ') );
9258 
9259  $('th, td', thead).removeClass( classes.sSortable+' '+
9260  classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
9261  );
9262 
9263  if ( settings.bJUI ) {
9264  $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach();
9265  $('th, td', thead).each( function () {
9266  var wrapper = $('div.'+classes.sSortJUIWrapper, this);
9267  $(this).append( wrapper.contents() );
9268  wrapper.detach();
9269  } );
9270  }
9271 
9272  // Add the TR elements back into the table in their original order
9273  jqTbody.children().detach();
9274  jqTbody.append( rows );
9275 
9276  // Remove the DataTables generated nodes, events and classes
9277  var removedMethod = remove ? 'remove' : 'detach';
9278  jqTable[ removedMethod ]();
9279  jqWrapper[ removedMethod ]();
9280 
9281  // If we need to reattach the table to the document
9282  if ( ! remove && orig ) {
9283  // insertBefore acts like appendChild if !arg[1]
9284  orig.insertBefore( table, settings.nTableReinsertBefore );
9285 
9286  // Restore the width of the original table - was read from the style property,
9287  // so we can restore directly to that
9288  jqTable
9289  .css( 'width', settings.sDestroyWidth )
9290  .removeClass( classes.sTable );
9291 
9292  // If the were originally stripe classes - then we add them back here.
9293  // Note this is not fool proof (for example if not all rows had stripe
9294  // classes - but it's a good effort without getting carried away
9295  ien = settings.asDestroyStripes.length;
9296 
9297  if ( ien ) {
9298  jqTbody.children().each( function (i) {
9299  $(this).addClass( settings.asDestroyStripes[i % ien] );
9300  } );
9301  }
9302  }
9303 
9304  /* Remove the settings object from the settings array */
9305  var idx = $.inArray( settings, DataTable.settings );
9306  if ( idx !== -1 ) {
9307  DataTable.settings.splice( idx, 1 );
9308  }
9309  } );
9310  } );
9311 
9312 
9313  // Add the `every()` method for rows, columns and cells in a compact form
9314  $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
9315  _api_register( type+'s().every()', function ( fn ) {
9316  var opts = this.selector.opts;
9317  var api = this;
9318 
9319  return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
9320  // Rows and columns:
9321  // arg1 - index
9322  // arg2 - table counter
9323  // arg3 - loop counter
9324  // arg4 - undefined
9325  // Cells:
9326  // arg1 - row index
9327  // arg2 - column index
9328  // arg3 - table counter
9329  // arg4 - loop counter
9330  fn.call(
9331  api[ type ](
9332  arg1,
9333  type==='cell' ? arg2 : opts,
9334  type==='cell' ? opts : undefined
9335  ),
9336  arg1, arg2, arg3, arg4
9337  );
9338  } );
9339  } );
9340  } );
9341 
9342 
9343  // i18n method for extensions to be able to use the language object from the
9344  // DataTable
9345  _api_register( 'i18n()', function ( token, def, plural ) {
9346  var ctx = this.context[0];
9347  var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
9348 
9349  if ( resolved === undefined ) {
9350  resolved = def;
9351  }
9352 
9353  if ( plural !== undefined && $.isPlainObject( resolved ) ) {
9354  resolved = resolved[ plural ] !== undefined ?
9355  resolved[ plural ] :
9356  resolved._;
9357  }
9358 
9359  return resolved.replace( '%d', plural ); // nb: plural might be undefined,
9360  } );
9361 
9370  DataTable.version = "1.10.12-dev";
9371 
9384  DataTable.settings = [];
9385 
9392  DataTable.models = {};
9393 
9394 
9395 
9401  DataTable.models.oSearch = {
9407  "bCaseInsensitive": true,
9408 
9414  "sSearch": "",
9415 
9423  "bRegex": false,
9424 
9430  "bSmart": true
9431  };
9432 
9433 
9434 
9435 
9442  DataTable.models.oRow = {
9448  "nTr": null,
9449 
9456  "anCells": null,
9457 
9467  "_aData": [],
9468 
9481  "_aSortData": null,
9482 
9490  "_aFilterData": null,
9491 
9501  "_sFilterRow": null,
9502 
9511  "_sRowStripe": "",
9512 
9522  "src": null,
9523 
9531  "idx": -1
9532  };
9533 
9534 
9546  DataTable.models.oColumn = {
9553  "idx": null,
9554 
9564  "aDataSort": null,
9565 
9574  "asSorting": null,
9575 
9581  "bSearchable": null,
9582 
9587  "bSortable": null,
9588 
9593  "bVisible": null,
9594 
9602  "_sManualType": null,
9603 
9611  "_bAttrSrc": false,
9612 
9625  "fnCreatedCell": null,
9626 
9641  "fnGetData": null,
9642 
9654  "fnSetData": null,
9655 
9663  "mData": null,
9664 
9673  "mRender": null,
9674 
9681  "nTh": null,
9682 
9690  "nTf": null,
9691 
9697  "sClass": null,
9698 
9710  "sContentPadding": null,
9711 
9719  "sDefaultContent": null,
9720 
9726  "sName": null,
9727 
9734  "sSortDataType": 'std',
9735 
9741  "sSortingClass": null,
9742 
9749  "sSortingClassJUI": null,
9750 
9755  "sTitle": null,
9756 
9762  "sType": null,
9763 
9769  "sWidth": null,
9770 
9776  "sWidthOrig": null
9777  };
9778 
9779 
9780  /*
9781  * Developer note: The properties of the object below are given in Hungarian
9782  * notation, that was used as the interface for DataTables prior to v1.10, however
9783  * from v1.10 onwards the primary interface is camel case. In order to avoid
9784  * breaking backwards compatibility utterly with this change, the Hungarian
9785  * version is still, internally the primary interface, but is is not documented
9786  * - hence the @name tags in each doc comment. This allows a Javascript function
9787  * to create a map from Hungarian notation to camel case (going the other direction
9788  * would require each property to be listed, which would at around 3K to the size
9789  * of DataTables, while this method is about a 0.5K hit.
9790  *
9791  * Ultimately this does pave the way for Hungarian notation to be dropped
9792  * completely, but that is a massive amount of work and will break current
9793  * installs (therefore is on-hold until v2).
9794  */
9795 
9801  DataTable.defaults = {
9861  "aaData": null,
9862 
9863 
9891  "aaSorting": [[0,'asc']],
9892 
9893 
9914  "aaSortingFixed": [],
9915 
9916 
10071  "ajax": null,
10072 
10073 
10097  "aLengthMenu": [ 10, 25, 50, 100 ],
10098 
10099 
10112  "aoColumns": null,
10113 
10133  "aoColumnDefs": null,
10134 
10135 
10160  "aoSearchCols": [],
10161 
10162 
10181  "asStripeClasses": null,
10182 
10183 
10201  "bAutoWidth": true,
10202 
10203 
10224  "bDeferRender": false,
10225 
10226 
10252  "bDestroy": false,
10253 
10254 
10276  "bFilter": true,
10277 
10278 
10296  "bInfo": true,
10297 
10298 
10316  "bJQueryUI": false,
10317 
10318 
10335  "bLengthChange": true,
10336 
10337 
10353  "bPaginate": true,
10354 
10355 
10374  "bProcessing": false,
10375 
10376 
10412  "bRetrieve": false,
10413 
10414 
10436  "bScrollCollapse": false,
10437 
10438 
10458  "bServerSide": false,
10459 
10460 
10477  "bSort": true,
10478 
10479 
10497  "bSortMulti": true,
10498 
10499 
10517  "bSortCellsTop": false,
10518 
10519 
10539  "bSortClasses": true,
10540 
10541 
10564  "bStateSave": false,
10565 
10566 
10592  "fnCreatedRow": null,
10593 
10594 
10613  "fnDrawCallback": null,
10614 
10615 
10641  "fnFooterCallback": null,
10642 
10643 
10671  "fnFormatNumber": function ( toFormat ) {
10672  return toFormat.toString().replace(
10673  /\B(?=(\d{3})+(?!\d))/g,
10674  this.oLanguage.sThousands
10675  );
10676  },
10677 
10678 
10705  "fnHeaderCallback": null,
10706 
10707 
10735  "fnInfoCallback": null,
10736 
10737 
10760  "fnInitComplete": null,
10761 
10762 
10786  "fnPreDrawCallback": null,
10787 
10788 
10815  "fnRowCallback": null,
10816 
10817 
10841  "fnServerData": null,
10842 
10843 
10868  "fnServerParams": null,
10869 
10870 
10906  "fnStateLoadCallback": function ( settings ) {
10907  try {
10908  return JSON.parse(
10909  (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
10910  'DataTables_'+settings.sInstance+'_'+location.pathname
10911  )
10912  );
10913  } catch (e) {}
10914  },
10915 
10916 
10952  "fnStateLoadParams": null,
10953 
10954 
10976  "fnStateLoaded": null,
10977 
10978 
11008  "fnStateSaveCallback": function ( settings, data ) {
11009  try {
11010  (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
11011  'DataTables_'+settings.sInstance+'_'+location.pathname,
11012  JSON.stringify( data )
11013  );
11014  } catch (e) {}
11015  },
11016 
11017 
11042  "fnStateSaveParams": null,
11043 
11044 
11062  "iStateDuration": 7200,
11063 
11064 
11106  "iDeferLoading": null,
11107 
11108 
11126  "iDisplayLength": 10,
11127 
11128 
11147  "iDisplayStart": 0,
11148 
11149 
11170  "iTabIndex": 0,
11171 
11172 
11181  "oClasses": {},
11182 
11183 
11191  "oLanguage": {
11199  "oAria": {
11221  "sSortAscending": ": activate to sort column ascending",
11222 
11244  "sSortDescending": ": activate to sort column descending"
11245  },
11246 
11253  "oPaginate": {
11274  "sFirst": "First",
11275 
11276 
11297  "sLast": "Last",
11298 
11299 
11320  "sNext": "Next",
11321 
11322 
11343  "sPrevious": "Previous"
11344  },
11345 
11366  "sEmptyTable": "No data available in table",
11367 
11368 
11398  "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
11399 
11400 
11419  "sInfoEmpty": "Showing 0 to 0 of 0 entries",
11420 
11421 
11441  "sInfoFiltered": "(filtered from _MAX_ total entries)",
11442 
11443 
11464  "sInfoPostFix": "",
11465 
11466 
11495  "sDecimal": "",
11496 
11497 
11518  "sThousands": ",",
11519 
11520 
11559  "sLengthMenu": "Show _MENU_ entries",
11560 
11561 
11583  "sLoadingRecords": "Loading...",
11584 
11585 
11604  "sProcessing": "Processing...",
11605 
11606 
11639  "sSearch": "Search:",
11640 
11641 
11650  "sSearchPlaceholder": "",
11651 
11652 
11675  "sUrl": "",
11676 
11677 
11697  "sZeroRecords": "No matching records found"
11698  },
11699 
11700 
11722  "oSearch": $.extend( {}, DataTable.models.oSearch ),
11723 
11724 
11743  "sAjaxDataProp": "data",
11744 
11745 
11762  "sAjaxSource": null,
11763 
11764 
11816  "sDom": "lfrtip",
11817 
11818 
11836  "searchDelay": null,
11837 
11838 
11863  "sPaginationType": "simple_numbers",
11864 
11865 
11888  "sScrollX": "",
11889 
11890 
11912  "sScrollXInner": "",
11913 
11914 
11936  "sScrollY": "",
11937 
11938 
11954  "sServerMethod": "GET",
11955 
11956 
11972  "renderer": null,
11973 
11974 
11983  "rowId": "DT_RowId"
11984  };
11985 
11986  _fnHungarianMap( DataTable.defaults );
11987 
11988 
11989 
11990  /*
11991  * Developer note - See note in model.defaults.js about the use of Hungarian
11992  * notation and camel case.
11993  */
11994 
11999  DataTable.defaults.column = {
12038  "aDataSort": null,
12039  "iDataSort": -1,
12040 
12041 
12078  "asSorting": [ 'asc', 'desc' ],
12079 
12080 
12111  "bSearchable": true,
12112 
12113 
12144  "bSortable": true,
12145 
12146 
12177  "bVisible": true,
12178 
12179 
12209  "fnCreatedCell": null,
12210 
12211 
12390  "mData": null,
12391 
12392 
12512  "mRender": null,
12513 
12514 
12536  "sCellType": "td",
12537 
12538 
12571  "sClass": "",
12572 
12604  "sContentPadding": "",
12605 
12606 
12647  "sDefaultContent": null,
12648 
12649 
12691  "sName": "",
12692 
12693 
12733  "sSortDataType": "std",
12734 
12735 
12769  "sTitle": null,
12770 
12771 
12810  "sType": null,
12811 
12812 
12848  "sWidth": null
12849  };
12850 
12851  _fnHungarianMap( DataTable.defaults.column );
12852 
12853 
12854 
12877  DataTable.models.oSettings = {
12882  "oFeatures": {
12883 
12891  "bAutoWidth": null,
12892 
12902  "bDeferRender": null,
12903 
12912  "bFilter": null,
12913 
12921  "bInfo": null,
12922 
12930  "bLengthChange": null,
12931 
12939  "bPaginate": null,
12940 
12948  "bProcessing": null,
12949 
12958  "bServerSide": null,
12959 
12966  "bSort": null,
12967 
12974  "bSortMulti": null,
12975 
12984  "bSortClasses": null,
12985 
12992  "bStateSave": null
12993  },
12994 
12995 
13000  "oScroll": {
13008  "bCollapse": null,
13009 
13016  "iBarWidth": 0,
13017 
13025  "sX": null,
13026 
13035  "sXInner": null,
13036 
13044  "sY": null
13045  },
13046 
13052  "oLanguage": {
13059  "fnInfoCallback": null
13060  },
13061 
13066  "oBrowser": {
13073  "bScrollOversize": false,
13074 
13082  "bScrollbarLeft": false,
13083 
13089  "bBounding": false,
13090 
13096  "barWidth": 0
13097  },
13098 
13099 
13100  "ajax": null,
13101 
13102 
13117  "aanFeatures": [],
13118 
13125  "aoData": [],
13126 
13132  "aiDisplay": [],
13133 
13139  "aiDisplayMaster": [],
13140 
13146  "aIds": {},
13147 
13153  "aoColumns": [],
13154 
13160  "aoHeader": [],
13161 
13167  "aoFooter": [],
13168 
13177  "oPreviousSearch": {},
13178 
13186  "aoPreSearchCols": [],
13187 
13200  "aaSorting": null,
13201 
13210  "aaSortingFixed": [],
13211 
13219  "asStripeClasses": null,
13220 
13226  "asDestroyStripes": [],
13227 
13233  "sDestroyWidth": 0,
13234 
13240  "aoRowCallback": [],
13241 
13247  "aoHeaderCallback": [],
13248 
13254  "aoFooterCallback": [],
13255 
13261  "aoDrawCallback": [],
13262 
13268  "aoRowCreatedCallback": [],
13269 
13276  "aoPreDrawCallback": [],
13277 
13283  "aoInitComplete": [],
13284 
13285 
13292  "aoStateSaveParams": [],
13293 
13300  "aoStateLoadParams": [],
13301 
13308  "aoStateLoaded": [],
13309 
13315  "sTableId": "",
13316 
13322  "nTable": null,
13323 
13329  "nTHead": null,
13330 
13336  "nTFoot": null,
13337 
13343  "nTBody": null,
13344 
13350  "nTableWrapper": null,
13351 
13360  "bDeferLoading": false,
13361 
13367  "bInitialised": false,
13368 
13375  "aoOpenRows": [],
13376 
13385  "sDom": null,
13386 
13392  "searchDelay": null,
13393 
13401  "sPaginationType": "two_button",
13402 
13410  "iStateDuration": 0,
13411 
13425  "aoStateSave": [],
13426 
13438  "aoStateLoad": [],
13439 
13445  "oSavedState": null,
13446 
13452  "oLoadedState": null,
13453 
13461  "sAjaxSource": null,
13462 
13471  "sAjaxDataProp": null,
13472 
13478  "bAjaxDataGet": true,
13479 
13487  "jqXHR": null,
13488 
13494  "json": undefined,
13495 
13501  "oAjaxData": undefined,
13502 
13509  "fnServerData": null,
13510 
13517  "aoServerParams": [],
13518 
13526  "sServerMethod": null,
13527 
13534  "fnFormatNumber": null,
13535 
13543  "aLengthMenu": null,
13544 
13551  "iDraw": 0,
13552 
13558  "bDrawing": false,
13559 
13565  "iDrawError": -1,
13566 
13572  "_iDisplayLength": 10,
13573 
13579  "_iDisplayStart": 0,
13580 
13590  "_iRecordsTotal": 0,
13591 
13601  "_iRecordsDisplay": 0,
13602 
13609  "bJUI": null,
13610 
13616  "oClasses": {},
13617 
13626  "bFiltered": false,
13627 
13636  "bSorted": false,
13637 
13646  "bSortCellsTop": null,
13647 
13653  "oInit": null,
13654 
13661  "aoDestroyCallback": [],
13662 
13663 
13668  "fnRecordsTotal": function ()
13669  {
13670  return _fnDataSource( this ) == 'ssp' ?
13671  this._iRecordsTotal * 1 :
13672  this.aiDisplayMaster.length;
13673  },
13674 
13679  "fnRecordsDisplay": function ()
13680  {
13681  return _fnDataSource( this ) == 'ssp' ?
13682  this._iRecordsDisplay * 1 :
13683  this.aiDisplay.length;
13684  },
13685 
13690  "fnDisplayEnd": function ()
13691  {
13692  var
13693  len = this._iDisplayLength,
13694  start = this._iDisplayStart,
13695  calc = start + len,
13696  records = this.aiDisplay.length,
13697  features = this.oFeatures,
13698  paginate = features.bPaginate;
13699 
13700  if ( features.bServerSide ) {
13701  return paginate === false || len === -1 ?
13702  start + records :
13703  Math.min( start+len, this._iRecordsDisplay );
13704  }
13705  else {
13706  return ! paginate || calc>records || len===-1 ?
13707  records :
13708  calc;
13709  }
13710  },
13711 
13717  "oInstance": null,
13718 
13726  "sInstance": null,
13727 
13732  "iTabIndex": 0,
13733 
13737  "nScrollHead": null,
13738 
13742  "nScrollFoot": null,
13743 
13749  "aLastSort": [],
13750 
13756  "oPlugins": {},
13757 
13763  "rowIdFn": null,
13764 
13770  "rowId": null
13771  };
13772 
13798  DataTable.ext = _ext = {
13807  buttons: {},
13808 
13809 
13816  classes: {},
13817 
13818 
13824  builder: "-source-",
13825 
13826 
13836  errMode: "alert",
13837 
13838 
13876  feature: [],
13877 
13878 
13937  search: [],
13938 
13939 
13964  selector: {
13965  cell: [],
13966  column: [],
13967  row: []
13968  },
13969 
13970 
13981  internal: {},
13982 
13983 
13990  legacy: {
13998  ajax: null
13999  },
14000 
14001 
14047  pager: {},
14048 
14049 
14050  renderer: {
14051  pageButton: {},
14052  header: {}
14053  },
14054 
14055 
14096  order: {},
14097 
14098 
14109  type: {
14150  detect: [],
14151 
14152 
14189  search: {},
14190 
14191 
14256  order: {}
14257  },
14258 
14265  _unique: 0,
14266 
14267 
14268  //
14269  // Depreciated
14270  // The following properties are retained for backwards compatiblity only.
14271  // The should not be used in new projects and will be removed in a future
14272  // version
14273  //
14274 
14280  fnVersionCheck: DataTable.fnVersionCheck,
14281 
14282 
14288  iApiIndex: 0,
14289 
14290 
14296  oJUIClasses: {},
14297 
14298 
14304  sVersion: DataTable.version
14305  };
14306 
14307 
14308  //
14309  // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
14310  //
14311  $.extend( _ext, {
14312  afnFiltering: _ext.search,
14313  aTypes: _ext.type.detect,
14314  ofnSearch: _ext.type.search,
14315  oSort: _ext.type.order,
14316  afnSortData: _ext.order,
14317  aoFeatures: _ext.feature,
14318  oApi: _ext.internal,
14319  oStdClasses: _ext.classes,
14320  oPagination: _ext.pager
14321  } );
14322 
14323 
14324  $.extend( DataTable.ext.classes, {
14325  "sTable": "dataTable",
14326  "sNoFooter": "no-footer",
14327 
14328  /* Paging buttons */
14329  "sPageButton": "paginate_button",
14330  "sPageButtonActive": "current",
14331  "sPageButtonDisabled": "disabled",
14332 
14333  /* Striping classes */
14334  "sStripeOdd": "odd",
14335  "sStripeEven": "even",
14336 
14337  /* Empty row */
14338  "sRowEmpty": "dataTables_empty",
14339 
14340  /* Features */
14341  "sWrapper": "dataTables_wrapper",
14342  "sFilter": "dataTables_filter",
14343  "sInfo": "dataTables_info",
14344  "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
14345  "sLength": "dataTables_length",
14346  "sProcessing": "dataTables_processing",
14347 
14348  /* Sorting */
14349  "sSortAsc": "sorting_asc",
14350  "sSortDesc": "sorting_desc",
14351  "sSortable": "sorting", /* Sortable in both directions */
14352  "sSortableAsc": "sorting_asc_disabled",
14353  "sSortableDesc": "sorting_desc_disabled",
14354  "sSortableNone": "sorting_disabled",
14355  "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
14356 
14357  /* Filtering */
14358  "sFilterInput": "",
14359 
14360  /* Page length */
14361  "sLengthSelect": "",
14362 
14363  /* Scrolling */
14364  "sScrollWrapper": "dataTables_scroll",
14365  "sScrollHead": "dataTables_scrollHead",
14366  "sScrollHeadInner": "dataTables_scrollHeadInner",
14367  "sScrollBody": "dataTables_scrollBody",
14368  "sScrollFoot": "dataTables_scrollFoot",
14369  "sScrollFootInner": "dataTables_scrollFootInner",
14370 
14371  /* Misc */
14372  "sHeaderTH": "",
14373  "sFooterTH": "",
14374 
14375  // Deprecated
14376  "sSortJUIAsc": "",
14377  "sSortJUIDesc": "",
14378  "sSortJUI": "",
14379  "sSortJUIAscAllowed": "",
14380  "sSortJUIDescAllowed": "",
14381  "sSortJUIWrapper": "",
14382  "sSortIcon": "",
14383  "sJUIHeader": "",
14384  "sJUIFooter": ""
14385  } );
14386 
14387 
14388  (function() {
14389 
14390  // Reused strings for better compression. Closure compiler appears to have a
14391  // weird edge case where it is trying to expand strings rather than use the
14392  // variable version. This results in about 200 bytes being added, for very
14393  // little preference benefit since it this run on script load only.
14394  var _empty = '';
14395  _empty = '';
14396 
14397  var _stateDefault = _empty + 'ui-state-default';
14398  var _sortIcon = _empty + 'css_right ui-icon ui-icon-';
14399  var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix';
14400 
14401  $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, {
14402  /* Full numbers paging buttons */
14403  "sPageButton": "fg-button ui-button "+_stateDefault,
14404  "sPageButtonActive": "ui-state-disabled",
14405  "sPageButtonDisabled": "ui-state-disabled",
14406 
14407  /* Features */
14408  "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
14409  "ui-buttonset-multi paging_", /* Note that the type is postfixed */
14410 
14411  /* Sorting */
14412  "sSortAsc": _stateDefault+" sorting_asc",
14413  "sSortDesc": _stateDefault+" sorting_desc",
14414  "sSortable": _stateDefault+" sorting",
14415  "sSortableAsc": _stateDefault+" sorting_asc_disabled",
14416  "sSortableDesc": _stateDefault+" sorting_desc_disabled",
14417  "sSortableNone": _stateDefault+" sorting_disabled",
14418  "sSortJUIAsc": _sortIcon+"triangle-1-n",
14419  "sSortJUIDesc": _sortIcon+"triangle-1-s",
14420  "sSortJUI": _sortIcon+"carat-2-n-s",
14421  "sSortJUIAscAllowed": _sortIcon+"carat-1-n",
14422  "sSortJUIDescAllowed": _sortIcon+"carat-1-s",
14423  "sSortJUIWrapper": "DataTables_sort_wrapper",
14424  "sSortIcon": "DataTables_sort_icon",
14425 
14426  /* Scrolling */
14427  "sScrollHead": "dataTables_scrollHead "+_stateDefault,
14428  "sScrollFoot": "dataTables_scrollFoot "+_stateDefault,
14429 
14430  /* Misc */
14431  "sHeaderTH": _stateDefault,
14432  "sFooterTH": _stateDefault,
14433  "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr",
14434  "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br"
14435  } );
14436 
14437  }());
14438 
14439 
14440 
14441  var extPagination = DataTable.ext.pager;
14442 
14443  function _numbers ( page, pages ) {
14444  var
14445  numbers = [],
14446  buttons = extPagination.numbers_length,
14447  half = Math.floor( buttons / 2 ),
14448  i = 1;
14449 
14450  if ( pages <= buttons ) {
14451  numbers = _range( 0, pages );
14452  }
14453  else if ( page <= half ) {
14454  numbers = _range( 0, buttons-2 );
14455  numbers.push( 'ellipsis' );
14456  numbers.push( pages-1 );
14457  }
14458  else if ( page >= pages - 1 - half ) {
14459  numbers = _range( pages-(buttons-2), pages );
14460  numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
14461  numbers.splice( 0, 0, 0 );
14462  }
14463  else {
14464  numbers = _range( page-half+2, page+half-1 );
14465  numbers.push( 'ellipsis' );
14466  numbers.push( pages-1 );
14467  numbers.splice( 0, 0, 'ellipsis' );
14468  numbers.splice( 0, 0, 0 );
14469  }
14470 
14471  numbers.DT_el = 'span';
14472  return numbers;
14473  }
14474 
14475 
14476  $.extend( extPagination, {
14477  simple: function ( page, pages ) {
14478  return [ 'previous', 'next' ];
14479  },
14480 
14481  full: function ( page, pages ) {
14482  return [ 'first', 'previous', 'next', 'last' ];
14483  },
14484 
14485  numbers: function ( page, pages ) {
14486  return [ _numbers(page, pages) ];
14487  },
14488 
14489  simple_numbers: function ( page, pages ) {
14490  return [ 'previous', _numbers(page, pages), 'next' ];
14491  },
14492 
14493  full_numbers: function ( page, pages ) {
14494  return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
14495  },
14496 
14497  // For testing and plug-ins to use
14498  _numbers: _numbers,
14499 
14500  // Number of number buttons (including ellipsis) to show. _Must be odd!_
14501  numbers_length: 7
14502  } );
14503 
14504 
14505  $.extend( true, DataTable.ext.renderer, {
14506  pageButton: {
14507  _: function ( settings, host, idx, buttons, page, pages ) {
14508  var classes = settings.oClasses;
14509  var lang = settings.oLanguage.oPaginate;
14510  var aria = settings.oLanguage.oAria.paginate || {};
14511  var btnDisplay, btnClass, counter=0;
14512 
14513  var attach = function( container, buttons ) {
14514  var i, ien, node, button;
14515  var clickHandler = function ( e ) {
14516  _fnPageChange( settings, e.data.action, true );
14517  };
14518 
14519  for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
14520  button = buttons[i];
14521 
14522  if ( $.isArray( button ) ) {
14523  var inner = $( '<'+(button.DT_el || 'div')+'/>' )
14524  .appendTo( container );
14525  attach( inner, button );
14526  }
14527  else {
14528  btnDisplay = null;
14529  btnClass = '';
14530 
14531  switch ( button ) {
14532  case 'ellipsis':
14533  container.append('<span class="ellipsis">&#x2026;</span>');
14534  break;
14535 
14536  case 'first':
14537  btnDisplay = lang.sFirst;
14538  btnClass = button + (page > 0 ?
14539  '' : ' '+classes.sPageButtonDisabled);
14540  break;
14541 
14542  case 'previous':
14543  btnDisplay = lang.sPrevious;
14544  btnClass = button + (page > 0 ?
14545  '' : ' '+classes.sPageButtonDisabled);
14546  break;
14547 
14548  case 'next':
14549  btnDisplay = lang.sNext;
14550  btnClass = button + (page < pages-1 ?
14551  '' : ' '+classes.sPageButtonDisabled);
14552  break;
14553 
14554  case 'last':
14555  btnDisplay = lang.sLast;
14556  btnClass = button + (page < pages-1 ?
14557  '' : ' '+classes.sPageButtonDisabled);
14558  break;
14559 
14560  default:
14561  btnDisplay = button + 1;
14562  btnClass = page === button ?
14563  classes.sPageButtonActive : '';
14564  break;
14565  }
14566 
14567  if ( btnDisplay !== null ) {
14568  node = $('<a>', {
14569  'class': classes.sPageButton+' '+btnClass,
14570  'aria-controls': settings.sTableId,
14571  'aria-label': aria[ button ],
14572  'data-dt-idx': counter,
14573  'tabindex': settings.iTabIndex,
14574  'id': idx === 0 && typeof button === 'string' ?
14575  settings.sTableId +'_'+ button :
14576  null
14577  } )
14578  .html( btnDisplay )
14579  .appendTo( container );
14580 
14581  _fnBindAction(
14582  node, {action: button}, clickHandler
14583  );
14584 
14585  counter++;
14586  }
14587  }
14588  }
14589  };
14590 
14591  // IE9 throws an 'unknown error' if document.activeElement is used
14592  // inside an iframe or frame. Try / catch the error. Not good for
14593  // accessibility, but neither are frames.
14594  var activeEl;
14595 
14596  try {
14597  // Because this approach is destroying and recreating the paging
14598  // elements, focus is lost on the select button which is bad for
14599  // accessibility. So we want to restore focus once the draw has
14600  // completed
14601  activeEl = $(host).find(document.activeElement).data('dt-idx');
14602  }
14603  catch (e) {}
14604 
14605  attach( $(host).empty(), buttons );
14606 
14607  if ( activeEl ) {
14608  $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
14609  }
14610  }
14611  }
14612  } );
14613 
14614 
14615 
14616  // Built in type detection. See model.ext.aTypes for information about
14617  // what is required from this methods.
14618  $.extend( DataTable.ext.type.detect, [
14619  // Plain numbers - first since V8 detects some plain numbers as dates
14620  // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
14621  function ( d, settings )
14622  {
14623  var decimal = settings.oLanguage.sDecimal;
14624  return _isNumber( d, decimal ) ? 'num'+decimal : null;
14625  },
14626 
14627  // Dates (only those recognised by the browser's Date.parse)
14628  function ( d, settings )
14629  {
14630  // V8 will remove any unknown characters at the start and end of the
14631  // expression, leading to false matches such as `$245.12` or `10%` being
14632  // a valid date. See forum thread 18941 for detail.
14633  if ( d && !(d instanceof Date) && ( ! _re_date_start.test(d) || ! _re_date_end.test(d) ) ) {
14634  return null;
14635  }
14636  var parsed = Date.parse(d);
14637  return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
14638  },
14639 
14640  // Formatted numbers
14641  function ( d, settings )
14642  {
14643  var decimal = settings.oLanguage.sDecimal;
14644  return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
14645  },
14646 
14647  // HTML numeric
14648  function ( d, settings )
14649  {
14650  var decimal = settings.oLanguage.sDecimal;
14651  return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
14652  },
14653 
14654  // HTML numeric, formatted
14655  function ( d, settings )
14656  {
14657  var decimal = settings.oLanguage.sDecimal;
14658  return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
14659  },
14660 
14661  // HTML (this is strict checking - there must be html)
14662  function ( d, settings )
14663  {
14664  return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
14665  'html' : null;
14666  }
14667  ] );
14668 
14669 
14670 
14671  // Filter formatting functions. See model.ext.ofnSearch for information about
14672  // what is required from these methods.
14673  //
14674  // Note that additional search methods are added for the html numbers and
14675  // html formatted numbers by `_addNumericSort()` when we know what the decimal
14676  // place is
14677 
14678 
14679  $.extend( DataTable.ext.type.search, {
14680  html: function ( data ) {
14681  return _empty(data) ?
14682  data :
14683  typeof data === 'string' ?
14684  data
14685  .replace( _re_new_lines, " " )
14686  .replace( _re_html, "" ) :
14687  '';
14688  },
14689 
14690  string: function ( data ) {
14691  return _empty(data) ?
14692  data :
14693  typeof data === 'string' ?
14694  data.replace( _re_new_lines, " " ) :
14695  data;
14696  }
14697  } );
14698 
14699 
14700 
14701  var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
14702  if ( d !== 0 && (!d || d === '-') ) {
14703  return -Infinity;
14704  }
14705 
14706  // If a decimal place other than `.` is used, it needs to be given to the
14707  // function so we can detect it and replace with a `.` which is the only
14708  // decimal place Javascript recognises - it is not locale aware.
14709  if ( decimalPlace ) {
14710  d = _numToDecimal( d, decimalPlace );
14711  }
14712 
14713  if ( d.replace ) {
14714  if ( re1 ) {
14715  d = d.replace( re1, '' );
14716  }
14717 
14718  if ( re2 ) {
14719  d = d.replace( re2, '' );
14720  }
14721  }
14722 
14723  return d * 1;
14724  };
14725 
14726 
14727  // Add the numeric 'deformatting' functions for sorting and search. This is done
14728  // in a function to provide an easy ability for the language options to add
14729  // additional methods if a non-period decimal place is used.
14730  function _addNumericSort ( decimalPlace ) {
14731  $.each(
14732  {
14733  // Plain numbers
14734  "num": function ( d ) {
14735  return __numericReplace( d, decimalPlace );
14736  },
14737 
14738  // Formatted numbers
14739  "num-fmt": function ( d ) {
14740  return __numericReplace( d, decimalPlace, _re_formatted_numeric );
14741  },
14742 
14743  // HTML numeric
14744  "html-num": function ( d ) {
14745  return __numericReplace( d, decimalPlace, _re_html );
14746  },
14747 
14748  // HTML numeric, formatted
14749  "html-num-fmt": function ( d ) {
14750  return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
14751  }
14752  },
14753  function ( key, fn ) {
14754  // Add the ordering method
14755  _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
14756 
14757  // For HTML types add a search formatter that will strip the HTML
14758  if ( key.match(/^html\-/) ) {
14759  _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
14760  }
14761  }
14762  );
14763  }
14764 
14765 
14766  // Default sort methods
14767  $.extend( _ext.type.order, {
14768  // Dates
14769  "date-pre": function ( d ) {
14770  return Date.parse( d ) || 0;
14771  },
14772 
14773  // html
14774  "html-pre": function ( a ) {
14775  return _empty(a) ?
14776  '' :
14777  a.replace ?
14778  a.replace( /<.*?>/g, "" ).toLowerCase() :
14779  a+'';
14780  },
14781 
14782  // string
14783  "string-pre": function ( a ) {
14784  // This is a little complex, but faster than always calling toString,
14785  // http://jsperf.com/tostring-v-check
14786  return _empty(a) ?
14787  '' :
14788  typeof a === 'string' ?
14789  a.toLowerCase() :
14790  ! a.toString ?
14791  '' :
14792  a.toString();
14793  },
14794 
14795  // string-asc and -desc are retained only for compatibility with the old
14796  // sort methods
14797  "string-asc": function ( x, y ) {
14798  return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14799  },
14800 
14801  "string-desc": function ( x, y ) {
14802  return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14803  }
14804  } );
14805 
14806 
14807  // Numeric sorting types - order doesn't matter here
14808  _addNumericSort( '' );
14809 
14810 
14811  $.extend( true, DataTable.ext.renderer, {
14812  header: {
14813  _: function ( settings, cell, column, classes ) {
14814  // No additional mark-up required
14815  // Attach a sort listener to update on sort - note that using the
14816  // `DT` namespace will allow the event to be removed automatically
14817  // on destroy, while the `dt` namespaced event is the one we are
14818  // listening for
14819  $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14820  if ( settings !== ctx ) { // need to check this this is the host
14821  return; // table, not a nested one
14822  }
14823 
14824  var colIdx = column.idx;
14825 
14826  cell
14827  .removeClass(
14828  column.sSortingClass +' '+
14829  classes.sSortAsc +' '+
14830  classes.sSortDesc
14831  )
14832  .addClass( columns[ colIdx ] == 'asc' ?
14833  classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14834  classes.sSortDesc :
14835  column.sSortingClass
14836  );
14837  } );
14838  },
14839 
14840  jqueryui: function ( settings, cell, column, classes ) {
14841  $('<div/>')
14842  .addClass( classes.sSortJUIWrapper )
14843  .append( cell.contents() )
14844  .append( $('<span/>')
14845  .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
14846  )
14847  .appendTo( cell );
14848 
14849  // Attach a sort listener to update on sort
14850  $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14851  if ( settings !== ctx ) {
14852  return;
14853  }
14854 
14855  var colIdx = column.idx;
14856 
14857  cell
14858  .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
14859  .addClass( columns[ colIdx ] == 'asc' ?
14860  classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14861  classes.sSortDesc :
14862  column.sSortingClass
14863  );
14864 
14865  cell
14866  .find( 'span.'+classes.sSortIcon )
14867  .removeClass(
14868  classes.sSortJUIAsc +" "+
14869  classes.sSortJUIDesc +" "+
14870  classes.sSortJUI +" "+
14871  classes.sSortJUIAscAllowed +" "+
14872  classes.sSortJUIDescAllowed
14873  )
14874  .addClass( columns[ colIdx ] == 'asc' ?
14875  classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
14876  classes.sSortJUIDesc :
14877  column.sSortingClassJUI
14878  );
14879  } );
14880  }
14881  }
14882  } );
14883 
14884  /*
14885  * Public helper functions. These aren't used internally by DataTables, or
14886  * called by any of the options passed into DataTables, but they can be used
14887  * externally by developers working with DataTables. They are helper functions
14888  * to make working with DataTables a little bit easier.
14889  */
14890 
14891  var __htmlEscapeEntities = function ( d ) {
14892  return typeof d === 'string' ?
14893  d.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;') :
14894  d;
14895  };
14896 
14923  DataTable.render = {
14924  number: function ( thousands, decimal, precision, prefix, postfix ) {
14925  return {
14926  display: function ( d ) {
14927  if ( typeof d !== 'number' && typeof d !== 'string' ) {
14928  return d;
14929  }
14930 
14931  var negative = d < 0 ? '-' : '';
14932  var flo = parseFloat( d );
14933 
14934  // If NaN then there isn't much formatting that we can do - just
14935  // return immediately, escaping any HTML (this was supposed to
14936  // be a number after all)
14937  if ( isNaN( flo ) ) {
14938  return __htmlEscapeEntities( d );
14939  }
14940 
14941  d = Math.abs( flo );
14942 
14943  var intPart = parseInt( d, 10 );
14944  var floatPart = precision ?
14945  decimal+(d - intPart).toFixed( precision ).substring( 2 ):
14946  '';
14947 
14948  return negative + (prefix||'') +
14949  intPart.toString().replace(
14950  /\B(?=(\d{3})+(?!\d))/g, thousands
14951  ) +
14952  floatPart +
14953  (postfix||'');
14954  }
14955  };
14956  },
14957 
14958  text: function () {
14959  return {
14960  display: __htmlEscapeEntities
14961  };
14962  }
14963  };
14964 
14965 
14966  /*
14967  * This is really a good bit rubbish this method of exposing the internal methods
14968  * publicly... - To be fixed in 2.0 using methods on the prototype
14969  */
14970 
14971 
14978  function _fnExternApiFunc (fn)
14979  {
14980  return function() {
14981  var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
14982  Array.prototype.slice.call(arguments)
14983  );
14984  return DataTable.ext.internal[fn].apply( this, args );
14985  };
14986  }
14987 
14988 
14996  $.extend( DataTable.ext.internal, {
14997  _fnExternApiFunc: _fnExternApiFunc,
14998  _fnBuildAjax: _fnBuildAjax,
14999  _fnAjaxUpdate: _fnAjaxUpdate,
15000  _fnAjaxParameters: _fnAjaxParameters,
15001  _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
15002  _fnAjaxDataSrc: _fnAjaxDataSrc,
15003  _fnAddColumn: _fnAddColumn,
15004  _fnColumnOptions: _fnColumnOptions,
15005  _fnAdjustColumnSizing: _fnAdjustColumnSizing,
15006  _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
15007  _fnColumnIndexToVisible: _fnColumnIndexToVisible,
15008  _fnVisbleColumns: _fnVisbleColumns,
15009  _fnGetColumns: _fnGetColumns,
15010  _fnColumnTypes: _fnColumnTypes,
15011  _fnApplyColumnDefs: _fnApplyColumnDefs,
15012  _fnHungarianMap: _fnHungarianMap,
15013  _fnCamelToHungarian: _fnCamelToHungarian,
15014  _fnLanguageCompat: _fnLanguageCompat,
15015  _fnBrowserDetect: _fnBrowserDetect,
15016  _fnAddData: _fnAddData,
15017  _fnAddTr: _fnAddTr,
15018  _fnNodeToDataIndex: _fnNodeToDataIndex,
15019  _fnNodeToColumnIndex: _fnNodeToColumnIndex,
15020  _fnGetCellData: _fnGetCellData,
15021  _fnSetCellData: _fnSetCellData,
15022  _fnSplitObjNotation: _fnSplitObjNotation,
15023  _fnGetObjectDataFn: _fnGetObjectDataFn,
15024  _fnSetObjectDataFn: _fnSetObjectDataFn,
15025  _fnGetDataMaster: _fnGetDataMaster,
15026  _fnClearTable: _fnClearTable,
15027  _fnDeleteIndex: _fnDeleteIndex,
15028  _fnInvalidate: _fnInvalidate,
15029  _fnGetRowElements: _fnGetRowElements,
15030  _fnCreateTr: _fnCreateTr,
15031  _fnBuildHead: _fnBuildHead,
15032  _fnDrawHead: _fnDrawHead,
15033  _fnDraw: _fnDraw,
15034  _fnReDraw: _fnReDraw,
15035  _fnAddOptionsHtml: _fnAddOptionsHtml,
15036  _fnDetectHeader: _fnDetectHeader,
15037  _fnGetUniqueThs: _fnGetUniqueThs,
15038  _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
15039  _fnFilterComplete: _fnFilterComplete,
15040  _fnFilterCustom: _fnFilterCustom,
15041  _fnFilterColumn: _fnFilterColumn,
15042  _fnFilter: _fnFilter,
15043  _fnFilterCreateSearch: _fnFilterCreateSearch,
15044  _fnEscapeRegex: _fnEscapeRegex,
15045  _fnFilterData: _fnFilterData,
15046  _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
15047  _fnUpdateInfo: _fnUpdateInfo,
15048  _fnInfoMacros: _fnInfoMacros,
15049  _fnInitialise: _fnInitialise,
15050  _fnInitComplete: _fnInitComplete,
15051  _fnLengthChange: _fnLengthChange,
15052  _fnFeatureHtmlLength: _fnFeatureHtmlLength,
15053  _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
15054  _fnPageChange: _fnPageChange,
15055  _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
15056  _fnProcessingDisplay: _fnProcessingDisplay,
15057  _fnFeatureHtmlTable: _fnFeatureHtmlTable,
15058  _fnScrollDraw: _fnScrollDraw,
15059  _fnApplyToChildren: _fnApplyToChildren,
15060  _fnCalculateColumnWidths: _fnCalculateColumnWidths,
15061  _fnThrottle: _fnThrottle,
15062  _fnConvertToWidth: _fnConvertToWidth,
15063  _fnGetWidestNode: _fnGetWidestNode,
15064  _fnGetMaxLenString: _fnGetMaxLenString,
15065  _fnStringToCss: _fnStringToCss,
15066  _fnSortFlatten: _fnSortFlatten,
15067  _fnSort: _fnSort,
15068  _fnSortAria: _fnSortAria,
15069  _fnSortListener: _fnSortListener,
15070  _fnSortAttachListener: _fnSortAttachListener,
15071  _fnSortingClasses: _fnSortingClasses,
15072  _fnSortData: _fnSortData,
15073  _fnSaveState: _fnSaveState,
15074  _fnLoadState: _fnLoadState,
15075  _fnSettingsFromNode: _fnSettingsFromNode,
15076  _fnLog: _fnLog,
15077  _fnMap: _fnMap,
15078  _fnBindAction: _fnBindAction,
15079  _fnCallbackReg: _fnCallbackReg,
15080  _fnCallbackFire: _fnCallbackFire,
15081  _fnLengthOverflow: _fnLengthOverflow,
15082  _fnRenderer: _fnRenderer,
15083  _fnDataSource: _fnDataSource,
15084  _fnRowAttributes: _fnRowAttributes,
15085  _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
15086  // in 1.10, so this dead-end function is
15087  // added to prevent errors
15088  } );
15089 
15090 
15091  // jQuery access
15092  $.fn.dataTable = DataTable;
15093 
15094  // Provide access to the host jQuery object (circular reference)
15095  DataTable.$ = $;
15096 
15097  // Legacy aliases
15098  $.fn.dataTableSettings = DataTable.settings;
15099  $.fn.dataTableExt = DataTable.ext;
15100 
15101  // With a capital `D` we return a DataTables API instance rather than a
15102  // jQuery object
15103  $.fn.DataTable = function ( opts ) {
15104  return $(this).dataTable( opts ).api();
15105  };
15106 
15107  // All properties that are available to $.fn.dataTable should also be
15108  // available on $.fn.DataTable
15109  $.each( DataTable, function ( prop, val ) {
15110  $.fn.DataTable[ prop ] = val;
15111  } );
15112 
15113 
15114  // Information about events fired by DataTables - for documentation.
15276  return $.fn.dataTable;
15277 }));