PageSourceSearch

https://museumexplorer.co.za/wp-content/plugins/wp-google-map-plugin/assets/js/wpgmp_frontend.js?ver=4.8.6

js museumexplorer.co.za collected 2026-09-24 13:00:04 UTC 515,938 bytes, 16,933 lines download raw bytes

1/*
2 * This combined file was created by the DataTables downloader builder:
3 *   https://datatables.net/download
4 *
5 * To rebuild or modify this file with the latest versions of the included
6 * software please visit:
7 *   https://datatables.net/download/#dt/dt-2.2.2
8 *
9 * Included libraries:
10 *   DataTables 2.2.2
11 */
12
13/*! DataTables 2.2.2
14 * © SpryMedia Ltd - datatables.net/license
15 */
16
17(function( factory ) {
18	"use strict";
19
20	if ( typeof define === 'function' && define.amd ) {
21		// AMD
22		define( ['jquery'], function ( $ ) {
23			return factory( $, window, document );
24		} );
25	}
26	else if ( typeof exports === 'object' ) {
27		// CommonJS
28		// jQuery's factory checks for a global window - if it isn't present then it
29		// returns a factory function that expects the window object
30		var jq = require('jquery');
31
32		if (typeof window === 'undefined') {
33			module.exports = function (root, $) {
34				if ( ! root ) {
35					// CommonJS environments without a window global must pass a
36					// root. This will give an error otherwise
37					root = window;
38				}
39
40				if ( ! $ ) {
41					$ = jq( root );
42				}
43
44				return factory( $, root, root.document );
45			};
46		}
47		else {
48			module.exports = factory( jq, window, window.document );
49		}
50	}
51	else {
52		// Browser
53		window.DataTable = factory( jQuery, window, document );
54	}
55}(function( $, window, document ) {
56	"use strict";
57
58	
59	var DataTable = function ( selector, options )
60	{
61		// Check if called with a window or jQuery object for DOM less applications
62		// This is for backwards compatibility
63		if (DataTable.factory(selector, options)) {
64			return DataTable;
65		}
66	
67		// When creating with `new`, create a new DataTable, returning the API instance
68		if (this instanceof DataTable) {
69			return $(selector).DataTable(options);
70		}
71		else {
72			// Argument switching
73			options = selector;
74		}
75	
76		var _that = this;
77		var emptyInit = options === undefined;
78		var len = this.length;
79	
80		if ( emptyInit ) {
81			options = {};
82		}
83	
84		// Method to get DT API instance from jQuery object
85		this.api = function ()
86		{
87			return new _Api( this );
88		};
89	
90		this.each(function() {
91			// For each initialisation we want to give it a clean initialisation
92			// object that can be bashed around
93			var o = {};
94			var oInit = len > 1 ? // optimisation for single table case
95				_fnExtend( o, options, true ) :
96				options;
97	
98			
99			var i=0, iLen;
100			var sId = this.getAttribute( 'id' );
101			var defaults = DataTable.defaults;
102			var $this = $(this);
103			
104			
105			/* Sanity check */
106			if ( this.nodeName.toLowerCase() != 'table' )
107			{
108				_fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
109				return;
110			}
111			
112			$(this).trigger( 'options.dt', oInit );
113			
114			/* Backwards compatibility for the defaults */
115			_fnCompatOpts( defaults );
116			_fnCompatCols( defaults.column );
117			
118			/* Convert the camel-case defaults to Hungarian */
119			_fnCamelToHungarian( defaults, defaults, true );
120			_fnCamelToHungarian( defaults.column, defaults.column, true );
121			
122			/* Setting up the initialisation object */
123			_fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true );
124			
125			
126			
127			/* Check to see if we are re-initialising a table */
128			var allSettings = DataTable.settings;
129			for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
130			{
131				var s = allSettings[i];
132			
133				/* Base check on table node */
134				if (
135					s.nTable == this ||
136					(s.nTHead && s.nTHead.parentNode == this) ||
137					(s.nTFoot && s.nTFoot.parentNode == this)
138				) {
139					var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
140					var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
141			
142					if ( emptyInit || bRetrieve )
143					{
144						return s.oInstance;
145					}
146					else if ( bDestroy )
147					{
148						new DataTable.Api(s).destroy();
149						break;
150					}
151					else
152					{
153						_fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
154						return;
155					}
156				}
157			
158				/* If the element we are initialising has the same ID as a table which was previously
159				 * initialised, but the table nodes don't match (from before) then we destroy the old
160				 * instance by simply deleting it. This is under the assumption that the table has been
161				 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
162				 */
163				if ( s.sTableId == this.id )
164				{
165					allSettings.splice( i, 1 );
166					break;
167				}
168			}
169			
170			/* Ensure the table has an ID - required for accessibility */
171			if ( sId === null || sId === "" )
172			{
173				sId = "DataTables_Table_"+(DataTable.ext._unique++);
174				this.id = sId;
175			}
176			
177			/* Create the settings object for this table and set some of the default parameters */
178			var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
179				"sDestroyWidth": $this[0].style.width,
180				"sInstance":     sId,
181				"sTableId":      sId,
182				colgroup: $('<colgroup>').prependTo(this),
183				fastData: function (row, column, type) {
184					return _fnGetCellData(oSettings, row, column, type);
185				}
186			} );
187			oSettings.nTable = this;
188			oSettings.oInit  = oInit;
189			
190			allSettings.push( oSettings );
191			
192			// Make a single API instance available for internal handling
193			oSettings.api = new _Api( oSettings );
194			
195			// Need to add the instance after the instance after the settings object has been added
196			// to the settings array, so we can self reference the table instance if more than one
197			oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
198			
199			// Backwards compatibility, before we apply all the defaults
200			_fnCompatOpts( oInit );
201			
202			// If the length menu is given, but the init display length is not, use the length menu
203			if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
204			{
205				oInit.iDisplayLength = Array.isArray(oInit.aLengthMenu[0])
206					? oInit.aLengthMenu[0][0]
207					: $.isPlainObject( oInit.aLengthMenu[0] )
208						? oInit.aLengthMenu[0].value
209						: oInit.aLengthMenu[0];
210			}
211			
212			// Apply the defaults and init options to make a single init object will all
213			// options defined from defaults and instance options.
214			oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
215			
216			
217			// Map the initialisation options onto the settings object
218			_fnMap( oSettings.oFeatures, oInit, [
219				"bPaginate",
220				"bLengthChange",
221				"bFilter",
222				"bSort",
223				"bSortMulti",
224				"bInfo",
225				"bProcessing",
226				"bAutoWidth",
227				"bSortClasses",
228				"bServerSide",
229				"bDeferRender"
230			] );
231			_fnMap( oSettings, oInit, [
232				"ajax",
233				"fnFormatNumber",
234				"sServerMethod",
235				"aaSorting",
236				"aaSortingFixed",
237				"aLengthMenu",
238				"sPaginationType",
239				"iStateDuration",
240				"bSortCellsTop",
241				"iTabIndex",
242				"sDom",
243				"fnStateLoadCallback",
244				"fnStateSaveCallback",
245				"renderer",
246				"searchDelay",
247				"rowId",
248				"caption",
249				"layout",
250				"orderDescReverse",
251				"typeDetect",
252				[ "iCookieDuration", "iStateDuration" ], // backwards compat
253				[ "oSearch", "oPreviousSearch" ],
254				[ "aoSearchCols", "aoPreSearchCols" ],
255				[ "iDisplayLength", "_iDisplayLength" ]
256			] );
257			_fnMap( oSettings.oScroll, oInit, [
258				[ "sScrollX", "sX" ],
259				[ "sScrollXInner", "sXInner" ],
260				[ "sScrollY", "sY" ],
261				[ "bScrollCollapse", "bCollapse" ]
262			] );
263			_fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
264			
265			/* Callback functions which are array driven */
266			_fnCallbackReg( oSettings, 'aoDrawCallback',       oInit.fnDrawCallback );
267			_fnCallbackReg( oSettings, 'aoStateSaveParams',    oInit.fnStateSaveParams );
268			_fnCallbackReg( oSettings, 'aoStateLoadParams',    oInit.fnStateLoadParams );
269			_fnCallbackReg( oSettings, 'aoStateLoaded',        oInit.fnStateLoaded );
270			_fnCallbackReg( oSettings, 'aoRowCallback',        oInit.fnRowCallback );
271			_fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow );
272			_fnCallbackReg( oSettings, 'aoHeaderCallback',     oInit.fnHeaderCallback );
273			_fnCallbackReg( oSettings, 'aoFooterCallback',     oInit.fnFooterCallback );
274			_fnCallbackReg( oSettings, 'aoInitComplete',       oInit.fnInitComplete );
275			_fnCallbackReg( oSettings, 'aoPreDrawCallback',    oInit.fnPreDrawCallback );
276			
277			oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
278			
279			/* Browser support detection */
280			_fnBrowserDetect( oSettings );
281			
282			var oClasses = oSettings.oClasses;
283			
284			$.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
285			$this.addClass( oClasses.table );
286			
287			if (! oSettings.oFeatures.bPaginate) {
288				oInit.iDisplayStart = 0;
289			}
290			
291			if ( oSettings.iInitDisplayStart === undefined )
292			{
293				/* Display start point, taking into account the save saving */
294				oSettings.iInitDisplayStart = oInit.iDisplayStart;
295				oSettings._iDisplayStart = oInit.iDisplayStart;
296			}
297			
298			var defer = oInit.iDeferLoading;
299			if ( defer !== null )
300			{
301				oSettings.deferLoading = true;
302			
303				var tmp = Array.isArray(defer);
304				oSettings._iRecordsDisplay = tmp ? defer[0] : defer;
305				oSettings._iRecordsTotal = tmp ? defer[1] : defer;
306			}
307			
308			/*
309			 * Columns
310			 * See if we should load columns automatically or use defined ones
311			 */
312			var columnsInit = [];
313			var thead = this.getElementsByTagName('thead');
314			var initHeaderLayout = _fnDetectHeader( oSettings, thead[0] );
315			
316			// If we don't have a columns array, then generate one with nulls
317			if ( oInit.aoColumns ) {
318				columnsInit = oInit.aoColumns;
319			}
320			else if ( initHeaderLayout.length ) {
321				for ( i=0, iLen=initHeaderLayout[0].length ; i<iLen ; i++ ) {
322					columnsInit.push( null );
323				}
324			}
325			
326			// Add the columns
327			for ( i=0, iLen=columnsInit.length ; i<iLen ; i++ ) {
328				_fnAddColumn( oSettings );
329			}
330			
331			// Apply the column definitions
332			_fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, columnsInit, initHeaderLayout, function (iCol, oDef) {
333				_fnColumnOptions( oSettings, iCol, oDef );
334			} );
335			
336			/* HTML5 attribute detection - build an mData object automatically if the
337			 * attributes are found
338			 */
339			var rowOne = $this.children('tbody').find('tr').eq(0);
340			
341			if ( rowOne.length ) {
342				var a = function ( cell, name ) {
343					return cell.getAttribute( 'data-'+name ) !== null ? name : null;
344				};
345			
346				$( rowOne[0] ).children('th, td').each( function (i, cell) {
347					var col = oSettings.aoColumns[i];
348			
349					if (! col) {
350						_fnLog( oSettings, 0, 'Incorrect column count', 18 );
351					}
352			
353					if ( col.mData === i ) {
354						var sort = a( cell, 'sort' ) || a( cell, 'order' );
355						var filter = a( cell, 'filter' ) || a( cell, 'search' );
356			
357						if ( sort !== null || filter !== null ) {
358							col.mData = {
359								_:      i+'.display',
360								sort:   sort !== null   ? i+'.@data-'+sort   : undefined,
361								type:   sort !== null   ? i+'.@data-'+sort   : undefined,
362								filter: filter !== null ? i+'.@data-'+filter : undefined
363							};
364							col._isArrayHost = true;
365			
366							_fnColumnOptions( oSettings, i );
367						}
368					}
369				} );
370			}
371			
372			// Must be done after everything which can be overridden by the state saving!
373			_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState );
374			
375			var features = oSettings.oFeatures;
376			if ( oInit.bStateSave )
377			{
378				features.bStateSave = true;
379			}
380			
381			// If aaSorting is not defined, then we use the first indicator in asSorting
382			// in case that has been altered, so the default sort reflects that option
383			if ( oInit.aaSorting === undefined ) {
384				var sorting = oSettings.aaSorting;
385				for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
386					sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
387				}
388			}
389			
390			// Do a first pass on the sorting classes (allows any size changes to be taken into
391			// account, and also will apply sorting disabled classes if disabled
392			_fnSortingClasses( oSettings );
393			
394			_fnCallbackReg( oSettings, 'aoDrawCallback', function () {
395				if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
396					_fnSortingClasses( oSettings );
397				}
398			} );
399			
400			
401			/*
402			 * Table HTML init
403			 * Cache the header, body and footer as required, creating them if needed
404			 */
405			var caption = $this.children('caption');
406			
407			if ( oSettings.caption ) {
408				if ( caption.length === 0 ) {
409					caption = $('<caption/>').appendTo( $this );
410				}
411			
412				caption.html( oSettings.caption );
413			}
414			
415			// Store the caption side, so we can remove the element from the document
416			// when creating the element
417			if (caption.length) {
418				caption[0]._captionSide = caption.css('caption-side');
419				oSettings.captionNode = caption[0];
420			}
421			
422			if ( thead.length === 0 ) {
423				thead = $('<thead/>').appendTo($this);
424			}
425			oSettings.nTHead = thead[0];
426			
427			var tbody = $this.children('tbody');
428			if ( tbody.length === 0 ) {
429				tbody = $('<tbody/>').insertAfter(thead);
430			}
431			oSettings.nTBody = tbody[0];
432			
433			var tfoot = $this.children('tfoot');
434			if ( tfoot.length === 0 ) {
435				// If we are a scrolling table, and no footer has been given, then we need to create
436				// a tfoot element for the caption element to be appended to
437				tfoot = $('<tfoot/>').appendTo($this);
438			}
439			oSettings.nTFoot = tfoot[0];
440			
441			// Copy the data index array
442			oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
443			
444			// Initialisation complete - table can be drawn
445			oSettings.bInitialised = true;
446			
447			// Language definitions
448			var oLanguage = oSettings.oLanguage;
449			$.extend( true, oLanguage, oInit.oLanguage );
450			
451			if ( oLanguage.sUrl ) {
452				// Get the language definitions from a file
453				$.ajax( {
454					dataType: 'json',
455					url: oLanguage.sUrl,
456					success: function ( json ) {
457						_fnCamelToHungarian( defaults.oLanguage, json );
458						$.extend( true, oLanguage, json, oSettings.oInit.oLanguage );
459			
460						_fnCallbackFire( oSettings, null, 'i18n', [oSettings], true);
461						_fnInitialise( oSettings );
462					},
463					error: function () {
464						// Error occurred loading language file
465						_fnLog( oSettings, 0, 'i18n file loading error', 21 );
466			
467						// Continue on as best we can
468						_fnInitialise( oSettings );
469					}
470				} );
471			}
472			else {
473				_fnCallbackFire( oSettings, null, 'i18n', [oSettings], true);
474				_fnInitialise( oSettings );
475			}
476		} );
477		_that = null;
478		return this;
479	};
480	
481	
482	
483	/**
484	 * DataTables extensions
485	 * 
486	 * This namespace acts as a collection area for plug-ins that can be used to
487	 * extend DataTables capabilities. Indeed many of the build in methods
488	 * use this method to provide their own capabilities (sorting methods for
489	 * example).
490	 *
491	 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
492	 * reasons
493	 *
494	 *  @namespace
495	 */
496	DataTable.ext = _ext = {
497		/**
498		 * Buttons. For use with the Buttons extension for DataTables. This is
499		 * defined here so other extensions can define buttons regardless of load
500		 * order. It is _not_ used by DataTables core.
501		 *
502		 *  @type object
503		 *  @default {}
504		 */
505		buttons: {},
506	
507	
508		/**
509		 * Element class names
510		 *
511		 *  @type object
512		 *  @default {}
513		 */
514		classes: {},
515	
516	
517		/**
518		 * DataTables build type (expanded by the download builder)
519		 *
520		 *  @type string
521		 */
522		builder: "dt/dt-2.2.2",
523	
524	
525		/**
526		 * Error reporting.
527		 * 
528		 * How should DataTables report an error. Can take the value 'alert',
529		 * 'throw', 'none' or a function.
530		 *
531		 *  @type string|function
532		 *  @default alert
533		 */
534		errMode: "alert",
535	
536	
537		/**
538		 * Legacy so v1 plug-ins don't throw js errors on load
539		 */
540		feature: [],
541	
542		/**
543		 * Feature plug-ins.
544		 * 
545		 * This is an object of callbacks which provide the features for DataTables
546		 * to be initialised via the `layout` option.
547		 */
548		features: {},
549	
550	
551		/**
552		 * Row searching.
553		 * 
554		 * This method of searching is complimentary to the default type based
555		 * searching, and a lot more comprehensive as it allows you complete control
556		 * over the searching logic. Each element in this array is a function
557		 * (parameters described below) that is called for every row in the table,
558		 * and your logic decides if it should be included in the searching data set
559		 * or not.
560		 *
561		 * Searching functions have the following input parameters:
562		 *
563		 * 1. `{object}` DataTables settings object: see
564		 *    {@link DataTable.models.oSettings}
565		 * 2. `{array|object}` Data for the row to be processed (same as the
566		 *    original format that was passed in as the data source, or an array
567		 *    from a DOM data source
568		 * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
569		 *    can be useful to retrieve the `TR` element if you need DOM interaction.
570		 *
571		 * And the following return is expected:
572		 *
573		 * * {boolean} Include the row in the searched result set (true) or not
574		 *   (false)
575		 *
576		 * Note that as with the main search ability in DataTables, technically this
577		 * is "filtering", since it is subtractive. However, for consistency in
578		 * naming we call it searching here.
579		 *
580		 *  @type array
581		 *  @default []
582		 *
583		 *  @example
584		 *    // The following example shows custom search being applied to the
585		 *    // fourth column (i.e. the data[3] index) based on two input values
586		 *    // from the end-user, matching the data in a certain range.
587		 *    $.fn.dataTable.ext.search.push(
588		 *      function( settings, data, dataIndex ) {
589		 *        var min = document.getElementById('min').value * 1;
590		 *        var max = document.getElementById('max').value * 1;
591		 *        var version = data[3] == "-" ? 0 : data[3]*1;
592		 *
593		 *        if ( min == "" && max == "" ) {
594		 *          return true;
595		 *        }
596		 *        else if ( min == "" && version < max ) {
597		 *          return true;
598		 *        }
599		 *        else if ( min < version && "" == max ) {
600		 *          return true;
601		 *        }
602		 *        else if ( min < version && version < max ) {
603		 *          return true;
604		 *        }
605		 *        return false;
606		 *      }
607		 *    );
608		 */
609		search: [],
610	
611	
612		/**
613		 * Selector extensions
614		 *
615		 * The `selector` option can be used to extend the options available for the
616		 * selector modifier options (`selector-modifier` object data type) that
617		 * each of the three built in selector types offer (row, column and cell +
618		 * their plural counterparts). For example the Select extension uses this
619		 * mechanism to provide an option to select only rows, columns and cells
620		 * that have been marked as selected by the end user (`{selected: true}`),
621		 * which can be used in conjunction with the existing built in selector
622		 * options.
623		 *
624		 * Each property is an array to which functions can be pushed. The functions
625		 * take three attributes:
626		 *
627		 * * Settings object for the host table
628		 * * Options object (`selector-modifier` object type)
629		 * * Array of selected item indexes
630		 *
631		 * The return is an array of the resulting item indexes after the custom
632		 * selector has been applied.
633		 *
634		 *  @type object
635		 */
636		selector: {
637			cell: [],
638			column: [],
639			row: []
640		},
641	
642	
643		/**
644		 * Legacy configuration options. Enable and disable legacy options that
645		 * are available in DataTables.
646		 *
647		 *  @type object
648		 */
649		legacy: {
650			/**
651			 * Enable / disable DataTables 1.9 compatible server-side processing
652			 * requests
653			 *
654			 *  @type boolean
655			 *  @default null
656			 */
657			ajax: null
658		},
659	
660	
661		/**
662		 * Pagination plug-in methods.
663		 * 
664		 * Each entry in this object is a function and defines which buttons should
665		 * be shown by the pagination rendering method that is used for the table:
666		 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
667		 * buttons are displayed in the document, while the functions here tell it
668		 * what buttons to display. This is done by returning an array of button
669		 * descriptions (what each button will do).
670		 *
671		 * Pagination types (the four built in options and any additional plug-in
672		 * options defined here) can be used through the `paginationType`
673		 * initialisation parameter.
674		 *
675		 * The functions defined take two parameters:
676		 *
677		 * 1. `{int} page` The current page index
678		 * 2. `{int} pages` The number of pages in the table
679		 *
680		 * Each function is expected to return an array where each element of the
681		 * array can be one of:
682		 *
683		 * * `first` - Jump to first page when activated
684		 * * `last` - Jump to last page when activated
685		 * * `previous` - Show previous page when activated
686		 * * `next` - Show next page when activated
687		 * * `{int}` - Show page of the index given
688		 * * `{array}` - A nested array containing the above elements to add a
689		 *   containing 'DIV' element (might be useful for styling).
690		 *
691		 * Note that DataTables v1.9- used this object slightly differently whereby
692		 * an object with two functions would be defined for each plug-in. That
693		 * ability is still supported by DataTables 1.10+ to provide backwards
694		 * compatibility, but this option of use is now decremented and no longer
695		 * documented in DataTables 1.10+.
696		 *
697		 *  @type object
698		 *  @default {}
699		 *
700		 *  @example
701		 *    // Show previous, next and current page buttons only
702		 *    $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
703		 *      return [ 'previous', page, 'next' ];
704		 *    };
705		 */
706		pager: {},
707	
708	
709		renderer: {
710			pageButton: {},
711			header: {}
712		},
713	
714	
715		/**
716		 * Ordering plug-ins - custom data source
717		 * 
718		 * The extension options for ordering of data available here is complimentary
719		 * to the default type based ordering that DataTables typically uses. It
720		 * allows much greater control over the the data that is being used to
721		 * order a column, but is necessarily therefore more complex.
722		 * 
723		 * This type of ordering is useful if you want to do ordering based on data
724		 * live from the DOM (for example the contents of an 'input' element) rather
725		 * than just the static string that DataTables knows of.
726		 * 
727		 * The way these plug-ins work is that you create an array of the values you
728		 * wish to be ordering for the column in question and then return that
729		 * array. The data in the array much be in the index order of the rows in
730		 * the table (not the currently ordering order!). Which order data gathering
731		 * function is run here depends on the `dt-init columns.orderDataType`
732		 * parameter that is used for the column (if any).
733		 *
734		 * The functions defined take two parameters:
735		 *
736		 * 1. `{object}` DataTables settings object: see
737		 *    {@link DataTable.models.oSettings}
738		 * 2. `{int}` Target column index
739		 *
740		 * Each function is expected to return an array:
741		 *
742		 * * `{array}` Data for the column to be ordering upon
743		 *
744		 *  @type array
745		 *
746		 *  @example
747		 *    // Ordering using `input` node values
748		 *    $.fn.dataTable.ext.order['dom-text'] = function  ( settings, col )
749		 *    {
750		 *      return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
751		 *        return $('input', td).val();
752		 *      } );
753		 *    }
754		 */
755		order: {},
756	
757	
758		/**
759		 * Type based plug-ins.
760		 *
761		 * Each column in DataTables has a type assigned to it, either by automatic
762		 * detection or by direct assignment using the `type` option for the column.
763		 * The type of a column will effect how it is ordering and search (plug-ins
764		 * can also make use of the column type if required).
765		 *
766		 * @namespace
767		 */
768		type: {
769			/**
770			 * Automatic column class assignment
771			 */
772			className: {},
773	
774			/**
775			 * Type detection functions.
776			 *
777			 * The functions defined in this object are used to automatically detect
778			 * a column's type, making initialisation of DataTables super easy, even
779			 * when complex data is in the table.
780			 *
781			 * The functions defined take two parameters:
782			 *
783		     *  1. `{*}` Data from the column cell to be analysed
784		     *  2. `{settings}` DataTables settings object. This can be used to
785		     *     perform context specific type detection - for example detection
786		     *     based on language settings such as using a comma for a decimal
787		     *     place. Generally speaking the options from the settings will not
788		     *     be required
789			 *
790			 * Each function is expected to return:
791			 *
792			 * * `{string|null}` Data type detected, or null if unknown (and thus
793			 *   pass it on to the other type detection functions.
794			 *
795			 *  @type array
796			 *
797			 *  @example
798			 *    // Currency type detection plug-in:
799			 *    $.fn.dataTable.ext.type.detect.push(
800			 *      function ( data, settings ) {
801			 *        // Check the numeric part
802			 *        if ( ! data.substring(1).match(/[0-9]/) ) {
803			 *          return null;
804			 *        }
805			 *
806			 *        // Check prefixed by currency
807			 *        if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
808			 *          return 'currency';
809			 *        }
810			 *        return null;
811			 *      }
812			 *    );
813			 */
814			detect: [],
815	
816			/**
817			 * Automatic renderer assignment
818			 */
819			render: {},
820	
821	
822			/**
823			 * Type based search formatting.
824			 *
825			 * The type based searching functions can be used to pre-format the
826			 * data to be search on. For example, it can be used to strip HTML
827			 * tags or to de-format telephone numbers for numeric only searching.
828			 *
829			 * Note that is a search is not defined for a column of a given type,
830			 * no search formatting will be performed.
831			 * 
832			 * Pre-processing of searching data plug-ins - When you assign the sType
833			 * for a column (or have it automatically detected for you by DataTables
834			 * or a type detection plug-in), you will typically be using this for
835			 * custom sorting, but it can also be used to provide custom searching
836			 * by allowing you to pre-processing the data and returning the data in
837			 * the format that should be searched upon. This is done by adding
838			 * functions this object with a parameter name which matches the sType
839			 * for that target column. This is the corollary of <i>afnSortData</i>
840			 * for searching data.
841			 *
842			 * The functions defined take a single parameter:
843			 *
844		     *  1. `{*}` Data from the column cell to be prepared for searching
845			 *
846			 * Each function is expected to return:
847			 *
848			 * * `{string|null}` Formatted string that will be used for the searching.
849			 *
850			 *  @type object
851			 *  @default {}
852			 *
853			 *  @example
854			 *    $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
855			 *      return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
856			 *    }
857			 */
858			search: {},
859	
860	
861			/**
862			 * Type based ordering.
863			 *
864			 * The column type tells DataTables what ordering to apply to the table
865			 * when a column is sorted upon. The order for each type that is defined,
866			 * is defined by the functions available in this object.
867			 *
868			 * Each ordering option can be described by three properties added to
869			 * this object:
870			 *
871			 * * `{type}-pre` - Pre-formatting function
872			 * * `{type}-asc` - Ascending order function
873			 * * `{type}-desc` - Descending order function
874			 *
875			 * All three can be used together, only `{type}-pre` or only
876			 * `{type}-asc` and `{type}-desc` together. It is generally recommended
877			 * that only `{type}-pre` is used, as this provides the optimal
878			 * implementation in terms of speed, although the others are provided
879			 * for compatibility with existing Javascript sort functions.
880			 *
881			 * `{type}-pre`: Functions defined take a single parameter:
882			 *
883		     *  1. `{*}` Data from the column cell to be prepared for ordering
884			 *
885			 * And return:
886			 *
887			 * * `{*}` Data to be sorted upon
888			 *
889			 * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
890			 * functions, taking two parameters:
891			 *
892		     *  1. `{*}` Data to compare to the second parameter
893		     *  2. `{*}` Data to compare to the first parameter
894			 *
895			 * And returning:
896			 *
897			 * * `{*}` Ordering match: <0 if first parameter should be sorted lower
898			 *   than the second parameter, ===0 if the two parameters are equal and
899			 *   >0 if the first parameter should be sorted height than the second
900			 *   parameter.
901			 * 
902			 *  @type object
903			 *  @default {}
904			 *
905			 *  @example
906			 *    // Numeric ordering of formatted numbers with a pre-formatter
907			 *    $.extend( $.fn.dataTable.ext.type.order, {
908			 *      "string-pre": function(x) {
909			 *        a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
910			 *        return parseFloat( a );
911			 *      }
912			 *    } );
913			 *
914			 *  @example
915			 *    // Case-sensitive string ordering, with no pre-formatting method
916			 *    $.extend( $.fn.dataTable.ext.order, {
917			 *      "string-case-asc": function(x,y) {
918			 *        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
919			 *      },
920			 *      "string-case-desc": function(x,y) {
921			 *        return ((x < y) ? 1 : ((x > y) ? -1 : 0));
922			 *      }
923			 *    } );
924			 */
925			order: {}
926		},
927	
928		/**
929		 * Unique DataTables instance counter
930		 *
931		 * @type int
932		 * @private
933		 */
934		_unique: 0,
935	
936	
937		//
938		// Depreciated
939		// The following properties are retained for backwards compatibility only.
940		// The should not be used in new projects and will be removed in a future
941		// version
942		//
943	
944		/**
945		 * Version check function.
946		 *  @type function
947		 *  @depreciated Since 1.10
948		 */
949		fnVersionCheck: DataTable.fnVersionCheck,
950	
951	
952		/**
953		 * Index for what 'this' index API functions should use
954		 *  @type int
955		 *  @deprecated Since v1.10
956		 */
957		iApiIndex: 0,
958	
959	
960		/**
961		 * Software version
962		 *  @type string
963		 *  @deprecated Since v1.10
964		 */
965		sVersion: DataTable.version
966	};
967	
968	
969	//
970	// Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
971	//
972	$.extend( _ext, {
973		afnFiltering: _ext.search,
974		aTypes:       _ext.type.detect,
975		ofnSearch:    _ext.type.search,
976		oSort:        _ext.type.order,
977		afnSortData:  _ext.order,
978		aoFeatures:   _ext.feature,
979		oStdClasses:  _ext.classes,
980		oPagination:  _ext.pager
981	} );
982	
983	
984	$.extend( DataTable.ext.classes, {
985		container: 'dt-container',
986		empty: {
987			row: 'dt-empty'
988		},
989		info: {
990			container: 'dt-info'
991		},
992		layout: {
993			row: 'dt-layout-row',
994			cell: 'dt-layout-cell',
995			tableRow: 'dt-layout-table',
996			tableCell: '',
997			start: 'dt-layout-start',
998			end: 'dt-layout-end',
999			full: 'dt-layout-full'
1000		},
1001		length: {
1002			container: 'dt-length',
1003			select: 'dt-input'
1004		},
1005		order: {
1006			canAsc: 'dt-orderable-asc',
1007			canDesc: 'dt-orderable-desc',
1008			isAsc: 'dt-ordering-asc',
1009			isDesc: 'dt-ordering-desc',
1010			none: 'dt-orderable-none',
1011			position: 'sorting_'
1012		},
1013		processing: {
1014			container: 'dt-processing'
1015		},
1016		scrolling: {
1017			body: 'dt-scroll-body',
1018			container: 'dt-scroll',
1019			footer: {
1020				self: 'dt-scroll-foot',
1021				inner: 'dt-scroll-footInner'
1022			},
1023			header: {
1024				self: 'dt-scroll-head',
1025				inner: 'dt-scroll-headInner'
1026			}
1027		},
1028		search: {
1029			container: 'dt-search',
1030			input: 'dt-input'
1031		},
1032		table: 'dataTable',	
1033		tbody: {
1034			cell: '',
1035			row: ''
1036		},
1037		thead: {
1038			cell: '',
1039			row: ''
1040		},
1041		tfoot: {
1042			cell: '',
1043			row: ''
1044		},
1045		paging: {
1046			active: 'current',
1047			button: 'dt-paging-button',
1048			container: 'dt-paging',
1049			disabled: 'disabled',
1050			nav: ''
1051		}
1052	} );
1053	
1054	
1055	/*
1056	 * It is useful to have variables which are scoped locally so only the
1057	 * DataTables functions can access them and they don't leak into global space.
1058	 * At the same time these functions are often useful over multiple files in the
1059	 * core and API, so we list, or at least document, all variables which are used
1060	 * by DataTables as private variables here. This also ensures that there is no
1061	 * clashing of variable names and that they can easily referenced for reuse.
1062	 */
1063	
1064	
1065	// Defined else where
1066	//  _selector_run
1067	//  _selector_opts
1068	//  _selector_row_indexes
1069	
1070	var _ext; // DataTable.ext
1071	var _Api; // DataTable.Api
1072	var _api_register; // DataTable.Api.register
1073	var _api_registerPlural; // DataTable.Api.registerPlural
1074	
1075	var _re_dic = {};
1076	var _re_new_lines = /[\r\n\u2028]/g;
1077	var _re_html = /<([^>]*>)/g;
1078	var _max_str_len = Math.pow(2, 28);
1079	
1080	// This is not strict ISO8601 - Date.parse() is quite lax, although
1081	// implementations differ between browsers.
1082	var _re_date = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/;
1083	
1084	// Escape regular expression special characters
1085	var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
1086	
1087	// https://en.wikipedia.org/wiki/Foreign_exchange_market
1088	// - \u20BD - Russian ruble.
1089	// - \u20a9 - South Korean Won
1090	// - \u20BA - Turkish Lira
1091	// - \u20B9 - Indian Rupee
1092	// - R - Brazil (R$) and South Africa
1093	// - fr - Swiss Franc
1094	// - kr - Swedish krona, Norwegian krone and Danish krone
1095	// - \u2009 is thin space and \u202F is narrow no-break space, both used in many
1096	// - Ƀ - Bitcoin
1097	// - Ξ - Ethereum
1098	//   standards as thousands separators.
1099	var _re_formatted_numeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi;
1100	
1101	
1102	var _empty = function ( d ) {
1103		return !d || d === true || d === '-' ? true : false;
1104	};
1105	
1106	
1107	var _intVal = function ( s ) {
1108		var integer = parseInt( s, 10 );
1109		return !isNaN(integer) && isFinite(s) ? integer : null;
1110	};
1111	
1112	// Convert from a formatted number with characters other than `.` as the
1113	// decimal place, to a Javascript number
1114	var _numToDecimal = function ( num, decimalPoint ) {
1115		// Cache created regular expressions for speed as this function is called often
1116		if ( ! _re_dic[ decimalPoint ] ) {
1117			_re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
1118		}
1119		return typeof num === 'string' && decimalPoint !== '.' ?
1120			num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
1121			num;
1122	};
1123	
1124	
1125	var _isNumber = function ( d, decimalPoint, formatted, allowEmpty ) {
1126		var type = typeof d;
1127		var strType = type === 'string';
1128	
1129		if ( type === 'number' || type === 'bigint') {
1130			return true;
1131		}
1132	
1133		// If empty return immediately so there must be a number if it is a
1134		// formatted string (this stops the string "k", or "kr", etc being detected
1135		// as a formatted number for currency
1136		if ( allowEmpty && _empty( d ) ) {
1137			return true;
1138		}
1139	
1140		if ( decimalPoint && strType ) {
1141			d = _numToDecimal( d, decimalPoint );
1142		}
1143	
1144		if ( formatted && strType ) {
1145			d = d.replace( _re_formatted_numeric, '' );
1146		}
1147	
1148		return !isNaN( parseFloat(d) ) && isFinite( d );
1149	};
1150	
1151	
1152	// A string without HTML in it can be considered to be HTML still
1153	var _isHtml = function ( d ) {
1154		return _empty( d ) || typeof d === 'string';
1155	};
1156	
1157	// Is a string a number surrounded by HTML?
1158	var _htmlNumeric = function ( d, decimalPoint, formatted, allowEmpty ) {
1159		if ( allowEmpty && _empty( d ) ) {
1160			return true;
1161		}
1162	
1163		// input and select strings mean that this isn't just a number
1164		if (typeof d === 'string' && d.match(/<(input|select)/i)) {
1165			return null;
1166		}
1167	
1168		var html = _isHtml( d );
1169		return ! html ?
1170			null :
1171			_isNumber( _stripHtml( d ), decimalPoint, formatted, allowEmpty ) ?
1172				true :
1173				null;
1174	};
1175	
1176	
1177	var _pluck = function ( a, prop, prop2 ) {
1178		var out = [];
1179		var i=0, ien=a.length;
1180	
1181		// Could have the test in the loop for slightly smaller code, but speed
1182		// is essential here
1183		if ( prop2 !== undefined ) {
1184			for ( ; i<ien ; i++ ) {
1185				if ( a[i] && a[i][ prop ] ) {
1186					out.push( a[i][ prop ][ prop2 ] );
1187				}
1188			}
1189		}
1190		else {
1191			for ( ; i<ien ; i++ ) {
1192				if ( a[i] ) {
1193					out.push( a[i][ prop ] );
1194				}
1195			}
1196		}
1197	
1198		return out;
1199	};
1200	
1201	
1202	// Basically the same as _pluck, but rather than looping over `a` we use `order`
1203	// as the indexes to pick from `a`
1204	var _pluck_order = function ( a, order, prop, prop2 )
1205	{
1206		var out = [];
1207		var i=0, ien=order.length;
1208	
1209		// Could have the test in the loop for slightly smaller code, but speed
1210		// is essential here
1211		if ( prop2 !== undefined ) {
1212			for ( ; i<ien ; i++ ) {
1213				if ( a[ order[i] ] && a[ order[i] ][ prop ] ) {
1214					out.push( a[ order[i] ][ prop ][ prop2 ] );
1215				}
1216			}
1217		}
1218		else {
1219			for ( ; i<ien ; i++ ) {
1220				if ( a[ order[i] ] ) {
1221					out.push( a[ order[i] ][ prop ] );
1222				}
1223			}
1224		}
1225	
1226		return out;
1227	};
1228	
1229	
1230	var _range = function ( len, start )
1231	{
1232		var out = [];
1233		var end;
1234	
1235		if ( start === undefined ) {
1236			start = 0;
1237			end = len;
1238		}
1239		else {
1240			end = start;
1241			start = len;
1242		}
1243	
1244		for ( var i=start ; i<end ; i++ ) {
1245			out.push( i );
1246		}
1247	
1248		return out;
1249	};
1250	
1251	
1252	var _removeEmpty = function ( a )
1253	{
1254		var out = [];
1255	
1256		for ( var i=0, ien=a.length ; i<ien ; i++ ) {
1257			if ( a[i] ) { // careful - will remove all falsy values!
1258				out.push( a[i] );
1259			}
1260		}
1261	
1262		return out;
1263	};
1264	
1265	// Replaceable function in api.util
1266	var _stripHtml = function (input) {
1267		if (! input || typeof input !== 'string') {
1268			return input;
1269		}
1270	
1271		// Irrelevant check to workaround CodeQL's false positive on the regex
1272		if (input.length > _max_str_len) {
1273			throw new Error('Exceeded max str len');
1274		}
1275	
1276		var previous;
1277	
1278		input = input.replace(_re_html, ''); // Complete tags
1279	
1280		// Safety for incomplete script tag - use do / while to ensure that
1281		// we get all instances
1282		do {
1283			previous = input;
1284			input = input.replace(/<script/i, '');
1285		} while (input !== previous);
1286	
1287		return previous;
1288	};
1289	
1290	// Replaceable function in api.util
1291	var _escapeHtml = function ( d ) {
1292		if (Array.isArray(d)) {
1293			d = d.join(',');
1294		}
1295	
1296		return typeof d === 'string' ?
1297			d
1298				.replace(/&/g, '&amp;')
1299				.replace(/</g, '&lt;')
1300				.replace(/>/g, '&gt;')
1301				.replace(/"/g, '&quot;') :
1302			d;
1303	};
1304	
1305	// Remove diacritics from a string by decomposing it and then removing
1306	// non-ascii characters
1307	var _normalize = function (str, both) {
1308		if (typeof str !== 'string') {
1309			return str;
1310		}
1311	
1312		// It is faster to just run `normalize` than it is to check if
1313		// we need to with a regex! (Check as it isn't available in old
1314		// Safari)
1315		var res = str.normalize
1316			? str.normalize("NFD")
1317			: str;
1318	
1319		// Equally, here we check if a regex is needed or not
1320		return res.length !== str.length
1321			? (both === true ? str + ' ' : '' ) + res.replace(/[\u0300-\u036f]/g, "")
1322			: res;
1323	}
1324	
1325	/**
1326	 * Determine if all values in the array are unique. This means we can short
1327	 * cut the _unique method at the cost of a single loop. A sorted array is used
1328	 * to easily check the values.
1329	 *
1330	 * @param  {array} src Source array
1331	 * @return {boolean} true if all unique, false otherwise
1332	 * @ignore
1333	 */
1334	var _areAllUnique = function ( src ) {
1335		if ( src.length < 2 ) {
1336			return true;
1337		}
1338	
1339		var sorted = src.slice().sort();
1340		var last = sorted[0];
1341	
1342		for ( var i=1, ien=sorted.length ; i<ien ; i++ ) {
1343			if ( sorted[i] === last ) {
1344				return false;
1345			}
1346	
1347			last = sorted[i];
1348		}
1349	
1350		return true;
1351	};
1352	
1353	
1354	/**
1355	 * Find the unique elements in a source array.
1356	 *
1357	 * @param  {array} src Source array
1358	 * @return {array} Array of unique items
1359	 * @ignore
1360	 */
1361	var _unique = function ( src )
1362	{
1363		if (Array.from && Set) {
1364			return Array.from(new Set(src));
1365		}
1366	
1367		if ( _areAllUnique( src ) ) {
1368			return src.slice();
1369		}
1370	
1371		// A faster unique method is to use object keys to identify used values,
1372		// but this doesn't work with arrays or objects, which we must also
1373		// consider. See jsperf.app/compare-array-unique-versions/4 for more
1374		// information.
1375		var
1376			out = [],
1377			val,
1378			i, ien=src.length,
1379			j, k=0;
1380	
1381		again: for ( i=0 ; i<ien ; i++ ) {
1382			val = src[i];
1383	
1384			for ( j=0 ; j<k ; j++ ) {
1385				if ( out[j] === val ) {
1386					continue again;
1387				}
1388			}
1389	
1390			out.push( val );
1391			k++;
1392		}
1393	
1394		return out;
1395	};
1396	
1397	// Surprisingly this is faster than [].concat.apply
1398	// https://jsperf.com/flatten-an-array-loop-vs-reduce/2
1399	var _flatten = function (out, val) {
1400		if (Array.isArray(val)) {
1401			for (var i=0 ; i<val.length ; i++) {
1402				_flatten(out, val[i]);
1403			}
1404		}
1405		else {
1406			out.push(val);
1407		}
1408	
1409		return out;
1410	}
1411	
1412	// Similar to jQuery's addClass, but use classList.add
1413	function _addClass(el, name) {
1414		if (name) {
1415			name.split(' ').forEach(function (n) {
1416				if (n) {
1417					// `add` does deduplication, so no need to check `contains`
1418					el.classList.add(n);
1419				}
1420			});
1421		}
1422	}
1423	
1424	/**
1425	 * DataTables utility methods
1426	 * 
1427	 * This namespace provides helper methods that DataTables uses internally to
1428	 * create a DataTable, but which are not exclusively used only for DataTables.
1429	 * These methods can be used by extension authors to save the duplication of
1430	 * code.
1431	 *
1432	 *  @namespace
1433	 */
1434	DataTable.util = {
1435		/**
1436		 * Return a string with diacritic characters decomposed
1437		 * @param {*} mixed Function or string to normalize
1438		 * @param {*} both Return original string and the normalized string
1439		 * @returns String or undefined
1440		 */
1441		diacritics: function (mixed, both) {
1442			var type = typeof mixed;
1443	
1444			if (type !== 'function') {
1445				return _normalize(mixed, both);
1446			}
1447			_normalize = mixed;
1448		},
1449	
1450		/**
1451		 * Debounce a function
1452		 *
1453		 * @param {function} fn Function to be called
1454		 * @param {integer} freq Call frequency in mS
1455		 * @return {function} Wrapped function
1456		 */
1457		debounce: function ( fn, timeout ) {
1458			var timer;
1459	
1460			return function () {
1461				var that = this;
1462				var args = arguments;
1463	
1464				clearTimeout(timer);
1465	
1466				timer = setTimeout( function () {
1467					fn.apply(that, args);
1468				}, timeout || 250 );
1469			};
1470		},
1471	
1472		/**
1473		 * Throttle the calls to a function. Arguments and context are maintained
1474		 * for the throttled function.
1475		 *
1476		 * @param {function} fn Function to be called
1477		 * @param {integer} freq Call frequency in mS
1478		 * @return {function} Wrapped function
1479		 */
1480		throttle: function ( fn, freq ) {
1481			var
1482				frequency = freq !== undefined ? freq : 200,
1483				last,
1484				timer;
1485	
1486			return function () {
1487				var
1488					that = this,
1489					now  = +new Date(),
1490					args = arguments;
1491	
1492				if ( last && now < last + frequency ) {
1493					clearTimeout( timer );
1494	
1495					timer = setTimeout( function () {
1496						last = undefined;
1497						fn.apply( that, args );
1498					}, frequency );
1499				}
1500				else {
1501					last = now;
1502					fn.apply( that, args );
1503				}
1504			};
1505		},
1506	
1507		/**
1508		 * Escape a string such that it can be used in a regular expression
1509		 *
1510		 *  @param {string} val string to escape
1511		 *  @returns {string} escaped string
1512		 */
1513		escapeRegex: function ( val ) {
1514			return val.replace( _re_escape_regex, '\\$1' );
1515		},
1516	
1517		/**
1518		 * Create a function that will write to a nested object or array
1519		 * @param {*} source JSON notation string
1520		 * @returns Write function
1521		 */
1522		set: function ( source ) {
1523			if ( $.isPlainObject( source ) ) {
1524				/* Unlike get, only the underscore (global) option is used for for
1525				 * setting data since we don't know the type here. This is why an object
1526				 * option is not documented for `mData` (which is read/write), but it is
1527				 * for `mRender` which is read only.
1528				 */
1529				return DataTable.util.set( source._ );
1530			}
1531			else if ( source === null ) {
1532				// Nothing to do when the data source is null
1533				return function () {};
1534			}
1535			else if ( typeof source === 'function' ) {
1536				return function (data, val, meta) {
1537					source( data, 'set', val, meta );
1538				};
1539			}
1540			else if (
1541				typeof source === 'string' && (source.indexOf('.') !== -1 ||
1542				source.indexOf('[') !== -1 || source.indexOf('(') !== -1)
1543			) {
1544				// Like the get, we need to get data from a nested object
1545				var setData = function (data, val, src) {
1546					var a = _fnSplitObjNotation( src ), b;
1547					var aLast = a[a.length-1];
1548					var arrayNotation, funcNotation, o, innerSrc;
1549		
1550					for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) {
1551						// Protect against prototype pollution
1552						if (a[i] === '__proto__' || a[i] === 'constructor') {
1553							throw new Error('Cannot set prototype values');
1554						}
1555		
1556						// Check if we are dealing with an array notation request
1557						arrayNotation = a[i].match(__reArray);
1558						funcNotation = a[i].match(__reFn);
1559		
1560						if ( arrayNotation ) {
1561							a[i] = a[i].replace(__reArray, '');
1562							data[ a[i] ] = [];
1563		
1564							// Get the remainder of the nested object to set so we can recurse
1565							b = a.slice();
1566							b.splice( 0, i+1 );
1567							innerSrc = b.join('.');
1568		
1569							// Traverse each entry in the array setting the properties requested
1570							if ( Array.isArray( val ) ) {
1571								for ( var j=0, jLen=val.length ; j<jLen ; j++ ) {
1572									o = {};
1573									setData( o, val[j], innerSrc );
1574									data[ a[i] ].push( o );
1575								}
1576							}
1577							else {
1578								// We've been asked to save data to an array, but it
1579								// isn't array data to be saved. Best that can be done
1580								// is to just save the value.
1581								data[ a[i] ] = val;
1582							}
1583		
1584							// The inner call to setData has already traversed through the remainder
1585							// of the source and has set the data, thus we can exit here
1586							return;
1587						}
1588						else if ( funcNotation ) {
1589							// Function call
1590							a[i] = a[i].replace(__reFn, '');
1591							data = data[ a[i] ]( val );
1592						}
1593		
1594						// If the nested object doesn't currently exist - since we are
1595						// trying to set the value - create it
1596						if ( data[ a[i] ] === null || data[ a[i] ] === undefined ) {
1597							data[ a[i] ] = {};
1598						}
1599						data = data[ a[i] ];
1600					}
1601		
1602					// Last item in the input - i.e, the actual set
1603					if ( aLast.match(__reFn ) ) {
1604						// Function call
1605						data = data[ aLast.replace(__reFn, '') ]( val );
1606					}
1607					else {
1608						// If array notation is used, we just want to strip it and use the property name
1609						// and assign the value. If it isn't used, then we get the result we want anyway
1610						data[ aLast.replace(__reArray, '') ] = val;
1611					}
1612				};
1613		
1614				return function (data, val) { // meta is also passed in, but not used
1615					return setData( data, val, source );
1616				};
1617			}
1618			else {
1619				// Array or flat object mapping
1620				return function (data, val) { // meta is also passed in, but not used
1621					data[source] = val;
1622				};
1623			}
1624		},
1625	
1626		/**
1627		 * Create a function that will read nested objects from arrays, based on JSON notation
1628		 * @param {*} source JSON notation string
1629		 * @returns Value read
1630		 */
1631		get: function ( source ) {
1632			if ( $.isPlainObject( source ) ) {
1633				// Build an object of get functions, and wrap them in a single call
1634				var o = {};
1635				$.each( source, function (key, val) {
1636					if ( val ) {
1637						o[key] = DataTable.util.get( val );
1638					}
1639				} );
1640		
1641				return function (data, type, row, meta) {
1642					var t = o[type] || o._;
1643					return t !== undefined ?
1644						t(data, type, row, meta) :
1645						data;
1646				};
1647			}
1648			else if ( source === null ) {
1649				// Give an empty string for rendering / sorting etc
1650				return function (data) { // type, row and meta also passed, but not used
1651					return data;
1652				};
1653			}
1654			else if ( typeof source === 'function' ) {
1655				return function (data, type, row, meta) {
1656					return source( data, type, row, meta );
1657				};
1658			}
1659			else if (
1660				typeof source === 'string' && (source.indexOf('.') !== -1 ||
1661				source.indexOf('[') !== -1 || source.indexOf('(') !== -1)
1662			) {
1663				/* If there is a . in the source string then the data source is in a
1664				 * nested object so we loop over the data for each level to get the next
1665				 * level down. On each loop we test for undefined, and if found immediately
1666				 * return. This allows entire objects to be missing and sDefaultContent to
1667				 * be used if defined, rather than throwing an error
1668				 */
1669				var fetchData = function (data, type, src) {
1670					var arrayNotation, funcNotation, out, innerSrc;
1671		
1672					if ( src !== "" ) {
1673						var a = _fnSplitObjNotation( src );
1674		
1675						for ( var i=0, iLen=a.length ; i<iLen ; i++ ) {
1676							// Check if we are dealing with special notation
1677							arrayNotation = a[i].match(__reArray);
1678							funcNotation = a[i].match(__reFn);
1679		
1680							if ( arrayNotation ) {
1681								// Array notation
1682								a[i] = a[i].replace(__reArray, '');
1683		
1684								// Condition allows simply [] to be passed in
1685								if ( a[i] !== "" ) {
1686									data = data[ a[i] ];
1687								}
1688								out = [];
1689		
1690								// Get the remainder of the nested object to get
1691								a.splice( 0, i+1 );
1692								innerSrc = a.join('.');
1693		
1694								// Traverse each entry in the array getting the properties requested
1695								if ( Array.isArray( data ) ) {
1696									for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
1697										out.push( fetchData( data[j], type, innerSrc ) );
1698									}
1699								}
1700		
1701								// If a string is given in between the array notation indicators, that
1702								// is used to join the strings together, otherwise an array is returned
1703								var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
1704								data = (join==="") ? out : out.join(join);
1705		
1706								// The inner call to fetchData has already traversed through the remainder
1707								// of the source requested, so we exit from the loop
1708								break;
1709							}
1710							else if ( funcNotation ) {
1711								// Function call
1712								a[i] = a[i].replace(__reFn, '');
1713								data = data[ a[i] ]();
1714								continue;
1715							}
1716		
1717							if (data === null || data[ a[i] ] === null) {
1718								return null;
1719							}
1720							else if ( data === undefined || data[ a[i] ] === undefined ) {
1721								return undefined;
1722							}
1723	
1724							data = data[ a[i] ];
1725						}
1726					}
1727		
1728					return data;
1729				};
1730		
1731				return function (data, type) { // row and meta also passed, but not used
1732					return fetchData( data, type, source );
1733				};
1734			}
1735			else {
1736				// Array or flat object mapping
1737				return function (data) { // row and meta also passed, but not used
1738					return data[source];
1739				};
1740			}
1741		},
1742	
1743		stripHtml: function (mixed) {
1744			var type = typeof mixed;
1745	
1746			if (type === 'function') {
1747				_stripHtml = mixed;
1748				return;
1749			}
1750			else if (type === 'string') {
1751				return _stripHtml(mixed);
1752			}
1753			return mixed;
1754		},
1755	
1756		escapeHtml: function (mixed) {
1757			var type = typeof mixed;
1758	
1759			if (type === 'function') {
1760				_escapeHtml = mixed;
1761				return;
1762			}
1763			else if (type === 'string' || Array.isArray(mixed)) {
1764				return _escapeHtml(mixed);
1765			}
1766			return mixed;
1767		},
1768	
1769		unique: _unique
1770	};
1771	
1772	
1773	
1774	/**
1775	 * Create a mapping object that allows camel case parameters to be looked up
1776	 * for their Hungarian counterparts. The mapping is stored in a private
1777	 * parameter called `_hungarianMap` which can be accessed on the source object.
1778	 *  @param {object} o
1779	 *  @memberof DataTable#oApi
1780	 */
1781	function _fnHungarianMap ( o )
1782	{
1783		var
1784			hungarian = 'a aa ai ao as b fn i m o s ',
1785			match,
1786			newKey,
1787			map = {};
1788	
1789		$.each( o, function (key) {
1790			match = key.match(/^([^A-Z]+?)([A-Z])/);
1791	
1792			if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
1793			{
1794				newKey = key.replace( match[0], match[2].toLowerCase() );
1795				map[ newKey ] = key;
1796	
1797				if ( match[1] === 'o' )
1798				{
1799					_fnHungarianMap( o[key] );
1800				}
1801			}
1802		} );
1803	
1804		o._hungarianMap = map;
1805	}
1806	
1807	
1808	/**
1809	 * Convert from camel case parameters to Hungarian, based on a Hungarian map
1810	 * created by _fnHungarianMap.
1811	 *  @param {object} src The model object which holds all parameters that can be
1812	 *    mapped.
1813	 *  @param {object} user The object to convert from camel case to Hungarian.
1814	 *  @param {boolean} force When set to `true`, properties which already have a
1815	 *    Hungarian value in the `user` object will be overwritten. Otherwise they
1816	 *    won't be.
1817	 *  @memberof DataTable#oApi
1818	 */
1819	function _fnCamelToHungarian ( src, user, force )
1820	{
1821		if ( ! src._hungarianMap ) {
1822			_fnHungarianMap( src );
1823		}
1824	
1825		var hungarianKey;
1826	
1827		$.each( user, function (key) {
1828			hungarianKey = src._hungarianMap[ key ];
1829	
1830			if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
1831			{
1832				// For objects, we need to buzz down into the object to copy parameters
1833				if ( hungarianKey.charAt(0) === 'o' )
1834				{
1835					// Copy the camelCase options over to the hungarian
1836					if ( ! user[ hungarianKey ] ) {
1837						user[ hungarianKey ] = {};
1838					}
1839					$.extend( true, user[hungarianKey], user[key] );
1840	
1841					_fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
1842				}
1843				else {
1844					user[hungarianKey] = user[ key ];
1845				}
1846			}
1847		} );
1848	}
1849	
1850	/**
1851	 * Map one parameter onto another
1852	 *  @param {object} o Object to map
1853	 *  @param {*} knew The new parameter name
1854	 *  @param {*} old The old parameter name
1855	 */
1856	var _fnCompatMap = function ( o, knew, old ) {
1857		if ( o[ knew ] !== undefined ) {
1858			o[ old ] = o[ knew ];
1859		}
1860	};
1861	
1862	
1863	/**
1864	 * Provide backwards compatibility for the main DT options. Note that the new
1865	 * options are mapped onto the old parameters, so this is an external interface
1866	 * change only.
1867	 *  @param {object} init Object to map
1868	 */
1869	function _fnCompatOpts ( init )
1870	{
1871		_fnCompatMap( init, 'ordering',      'bSort' );
1872		_fnCompatMap( init, 'orderMulti',    'bSortMulti' );
1873		_fnCompatMap( init, 'orderClasses',  'bSortClasses' );
1874		_fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
1875		_fnCompatMap( init, 'order',         'aaSorting' );
1876		_fnCompatMap( init, 'orderFixed',    'aaSortingFixed' );
1877		_fnCompatMap( init, 'paging',        'bPaginate' );
1878		_fnCompatMap( init, 'pagingType',    'sPaginationType' );
1879		_fnCompatMap( init, 'pageLength',    'iDisplayLength' );
1880		_fnCompatMap( init, 'searching',     'bFilter' );
1881	
1882		// Boolean initialisation of x-scrolling
1883		if ( typeof init.sScrollX === 'boolean' ) {
1884			init.sScrollX = init.sScrollX ? '100%' : '';
1885		}
1886		if ( typeof init.scrollX === 'boolean' ) {
1887			init.scrollX = init.scrollX ? '100%' : '';
1888		}
1889	
1890		// Column search objects are in an array, so it needs to be converted
1891		// element by element
1892		var searchCols = init.aoSearchCols;
1893	
1894		if ( searchCols ) {
1895			for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
1896				if ( searchCols[i] ) {
1897					_fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
1898				}
1899			}
1900		}
1901	
1902		// Enable search delay if server-side processing is enabled
1903		if (init.serverSide && ! init.searchDelay) {
1904			init.searchDelay = 400;
1905		}
1906	}
1907	
1908	
1909	/**
1910	 * Provide backwards compatibility for column options. Note that the new options
1911	 * are mapped onto the old parameters, so this is an external interface change
1912	 * only.
1913	 *  @param {object} init Object to map
1914	 */
1915	function _fnCompatCols ( init )
1916	{
1917		_fnCompatMap( init, 'orderable',     'bSortable' );
1918		_fnCompatMap( init, 'orderData',     'aDataSort' );
1919		_fnCompatMap( init, 'orderSequence', 'asSorting' );
1920		_fnCompatMap( init, 'orderDataType', 'sortDataType' );
1921	
1922		// orderData can be given as an integer
1923		var dataSort = init.aDataSort;
1924		if ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) {
1925			init.aDataSort = [ dataSort ];
1926		}
1927	}
1928	
1929	
1930	/**
1931	 * Browser feature detection for capabilities, quirks
1932	 *  @param {object} settings dataTables settings object
1933	 *  @memberof DataTable#oApi
1934	 */
1935	function _fnBrowserDetect( settings )
1936	{
1937		// We don't need to do this every time DataTables is constructed, the values
1938		// calculated are specific to the browser and OS configuration which we
1939		// don't expect to change between initialisations
1940		if ( ! DataTable.__browser ) {
1941			var browser = {};
1942			DataTable.__browser = browser;
1943	
1944			// Scrolling feature / quirks detection
1945			var n = $('<div/>')
1946				.css( {
1947					position: 'fixed',
1948					top: 0,
1949					left: -1 * window.pageXOffset, // allow for scrolling
1950					height: 1,
1951					width: 1,
1952					overflow: 'hidden'
1953				} )
1954				.append(
1955					$('<div/>')
1956						.css( {
1957							position: 'absolute',
1958							top: 1,
1959							left: 1,
1960							width: 100,
1961							overflow: 'scroll'
1962						} )
1963						.append(
1964							$('<div/>')
1965								.css( {
1966									width: '100%',
1967									height: 10
1968								} )
1969						)
1970				)
1971				.appendTo( 'body' );
1972	
1973			var outer = n.children();
1974			var inner = outer.children();
1975	
1976			// Get scrollbar width
1977			browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
1978	
1979			// In rtl text layout, some browsers (most, but not all) will place the
1980			// scrollbar on the left, rather than the right.
1981			browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
1982	
1983			n.remove();
1984		}
1985	
1986		$.extend( settings.oBrowser, DataTable.__browser );
1987		settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
1988	}
1989	
1990	/**
1991	 * Add a column to the list used for the table with default values
1992	 *  @param {object} oSettings dataTables settings object
1993	 *  @memberof DataTable#oApi
1994	 */
1995	function _fnAddColumn( oSettings )
1996	{
1997		// Add column to aoColumns array
1998		var oDefaults = DataTable.defaults.column;
1999		var iCol = oSettings.aoColumns.length;
2000		var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
2001			"aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
2002			"mData": oDefaults.mData ? oDefaults.mData : iCol,
2003			idx: iCol,
2004			searchFixed: {},
2005			colEl: $('<col>').attr('data-dt-column', iCol)
2006		} );
2007		oSettings.aoColumns.push( oCol );
2008	
2009		// Add search object for column specific search. Note that the `searchCols[ iCol ]`
2010		// passed into extend can be undefined. This allows the user to give a default
2011		// with only some of the parameters defined, and also not give a default
2012		var searchCols = oSettings.aoPreSearchCols;
2013		searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
2014	}
2015	
2016	
2017	/**
2018	 * Apply options for a column
2019	 *  @param {object} oSettings dataTables settings object
2020	 *  @param {int} iCol column index to consider
2021	 *  @param {object} oOptions object with sType, bVisible and bSearchable etc
2022	 *  @memberof DataTable#oApi
2023	 */
2024	function _fnColumnOptions( oSettings, iCol, oOptions )
2025	{
2026		var oCol = oSettings.aoColumns[ iCol ];
2027	
2028		/* User specified column options */
2029		if ( oOptions !== undefined && oOptions !== null )
2030		{
2031			// Backwards compatibility
2032			_fnCompatCols( oOptions );
2033	
2034			// Map camel case parameters to their Hungarian counterparts
2035			_fnCamelToHungarian( DataTable.defaults.column, oOptions, true );
2036	
2037			/* Backwards compatibility for mDataProp */
2038			if ( oOptions.mDataProp !== undefined && !oOptions.mData )
2039			{
2040				oOptions.mData = oOptions.mDataProp;
2041			}
2042	
2043			if ( oOptions.sType )
2044			{
2045				oCol._sManualType = oOptions.sType;
2046			}
2047		
2048			// `class` is a reserved word in Javascript, so we need to provide
2049			// the ability to use a valid name for the camel case input
2050			if ( oOptions.className && ! oOptions.sClass )
2051			{
2052				oOptions.sClass = oOptions.className;
2053			}
2054	
2055			var origClass = oCol.sClass;
2056	
2057			$.extend( oCol, oOptions );
2058			_fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
2059	
2060			// Merge class from previously defined classes with this one, rather than just
2061			// overwriting it in the extend above
2062			if (origClass !== oCol.sClass) {
2063				oCol.sClass = origClass + ' ' + oCol.sClass;
2064			}
2065	
2066			/* iDataSort to be applied (backwards compatibility), but aDataSort will take
2067			 * priority if defined
2068			 */
2069			if ( oOptions.iDataSort !== undefined )
2070			{
2071				oCol.aDataSort = [ oOptions.iDataSort ];
2072			}
2073			_fnMap( oCol, oOptions, "aDataSort" );
2074		}
2075	
2076		/* Cache the data get and set functions for speed */
2077		var mDataSrc = oCol.mData;
2078		var mData = _fnGetObjectDataFn( mDataSrc );
2079	
2080		// The `render` option can be given as an array to access the helper rendering methods.
2081		// The first element is the rendering method to use, the rest are the parameters to pass
2082		if ( oCol.mRender && Array.isArray( oCol.mRender ) ) {
2083			var copy = oCol.mRender.slice();
2084			var name = copy.shift();
2085	
2086			oCol.mRender = DataTable.render[name].apply(window, copy);
2087		}
2088	
2089		oCol._render = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
2090	
2091		var attrTest = function( src ) {
2092			return typeof src === 'string' && src.indexOf('@') !== -1;
2093		};
2094		oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
2095			attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
2096		);
2097		oCol._setter = null;
2098	
2099		oCol.fnGetData = function (rowData, type, meta) {
2100			var innerData = mData( rowData, type, undefined, meta );
2101	
2102			return oCol._render && type ?
2103				oCol._render( innerData, type, rowData, meta ) :
2104				innerData;
2105		};
2106		oCol.fnSetData = function ( rowData, val, meta ) {
2107			return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
2108		};
2109	
2110		// Indicate if DataTables should read DOM data as an object or array
2111		// Used in _fnGetRowElements
2112		if ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) {
2113			oSettings._rowReadObject = true;
2114		}
2115	
2116		/* Feature sorting overrides column specific when off */
2117		if ( !oSettings.oFeatures.bSort )
2118		{
2119			oCol.bSortable = false;
2120		}
2121	}
2122	
2123	
2124	/**
2125	 * Adjust the table column widths for new data. Note: you would probably want to
2126	 * do a redraw after calling this function!
2127	 *  @param {object} settings dataTables settings object
2128	 *  @memberof DataTable#oApi
2129	 */
2130	function _fnAdjustColumnSizing ( settings )
2131	{
2132		_fnCalculateColumnWidths( settings );
2133		_fnColumnSizes( settings );
2134	
2135		var scroll = settings.oScroll;
2136		if ( scroll.sY !== '' || scroll.sX !== '') {
2137			_fnScrollDraw( settings );
2138		}
2139	
2140		_fnCallbackFire( settings, null, 'column-sizing', [settings] );
2141	}
2142	
2143	/**
2144	 * Apply column sizes
2145	 *
2146	 * @param {*} settings DataTables settings object
2147	 */
2148	function _fnColumnSizes ( settings )
2149	{
2150		var cols = settings.aoColumns;
2151	
2152		for (var i=0 ; i<cols.length ; i++) {
2153			var width = _fnColumnsSumWidth(settings, [i], false, false);
2154	
2155			cols[i].colEl.css('width', width);
2156	
2157			if (settings.oScroll.sX) {
2158				cols[i].colEl.css('min-width', width);
2159			}
2160		}
2161	}
2162	
2163	
2164	/**
2165	 * Convert the index of a visible column to the index in the data array (take account
2166	 * of hidden columns)
2167	 *  @param {object} oSettings dataTables settings object
2168	 *  @param {int} iMatch Visible column index to lookup
2169	 *  @returns {int} i the data index
2170	 *  @memberof DataTable#oApi
2171	 */
2172	function _fnVisibleToColumnIndex( oSettings, iMatch )
2173	{
2174		var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2175	
2176		return typeof aiVis[iMatch] === 'number' ?
2177			aiVis[iMatch] :
2178			null;
2179	}
2180	
2181	
2182	/**
2183	 * Convert the index of an index in the data array and convert it to the visible
2184	 *   column index (take account of hidden columns)
2185	 *  @param {int} iMatch Column index to lookup
2186	 *  @param {object} oSettings dataTables settings object
2187	 *  @returns {int} i the data index
2188	 *  @memberof DataTable#oApi
2189	 */
2190	function _fnColumnIndexToVisible( oSettings, iMatch )
2191	{
2192		var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2193		var iPos = aiVis.indexOf(iMatch);
2194	
2195		return iPos !== -1 ? iPos : null;
2196	}
2197	
2198	
2199	/**
2200	 * Get the number of visible columns
2201	 *  @param {object} oSettings dataTables settings object
2202	 *  @returns {int} i the number of visible columns
2203	 *  @memberof DataTable#oApi
2204	 */
2205	function _fnVisbleColumns( settings )
2206	{
2207		var layout = settings.aoHeader;
2208		var columns = settings.aoColumns;
2209		var vis = 0;
2210	
2211		if ( layout.length ) {
2212			for ( var i=0, ien=layout[0].length ; i<ien ; i++ ) {
2213				if ( columns[i].bVisible && $(layout[0][i].cell).css('display') !== 'none' ) {
2214					vis++;
2215				}
2216			}
2217		}
2218	
2219		return vis;
2220	}
2221	
2222	
2223	/**
2224	 * Get an array of column indexes that match a given property
2225	 *  @param {object} oSettings dataTables settings object
2226	 *  @param {string} sParam Parameter in aoColumns to look for - typically
2227	 *    bVisible or bSearchable
2228	 *  @returns {array} Array of indexes with matched properties
2229	 *  @memberof DataTable#oApi
2230	 */
2231	function _fnGetColumns( oSettings, sParam )
2232	{
2233		var a = [];
2234	
2235		oSettings.aoColumns.map( function(val, i) {
2236			if ( val[sParam] ) {
2237				a.push( i );
2238			}
2239		} );
2240	
2241		return a;
2242	}
2243	
2244	/**
2245	 * Allow the result from a type detection function to be `true` while
2246	 * translating that into a string. Old type detection functions will
2247	 * return the type name if it passes. An obect store would be better,
2248	 * but not backwards compatible.
2249	 *
2250	 * @param {*} typeDetect Object or function for type detection
2251	 * @param {*} res Result from the type detection function
2252	 * @returns Type name or false
2253	 */
2254	function _typeResult (typeDetect, res) {
2255		return res === true
2256			? typeDetect._name
2257			: res;
2258	}
2259	
2260	/**
2261	 * Calculate the 'type' of a column
2262	 *  @param {object} settings dataTables settings object
2263	 *  @memberof DataTable#oApi
2264	 */
2265	function _fnColumnTypes ( settings )
2266	{
2267		var columns = settings.aoColumns;
2268		var data = settings.aoData;
2269		var types = DataTable.ext.type.detect;
2270		var i, ien, j, jen, k, ken;
2271		var col, detectedType, cache;
2272	
2273		// For each column, spin over the data type detection functions, seeing if one matches
2274		for ( i=0, ien=columns.length ; i<ien ; i++ ) {
2275			col = columns[i];
2276			cache = [];
2277	
2278			if ( ! col.sType && col._sManualType ) {
2279				col.sType = col._sManualType;
2280			}
2281			else if ( ! col.sType ) {
2282				// With SSP type detection can be unreliable and error prone, so we provide a way
2283				// to turn it off.
2284				if (! settings.typeDetect) {
2285					return;
2286				}
2287	
2288				for ( j=0, jen=types.length ; j<jen ; j++ ) {
2289					var typeDetect = types[j];
2290	
2291					// There can be either one, or three type detection functions
2292					var oneOf = typeDetect.oneOf;
2293					var allOf = typeDetect.allOf || typeDetect;
2294					var init = typeDetect.init;
2295					var one = false;
2296	
2297					detectedType = null;
2298	
2299					// Fast detect based on column assignment
2300					if (init) {
2301						detectedType = _typeResult(typeDetect, init(settings, col, i));
2302	
2303						if (detectedType) {
2304							col.sType = detectedType;
2305							break;
2306						}
2307					}
2308	
2309					for ( k=0, ken=data.length ; k<ken ; k++ ) {
2310						if (! data[k]) {
2311							continue;
2312						}
2313	
2314						// Use a cache array so we only need to get the type data
2315						// from the formatter once (when using multiple detectors)
2316						if ( cache[k] === undefined ) {
2317							cache[k] = _fnGetCellData( settings, k, i, 'type' );
2318						}
2319	
2320						// Only one data point in the column needs to match this function
2321						if (oneOf && ! one) {
2322							one = _typeResult(typeDetect, oneOf( cache[k], settings ));
2323						}
2324	
2325						// All data points need to match this function
2326						detectedType = _typeResult(typeDetect, allOf( cache[k], settings ));
2327	
2328						// If null, then this type can't apply to this column, so
2329						// rather than testing all cells, break out. There is an
2330						// exception for the last type which is `html`. We need to
2331						// scan all rows since it is possible to mix string and HTML
2332						// types
2333						if ( ! detectedType && j !== types.length-3 ) {
2334							break;
2335						}
2336	
2337						// Only a single match is needed for html type since it is
2338						// bottom of the pile and very similar to string - but it
2339						// must not be empty
2340						if ( detectedType === 'html' && ! _empty(cache[k]) ) {
2341							break;
2342						}
2343					}
2344	
2345					// Type is valid for all data points in the column - use this
2346					// type
2347					if ( (oneOf && one && detectedType) || (!oneOf && detectedType) ) {
2348						col.sType = detectedType;
2349						break;
2350					}
2351				}
2352	
2353				// Fall back - if no type was detected, always use string
2354				if ( ! col.sType ) {
2355					col.sType = 'string';
2356				}
2357			}
2358	
2359			// Set class names for header / footer for auto type classes
2360			var autoClass = _ext.type.className[col.sType];
2361	
2362			if (autoClass) {
2363				_columnAutoClass(settings.aoHeader, i, autoClass);
2364				_columnAutoClass(settings.aoFooter, i, autoClass);
2365			}
2366	
2367			var renderer = _ext.type.render[col.sType];
2368	
2369			// This can only happen once! There is no way to remove
2370			// a renderer. After the first time the renderer has
2371			// already been set so createTr will run the renderer itself.
2372			if (renderer && ! col._render) {
2373				col._render = DataTable.util.get(renderer);
2374	
2375				_columnAutoRender(settings, i);
2376			}
2377		}
2378	}
2379	
2380	/**
2381	 * Apply an auto detected renderer to data which doesn't yet have
2382	 * a renderer
2383	 */
2384	function _columnAutoRender(settings, colIdx) {
2385		var data = settings.aoData;
2386	
2387		for (var i=0 ; i<data.length ; i++) {
2388			if (data[i].nTr) {
2389				// We have to update the display here since there is no
2390				// invalidation check for the data
2391				var display = _fnGetCellData( settings, i, colIdx, 'display' );
2392	
2393				data[i].displayData[colIdx] = display;
2394				_fnWriteCell(data[i].anCells[colIdx], display);
2395	
2396				// No need to update sort / filter data since it has
2397				// been invalidated and will be re-read with the
2398				// renderer now applied
2399			}
2400		}
2401	}
2402	
2403	/**
2404	 * Apply a class name to a column's header cells
2405	 */
2406	function _columnAutoClass(container, colIdx, className) {
2407		container.forEach(function (row) {
2408			if (row[colIdx] && row[colIdx].unique) {
2409				_addClass(row[colIdx].cell, className);
2410			}
2411		});
2412	}
2413	
2414	/**
2415	 * Take the column definitions and static columns arrays and calculate how
2416	 * they relate to column indexes. The callback function will then apply the
2417	 * definition found for a column to a suitable configuration object.
2418	 *  @param {object} oSettings dataTables settings object
2419	 *  @param {array} aoColDefs The aoColumnDefs array that is to be applied
2420	 *  @param {array} aoCols The aoColumns array that defines columns individually
2421	 *  @param {array} headerLayout Layout for header as it was loaded
2422	 *  @param {function} fn Callback function - takes two parameters, the calculated
2423	 *    column index and the definition for that column.
2424	 *  @memberof DataTable#oApi
2425	 */
2426	function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, headerLayout, fn )
2427	{
2428		var i, iLen, j, jLen, k, kLen, def;
2429		var columns = oSettings.aoColumns;
2430	
2431		if ( aoCols ) {
2432			for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) {
2433				if (aoCols[i] && aoCols[i].name) {
2434					columns[i].sName = aoCols[i].name;
2435				}
2436			}
2437		}
2438	
2439		// Column definitions with aTargets
2440		if ( aoColDefs )
2441		{
2442			/* Loop over the definitions array - loop in reverse so first instance has priority */
2443			for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
2444			{
2445				def = aoColDefs[i];
2446	
2447				/* Each definition can target multiple columns, as it is an array */
2448				var aTargets = def.target !== undefined
2449					? def.target
2450					: def.targets !== undefined
2451						? def.targets
2452						: def.aTargets;
2453	
2454				if ( ! Array.isArray( aTargets ) )
2455				{
2456					aTargets = [ aTargets ];
2457				}
2458	
2459				for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
2460				{
2461					var target = aTargets[j];
2462	
2463					if ( typeof target === 'number' && target >= 0 )
2464					{
2465						/* Add columns that we don't yet know about */
2466						while( columns.length <= target )
2467						{
2468							_fnAddColumn( oSettings );
2469						}
2470	
2471						/* Integer, basic index */
2472						fn( target, def );
2473					}
2474					else if ( typeof target === 'number' && target < 0 )
2475					{
2476						/* Negative integer, right to left column counting */
2477						fn( columns.length+target, def );
2478					}
2479					else if ( typeof target === 'string' )
2480					{
2481						for ( k=0, kLen=columns.length ; k<kLen ; k++ ) {
2482							if (target === '_all') {
2483								// Apply to all columns
2484								fn( k, def );
2485							}
2486							else if (target.indexOf(':name') !== -1) {
2487								// Column selector
2488								if (columns[k].sName === target.replace(':name', '')) {
2489									fn( k, def );
2490								}
2491							}
2492							else {
2493								// Cell selector
2494								headerLayout.forEach(function (row) {
2495									if (row[k]) {
2496										var cell = $(row[k].cell);
2497	
2498										// Legacy support. Note that it means that we don't support
2499										// an element name selector only, since they are treated as
2500										// class names for 1.x compat.
2501										if (target.match(/^[a-z][\w-]*$/i)) {
2502											target = '.' + target;
2503										}
2504	
2505										if (cell.is( target )) {
2506											fn( k, def );
2507										}
2508									}
2509								});
2510							}
2511						}
2512					}
2513				}
2514			}
2515		}
2516	
2517		// Statically defined columns array
2518		if ( aoCols ) {
2519			for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) {
2520				fn( i, aoCols[i] );
2521			}
2522		}
2523	}
2524	
2525	
2526	/**
2527	 * Get the width for a given set of columns
2528	 *
2529	 * @param {*} settings DataTables settings object
2530	 * @param {*} targets Columns - comma separated string or array of numbers
2531	 * @param {*} original Use the original width (true) or calculated (false)
2532	 * @param {*} incVisible Include visible columns (true) or not (false)
2533	 * @returns Combined CSS value
2534	 */
2535	function _fnColumnsSumWidth( settings, targets, original, incVisible ) {
2536		if ( ! Array.isArray( targets ) ) {
2537			targets = _fnColumnsFromHeader( targets );
2538		}
2539	
2540		var sum = 0;
2541		var unit;
2542		var columns = settings.aoColumns;
2543		
2544		for ( var i=0, ien=targets.length ; i<ien ; i++ ) {
2545			var column = columns[ targets[i] ];
2546			var definedWidth = original ?
2547				column.sWidthOrig :
2548				column.sWidth;
2549	
2550			if ( ! incVisible && column.bVisible === false ) {
2551				continue;
2552			}
2553	
2554			if ( definedWidth === null || definedWidth === undefined ) {
2555				return null; // can't determine a defined width - browser defined
2556			}
2557			else if ( typeof definedWidth === 'number' ) {
2558				unit = 'px';
2559				sum += definedWidth;
2560			}
2561			else {
2562				var matched = definedWidth.match(/([\d\.]+)([^\d]*)/);
2563	
2564				if ( matched ) {
2565					sum += matched[1] * 1;
2566					unit = matched.length === 3 ?
2567						matched[2] :
2568						'px';
2569				}
2570			}
2571		}
2572	
2573		return sum + unit;
2574	}
2575	
2576	function _fnColumnsFromHeader( cell )
2577	{
2578		var attr = $(cell).closest('[data-dt-column]').attr('data-dt-column');
2579	
2580		if ( ! attr ) {
2581			return [];
2582		}
2583	
2584		return attr.split(',').map( function (val) {
2585			return val * 1;
2586		} );
2587	}
2588	/**
2589	 * Add a data array to the table, creating DOM node etc. This is the parallel to
2590	 * _fnGatherData, but for adding rows from a Javascript source, rather than a
2591	 * DOM source.
2592	 *  @param {object} settings dataTables settings object
2593	 *  @param {array} data data array to be added
2594	 *  @param {node} [tr] TR element to add to the table - optional. If not given,
2595	 *    DataTables will create a row automatically
2596	 *  @param {array} [tds] Array of TD|TH elements for the row - must be given
2597	 *    if nTr is.
2598	 *  @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
2599	 *  @memberof DataTable#oApi
2600	 */
2601	function _fnAddData ( settings, dataIn, tr, tds )
2602	{
2603		/* Create the object for storing information about this new row */
2604		var rowIdx = settings.aoData.length;
2605		var rowModel = $.extend( true, {}, DataTable.models.oRow, {
2606			src: tr ? 'dom' : 'data',
2607			idx: rowIdx
2608		} );
2609	
2610		rowModel._aData = dataIn;
2611		settings.aoData.push( rowModel );
2612	
2613		var columns = settings.aoColumns;
2614	
2615		for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
2616		{
2617			// Invalidate the column types as the new data needs to be revalidated
2618			columns[i].sType = null;
2619		}
2620	
2621		/* Add to the display array */
2622		settings.aiDisplayMaster.push( rowIdx );
2623	
2624		var id = settings.rowIdFn( dataIn );
2625		if ( id !== undefined ) {
2626			settings.aIds[ id ] = rowModel;
2627		}
2628	
2629		/* Create the DOM information, or register it if already present */
2630		if ( tr || ! settings.oFeatures.bDeferRender )
2631		{
2632			_fnCreateTr( settings, rowIdx, tr, tds );
2633		}
2634	
2635		return rowIdx;
2636	}
2637	
2638	
2639	/**
2640	 * Add one or more TR elements to the table. Generally we'd expect to
2641	 * use this for reading data from a DOM sourced table, but it could be
2642	 * used for an TR element. Note that if a TR is given, it is used (i.e.
2643	 * it is not cloned).
2644	 *  @param {object} settings dataTables settings object
2645	 *  @param {array|node|jQuery} trs The TR element(s) to add to the table
2646	 *  @returns {array} Array of indexes for the added rows
2647	 *  @memberof DataTable#oApi
2648	 */
2649	function _fnAddTr( settings, trs )
2650	{
2651		var row;
2652	
2653		// Allow an individual node to be passed in
2654		if ( ! (trs instanceof $) ) {
2655			trs = $(trs);
2656		}
2657	
2658		return trs.map( function (i, el) {
2659			row = _fnGetRowElements( settings, el );
2660			return _fnAddData( settings, row.data, el, row.cells );
2661		} );
2662	}
2663	
2664	
2665	/**
2666	 * Get the data for a given cell from the internal cache, taking into account data mapping
2667	 *  @param {object} settings dataTables settings object
2668	 *  @param {int} rowIdx aoData row id
2669	 *  @param {int} colIdx Column index
2670	 *  @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order')
2671	 *  @returns {*} Cell data
2672	 *  @memberof DataTable#oApi
2673	 */
2674	function _fnGetCellData( settings, rowIdx, colIdx, type )
2675	{
2676		if (type === 'search') {
2677			type = 'filter';
2678		}
2679		else if (type === 'order') {
2680			type = 'sort';
2681		}
2682	
2683		var row = settings.aoData[rowIdx];
2684	
2685		if (! row) {
2686			return undefined;
2687		}
2688	
2689		var draw           = settings.iDraw;
2690		var col            = settings.aoColumns[colIdx];
2691		var rowData        = row._aData;
2692		var defaultContent = col.sDefaultContent;
2693		var cellData       = col.fnGetData( rowData, type, {
2694			settings: settings,
2695			row:      rowIdx,
2696			col:      colIdx
2697		} );
2698	
2699		// Allow for a node being returned for non-display types
2700		if (type !== 'display' && cellData && typeof cellData === 'object' && cellData.nodeName) {
2701			cellData = cellData.innerHTML;
2702		}
2703	
2704		if ( cellData === undefined ) {
2705			if ( settings.iDrawError != draw && defaultContent === null ) {
2706				_fnLog( settings, 0, "Requested unknown parameter "+
2707					(typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
2708					" for row "+rowIdx+", column "+colIdx, 4 );
2709				settings.iDrawError = draw;
2710			}
2711			return defaultContent;
2712		}
2713	
2714		// When the data source is null and a specific data type is requested (i.e.
2715		// not the original data), we can use default column data
2716		if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
2717			cellData = defaultContent;
2718		}
2719		else if ( typeof cellData === 'function' ) {
2720			// If the data source is a function, then we run it and use the return,
2721			// executing in the scope of the data object (for instances)
2722			return cellData.call( rowData );
2723		}
2724	
2725		if ( cellData === null && type === 'display' ) {
2726			return '';
2727		}
2728	
2729		if ( type === 'filter' ) {
2730			var fomatters = DataTable.ext.type.search;
2731	
2732			if ( fomatters[ col.sType ] ) {
2733				cellData = fomatters[ col.sType ]( cellData );
2734			}
2735		}
2736	
2737		return cellData;
2738	}
2739	
2740	
2741	/**
2742	 * Set the value for a specific cell, into the internal data cache
2743	 *  @param {object} settings dataTables settings object
2744	 *  @param {int} rowIdx aoData row id
2745	 *  @param {int} colIdx Column index
2746	 *  @param {*} val Value to set
2747	 *  @memberof DataTable#oApi
2748	 */
2749	function _fnSetCellData( settings, rowIdx, colIdx, val )
2750	{
2751		var col     = settings.aoColumns[colIdx];
2752		var rowData = settings.aoData[rowIdx]._aData;
2753	
2754		col.fnSetData( rowData, val, {
2755			settings: settings,
2756			row:      rowIdx,
2757			col:      colIdx
2758		}  );
2759	}
2760	
2761	/**
2762	 * Write a value to a cell
2763	 * @param {*} td Cell
2764	 * @param {*} val Value
2765	 */
2766	function _fnWriteCell(td, val)
2767	{
2768		if (val && typeof val === 'object' && val.nodeName) {
2769			$(td)
2770				.empty()
2771				.append(val);
2772		}
2773		else {
2774			td.innerHTML = val;
2775		}
2776	}
2777	
2778	
2779	// Private variable that is used to match action syntax in the data property object
2780	var __reArray = /\[.*?\]$/;
2781	var __reFn = /\(\)$/;
2782	
2783	/**
2784	 * Split string on periods, taking into account escaped periods
2785	 * @param  {string} str String to split
2786	 * @return {array} Split string
2787	 */
2788	function _fnSplitObjNotation( str )
2789	{
2790		var parts = str.match(/(\\.|[^.])+/g) || [''];
2791	
2792		return parts.map( function ( s ) {
2793			return s.replace(/\\\./g, '.');
2794		} );
2795	}
2796	
2797	
2798	/**
2799	 * Return a function that can be used to get data from a source object, taking
2800	 * into account the ability to use nested objects as a source
2801	 *  @param {string|int|function} mSource The data source for the object
2802	 *  @returns {function} Data get function
2803	 *  @memberof DataTable#oApi
2804	 */
2805	var _fnGetObjectDataFn = DataTable.util.get;
2806	
2807	
2808	/**
2809	 * Return a function that can be used to set data from a source object, taking
2810	 * into account the ability to use nested objects as a source
2811	 *  @param {string|int|function} mSource The data source for the object
2812	 *  @returns {function} Data set function
2813	 *  @memberof DataTable#oApi
2814	 */
2815	var _fnSetObjectDataFn = DataTable.util.set;
2816	
2817	
2818	/**
2819	 * Return an array with the full table data
2820	 *  @param {object} oSettings dataTables settings object
2821	 *  @returns array {array} aData Master data array
2822	 *  @memberof DataTable#oApi
2823	 */
2824	function _fnGetDataMaster ( settings )
2825	{
2826		return _pluck( settings.aoData, '_aData' );
2827	}
2828	
2829	
2830	/**
2831	 * Nuke the table
2832	 *  @param {object} oSettings dataTables settings object
2833	 *  @memberof DataTable#oApi
2834	 */
2835	function _fnClearTable( settings )
2836	{
2837		settings.aoData.length = 0;
2838		settings.aiDisplayMaster.length = 0;
2839		settings.aiDisplay.length = 0;
2840		settings.aIds = {};
2841	}
2842	
2843	
2844	/**
2845	 * Mark cached data as invalid such that a re-read of the data will occur when
2846	 * the cached data is next requested. Also update from the data source object.
2847	 *
2848	 * @param {object} settings DataTables settings object
2849	 * @param {int}    rowIdx   Row index to invalidate
2850	 * @param {string} [src]    Source to invalidate from: undefined, 'auto', 'dom'
2851	 *     or 'data'
2852	 * @param {int}    [colIdx] Column index to invalidate. If undefined the whole
2853	 *     row will be invalidated
2854	 * @memberof DataTable#oApi
2855	 *
2856	 * @todo For the modularisation of v1.11 this will need to become a callback, so
2857	 *   the sort and filter methods can subscribe to it. That will required
2858	 *   initialisation options for sorting, which is why it is not already baked in
2859	 */
2860	function _fnInvalidate( settings, rowIdx, src, colIdx )
2861	{
2862		var row = settings.aoData[ rowIdx ];
2863		var i, ien;
2864	
2865		// Remove the cached data for the row
2866		row._aSortData = null;
2867		row._aFilterData = null;
2868		row.displayData = null;
2869	
2870		// Are we reading last data from DOM or the data object?
2871		if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
2872			// Read the data from the DOM
2873			row._aData = _fnGetRowElements(
2874					settings, row, colIdx, colIdx === undefined ? undefined : row._aData
2875				)
2876				.data;
2877		}
2878		else {
2879			// Reading from data object, update the DOM
2880			var cells = row.anCells;
2881			var display = _fnGetRowDisplay(settings, rowIdx);
2882	
2883			if ( cells ) {
2884				if ( colIdx !== undefined ) {
2885					_fnWriteCell(cells[colIdx], display[colIdx]);
2886				}
2887				else {
2888					for ( i=0, ien=cells.length ; i<ien ; i++ ) {
2889						_fnWriteCell(cells[i], display[i]);
2890					}
2891				}
2892			}
2893		}
2894	
2895		// Column specific invalidation
2896		var cols = settings.aoColumns;
2897		if ( colIdx !== undefined ) {
2898			// Type - the data might have changed
2899			cols[ colIdx ].sType = null;
2900	
2901			// Max length string. Its a fairly cheep recalculation, so not worth
2902			// something more complicated
2903			cols[ colIdx ].maxLenString = null;
2904		}
2905		else {
2906			for ( i=0, ien=cols.length ; i<ien ; i++ ) {
2907				cols[i].sType = null;
2908				cols[i].maxLenString = null;
2909			}
2910	
2911			// Update DataTables special `DT_*` attributes for the row
2912			_fnRowAttributes( settings, row );
2913		}
2914	}
2915	
2916	
2917	/**
2918	 * Build a data source object from an HTML row, reading the contents of the
2919	 * cells that are in the row.
2920	 *
2921	 * @param {object} settings DataTables settings object
2922	 * @param {node|object} TR element from which to read data or existing row
2923	 *   object from which to re-read the data from the cells
2924	 * @param {int} [colIdx] Optional column index
2925	 * @param {array|object} [d] Data source object. If `colIdx` is given then this
2926	 *   parameter should also be given and will be used to write the data into.
2927	 *   Only the column in question will be written
2928	 * @returns {object} Object with two parameters: `data` the data read, in
2929	 *   document order, and `cells` and array of nodes (they can be useful to the
2930	 *   caller, so rather than needing a second traversal to get them, just return
2931	 *   them from here).
2932	 * @memberof DataTable#oApi
2933	 */
2934	function _fnGetRowElements( settings, row, colIdx, d )
2935	{
2936		var
2937			tds = [],
2938			td = row.firstChild,
2939			name, col, i=0, contents,
2940			columns = settings.aoColumns,
2941			objectRead = settings._rowReadObject;
2942	
2943		// Allow the data object to be passed in, or construct
2944		d = d !== undefined ?
2945			d :
2946			objectRead ?
2947				{} :
2948				[];
2949	
2950		var attr = function ( str, td  ) {
2951			if ( typeof str === 'string' ) {
2952				var idx = str.indexOf('@');
2953	
2954				if ( idx !== -1 ) {
2955					var attr = str.substring( idx+1 );
2956					var setter = _fnSetObjectDataFn( str );
2957					setter( d, td.getAttribute( attr ) );
2958				}
2959			}
2960		};
2961	
2962		// Read data from a cell and store into the data object
2963		var cellProcess = function ( cell ) {
2964			if ( colIdx === undefined || colIdx === i ) {
2965				col = columns[i];
2966				contents = (cell.innerHTML).trim();
2967	
2968				if ( col && col._bAttrSrc ) {
2969					var setter = _fnSetObjectDataFn( col.mData._ );
2970					setter( d, contents );
2971	
2972					attr( col.mData.sort, cell );
2973					attr( col.mData.type, cell );
2974					attr( col.mData.filter, cell );
2975				}
2976				else {
2977					// Depending on the `data` option for the columns the data can
2978					// be read to either an object or an array.
2979					if ( objectRead ) {
2980						if ( ! col._setter ) {
2981							// Cache the setter function
2982							col._setter = _fnSetObjectDataFn( col.mData );
2983						}
2984						col._setter( d, contents );
2985					}
2986					else {
2987						d[i] = contents;
2988					}
2989				}
2990			}
2991	
2992			i++;
2993		};
2994	
2995		if ( td ) {
2996			// `tr` element was passed in
2997			while ( td ) {
2998				name = td.nodeName.toUpperCase();
2999	
3000				if ( name == "TD" || name == "TH" ) {
3001					cellProcess( td );
3002					tds.push( td );
3003				}
3004	
3005				td = td.nextSibling;
3006			}
3007		}
3008		else {
3009			// Existing row object passed in
3010			tds = row.anCells;
3011	
3012			for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
3013				cellProcess( tds[j] );
3014			}
3015		}
3016	
3017		// Read the ID from the DOM if present
3018		var rowNode = row.firstChild ? row : row.nTr;
3019	
3020		if ( rowNode ) {
3021			var id = rowNode.getAttribute( 'id' );
3022	
3023			if ( id ) {
3024				_fnSetObjectDataFn( settings.rowId )( d, id );
3025			}
3026		}
3027	
3028		return {
3029			data: d,
3030			cells: tds
3031		};
3032	}
3033	
3034	/**
3035	 * Render and cache a row's display data for the columns, if required
3036	 * @returns 
3037	 */
3038	function _fnGetRowDisplay (settings, rowIdx) {
3039		var rowModal = settings.aoData[rowIdx];
3040		var columns = settings.aoColumns;
3041	
3042		if (! rowModal.displayData) {
3043			// Need to render and cache
3044			rowModal.displayData = [];
3045		
3046			for ( var colIdx=0, len=columns.length ; colIdx<len ; colIdx++ ) {
3047				rowModal.displayData.push(
3048					_fnGetCellData( settings, rowIdx, colIdx, 'display' )
3049				);
3050			}
3051		}
3052	
3053		return rowModal.displayData;
3054	}
3055	
3056	/**
3057	 * Create a new TR element (and it's TD children) for a row
3058	 *  @param {object} oSettings dataTables settings object
3059	 *  @param {int} iRow Row to consider
3060	 *  @param {node} [nTrIn] TR element to add to the table - optional. If not given,
3061	 *    DataTables will create a row automatically
3062	 *  @param {array} [anTds] Array of TD|TH elements for the row - must be given
3063	 *    if nTr is.
3064	 *  @memberof DataTable#oApi
3065	 */
3066	function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
3067	{
3068		var
3069			row = oSettings.aoData[iRow],
3070			rowData = row._aData,
3071			cells = [],
3072			nTr, nTd, oCol,
3073			i, iLen, create,
3074			trClass = oSettings.oClasses.tbody.row;
3075	
3076		if ( row.nTr === null )
3077		{
3078			nTr = nTrIn || document.createElement('tr');
3079	
3080			row.nTr = nTr;
3081			row.anCells = cells;
3082	
3083			_addClass(nTr, trClass);
3084	
3085			/* Use a private property on the node to allow reserve mapping from the node
3086			 * to the aoData array for fast look up
3087			 */
3088			nTr._DT_RowIndex = iRow;
3089	
3090			/* Special parameters can be given by the data source to be used on the row */
3091			_fnRowAttributes( oSettings, row );
3092	
3093			/* Process each column */
3094			for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
3095			{
3096				oCol = oSettings.aoColumns[i];
3097				create = nTrIn && anTds[i] ? false : true;
3098	
3099				nTd = create ? document.createElement( oCol.sCellType ) : anTds[i];
3100	
3101				if (! nTd) {
3102					_fnLog( oSettings, 0, 'Incorrect column count', 18 );
3103				}
3104	
3105				nTd._DT_CellIndex = {
3106					row: iRow,
3107					column: i
3108				};
3109				
3110				cells.push( nTd );
3111				
3112				var display = _fnGetRowDisplay(oSettings, iRow);
3113	
3114				// Need to create the HTML if new, or if a rendering function is defined
3115				if (
3116					create ||
3117					(
3118						(oCol.mRender || oCol.mData !== i) &&
3119						(!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
3120					)
3121				) {
3122					_fnWriteCell(nTd, display[i]);
3123				}
3124	
3125				// column class
3126				_addClass(nTd, oCol.sClass);
3127	
3128				// Visibility - add or remove as required
3129				if ( oCol.bVisible && create )
3130				{
3131					nTr.appendChild( nTd );
3132				}
3133				else if ( ! oCol.bVisible && ! create )
3134				{
3135					nTd.parentNode.removeChild( nTd );
3136				}
3137	
3138				if ( oCol.fnCreatedCell )
3139				{
3140					oCol.fnCreatedCell.call( oSettings.oInstance,
3141						nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
3142					);
3143				}
3144			}
3145	
3146			_fnCallbackFire( oSettings, 'aoRowCreatedCallback', 'row-created', [nTr, rowData, iRow, cells] );
3147		}
3148		else {
3149			_addClass(row.nTr, trClass);
3150		}
3151	}
3152	
3153	
3154	/**
3155	 * Add attributes to a row based on the special `DT_*` parameters in a data
3156	 * source object.
3157	 *  @param {object} settings DataTables settings object
3158	 *  @param {object} DataTables row object for the row to be modified
3159	 *  @memberof DataTable#oApi
3160	 */
3161	function _fnRowAttributes( settings, row )
3162	{
3163		var tr = row.nTr;
3164		var data = row._aData;
3165	
3166		if ( tr ) {
3167			var id = settings.rowIdFn( data );
3168	
3169			if ( id ) {
3170				tr.id = id;
3171			}
3172	
3173			if ( data.DT_RowClass ) {
3174				// Remove any classes added by DT_RowClass before
3175				var a = data.DT_RowClass.split(' ');
3176				row.__rowc = row.__rowc ?
3177					_unique( row.__rowc.concat( a ) ) :
3178					a;
3179	
3180				$(tr)
3181					.removeClass( row.__rowc.join(' ') )
3182					.addClass( data.DT_RowClass );
3183			}
3184	
3185			if ( data.DT_RowAttr ) {
3186				$(tr).attr( data.DT_RowAttr );
3187			}
3188	
3189			if ( data.DT_RowData ) {
3190				$(tr).data( data.DT_RowData );
3191			}
3192		}
3193	}
3194	
3195	
3196	/**
3197	 * Create the HTML header for the table
3198	 *  @param {object} oSettings dataTables settings object
3199	 *  @memberof DataTable#oApi
3200	 */
3201	function _fnBuildHead( settings, side )
3202	{
3203		var classes = settings.oClasses;
3204		var columns = settings.aoColumns;
3205		var i, ien, row;
3206		var target = side === 'header'
3207			? settings.nTHead
3208			: settings.nTFoot;
3209		var titleProp = side === 'header' ? 'sTitle' : side;
3210	
3211		// Footer might be defined
3212		if (! target) {
3213			return;
3214		}
3215	
3216		// If no cells yet and we have content for them, then create
3217		if (side === 'header' || _pluck(settings.aoColumns, titleProp).join('')) {
3218			row = $('tr', target);
3219	
3220			// Add a row if needed
3221			if (! row.length) {
3222				row = $('<tr/>').appendTo(target)
3223			}
3224	
3225			// Add the number of cells needed to make up to the number of columns
3226			if (row.length === 1) {
3227				var cellCount = 0;
3228				
3229				$('td, th', row).each(function () {
3230					cellCount += this.colSpan;
3231				});
3232	
3233				for ( i=cellCount, ien=columns.length ; i<ien ; i++ ) {
3234					$('<th/>')
3235						.html( columns[i][titleProp] || '' )
3236						.appendTo( row );
3237				}
3238			}
3239		}
3240	
3241		var detected = _fnDetectHeader( settings, target, true );
3242	
3243		if (side === 'header') {
3244			settings.aoHeader = detected;
3245			$('tr', target).addClass(classes.thead.row);
3246		}
3247		else {
3248			settings.aoFooter = detected;
3249			$('tr', target).addClass(classes.tfoot.row);
3250		}
3251	
3252		// Every cell needs to be passed through the renderer
3253		$(target).children('tr').children('th, td')
3254			.each( function () {
3255				_fnRenderer( settings, side )(
3256					settings, $(this), classes
3257				);
3258			} );
3259	}
3260	
3261	/**
3262	 * Build a layout structure for a header or footer
3263	 *
3264	 * @param {*} settings DataTables settings
3265	 * @param {*} source Source layout array
3266	 * @param {*} incColumns What columns should be included
3267	 * @returns Layout array
3268	 */
3269	function _fnHeaderLayout( settings, source, incColumns )
3270	{
3271		var row, column, cell;
3272		var local = [];
3273		var structure = [];
3274		var columns = settings.aoColumns;
3275		var columnCount = columns.length;
3276		var rowspan, colspan;
3277	
3278		if ( ! source ) {
3279			return;
3280		}
3281	
3282		// Default is to work on only visible columns
3283		if ( ! incColumns ) {
3284			incColumns = _range(columnCount)
3285				.filter(function (idx) {
3286					return columns[idx].bVisible;
3287				});
3288		}
3289	
3290		// Make a copy of the master layout array, but with only the columns we want
3291		for ( row=0 ; row<source.length ; row++ ) {
3292			// Remove any columns we haven't selected
3293			local[row] = source[row].slice().filter(function (cell, i) {
3294				return incColumns.includes(i);
3295			});
3296	
3297			// Prep the structure array - it needs an element for each row
3298			structure.push( [] );
3299		}
3300	
3301		for ( row=0 ; row<local.length ; row++ ) {
3302			for ( column=0 ; column<local[row].length ; column++ ) {
3303				rowspan = 1;
3304				colspan = 1;
3305	
3306				// Check to see if there is already a cell (row/colspan) covering our target
3307				// insert point. If there is, then there is nothing to do.
3308				if ( structure[row][column] === undefined ) {
3309					cell = local[row][column].cell;
3310	
3311					// Expand for rowspan
3312					while (
3313						local[row+rowspan] !== undefined &&
3314						local[row][column].cell == local[row+rowspan][column].cell
3315					) {
3316						structure[row+rowspan][column] = null;
3317						rowspan++;
3318					}
3319	
3320					// And for colspan
3321					while (
3322						local[row][column+colspan] !== undefined &&
3323						local[row][column].cell == local[row][column+colspan].cell
3324					) {
3325						// Which also needs to go over rows
3326						for ( var k=0 ; k<rowspan ; k++ ) {
3327							structure[row+k][column+colspan] = null;
3328						}
3329	
3330						colspan++;
3331					}
3332	
3333					var titleSpan = $('span.dt-column-title', cell);
3334	
3335					structure[row][column] = {
3336						cell: cell,
3337						colspan: colspan,
3338						rowspan: rowspan,
3339						title: titleSpan.length
3340							? titleSpan.html()
3341							: $(cell).html()
3342					};
3343				}
3344			}
3345		}
3346	
3347		return structure;
3348	}
3349	
3350	
3351	/**
3352	 * Draw the header (or footer) element based on the column visibility states.
3353	 *
3354	 *  @param object oSettings dataTables settings object
3355	 *  @param array aoSource Layout array from _fnDetectHeader
3356	 *  @memberof DataTable#oApi
3357	 */
3358	function _fnDrawHead( settings, source )
3359	{
3360		var layout = _fnHeaderLayout(settings, source);
3361		var tr, n;
3362	
3363		for ( var row=0 ; row<source.length ; row++ ) {
3364			tr = source[row].row;
3365	
3366			// All cells are going to be replaced, so empty out the row
3367			// Can't use $().empty() as that kills event handlers
3368			if (tr) {
3369				while( (n = tr.firstChild) ) {
3370					tr.removeChild( n );
3371				}
3372			}
3373	
3374			for ( var column=0 ; column<layout[row].length ; column++ ) {
3375				var point = layout[row][column];
3376	
3377				if (point) {
3378					$(point.cell)
3379						.appendTo(tr)
3380						.attr('rowspan', point.rowspan)
3381						.attr('colspan', point.colspan);
3382				}
3383			}
3384		}
3385	}
3386	
3387	
3388	/**
3389	 * Insert the required TR nodes into the table for display
3390	 *  @param {object} oSettings dataTables settings object
3391	 *  @param ajaxComplete true after ajax call to complete rendering
3392	 *  @memberof DataTable#oApi
3393	 */
3394	function _fnDraw( oSettings, ajaxComplete )
3395	{
3396		// Allow for state saving and a custom start position
3397		_fnStart( oSettings );
3398	
3399		/* Provide a pre-callback function which can be used to cancel the draw is false is returned */
3400		var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
3401		if ( aPreDraw.indexOf(false) !== -1 )
3402		{
3403			_fnProcessingDisplay( oSettings, false );
3404			return;
3405		}
3406	
3407		var anRows = [];
3408		var iRowCount = 0;
3409		var bServerSide = _fnDataSource( oSettings ) == 'ssp';
3410		var aiDisplay = oSettings.aiDisplay;
3411		var iDisplayStart = oSettings._iDisplayStart;
3412		var iDisplayEnd = oSettings.fnDisplayEnd();
3413		var columns = oSettings.aoColumns;
3414		var body = $(oSettings.nTBody);
3415	
3416		oSettings.bDrawing = true;
3417	
3418		/* Server-side processing draw intercept */
3419		if ( oSettings.deferLoading )
3420		{
3421			oSettings.deferLoading = false;
3422			oSettings.iDraw++;
3423			_fnProcessingDisplay( oSettings, false );
3424		}
3425		else if ( !bServerSide )
3426		{
3427			oSettings.iDraw++;
3428		}
3429		else if ( !oSettings.bDestroying && !ajaxComplete)
3430		{
3431			// Show loading message for server-side processing
3432			if (oSettings.iDraw === 0) {
3433				body.empty().append(_emptyRow(oSettings));
3434			}
3435	
3436			_fnAjaxUpdate( oSettings );
3437			return;
3438		}
3439	
3440		if ( aiDisplay.length !== 0 )
3441		{
3442			var iStart = bServerSide ? 0 : iDisplayStart;
3443			var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
3444	
3445			for ( var j=iStart ; j<iEnd ; j++ )
3446			{
3447				var iDataIndex = aiDisplay[j];
3448				var aoData = oSettings.aoData[ iDataIndex ];
3449				if ( aoData.nTr === null )
3450				{
3451					_fnCreateTr( oSettings, iDataIndex );
3452				}
3453	
3454				var nRow = aoData.nTr;
3455	
3456				// Add various classes as needed
3457				for (var i=0 ; i<columns.length ; i++) {
3458					var col = columns[i];
3459					var td = aoData.anCells[i];
3460	
3461					_addClass(td, _ext.type.className[col.sType]); // auto class
3462					_addClass(td, oSettings.oClasses.tbody.cell); // all cells
3463				}
3464	
3465				// Row callback functions - might want to manipulate the row
3466				// iRowCount and j are not currently documented. Are they at all
3467				// useful?
3468				_fnCallbackFire( oSettings, 'aoRowCallback', null,
3469					[nRow, aoData._aData, iRowCount, j, iDataIndex] );
3470	
3471				anRows.push( nRow );
3472				iRowCount++;
3473			}
3474		}
3475		else
3476		{
3477			anRows[ 0 ] = _emptyRow(oSettings);
3478		}
3479	
3480		/* Header and footer callbacks */
3481		_fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
3482			_fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3483	
3484		_fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
3485			_fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3486	
3487		// replaceChildren is faster, but only became widespread in 2020,
3488		// so a fall back in jQuery is provided for older browsers.
3489		if (body[0].replaceChildren) {
3490			body[0].replaceChildren.apply(body[0], anRows);
3491		}
3492		else {
3493			body.children().detach();
3494			body.append( $(anRows) );
3495		}
3496	
3497		// Empty table needs a specific class
3498		$(oSettings.nTableWrapper).toggleClass('dt-empty-footer', $('tr', oSettings.nTFoot).length === 0);
3499	
3500		/* Call all required callback functions for the end of a draw */
3501		_fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings], true );
3502	
3503		/* Draw is complete, sorting and filtering must be as well */
3504		oSettings.bSorted = false;
3505		oSettings.bFiltered = false;
3506		oSettings.bDrawing = false;
3507	}
3508	
3509	
3510	/**
3511	 * Redraw the table - taking account of the various features which are enabled
3512	 *  @param {object} oSettings dataTables settings object
3513	 *  @param {boolean} [holdPosition] Keep the current paging position. By default
3514	 *    the paging is reset to the first page
3515	 *  @memberof DataTable#oApi
3516	 */
3517	function _fnReDraw( settings, holdPosition, recompute )
3518	{
3519		var
3520			features = settings.oFeatures,
3521			sort     = features.bSort,
3522			filter   = features.bFilter;
3523	
3524		if (recompute === undefined || recompute === true) {
3525			// Resolve any column types that are unknown due to addition or invalidation
3526			_fnColumnTypes( settings );
3527	
3528			if ( sort ) {
3529				_fnSort( settings );
3530			}
3531	
3532			if ( filter ) {
3533				_fnFilterComplete( settings, settings.oPreviousSearch );
3534			}
3535			else {
3536				// No filtering, so we want to just use the display master
3537				settings.aiDisplay = settings.aiDisplayMaster.slice();
3538			}
3539		}
3540	
3541		if ( holdPosition !== true ) {
3542			settings._iDisplayStart = 0;
3543		}
3544	
3545		// Let any modules know about the draw hold position state (used by
3546		// scrolling internally)
3547		settings._drawHold = holdPosition;
3548	
3549		_fnDraw( settings );
3550	
3551		settings._drawHold = false;
3552	}
3553	
3554	
3555	/*
3556	 * Table is empty - create a row with an empty message in it
3557	 */
3558	function _emptyRow ( settings ) {
3559		var oLang = settings.oLanguage;
3560		var zero = oLang.sZeroRecords;
3561		var dataSrc = _fnDataSource( settings );
3562	
3563		if (
3564			(settings.iDraw < 1 && dataSrc === 'ssp') ||
3565			(settings.iDraw <= 1 && dataSrc === 'ajax')
3566		) {
3567			zero = oLang.sLoadingRecords;
3568		}
3569		else if ( oLang.sEmptyTable && settings.fnRecordsTotal() === 0 )
3570		{
3571			zero = oLang.sEmptyTable;
3572		}
3573	
3574		return $( '<tr/>' )
3575			.append( $('<td />', {
3576				'colSpan': _fnVisbleColumns( settings ),
3577				'class':   settings.oClasses.empty.row
3578			} ).html( zero ) )[0];
3579	}
3580	
3581	
3582	/**
3583	 * Expand the layout items into an object for the rendering function
3584	 */
3585	function _layoutItems (row, align, items) {
3586		if ( Array.isArray(items)) {
3587			for (var i=0 ; i<items.length ; i++) {
3588				_layoutItems(row, align, items[i]);
3589			}
3590	
3591			return;
3592		}
3593	
3594		var rowCell = row[align];
3595	
3596		// If it is an object, then there can be multiple features contained in it
3597		if ( $.isPlainObject( items ) ) {
3598			// A feature plugin cannot be named "features" due to this check
3599			if (items.features) {
3600				if (items.rowId) {
3601					row.id = items.rowId;
3602				}
3603				if (items.rowClass) {
3604					row.className = items.rowClass;
3605				}
3606	
3607				rowCell.id = items.id;
3608				rowCell.className = items.className;
3609	
3610				_layoutItems(row, align, items.features);
3611			}
3612			else {
3613				Object.keys(items).map(function (key) {
3614					rowCell.contents.push( {
3615						feature: key,
3616						opts: items[key]
3617					});
3618				});
3619			}
3620		}
3621		else {
3622			rowCell.contents.push(items);
3623		}
3624	}
3625	
3626	/**
3627	 * Find, or create a layout row
3628	 */
3629	function _layoutGetRow(rows, rowNum, align) {
3630		var row;
3631	
3632		// Find existing rows
3633		for (var i=0; i<rows.length; i++) {
3634			row = rows[i];
3635	
3636			if (row.rowNum === rowNum) {
3637				// full is on its own, but start and end share a row
3638				if (
3639					(align === 'full' && row.full) ||
3640					((align === 'start' || align === 'end') && (row.start || row.end))
3641				) {
3642					if (! row[align]) {
3643						row[align] = {
3644							contents: []
3645						};
3646					}
3647	
3648					return row;
3649				}
3650			}
3651		}
3652	
3653		// If we get this far, then there was no match, create a new row
3654		row = {
3655			rowNum: rowNum	
3656		};
3657	
3658		row[align] = {
3659			contents: []
3660		};
3661	
3662		rows.push(row);
3663	
3664		return row;
3665	}
3666	
3667	/**
3668	 * Convert a `layout` object given by a user to the object structure needed
3669	 * for the renderer. This is done twice, once for above and once for below
3670	 * the table. Ordering must also be considered.
3671	 *
3672	 * @param {*} settings DataTables settings object
3673	 * @param {*} layout Layout object to convert
3674	 * @param {string} side `top` or `bottom`
3675	 * @returns Converted array structure - one item for each row.
3676	 */
3677	function _layoutArray ( settings, layout, side ) {
3678		var rows = [];
3679		
3680		// Split out into an array
3681		$.each( layout, function ( pos, items ) {
3682			if (items === null) {
3683				return;
3684			}
3685	
3686			var parts = pos.match(/^([a-z]+)([0-9]*)([A-Za-z]*)$/);
3687			var rowNum = parts[2]
3688				? parts[2] * 1
3689				: 0;
3690			var align = parts[3]
3691				? parts[3].toLowerCase()
3692				: 'full';
3693	
3694			// Filter out the side we aren't interested in
3695			if (parts[1] !== side) {
3696				return;
3697			}
3698	
3699			// Get or create the row we should attach to
3700			var row = _layoutGetRow(rows, rowNum, align);
3701	
3702			_layoutItems(row, align, items);
3703		});
3704	
3705		// Order by item identifier
3706		rows.sort( function ( a, b ) {
3707			var order1 = a.rowNum;
3708			var order2 = b.rowNum;
3709	
3710			// If both in the same row, then the row with `full` comes first
3711			if (order1 === order2) {
3712				var ret = a.full && ! b.full ? -1 : 1;
3713	
3714				return side === 'bottom'
3715					? ret * -1
3716					: ret;
3717			}
3718	
3719			return order2 - order1;
3720		} );
3721	
3722		// Invert for below the table
3723		if ( side === 'bottom' ) {
3724			rows.reverse();
3725		}
3726	
3727		for (var row = 0; row<rows.length; row++) {
3728			delete rows[row].rowNum;
3729	
3730			_layoutResolve(settings, rows[row]);
3731		}
3732	
3733		return rows;
3734	}
3735	
3736	
3737	/**
3738	 * Convert the contents of a row's layout object to nodes that can be inserted
3739	 * into the document by a renderer. Execute functions, look up plug-ins, etc.
3740	 *
3741	 * @param {*} settings DataTables settings object
3742	 * @param {*} row Layout object for this row
3743	 */
3744	function _layoutResolve( settings, row ) {
3745		var getFeature = function (feature, opts) {
3746			if ( ! _ext.features[ feature ] ) {
3747				_fnLog( settings, 0, 'Unknown feature: '+ feature );
3748			}
3749	
3750			return _ext.features[ feature ].apply( this, [settings, opts] );
3751		};
3752	
3753		var resolve = function ( item ) {
3754			if (! row[ item ]) {
3755				return;
3756			}
3757	
3758			var line = row[ item ].contents;
3759	
3760			for ( var i=0, ien=line.length ; i<ien ; i++ ) {
3761				if ( ! line[i] ) {
3762					continue;
3763				}
3764				else if ( typeof line[i] === 'string' ) {
3765					line[i] = getFeature( line[i], null );
3766				}
3767				else if ( $.isPlainObject(line[i]) ) {
3768					// If it's an object, it just has feature and opts properties from
3769					// the transform in _layoutArray
3770					line[i] = getFeature(line[i].feature, line[i].opts);
3771				}
3772				else if ( typeof line[i].node === 'function' ) {
3773					line[i] = line[i].node( settings );
3774				}
3775				else if ( typeof line[i] === 'function' ) {
3776					var inst = line[i]( settings );
3777	
3778					line[i] = typeof inst.node === 'function' ?
3779						inst.node() :
3780						inst;
3781				}
3782			}
3783		};
3784	
3785		resolve('start');
3786		resolve('end');
3787		resolve('full');
3788	}
3789	
3790	
3791	/**
3792	 * Add the options to the page HTML for the table
3793	 *  @param {object} settings DataTables settings object
3794	 *  @memberof DataTable#oApi
3795	 */
3796	function _fnAddOptionsHtml ( settings )
3797	{
3798		var classes = settings.oClasses;
3799		var table = $(settings.nTable);
3800	
3801		// Wrapper div around everything DataTables controls
3802		var insert = $('<div/>')
3803			.attr({
3804				id:      settings.sTableId+'_wrapper',
3805				'class': classes.container
3806			})
3807			.insertBefore(table);
3808	
3809		settings.nTableWrapper = insert[0];
3810	
3811		if (settings.sDom) {
3812			// Legacy
3813			_fnLayoutDom(settings, settings.sDom, insert);
3814		}
3815		else {
3816			var top = _layoutArray( settings, settings.layout, 'top' );
3817			var bottom = _layoutArray( settings, settings.layout, 'bottom' );
3818			var renderer = _fnRenderer( settings, 'layout' );
3819		
3820			// Everything above - the renderer will actually insert the contents into the document
3821			top.forEach(function (item) {
3822				renderer( settings, insert, item );
3823			});
3824	
3825			// The table - always the center of attention
3826			renderer( settings, insert, {
3827				full: {
3828					table: true,
3829					contents: [ _fnFeatureHtmlTable(settings) ]
3830				}
3831			} );
3832	
3833			// Everything below
3834			bottom.forEach(function (item) {
3835				renderer( settings, insert, item );
3836			});
3837		}
3838	
3839		// Processing floats on top, so it isn't an inserted feature
3840		_processingHtml( settings );
3841	}
3842	
3843	/**
3844	 * Draw the table with the legacy DOM property
3845	 * @param {*} settings DT settings object
3846	 * @param {*} dom DOM string
3847	 * @param {*} insert Insert point
3848	 */
3849	function _fnLayoutDom( settings, dom, insert )
3850	{
3851		var parts = dom.match(/(".*?")|('.*?')|./g);
3852		var featureNode, option, newNode, next, attr;
3853	
3854		for ( var i=0 ; i<parts.length ; i++ ) {
3855			featureNode = null;
3856			option = parts[i];
3857	
3858			if ( option == '<' ) {
3859				// New container div
3860				newNode = $('<div/>');
3861	
3862				// Check to see if we should append an id and/or a class name to the container
3863				next = parts[i+1];
3864	
3865				if ( next[0] == "'" || next[0] == '"' ) {
3866					attr = next.replace(/['"]/g, '');
3867	
3868					var id = '', className;
3869	
3870					/* The attribute can be in the format of "#id.class", "#id" or "class" This logic
3871					 * breaks the string into parts and applies them as needed
3872					 */
3873					if ( attr.indexOf('.') != -1 ) {
3874						var split = attr.split('.');
3875	
3876						id = split[0];
3877						className = split[1];
3878					}
3879					else if ( attr[0] == "#" ) {
3880						id = attr;
3881					}
3882					else {
3883						className = attr;
3884					}
3885	
3886					newNode
3887						.attr('id', id.substring(1))
3888						.addClass(className);
3889	
3890					i++; // Move along the position array
3891				}
3892	
3893				insert.append( newNode );
3894				insert = newNode;
3895			}
3896			else if ( option == '>' ) {
3897				// End container div
3898				insert = insert.parent();
3899			}
3900			else if ( option == 't' ) {
3901				// Table
3902				featureNode = _fnFeatureHtmlTable( settings );
3903			}
3904			else
3905			{
3906				DataTable.ext.feature.forEach(function(feature) {
3907					if ( option == feature.cFeature ) {
3908						featureNode = feature.fnInit( settings );
3909					}
3910				});
3911			}
3912	
3913			// Add to the display
3914			if ( featureNode ) {
3915				insert.append( featureNode );
3916			}
3917		}
3918	}
3919	
3920	
3921	/**
3922	 * Use the DOM source to create up an array of header cells. The idea here is to
3923	 * create a layout grid (array) of rows x columns, which contains a reference
3924	 * to the cell that that point in the grid (regardless of col/rowspan), such that
3925	 * any column / row could be removed and the new grid constructed
3926	 *  @param {node} thead The header/footer element for the table
3927	 *  @returns {array} Calculated layout array
3928	 *  @memberof DataTable#oApi
3929	 */
3930	function _fnDetectHeader ( settings, thead, write )
3931	{
3932		var columns = settings.aoColumns;
3933		var rows = $(thead).children('tr');
3934		var row, cell;
3935		var i, k, l, iLen, shifted, column, colspan, rowspan;
3936		var isHeader = thead && thead.nodeName.toLowerCase() === 'thead';
3937		var layout = [];
3938		var unique;
3939		var shift = function ( a, i, j ) {
3940			var k = a[i];
3941			while ( k[j] ) {
3942				j++;
3943			}
3944			return j;
3945		};
3946	
3947		// We know how many rows there are in the layout - so prep it
3948		for ( i=0, iLen=rows.length ; i<iLen ; i++ ) {
3949			layout.push( [] );
3950		}
3951	
3952		for ( i=0, iLen=rows.length ; i<iLen ; i++ ) {
3953			row = rows[i];
3954			column = 0;
3955	
3956			// For every cell in the row..
3957			cell = row.firstChild;
3958			while ( cell ) {
3959				if (
3960					cell.nodeName.toUpperCase() == 'TD' ||
3961					cell.nodeName.toUpperCase() == 'TH'
3962				) {
3963					var cols = [];
3964	
3965					// Get the col and rowspan attributes from the DOM and sanitise them
3966					colspan = cell.getAttribute('colspan') * 1;
3967					rowspan = cell.getAttribute('rowspan') * 1;
3968					colspan = (!colspan || colspan===0 || colspan===1) ? 1 : colspan;
3969					rowspan = (!rowspan || rowspan===0 || rowspan===1) ? 1 : rowspan;
3970	
3971					// There might be colspan cells already in this row, so shift our target
3972					// accordingly
3973					shifted = shift( layout, i, column );
3974	
3975					// Cache calculation for unique columns
3976					unique = colspan === 1 ?
3977						true :
3978						false;
3979					
3980					// Perform header setup
3981					if ( write ) {
3982						if (unique) {
3983							// Allow column options to be set from HTML attributes
3984							_fnColumnOptions( settings, shifted, $(cell).data() );
3985							
3986							// Get the width for the column. This can be defined from the
3987							// width attribute, style attribute or `columns.width` option
3988							var columnDef = columns[shifted];
3989							var width = cell.getAttribute('width') || null;
3990							var t = cell.style.width.match(/width:\s*(\d+[pxem%]+)/);
3991							if ( t ) {
3992								width = t[1];
3993							}
3994	
3995							columnDef.sWidthOrig = columnDef.sWidth || width;
3996	
3997							if (isHeader) {
3998								// Column title handling - can be user set, or read from the DOM
3999								// This happens before the render, so the original is still in place
4000								if ( columnDef.sTitle !== null && ! columnDef.autoTitle ) {
4001									cell.innerHTML = columnDef.sTitle;
4002								}
4003	
4004								if (! columnDef.sTitle && unique) {
4005									columnDef.sTitle = _stripHtml(cell.innerHTML);
4006									columnDef.autoTitle = true;
4007								}
4008							}
4009							else {
4010								// Footer specific operations
4011								if (columnDef.footer) {
4012									cell.innerHTML = columnDef.footer;
4013								}
4014							}
4015	
4016							// Fall back to the aria-label attribute on the table header if no ariaTitle is
4017							// provided.
4018							if (! columnDef.ariaTitle) {
4019								columnDef.ariaTitle = $(cell).attr("aria-label") || columnDef.sTitle;
4020							}
4021	
4022							// Column specific class names
4023							if ( columnDef.className ) {
4024								$(cell).addClass( columnDef.className );
4025							}
4026						}
4027	
4028						// Wrap the column title so we can write to it in future
4029						if ( $('span.dt-column-title', cell).length === 0) {
4030							$('<span>')
4031								.addClass('dt-column-title')
4032								.append(cell.childNodes)
4033								.appendTo(cell);
4034						}
4035	
4036						if ( isHeader && $('span.dt-column-order', cell).length === 0) {
4037							$('<span>')
4038								.addClass('dt-column-order')
4039								.appendTo(cell);
4040						}
4041					}
4042	
4043					// If there is col / rowspan, copy the information into the layout grid
4044					for ( l=0 ; l<colspan ; l++ ) {
4045						for ( k=0 ; k<rowspan ; k++ ) {
4046							layout[i+k][shifted+l] = {
4047								cell: cell,
4048								unique: unique
4049							};
4050	
4051							layout[i+k].row = row;
4052						}
4053	
4054						cols.push( shifted+l );
4055					}
4056	
4057					// Assign an attribute so spanning cells can still be identified
4058					// as belonging to a column
4059					cell.setAttribute('data-dt-column', _unique(cols).join(','));
4060				}
4061	
4062				cell = cell.nextSibling;
4063			}
4064		}
4065	
4066		return layout;
4067	}
4068	
4069	/**
4070	 * Set the start position for draw
4071	 *  @param {object} oSettings dataTables settings object
4072	 */
4073	function _fnStart( oSettings )
4074	{
4075		var bServerSide = _fnDataSource( oSettings ) == 'ssp';
4076		var iInitDisplayStart = oSettings.iInitDisplayStart;
4077	
4078		// Check and see if we have an initial draw position from state saving
4079		if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
4080		{
4081			oSettings._iDisplayStart = bServerSide ?
4082				iInitDisplayStart :
4083				iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
4084					0 :
4085					iInitDisplayStart;
4086	
4087			oSettings.iInitDisplayStart = -1;
4088		}
4089	}
4090	
4091	/**
4092	 * Create an Ajax call based on the table's settings, taking into account that
4093	 * parameters can have multiple forms, and backwards compatibility.
4094	 *
4095	 * @param {object} oSettings dataTables settings object
4096	 * @param {array} data Data to send to the server, required by
4097	 *     DataTables - may be augmented by developer callbacks
4098	 * @param {function} fn Callback function to run when data is obtained
4099	 */
4100	function _fnBuildAjax( oSettings, data, fn )
4101	{
4102		var ajaxData;
4103		var ajax = oSettings.ajax;
4104		var instance = oSettings.oInstance;
4105		var callback = function ( json ) {
4106			var status = oSettings.jqXHR
4107				? oSettings.jqXHR.status
4108				: null;
4109	
4110			if ( json === null || (typeof status === 'number' && status == 204 ) ) {
4111				json = {};
4112				_fnAjaxDataSrc( oSettings, json, [] );
4113			}
4114	
4115			var error = json.error || json.sError;
4116			if ( error ) {
4117				_fnLog( oSettings, 0, error );
4118			}
4119	
4120			// Microsoft often wrap JSON as a string in another JSON object
4121			// Let's handle that automatically
4122			if (json.d && typeof json.d === 'string') {
4123				try {
4124					json = JSON.parse(json.d);
4125				}
4126				catch (e) {
4127					// noop
4128				}
4129			}
4130	
4131			oSettings.json = json;
4132	
4133			_fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR], true );
4134			fn( json );
4135		};
4136	
4137		if ( $.isPlainObject( ajax ) && ajax.data )
4138		{
4139			ajaxData = ajax.data;
4140	
4141			var newData = typeof ajaxData === 'function' ?
4142				ajaxData( data, oSettings ) :  // fn can manipulate data or return
4143				ajaxData;                      // an object object or array to merge
4144	
4145			// If the function returned something, use that alone
4146			data = typeof ajaxData === 'function' && newData ?
4147				newData :
4148				$.extend( true, data, newData );
4149	
4150			// Remove the data property as we've resolved it already and don't want
4151			// jQuery to do it again (it is restored at the end of the function)
4152			delete ajax.data;
4153		}
4154	
4155		var baseAjax = {
4156			"url": typeof ajax === 'string' ?
4157				ajax :
4158				'',
4159			"data": data,
4160			"success": callback,
4161			"dataType": "json",
4162			"cache": false,
4163			"type": oSettings.sServerMethod,
4164			"error": function (xhr, error) {
4165				var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR], true );
4166	
4167				if ( ret.indexOf(true) === -1 ) {
4168					if ( error == "parsererror" ) {
4169						_fnLog( oSettings, 0, 'Invalid JSON response', 1 );
4170					}
4171					else if ( xhr.readyState === 4 ) {
4172						_fnLog( oSettings, 0, 'Ajax error', 7 );
4173					}
4174				}
4175	
4176				_fnProcessingDisplay( oSettings, false );
4177			}
4178		};
4179	
4180		// If `ajax` option is an object, extend and override our default base
4181		if ( $.isPlainObject( ajax ) ) {
4182			$.extend( baseAjax, ajax )
4183		}
4184	
4185		// Store the data submitted for the API
4186		oSettings.oAjaxData = data;
4187	
4188		// Allow plug-ins and external processes to modify the data
4189		_fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data, baseAjax], true );
4190	
4191		if ( typeof ajax === 'function' )
4192		{
4193			// Is a function - let the caller define what needs to be done
4194			oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
4195		}
4196		else if (ajax.url === '') {
4197			// No url, so don't load any data. Just apply an empty data array
4198			// to the object for the callback.
4199			var empty = {};
4200	
4201			DataTable.util.set(ajax.dataSrc)(empty, []);
4202			callback(empty);
4203		}
4204		else {
4205			// Object to extend the base settings
4206			oSettings.jqXHR = $.ajax( baseAjax );
4207		}
4208	
4209		// Restore for next time around
4210		if ( ajaxData ) {
4211			ajax.data = ajaxData;
4212		}
4213	}
4214	
4215	
4216	/**
4217	 * Update the table using an Ajax call
4218	 *  @param {object} settings dataTables settings object
4219	 *  @returns {boolean} Block the table drawing or not
4220	 *  @memberof DataTable#oApi
4221	 */
4222	function _fnAjaxUpdate( settings )
4223	{
4224		settings.iDraw++;
4225		_fnProcessingDisplay( settings, true );
4226	
4227		_fnBuildAjax(
4228			settings,
4229			_fnAjaxParameters( settings ),
4230			function(json) {
4231				_fnAjaxUpdateDraw( settings, json );
4232			}
4233		);
4234	}
4235	
4236	
4237	/**
4238	 * Build up the parameters in an object needed for a server-side processing
4239	 * request.
4240	 *  @param {object} oSettings dataTables settings object
4241	 *  @returns {bool} block the table drawing or not
4242	 *  @memberof DataTable#oApi
4243	 */
4244	function _fnAjaxParameters( settings )
4245	{
4246		var
4247			columns = settings.aoColumns,
4248			features = settings.oFeatures,
4249			preSearch = settings.oPreviousSearch,
4250			preColSearch = settings.aoPreSearchCols,
4251			colData = function ( idx, prop ) {
4252				return typeof columns[idx][prop] === 'function' ?
4253					'function' :
4254					columns[idx][prop];
4255			};
4256	
4257		return {
4258			draw: settings.iDraw,
4259			columns: columns.map( function ( column, i ) {
4260				return {
4261					data: colData(i, 'mData'),
4262					name: column.sName,
4263					searchable: column.bSearchable,
4264					orderable: column.bSortable,
4265					search: {
4266						value: preColSearch[i].search,
4267						regex: preColSearch[i].regex,
4268						fixed: Object.keys(column.searchFixed).map( function(name) {
4269							return {
4270								name: name,
4271								term: column.searchFixed[name].toString()
4272							}
4273						})
4274					}
4275				};
4276			} ),
4277			order: _fnSortFlatten( settings ).map( function ( val ) {
4278				return {
4279					column: val.col,
4280					dir: val.dir,
4281					name: colData(val.col, 'sName')
4282				};
4283			} ),
4284			start: settings._iDisplayStart,
4285			length: features.bPaginate ?
4286				settings._iDisplayLength :
4287				-1,
4288			search: {
4289				value: preSearch.search,
4290				regex: preSearch.regex,
4291				fixed: Object.keys(settings.searchFixed).map( function(name) {
4292					return {
4293						name: name,
4294						term: settings.searchFixed[name].toString()
4295					}
4296				})
4297			}
4298		};
4299	}
4300	
4301	
4302	/**
4303	 * Data the data from the server (nuking the old) and redraw the table
4304	 *  @param {object} oSettings dataTables settings object
4305	 *  @param {object} json json data return from the server.
4306	 *  @param {string} json.sEcho Tracking flag for DataTables to match requests
4307	 *  @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
4308	 *  @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
4309	 *  @param {array} json.aaData The data to display on this page
4310	 *  @param {string} [json.sColumns] Column ordering (sName, comma separated)
4311	 *  @memberof DataTable#oApi
4312	 */
4313	function _fnAjaxUpdateDraw ( settings, json )
4314	{
4315		var data = _fnAjaxDataSrc(settings, json);
4316		var draw = _fnAjaxDataSrcParam(settings, 'draw', json);
4317		var recordsTotal = _fnAjaxDataSrcParam(settings, 'recordsTotal', json);
4318		var recordsFiltered = _fnAjaxDataSrcParam(settings, 'recordsFiltered', json);
4319	
4320		if ( draw !== undefined ) {
4321			// Protect against out of sequence returns
4322			if ( draw*1 < settings.iDraw ) {
4323				return;
4324			}
4325			settings.iDraw = draw * 1;
4326		}
4327	
4328		// No data in returned object, so rather than an array, we show an empty table
4329		if ( ! data ) {
4330			data = [];
4331		}
4332	
4333		_fnClearTable( settings );
4334		settings._iRecordsTotal   = parseInt(recordsTotal, 10);
4335		settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
4336	
4337		for ( var i=0, ien=data.length ; i<ien ; i++ ) {
4338			_fnAddData( settings, data[i] );
4339		}
4340		settings.aiDisplay = settings.aiDisplayMaster.slice();
4341	
4342		_fnColumnTypes(settings);
4343		_fnDraw( settings, true );
4344		_fnInitComplete( settings );
4345		_fnProcessingDisplay( settings, false );
4346	}
4347	
4348	
4349	/**
4350	 * Get the data from the JSON data source to use for drawing a table. Using
4351	 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
4352	 * source object, or from a processing function.
4353	 *  @param {object} settings dataTables settings object
4354	 *  @param  {object} json Data source object / array from the server
4355	 *  @return {array} Array of data to use
4356	 */
4357	function _fnAjaxDataSrc ( settings, json, write )
4358	{
4359		var dataProp = 'data';
4360	
4361		if ($.isPlainObject( settings.ajax ) && settings.ajax.dataSrc !== undefined) {
4362			// Could in inside a `dataSrc` object, or not!
4363			var dataSrc = settings.ajax.dataSrc;
4364	
4365			// string, function and object are valid types
4366			if (typeof dataSrc === 'string' || typeof dataSrc === 'function') {
4367				dataProp = dataSrc;
4368			}
4369			else if (dataSrc.data !== undefined) {
4370				dataProp = dataSrc.data;
4371			}
4372		}
4373	
4374		if ( ! write ) {
4375			if ( dataProp === 'data' ) {
4376				// If the default, then we still want to support the old style, and safely ignore
4377				// it if possible
4378				return json.aaData || json[dataProp];
4379			}
4380	
4381			return dataProp !== "" ?
4382				_fnGetObjectDataFn( dataProp )( json ) :
4383				json;
4384		}
4385		
4386		// set
4387		_fnSetObjectDataFn( dataProp )( json, write );
4388	}
4389	
4390	/**
4391	 * Very similar to _fnAjaxDataSrc, but for the other SSP properties
4392	 * @param {*} settings DataTables settings object
4393	 * @param {*} param Target parameter
4394	 * @param {*} json JSON data
4395	 * @returns Resolved value
4396	 */
4397	function _fnAjaxDataSrcParam (settings, param, json) {
4398		var dataSrc = $.isPlainObject( settings.ajax )
4399			? settings.ajax.dataSrc
4400			: null;
4401	
4402		if (dataSrc && dataSrc[param]) {
4403			// Get from custom location
4404			return _fnGetObjectDataFn( dataSrc[param] )( json );
4405		}
4406	
4407		// else - Default behaviour
4408		var old = '';
4409	
4410		// Legacy support
4411		if (param === 'draw') {
4412			old = 'sEcho';
4413		}
4414		else if (param === 'recordsTotal') {
4415			old = 'iTotalRecords';
4416		}
4417		else if (param === 'recordsFiltered') {
4418			old = 'iTotalDisplayRecords';
4419		}
4420	
4421		return json[old] !== undefined
4422			? json[old]
4423			: json[param];
4424	}
4425	
4426	
4427	/**
4428	 * Filter the table using both the global filter and column based filtering
4429	 *  @param {object} settings dataTables settings object
4430	 *  @param {object} input search information
4431	 *  @memberof DataTable#oApi
4432	 */
4433	function _fnFilterComplete ( settings, input )
4434	{
4435		var columnsSearch = settings.aoPreSearchCols;
4436	
4437		// In server-side processing all filtering is done by the server, so no point hanging around here
4438		if ( _fnDataSource( settings ) != 'ssp' )
4439		{
4440			// Check if any of the rows were invalidated
4441			_fnFilterData( settings );
4442	
4443			// Start from the full data set
4444			settings.aiDisplay = settings.aiDisplayMaster.slice();
4445	
4446			// Global filter first
4447			_fnFilter( settings.aiDisplay, settings, input.search, input );
4448	
4449			$.each(settings.searchFixed, function (name, term) {
4450				_fnFilter(settings.aiDisplay, settings, term, {});
4451			});
4452	
4453			// Then individual column filters
4454			for ( var i=0 ; i<columnsSearch.length ; i++ )
4455			{
4456				var col = columnsSearch[i];
4457	
4458				_fnFilter(
4459					settings.aiDisplay,
4460					settings,
4461					col.search,
4462					col,
4463					i
4464				);
4465	
4466				$.each(settings.aoColumns[i].searchFixed, function (name, term) {
4467					_fnFilter(settings.aiDisplay, settings, term, {}, i);
4468				});
4469			}
4470	
4471			// And finally global filtering
4472			_fnFilterCustom( settings );
4473		}
4474	
4475		// Tell the draw function we have been filtering
4476		settings.bFiltered = true;
4477	
4478		_fnCallbackFire( settings, null, 'search', [settings] );
4479	}
4480	
4481	
4482	/**
4483	 * Apply custom filtering functions
4484	 * 
4485	 * This is legacy now that we have named functions, but it is widely used
4486	 * from 1.x, so it is not yet deprecated.
4487	 *  @param {object} oSettings dataTables settings object
4488	 *  @memberof DataTable#oApi
4489	 */
4490	function _fnFilterCustom( settings )
4491	{
4492		var filters = DataTable.ext.search;
4493		var displayRows = settings.aiDisplay;
4494		var row, rowIdx;
4495	
4496		for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
4497			var rows = [];
4498	
4499			// Loop over each row and see if it should be included
4500			for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
4501				rowIdx = displayRows[ j ];
4502				row = settings.aoData[ rowIdx ];
4503	
4504				if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
4505					rows.push( rowIdx );
4506				}
4507			}
4508	
4509			// So the array reference doesn't break set the results into the
4510			// existing array
4511			displayRows.length = 0;
4512			_fnArrayApply(displayRows, rows);
4513		}
4514	}
4515	
4516	
4517	/**
4518	 * Filter the data table based on user input and draw the table
4519	 */
4520	function _fnFilter( searchRows, settings, input, options, column )
4521	{
4522		if ( input === '' ) {
4523			return;
4524		}
4525	
4526		var i = 0;
4527		var matched = [];
4528	
4529		// Search term can be a function, regex or string - if a string we apply our
4530		// smart filtering regex (assuming the options require that)
4531		var searchFunc = typeof input === 'function' ? input : null;
4532		var rpSearch = input instanceof RegExp
4533			? input
4534			: searchFunc
4535				? null
4536				: _fnFilterCreateSearch( input, options );
4537	
4538		// Then for each row, does the test pass. If not, lop the row from the array
4539		for (i=0 ; i<searchRows.length ; i++) {
4540			var row = settings.aoData[ searchRows[i] ];
4541			var data = column === undefined
4542				? row._sFilterRow
4543				: row._aFilterData[ column ];
4544	
4545			if ( (searchFunc && searchFunc(data, row._aData, searchRows[i], column)) || (rpSearch && rpSearch.test(data)) ) {
4546				matched.push(searchRows[i]);
4547			}
4548		}
4549	
4550		// Mutate the searchRows array
4551		searchRows.length = matched.length;
4552	
4553		for (i=0 ; i<matched.length ; i++) {
4554			searchRows[i] = matched[i];
4555		}
4556	}
4557	
4558	
4559	/**
4560	 * Build a regular expression object suitable for searching a table
4561	 *  @param {string} sSearch string to search for
4562	 *  @param {bool} bRegex treat as a regular expression or not
4563	 *  @param {bool} bSmart perform smart filtering or not
4564	 *  @param {bool} bCaseInsensitive Do case insensitive matching or not
4565	 *  @returns {RegExp} constructed object
4566	 *  @memberof DataTable#oApi
4567	 */
4568	function _fnFilterCreateSearch( search, inOpts )
4569	{
4570		var not = [];
4571		var options = $.extend({}, {
4572			boundary: false,
4573			caseInsensitive: true,
4574			exact: false,
4575			regex: false,
4576			smart: true
4577		}, inOpts);
4578	
4579		if (typeof search !== 'string') {
4580			search = search.toString();
4581		}
4582	
4583		// Remove diacritics if normalize is set up to do so
4584		search = _normalize(search);
4585	
4586		if (options.exact) {
4587			return new RegExp(
4588				'^'+_fnEscapeRegex(search)+'$',
4589				options.caseInsensitive ? 'i' : ''
4590			);
4591		}
4592	
4593		search = options.regex ?
4594			search :
4595			_fnEscapeRegex( search );
4596		
4597		if ( options.smart ) {
4598			/* For smart filtering we want to allow the search to work regardless of
4599			 * word order. We also want double quoted text to be preserved, so word
4600			 * order is important - a la google. And a negative look around for
4601			 * finding rows which don't contain a given string.
4602			 * 
4603			 * So this is the sort of thing we want to generate:
4604			 * 
4605			 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
4606			 */
4607			var parts = search.match( /!?["\u201C][^"\u201D]+["\u201D]|[^ ]+/g ) || [''];
4608			var a = parts.map( function ( word ) {
4609				var negative = false;
4610				var m;
4611	
4612				// Determine if it is a "does not include"
4613				if ( word.charAt(0) === '!' ) {
4614					negative = true;
4615					word = word.substring(1);
4616				}
4617	
4618				// Strip the quotes from around matched phrases
4619				if ( word.charAt(0) === '"' ) {
4620					m = word.match( /^"(.*)"$/ );
4621					word = m ? m[1] : word;
4622				}
4623				else if ( word.charAt(0) === '\u201C' ) {
4624					// Smart quote match (iPhone users)
4625					m = word.match( /^\u201C(.*)\u201D$/ );
4626					word = m ? m[1] : word;
4627				}
4628	
4629				// For our "not" case, we need to modify the string that is
4630				// allowed to match at the end of the expression.
4631				if (negative) {
4632					if (word.length > 1) {
4633						not.push('(?!'+word+')');
4634					}
4635	
4636					word = '';
4637				}
4638	
4639				return word.replace(/"/g, '');
4640			} );
4641	
4642			var match = not.length
4643				? not.join('')
4644				: '';
4645	
4646			var boundary = options.boundary
4647				? '\\b'
4648				: '';
4649	
4650			search = '^(?=.*?'+boundary+a.join( ')(?=.*?'+boundary )+')('+match+'.)*$';
4651		}
4652	
4653		return new RegExp( search, options.caseInsensitive ? 'i' : '' );
4654	}
4655	
4656	
4657	/**
4658	 * Escape a string such that it can be used in a regular expression
4659	 *  @param {string} sVal string to escape
4660	 *  @returns {string} escaped string
4661	 *  @memberof DataTable#oApi
4662	 */
4663	var _fnEscapeRegex = DataTable.util.escapeRegex;
4664	
4665	var __filter_div = $('<div>')[0];
4666	var __filter_div_textContent = __filter_div.textContent !== undefined;
4667	
4668	// Update the filtering data for each row if needed (by invalidation or first run)
4669	function _fnFilterData ( settings )
4670	{
4671		var columns = settings.aoColumns;
4672		var data = settings.aoData;
4673		var column;
4674		var j, jen, filterData, cellData, row;
4675		var wasInvalidated = false;
4676	
4677		for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) {
4678			if (! data[rowIdx]) {
4679				continue;
4680			}
4681	
4682			row = data[rowIdx];
4683	
4684			if ( ! row._aFilterData ) {
4685				filterData = [];
4686	
4687				for ( j=0, jen=columns.length ; j<jen ; j++ ) {
4688					column = columns[j];
4689	
4690					if ( column.bSearchable ) {
4691						cellData = _fnGetCellData( settings, rowIdx, j, 'filter' );
4692	
4693						// Search in DataTables is string based
4694						if ( cellData === null ) {
4695							cellData = '';
4696						}
4697	
4698						if ( typeof cellData !== 'string' && cellData.toString ) {
4699							cellData = cellData.toString();
4700						}
4701					}
4702					else {
4703						cellData = '';
4704					}
4705	
4706					// If it looks like there is an HTML entity in the string,
4707					// attempt to decode it so sorting works as expected. Note that
4708					// we could use a single line of jQuery to do this, but the DOM
4709					// method used here is much faster https://jsperf.com/html-decode
4710					if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
4711						__filter_div.innerHTML = cellData;
4712						cellData = __filter_div_textContent ?
4713							__filter_div.textContent :
4714							__filter_div.innerText;
4715					}
4716	
4717					if ( cellData.replace ) {
4718						cellData = cellData.replace(/[\r\n\u2028]/g, '');
4719					}
4720	
4721					filterData.push( cellData );
4722				}
4723	
4724				row._aFilterData = filterData;
4725				row._sFilterRow = filterData.join('  ');
4726				wasInvalidated = true;
4727			}
4728		}
4729	
4730		return wasInvalidated;
4731	}
4732	
4733	
4734	/**
4735	 * Draw the table for the first time, adding all required features
4736	 *  @param {object} settings dataTables settings object
4737	 *  @memberof DataTable#oApi
4738	 */
4739	function _fnInitialise ( settings )
4740	{
4741		var i;
4742		var init = settings.oInit;
4743		var deferLoading = settings.deferLoading;
4744		var dataSrc = _fnDataSource( settings );
4745	
4746		// Ensure that the table data is fully initialised
4747		if ( ! settings.bInitialised ) {
4748			setTimeout( function(){ _fnInitialise( settings ); }, 200 );
4749			return;
4750		}
4751	
4752		// Build the header / footer for the table
4753		_fnBuildHead( settings, 'header' );
4754		_fnBuildHead( settings, 'footer' );
4755	
4756		// Load the table's state (if needed) and then render around it and draw
4757		_fnLoadState( settings, init, function () {
4758			// Then draw the header / footer
4759			_fnDrawHead( settings, settings.aoHeader );
4760			_fnDrawHead( settings, settings.aoFooter );
4761	
4762			// Cache the paging start point, as the first redraw will reset it
4763			var iAjaxStart = settings.iInitDisplayStart
4764	
4765			// Local data load
4766			// Check if there is data passing into the constructor
4767			if ( init.aaData ) {
4768				for ( i=0 ; i<init.aaData.length ; i++ ) {
4769					_fnAddData( settings, init.aaData[ i ] );
4770				}
4771			}
4772			else if ( deferLoading || dataSrc == 'dom' ) {
4773				// Grab the data from the page
4774				_fnAddTr( settings, $(settings.nTBody).children('tr') );
4775			}
4776	
4777			// Filter not yet applied - copy the display master
4778			settings.aiDisplay = settings.aiDisplayMaster.slice();
4779	
4780			// Enable features
4781			_fnAddOptionsHtml( settings );
4782			_fnSortInit( settings );
4783	
4784			_colGroup( settings );
4785	
4786			/* Okay to show that something is going on now */
4787			_fnProcessingDisplay( settings, true );
4788	
4789			_fnCallbackFire( settings, null, 'preInit', [settings], true );
4790	
4791			// If there is default sorting required - let's do it. The sort function
4792			// will do the drawing for us. Otherwise we draw the table regardless of the
4793			// Ajax source - this allows the table to look initialised for Ajax sourcing
4794			// data (show 'loading' message possibly)
4795			_fnReDraw( settings );
4796	
4797			// Server-side processing init complete is done by _fnAjaxUpdateDraw
4798			if ( dataSrc != 'ssp' || deferLoading ) {
4799				// if there is an ajax source load the data
4800				if ( dataSrc == 'ajax' ) {
4801					_fnBuildAjax( settings, {}, function(json) {
4802						var aData = _fnAjaxDataSrc( settings, json );
4803	
4804						// Got the data - add it to the table
4805						for ( i=0 ; i<aData.length ; i++ ) {
4806							_fnAddData( settings, aData[i] );
4807						}
4808	
4809						// Reset the init display for cookie saving. We've already done
4810						// a filter, and therefore cleared it before. So we need to make
4811						// it appear 'fresh'
4812						settings.iInitDisplayStart = iAjaxStart;
4813	
4814						_fnReDraw( settings );
4815						_fnProcessingDisplay( settings, false );
4816						_fnInitComplete( settings );
4817					}, settings );
4818				}
4819				else {
4820					_fnInitComplete( settings );
4821					_fnProcessingDisplay( settings, false );
4822				}
4823			}
4824		} );
4825	}
4826	
4827	
4828	/**
4829	 * Draw the table for the first time, adding all required features
4830	 *  @param {object} settings dataTables settings object
4831	 *  @memberof DataTable#oApi
4832	 */
4833	function _fnInitComplete ( settings )
4834	{
4835		if (settings._bInitComplete) {
4836			return;
4837		}
4838	
4839		var args = [settings, settings.json];
4840	
4841		settings._bInitComplete = true;
4842	
4843		// Table is fully set up and we have data, so calculate the
4844		// column widths
4845		_fnAdjustColumnSizing( settings );
4846	
4847		_fnCallbackFire( settings, null, 'plugin-init', args, true );
4848		_fnCallbackFire( settings, 'aoInitComplete', 'init', args, true );
4849	}
4850	
4851	function _fnLengthChange ( settings, val )
4852	{
4853		var len = parseInt( val, 10 );
4854		settings._iDisplayLength = len;
4855	
4856		_fnLengthOverflow( settings );
4857	
4858		// Fire length change event
4859		_fnCallbackFire( settings, null, 'length', [settings, len] );
4860	}
4861	
4862	/**
4863	 * Alter the display settings to change the page
4864	 *  @param {object} settings DataTables settings object
4865	 *  @param {string|int} action Paging action to take: "first", "previous",
4866	 *    "next" or "last" or page number to jump to (integer)
4867	 *  @param [bool] redraw Automatically draw the update or not
4868	 *  @returns {bool} true page has changed, false - no change
4869	 *  @memberof DataTable#oApi
4870	 */
4871	function _fnPageChange ( settings, action, redraw )
4872	{
4873		var
4874			start     = settings._iDisplayStart,
4875			len       = settings._iDisplayLength,
4876			records   = settings.fnRecordsDisplay();
4877	
4878		if ( records === 0 || len === -1 )
4879		{
4880			start = 0;
4881		}
4882		else if ( typeof action === "number" )
4883		{
4884			start = action * len;
4885	
4886			if ( start > records )
4887			{
4888				start = 0;
4889			}
4890		}
4891		else if ( action == "first" )
4892		{
4893			start = 0;
4894		}
4895		else if ( action == "previous" )
4896		{
4897			start = len >= 0 ?
4898				start - len :
4899				0;
4900	
4901			if ( start < 0 )
4902			{
4903				start = 0;
4904			}
4905		}
4906		else if ( action == "next" )
4907		{
4908			if ( start + len < records )
4909			{
4910				start += len;
4911			}
4912		}
4913		else if ( action == "last" )
4914		{
4915			start = Math.floor( (records-1) / len) * len;
4916		}
4917		else if ( action === 'ellipsis' )
4918		{
4919			return;
4920		}
4921		else
4922		{
4923			_fnLog( settings, 0, "Unknown paging action: "+action, 5 );
4924		}
4925	
4926		var changed = settings._iDisplayStart !== start;
4927		settings._iDisplayStart = start;
4928	
4929		_fnCallbackFire( settings, null, changed ? 'page' : 'page-nc', [settings] );
4930	
4931		if ( changed && redraw ) {
4932			_fnDraw( settings );
4933		}
4934	
4935		return changed;
4936	}
4937	
4938	
4939	/**
4940	 * Generate the node required for the processing node
4941	 *  @param {object} settings DataTables settings object
4942	 */
4943	function _processingHtml ( settings )
4944	{
4945		var table = settings.nTable;
4946		var scrolling = settings.oScroll.sX !== '' || settings.oScroll.sY !== '';
4947	
4948		if ( settings.oFeatures.bProcessing ) {
4949			var n = $('<div/>', {
4950					'id': settings.sTableId + '_processing',
4951					'class': settings.oClasses.processing.container,
4952					'role': 'status'
4953				} )
4954				.html( settings.oLanguage.sProcessing )
4955				.append('<div><div></div><div></div><div></div><div></div></div>');
4956	
4957			// Different positioning depending on if scrolling is enabled or not
4958			if (scrolling) {
4959				n.prependTo( $('div.dt-scroll', settings.nTableWrapper) );
4960			}
4961			else {
4962				n.insertBefore( table );
4963			}
4964	
4965			$(table).on( 'processing.dt.DT', function (e, s, show) {
4966				n.css( 'display', show ? 'block' : 'none' );
4967			} );
4968		}
4969	}
4970	
4971	
4972	/**
4973	 * Display or hide the processing indicator
4974	 *  @param {object} settings DataTables settings object
4975	 *  @param {bool} show Show the processing indicator (true) or not (false)
4976	 */
4977	function _fnProcessingDisplay ( settings, show )
4978	{
4979		// Ignore cases when we are still redrawing
4980		if (settings.bDrawing && show === false) {
4981			return;
4982		}
4983	
4984		_fnCallbackFire( settings, null, 'processing', [settings, show] );
4985	}
4986	
4987	/**
4988	 * Show the processing element if an action takes longer than a given time
4989	 *
4990	 * @param {*} settings DataTables settings object
4991	 * @param {*} enable Do (true) or not (false) async processing (local feature enablement)
4992	 * @param {*} run Function to run
4993	 */
4994	function _fnProcessingRun( settings, enable, run ) {
4995		if (! enable) {
4996			// Immediate execution, synchronous
4997			run();
4998		}
4999		else {
5000			_fnProcessingDisplay(settings, true);
5001			
5002			// Allow the processing display to show if needed
5003			setTimeout(function () {
5004				run();
5005	
5006				_fnProcessingDisplay(settings, false);
5007			}, 0);
5008		}
5009	}
5010	/**
5011	 * Add any control elements for the table - specifically scrolling
5012	 *  @param {object} settings dataTables settings object
5013	 *  @returns {node} Node to add to the DOM
5014	 *  @memberof DataTable#oApi
5015	 */
5016	function _fnFeatureHtmlTable ( settings )
5017	{
5018		var table = $(settings.nTable);
5019	
5020		// Scrolling from here on in
5021		var scroll = settings.oScroll;
5022	
5023		if ( scroll.sX === '' && scroll.sY === '' ) {
5024			return settings.nTable;
5025		}
5026	
5027		var scrollX = scroll.sX;
5028		var scrollY = scroll.sY;
5029		var classes = settings.oClasses.scrolling;
5030		var caption = settings.captionNode;
5031		var captionSide = caption ? caption._captionSide : null;
5032		var headerClone = $( table[0].cloneNode(false) );
5033		var footerClone = $( table[0].cloneNode(false) );
5034		var footer = table.children('tfoot');
5035		var _div = '<div/>';
5036		var size = function ( s ) {
5037			return !s ? null : _fnStringToCss( s );
5038		};
5039	
5040		if ( ! footer.length ) {
5041			footer = null;
5042		}
5043	
5044		/*
5045		 * The HTML structure that we want to generate in this function is:
5046		 *  div - scroller
5047		 *    div - scroll head
5048		 *      div - scroll head inner
5049		 *        table - scroll head table
5050		 *          thead - thead
5051		 *    div - scroll body
5052		 *      table - table (master table)
5053		 *        thead - thead clone for sizing
5054		 *        tbody - tbody
5055		 *    div - scroll foot
5056		 *      div - scroll foot inner
5057		 *        table - scroll foot table
5058		 *          tfoot - tfoot
5059		 */
5060		var scroller = $( _div, { 'class': classes.container } )
5061			.append(
5062				$(_div, { 'class': classes.header.self } )
5063					.css( {
5064						overflow: 'hidden',
5065						position: 'relative',
5066						border: 0,
5067						width: scrollX ? size(scrollX) : '100%'
5068					} )
5069					.append(
5070						$(_div, { 'class': classes.header.inner } )
5071							.css( {
5072								'box-sizing': 'content-box',
5073								width: scroll.sXInner || '100%'
5074							} )
5075							.append(
5076								headerClone
5077									.removeAttr('id')
5078									.css( 'margin-left', 0 )
5079									.append( captionSide === 'top' ? caption : null )
5080									.append(
5081										table.children('thead')
5082									)
5083							)
5084					)
5085			)
5086			.append(
5087				$(_div, { 'class': classes.body } )
5088					.css( {
5089						position: 'relative',
5090						overflow: 'auto',
5091						width: size( scrollX )
5092					} )
5093					.append( table )
5094			);
5095	
5096		if ( footer ) {
5097			scroller.append(
5098				$(_div, { 'class': classes.footer.self } )
5099					.css( {
5100						overflow: 'hidden',
5101						border: 0,
5102						width: scrollX ? size(scrollX) : '100%'
5103					} )
5104					.append(
5105						$(_div, { 'class': classes.footer.inner } )
5106							.append(
5107								footerClone
5108									.removeAttr('id')
5109									.css( 'margin-left', 0 )
5110									.append( captionSide === 'bottom' ? caption : null )
5111									.append(
5112										table.children('tfoot')
5113									)
5114							)
5115					)
5116			);
5117		}
5118	
5119		var children = scroller.children();
5120		var scrollHead = children[0];
5121		var scrollBody = children[1];
5122		var scrollFoot = footer ? children[2] : null;
5123	
5124		// When the body is scrolled, then we also want to scroll the headers
5125		$(scrollBody).on( 'scroll.DT', function () {
5126			var scrollLeft = this.scrollLeft;
5127	
5128			scrollHead.scrollLeft = scrollLeft;
5129	
5130			if ( footer ) {
5131				scrollFoot.scrollLeft = scrollLeft;
5132			}
5133		} );
5134	
5135		// When focus is put on the header cells, we might need to scroll the body
5136		$('th, td', scrollHead).on('focus', function () {
5137			var scrollLeft = scrollHead.scrollLeft;
5138	
5139			scrollBody.scrollLeft = scrollLeft;
5140	
5141			if ( footer ) {
5142				scrollBody.scrollLeft = scrollLeft;
5143			}
5144		});
5145	
5146		$(scrollBody).css('max-height', scrollY);
5147		if (! scroll.bCollapse) {
5148			$(scrollBody).css('height', scrollY);
5149		}
5150	
5151		settings.nScrollHead = scrollHead;
5152		settings.nScrollBody = scrollBody;
5153		settings.nScrollFoot = scrollFoot;
5154	
5155		// On redraw - align columns
5156		settings.aoDrawCallback.push(_fnScrollDraw);
5157	
5158		return scroller[0];
5159	}
5160	
5161	
5162	
5163	/**
5164	 * Update the header, footer and body tables for resizing - i.e. column
5165	 * alignment.
5166	 *
5167	 * Welcome to the most horrible function DataTables. The process that this
5168	 * function follows is basically:
5169	 *   1. Re-create the table inside the scrolling div
5170	 *   2. Correct colgroup > col values if needed
5171	 *   3. Copy colgroup > col over to header and footer
5172	 *   4. Clean up
5173	 *
5174	 *  @param {object} settings dataTables settings object
5175	 *  @memberof DataTable#oApi
5176	 */
5177	function _fnScrollDraw ( settings )
5178	{
5179		// Given that this is such a monster function, a lot of variables are use
5180		// to try and keep the minimised size as small as possible
5181		var
5182			scroll         = settings.oScroll,
5183			barWidth       = scroll.iBarWidth,
5184			divHeader      = $(settings.nScrollHead),
5185			divHeaderInner = divHeader.children('div'),
5186			divHeaderTable = divHeaderInner.children('table'),
5187			divBodyEl      = settings.nScrollBody,
5188			divBody        = $(divBodyEl),
5189			divFooter      = $(settings.nScrollFoot),
5190			divFooterInner = divFooter.children('div'),
5191			divFooterTable = divFooterInner.children('table'),
5192			header         = $(settings.nTHead),
5193			table          = $(settings.nTable),
5194			footer         = settings.nTFoot && $('th, td', settings.nTFoot).length ? $(settings.nTFoot) : null,
5195			browser        = settings.oBrowser,
5196			headerCopy, footerCopy;
5197	
5198		// If the scrollbar visibility has changed from the last draw, we need to
5199		// adjust the column sizes as the table width will have changed to account
5200		// for the scrollbar
5201		var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
5202		
5203		if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
5204			settings.scrollBarVis = scrollBarVis;
5205			_fnAdjustColumnSizing( settings );
5206			return; // adjust column sizing will call this function again
5207		}
5208		else {
5209			settings.scrollBarVis = scrollBarVis;
5210		}
5211	
5212		// 1. Re-create the table inside the scrolling div
5213		// Remove the old minimised thead and tfoot elements in the inner table
5214		table.children('thead, tfoot').remove();
5215	
5216		// Clone the current header and footer elements and then place it into the inner table
5217		headerCopy = header.clone().prependTo( table );
5218		headerCopy.find('th, td').removeAttr('tabindex');
5219		headerCopy.find('[id]').removeAttr('id');
5220	
5221		if ( footer ) {
5222			footerCopy = footer.clone().prependTo( table );
5223			footerCopy.find('[id]').removeAttr('id');
5224		}
5225	
5226		// 2. Correct colgroup > col values if needed
5227		// It is possible that the cell sizes are smaller than the content, so we need to
5228		// correct colgroup>col for such cases. This can happen if the auto width detection
5229		// uses a cell which has a longer string, but isn't the widest! For example 
5230		// "Chief Executive Officer (CEO)" is the longest string in the demo, but
5231		// "Systems Administrator" is actually the widest string since it doesn't collapse.
5232		// Note the use of translating into a column index to get the `col` element. This
5233		// is because of Responsive which might remove `col` elements, knocking the alignment
5234		// of the indexes out.
5235		if (settings.aiDisplay.length) {
5236			// Get the column sizes from the first row in the table. This should really be a
5237			// [].find, but it wasn't supported in Chrome until Sept 2015, and DT has 10 year
5238			// browser support
5239			var firstTr = null;
5240			var start = _fnDataSource( settings ) !== 'ssp'
5241				? settings._iDisplayStart
5242				: 0;
5243	
5244			for (i=start ; i<start + settings.aiDisplay.length ; i++) {
5245				var idx = settings.aiDisplay[i];
5246				var tr = settings.aoData[idx].nTr;
5247	
5248				if (tr) {
5249					firstTr = tr;
5250					break;
5251				}
5252			}
5253	
5254			if (firstTr) {
5255				var colSizes = $(firstTr).children('th, td').map(function (vis) {
5256					return {
5257						idx: _fnVisibleToColumnIndex(settings, vis),
5258						width: $(this).outerWidth()
5259					};
5260				});
5261	
5262				// Check against what the colgroup > col is set to and correct if needed
5263				for (var i=0 ; i<colSizes.length ; i++) {
5264					var colEl = settings.aoColumns[ colSizes[i].idx ].colEl[0];
5265					var colWidth = colEl.style.width.replace('px', '');
5266	
5267					if (colWidth !== colSizes[i].width) {
5268						colEl.style.width = colSizes[i].width + 'px';
5269	
5270						if (scroll.sX) {
5271							colEl.style.minWidth = colSizes[i].width + 'px';
5272						}
5273					}
5274				}
5275			}
5276		}
5277	
5278		// 3. Copy the colgroup over to the header and footer
5279		divHeaderTable
5280			.find('colgroup')
5281			.remove();
5282	
5283		divHeaderTable.append(settings.colgroup.clone());
5284	
5285		if ( footer ) {
5286			divFooterTable
5287				.find('colgroup')
5288				.remove();
5289	
5290			divFooterTable.append(settings.colgroup.clone());
5291		}
5292	
5293		// "Hide" the header and footer that we used for the sizing. We need to keep
5294		// the content of the cell so that the width applied to the header and body
5295		// both match, but we want to hide it completely.
5296		$('th, td', headerCopy).each(function () {
5297			$(this.childNodes).wrapAll('<div class="dt-scroll-sizing">');
5298		});
5299	
5300		if ( footer ) {
5301			$('th, td', footerCopy).each(function () {
5302				$(this.childNodes).wrapAll('<div class="dt-scroll-sizing">');
5303			});
5304		}
5305	
5306		// 4. Clean up
5307		// Figure out if there are scrollbar present - if so then we need a the header and footer to
5308		// provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
5309		var isScrolling = Math.floor(table.height()) > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
5310		var paddingSide = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
5311	
5312		// Set the width's of the header and footer tables
5313		var outerWidth = table.outerWidth();
5314	
5315		divHeaderTable.css('width', _fnStringToCss( outerWidth ));
5316		divHeaderInner
5317			.css('width', _fnStringToCss( outerWidth ))
5318			.css(paddingSide, isScrolling ? barWidth+"px" : "0px");
5319	
5320		if ( footer ) {
5321			divFooterTable.css('width', _fnStringToCss( outerWidth ));
5322			divFooterInner
5323				.css('width', _fnStringToCss( outerWidth ))
5324				.css(paddingSide, isScrolling ? barWidth+"px" : "0px");
5325		}
5326	
5327		// Correct DOM ordering for colgroup - comes before the thead
5328		table.children('colgroup').prependTo(table);
5329	
5330		// Adjust the position of the header in case we loose the y-scrollbar
5331		divBody.trigger('scroll');
5332	
5333		// If sorting or filtering has occurred, jump the scrolling back to the top
5334		// only if we aren't holding the position
5335		if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
5336			divBodyEl.scrollTop = 0;
5337		}
5338	}
5339	
5340	/**
5341	 * Calculate the width of columns for the table
5342	 *  @param {object} settings dataTables settings object
5343	 *  @memberof DataTable#oApi
5344	 */
5345	function _fnCalculateColumnWidths ( settings )
5346	{
5347		// Not interested in doing column width calculation if auto-width is disabled
5348		if (! settings.oFeatures.bAutoWidth) {
5349			return;
5350		}
5351	
5352		var
5353			table = settings.nTable,
5354			columns = settings.aoColumns,
5355			scroll = settings.oScroll,
5356			scrollY = scroll.sY,
5357			scrollX = scroll.sX,
5358			scrollXInner = scroll.sXInner,
5359			visibleColumns = _fnGetColumns( settings, 'bVisible' ),
5360			tableWidthAttr = table.getAttribute('width'), // from DOM element
5361			tableContainer = table.parentNode,
5362			i, column, columnIdx;
5363			
5364		var styleWidth = table.style.width;
5365		var containerWidth = _fnWrapperWidth(settings);
5366	
5367		// Don't re-run for the same width as the last time
5368		if (containerWidth === settings.containerWidth) {
5369			return false;
5370		}
5371	
5372		settings.containerWidth = containerWidth;
5373	
5374		// If there is no width applied as a CSS style or as an attribute, we assume that
5375		// the width is intended to be 100%, which is usually is in CSS, but it is very
5376		// difficult to correctly parse the rules to get the final result.
5377		if ( ! styleWidth && ! tableWidthAttr) {
5378			table.style.width = '100%';
5379			styleWidth = '100%';
5380		}
5381	
5382		if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
5383			tableWidthAttr = styleWidth;
5384		}
5385	
5386		// Let plug-ins know that we are doing a recalc, in case they have changed any of the
5387		// visible columns their own way (e.g. Responsive uses display:none).
5388		_fnCallbackFire(
5389			settings,
5390			null,
5391			'column-calc',
5392			{visible: visibleColumns},
5393			false
5394		);
5395	
5396		// Construct a single row, worst case, table with the widest
5397		// node in the data, assign any user defined widths, then insert it into
5398		// the DOM and allow the browser to do all the hard work of calculating
5399		// table widths
5400		var tmpTable = $(table.cloneNode())
5401			.css( 'visibility', 'hidden' )
5402			.removeAttr( 'id' );
5403	
5404		// Clean up the table body
5405		tmpTable.append('<tbody>')
5406		var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
5407	
5408		// Clone the table header and footer - we can't use the header / footer
5409		// from the cloned table, since if scrolling is active, the table's
5410		// real header and footer are contained in different table tags
5411		tmpTable
5412			.append( $(settings.nTHead).clone() )
5413			.append( $(settings.nTFoot).clone() );
5414	
5415		// Remove any assigned widths from the footer (from scrolling)
5416		tmpTable.find('tfoot th, tfoot td').css('width', '');
5417	
5418		// Apply custom sizing to the cloned header
5419		tmpTable.find('thead th, thead td').each( function () {
5420			// Get the `width` from the header layout
5421			var width = _fnColumnsSumWidth( settings, this, true, false );
5422	
5423			if ( width ) {
5424				this.style.width = width;
5425	
5426				// For scrollX we need to force the column width otherwise the
5427				// browser will collapse it. If this width is smaller than the
5428				// width the column requires, then it will have no effect
5429				if ( scrollX ) {
5430					this.style.minWidth = width;
5431	
5432					$( this ).append( $('<div/>').css( {
5433						width: width,
5434						margin: 0,
5435						padding: 0,
5436						border: 0,
5437						height: 1
5438					} ) );
5439				}
5440			}
5441			else {
5442				this.style.width = '';
5443			}
5444		} );
5445	
5446		// Find the widest piece of data for each column and put it into the table
5447		for ( i=0 ; i<visibleColumns.length ; i++ ) {
5448			columnIdx = visibleColumns[i];
5449			column = columns[ columnIdx ];
5450	
5451			var longest = _fnGetMaxLenString(settings, columnIdx);
5452			var autoClass = _ext.type.className[column.sType];
5453			var text = longest + column.sContentPadding;
5454			var insert = longest.indexOf('<') === -1
5455				? document.createTextNode(text)
5456				: text
5457			
5458			$('<td/>')
5459				.addClass(autoClass)
5460				.addClass(column.sClass)
5461				.append(insert)
5462				.appendTo(tr);
5463		}
5464	
5465		// Tidy the temporary table - remove name attributes so there aren't
5466		// duplicated in the dom (radio elements for example)
5467		$('[name]', tmpTable).removeAttr('name');
5468	
5469		// Table has been built, attach to the document so we can work with it.
5470		// A holding element is used, positioned at the top of the container
5471		// with minimal height, so it has no effect on if the container scrolls
5472		// or not. Otherwise it might trigger scrolling when it actually isn't
5473		// needed
5474		var holder = $('<div/>').css( scrollX || scrollY ?
5475				{
5476					position: 'absolute',
5477					top: 0,
5478					left: 0,
5479					height: 1,
5480					right: 0,
5481					overflow: 'hidden'
5482				} :
5483				{}
5484			)
5485			.append( tmpTable )
5486			.appendTo( tableContainer );
5487	
5488		// When scrolling (X or Y) we want to set the width of the table as 
5489		// appropriate. However, when not scrolling leave the table width as it
5490		// is. This results in slightly different, but I think correct behaviour
5491		if ( scrollX && scrollXInner ) {
5492			tmpTable.width( scrollXInner );
5493		}
5494		else if ( scrollX ) {
5495			tmpTable.css( 'width', 'auto' );
5496			tmpTable.removeAttr('width');
5497	
5498			// If there is no width attribute or style, then allow the table to
5499			// collapse
5500			if ( tmpTable.outerWidth() < tableContainer.clientWidth && tableWidthAttr ) {
5501				tmpTable.outerWidth( tableContainer.clientWidth );
5502			}
5503		}
5504		else if ( scrollY ) {
5505			tmpTable.outerWidth( tableContainer.clientWidth );
5506		}
5507		else if ( tableWidthAttr ) {
5508			tmpTable.outerWidth( tableWidthAttr );
5509		}
5510	
5511		// Get the width of each column in the constructed table
5512		var total = 0;
5513		var bodyCells = tmpTable.find('tbody tr').eq(0).children();
5514	
5515		for ( i=0 ; i<visibleColumns.length ; i++ ) {
5516			// Use getBounding for sub-pixel accuracy, which we then want to round up!
5517			var bounding = bodyCells[i].getBoundingClientRect().width;
5518	
5519			// Total is tracked to remove any sub-pixel errors as the outerWidth
5520			// of the table might not equal the total given here
5521			total += bounding;
5522	
5523			// Width for each column to use
5524			columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding );
5525		}
5526	
5527		table.style.width = _fnStringToCss( total );
5528	
5529		// Finished with the table - ditch it
5530		holder.remove();
5531	
5532		// If there is a width attr, we want to attach an event listener which
5533		// allows the table sizing to automatically adjust when the window is
5534		// resized. Use the width attr rather than CSS, since we can't know if the
5535		// CSS is a relative value or absolute - DOM read is always px.
5536		if ( tableWidthAttr ) {
5537			table.style.width = _fnStringToCss( tableWidthAttr );
5538		}
5539	
5540		if ( (tableWidthAttr || scrollX) && ! settings._reszEvt ) {
5541			var resize = DataTable.util.throttle( function () {
5542				var newWidth = _fnWrapperWidth(settings);
5543	
5544				// Don't do it if destroying or the container width is 0
5545				if (! settings.bDestroying && newWidth !== 0) {
5546					_fnAdjustColumnSizing( settings );
5547				}
5548			} );
5549	
5550			// For browsers that support it (~2020 onwards for wide support) we can watch for the
5551			// container changing width.
5552			if (window.ResizeObserver) {
5553				// This is a tricky beast - if the element is visible when `.observe()` is called,
5554				// then the callback is immediately run. Which we don't want. If the element isn't
5555				// visible, then it isn't run, but we want it to run when it is then made visible.
5556				// This flag allows the above to be satisfied.
5557				var first = $(settings.nTableWrapper).is(':visible');
5558	
5559				// Use an empty div to attach the observer so it isn't impacted by height changes
5560				var resizer = $('<div>')
5561					.css({
5562						width: '100%',
5563						height: 0
5564					})
5565					.addClass('dt-autosize')
5566					.appendTo(settings.nTableWrapper);
5567	
5568				settings.resizeObserver = new ResizeObserver(function (e) {
5569					if (first) {
5570						first = false;
5571					}
5572					else {
5573						resize();
5574					}
5575				});
5576	
5577				settings.resizeObserver.observe(resizer[0]);
5578			}
5579			else {
5580				// For old browsers, the best we can do is listen for a window resize
5581				$(window).on('resize.DT-'+settings.sInstance, resize);
5582			}
5583	
5584			settings._reszEvt = true;
5585		}
5586	}
5587	
5588	/**
5589	 * Get the width of the DataTables wrapper element
5590	 *
5591	 * @param {*} settings DataTables settings object
5592	 * @returns Width
5593	 */
5594	function _fnWrapperWidth(settings) {
5595		return $(settings.nTableWrapper).is(':visible')
5596			? $(settings.nTableWrapper).width()
5597			: 0;
5598	}
5599	
5600	/**
5601	 * Get the maximum strlen for each data column
5602	 *  @param {object} settings dataTables settings object
5603	 *  @param {int} colIdx column of interest
5604	 *  @returns {string} string of the max length
5605	 *  @memberof DataTable#oApi
5606	 */
5607	function _fnGetMaxLenString( settings, colIdx )
5608	{
5609		var column = settings.aoColumns[colIdx];
5610	
5611		if (! column.maxLenString) {
5612			var s, max='', maxLen = -1;
5613		
5614			for ( var i=0, ien=settings.aiDisplayMaster.length ; i<ien ; i++ ) {
5615				var rowIdx = settings.aiDisplayMaster[i];
5616				var data = _fnGetRowDisplay(settings, rowIdx)[colIdx];
5617	
5618				var cellString = data && typeof data === 'object' && data.nodeType
5619					? data.innerHTML
5620					: data+'';
5621	
5622				// Remove id / name attributes from elements so they
5623				// don't interfere with existing elements
5624				cellString = cellString
5625					.replace(/id=".*?"/g, '')
5626					.replace(/name=".*?"/g, '');
5627	
5628				s = _stripHtml(cellString)
5629					.replace( /&nbsp;/g, ' ' );
5630		
5631				if ( s.length > maxLen ) {
5632					// We want the HTML in the string, but the length that
5633					// is important is the stripped string
5634					max = cellString;
5635					maxLen = s.length;
5636				}
5637			}
5638	
5639			column.maxLenString = max;
5640		}
5641	
5642		return column.maxLenString;
5643	}
5644	
5645	
5646	/**
5647	 * Append a CSS unit (only if required) to a string
5648	 *  @param {string} value to css-ify
5649	 *  @returns {string} value with css unit
5650	 *  @memberof DataTable#oApi
5651	 */
5652	function _fnStringToCss( s )
5653	{
5654		if ( s === null ) {
5655			return '0px';
5656		}
5657	
5658		if ( typeof s == 'number' ) {
5659			return s < 0 ?
5660				'0px' :
5661				s+'px';
5662		}
5663	
5664		// Check it has a unit character already
5665		return s.match(/\d$/) ?
5666			s+'px' :
5667			s;
5668	}
5669	
5670	/**
5671	 * Re-insert the `col` elements for current visibility
5672	 *
5673	 * @param {*} settings DT settings
5674	 */
5675	function _colGroup( settings ) {
5676		var cols = settings.aoColumns;
5677	
5678		settings.colgroup.empty();
5679	
5680		for (i=0 ; i<cols.length ; i++) {
5681			if (cols[i].bVisible) {
5682				settings.colgroup.append(cols[i].colEl);
5683			}
5684		}
5685	}
5686	
5687	
5688	function _fnSortInit( settings ) {
5689		var target = settings.nTHead;
5690		var headerRows = target.querySelectorAll('tr');
5691		var legacyTop = settings.bSortCellsTop;
5692		var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])';
5693		
5694		// Legacy support for `orderCellsTop`
5695		if (legacyTop === true) {
5696			target = headerRows[0];
5697		}
5698		else if (legacyTop === false) {
5699			target = headerRows[ headerRows.length - 1 ];
5700		}
5701	
5702		_fnSortAttachListener(
5703			settings,
5704			target,
5705			target === settings.nTHead
5706				? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector
5707				: 'th'+notSelector+', td'+notSelector
5708		);
5709	
5710		// Need to resolve the user input array into our internal structure
5711		var order = [];
5712		_fnSortResolve( settings, order, settings.aaSorting );
5713	
5714		settings.aaSorting = order;
5715	}
5716	
5717	
5718	function _fnSortAttachListener(settings, node, selector, column, callback) {
5719		_fnBindAction( node, selector, function (e) {
5720			var run = false;
5721			var columns = column === undefined
5722				? _fnColumnsFromHeader( e.target )
5723				: [column];
5724	
5725			if ( columns.length ) {
5726				for ( var i=0, ien=columns.length ; i<ien ; i++ ) {
5727					var ret = _fnSortAdd( settings, columns[i], i, e.shiftKey );
5728	
5729					if (ret !== false) {
5730						run = true;
5731					}					
5732	
5733					// If the first entry is no sort, then subsequent
5734					// sort columns are ignored
5735					if (settings.aaSorting.length === 1 && settings.aaSorting[0][1] === '') {
5736						break;
5737					}
5738				}
5739	
5740				if (run) {
5741					_fnProcessingRun(settings, true, function () {
5742						_fnSort( settings );
5743						_fnSortDisplay( settings, settings.aiDisplay );
5744	
5745						_fnReDraw( settings, false, false );
5746	
5747						if (callback) {
5748							callback();
5749						}
5750					});
5751				}
5752			}
5753		} );
5754	}
5755	
5756	/**
5757	 * Sort the display array to match the master's order
5758	 * @param {*} settings
5759	 */
5760	function _fnSortDisplay(settings, display) {
5761		if (display.length < 2) {
5762			return;
5763		}
5764	
5765		var master = settings.aiDisplayMaster;
5766		var masterMap = {};
5767		var map = {};
5768		var i;
5769	
5770		// Rather than needing an `indexOf` on master array, we can create a map
5771		for (i=0 ; i<master.length ; i++) {
5772			masterMap[master[i]] = i;
5773		}
5774	
5775		// And then cache what would be the indexOf fom the display
5776		for (i=0 ; i<display.length ; i++) {
5777			map[display[i]] = masterMap[display[i]];
5778		}
5779	
5780		display.sort(function(a, b){
5781			// Short version of this function is simply `master.indexOf(a) - master.indexOf(b);`
5782			return map[a] - map[b];
5783		});
5784	}
5785	
5786	
5787	function _fnSortResolve (settings, nestedSort, sort) {
5788		var push = function ( a ) {
5789			if ($.isPlainObject(a)) {
5790				if (a.idx !== undefined) {
5791					// Index based ordering
5792					nestedSort.push([a.idx, a.dir]);
5793				}
5794				else if (a.name) {
5795					// Name based ordering
5796					var cols = _pluck( settings.aoColumns, 'sName');
5797					var idx = cols.indexOf(a.name);
5798	
5799					if (idx !== -1) {
5800						nestedSort.push([idx, a.dir]);
5801					}
5802				}
5803			}
5804			else {
5805				// Plain column index and direction pair
5806				nestedSort.push(a);
5807			}
5808		};
5809	
5810		if ( $.isPlainObject(sort) ) {
5811			// Object
5812			push(sort);
5813		}
5814		else if ( sort.length && typeof sort[0] === 'number' ) {
5815			// 1D array
5816			push(sort);
5817		}
5818		else if ( sort.length ) {
5819			// 2D array
5820			for (var z=0; z<sort.length; z++) {
5821				push(sort[z]); // Object or array
5822			}
5823		}
5824	}
5825	
5826	
5827	function _fnSortFlatten ( settings )
5828	{
5829		var
5830			i, k, kLen,
5831			aSort = [],
5832			extSort = DataTable.ext.type.order,
5833			aoColumns = settings.aoColumns,
5834			aDataSort, iCol, sType, srcCol,
5835			fixed = settings.aaSortingFixed,
5836			fixedObj = $.isPlainObject( fixed ),
5837			nestedSort = [];
5838		
5839		if ( ! settings.oFeatures.bSort ) {
5840			return aSort;
5841		}
5842	
5843		// Build the sort array, with pre-fix and post-fix options if they have been
5844		// specified
5845		if ( Array.isArray( fixed ) ) {
5846			_fnSortResolve( settings, nestedSort, fixed );
5847		}
5848	
5849		if ( fixedObj && fixed.pre ) {
5850			_fnSortResolve( settings, nestedSort, fixed.pre );
5851		}
5852	
5853		_fnSortResolve( settings, nestedSort, settings.aaSorting );
5854	
5855		if (fixedObj && fixed.post ) {
5856			_fnSortResolve( settings, nestedSort, fixed.post );
5857		}
5858	
5859		for ( i=0 ; i<nestedSort.length ; i++ )
5860		{
5861			srcCol = nestedSort[i][0];
5862	
5863			if ( aoColumns[ srcCol ] ) {
5864				aDataSort = aoColumns[ srcCol ].aDataSort;
5865	
5866				for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
5867				{
5868					iCol = aDataSort[k];
5869					sType = aoColumns[ iCol ].sType || 'string';
5870	
5871					if ( nestedSort[i]._idx === undefined ) {
5872						nestedSort[i]._idx = aoColumns[iCol].asSorting.indexOf(nestedSort[i][1]);
5873					}
5874	
5875					if ( nestedSort[i][1] ) {
5876						aSort.push( {
5877							src:       srcCol,
5878							col:       iCol,
5879							dir:       nestedSort[i][1],
5880							index:     nestedSort[i]._idx,
5881							type:      sType,
5882							formatter: extSort[ sType+"-pre" ],
5883							sorter:    extSort[ sType+"-"+nestedSort[i][1] ]
5884						} );
5885					}
5886				}
5887			}
5888		}
5889	
5890		return aSort;
5891	}
5892	
5893	/**
5894	 * Change the order of the table
5895	 *  @param {object} oSettings dataTables settings object
5896	 *  @memberof DataTable#oApi
5897	 */
5898	function _fnSort ( oSettings, col, dir )
5899	{
5900		var
5901			i, ien, iLen,
5902			aiOrig = [],
5903			extSort = DataTable.ext.type.order,
5904			aoData = oSettings.aoData,
5905			sortCol,
5906			displayMaster = oSettings.aiDisplayMaster,
5907			aSort;
5908	
5909		// Make sure the columns all have types defined
5910		_fnColumnTypes(oSettings);
5911	
5912		// Allow a specific column to be sorted, which will _not_ alter the display
5913		// master
5914		if (col !== undefined) {
5915			var srcCol = oSettings.aoColumns[col];
5916	
5917			aSort = [{
5918				src:       col,
5919				col:       col,
5920				dir:       dir,
5921				index:     0,
5922				type:      srcCol.sType,
5923				formatter: extSort[ srcCol.sType+"-pre" ],
5924				sorter:    extSort[ srcCol.sType+"-"+dir ]
5925			}];
5926			displayMaster = displayMaster.slice();
5927		}
5928		else {
5929			aSort = _fnSortFlatten( oSettings );
5930		}
5931	
5932		for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
5933			sortCol = aSort[i];
5934	
5935			// Load the data needed for the sort, for each cell
5936			_fnSortData( oSettings, sortCol.col );
5937		}
5938	
5939		/* No sorting required if server-side or no sorting array */
5940		if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
5941		{
5942			// Reset the initial positions on each pass so we get a stable sort
5943			for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
5944				aiOrig[ i ] = i;
5945			}
5946	
5947			// If the first sort is desc, then reverse the array to preserve original
5948			// order, just in reverse
5949			if (aSort.length && aSort[0].dir === 'desc' && oSettings.orderDescReverse) {
5950				aiOrig.reverse();
5951			}
5952	
5953			/* Do the sort - here we want multi-column sorting based on a given data source (column)
5954			 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
5955			 * follow on it's own, but this is what we want (example two column sorting):
5956			 *  fnLocalSorting = function(a,b){
5957			 *    var test;
5958			 *    test = oSort['string-asc']('data11', 'data12');
5959			 *      if (test !== 0)
5960			 *        return test;
5961			 *    test = oSort['numeric-desc']('data21', 'data22');
5962			 *    if (test !== 0)
5963			 *      return test;
5964			 *    return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
5965			 *  }
5966			 * Basically we have a test for each sorting column, if the data in that column is equal,
5967			 * test the next column. If all columns match, then we use a numeric sort on the row
5968			 * positions in the original data array to provide a stable sort.
5969			 */
5970			displayMaster.sort( function ( a, b ) {
5971				var
5972					x, y, k, test, sort,
5973					len=aSort.length,
5974					dataA = aoData[a]._aSortData,
5975					dataB = aoData[b]._aSortData;
5976	
5977				for ( k=0 ; k<len ; k++ ) {
5978					sort = aSort[k];
5979	
5980					// Data, which may have already been through a `-pre` function
5981					x = dataA[ sort.col ];
5982					y = dataB[ sort.col ];
5983	
5984					if (sort.sorter) {
5985						// If there is a custom sorter (`-asc` or `-desc`) for this
5986						// data type, use it
5987						test = sort.sorter(x, y);
5988	
5989						if ( test !== 0 ) {
5990							return test;
5991						}
5992					}
5993					else {
5994						// Otherwise, use generic sorting
5995						test = x<y ? -1 : x>y ? 1 : 0;
5996	
5997						if ( test !== 0 ) {
5998							return sort.dir === 'asc' ? test : -test;
5999						}
6000					}
6001				}
6002	
6003				x = aiOrig[a];
6004				y = aiOrig[b];
6005	
6006				return x<y ? -1 : x>y ? 1 : 0;
6007			} );
6008		}
6009		else if ( aSort.length === 0 ) {
6010			// Apply index order
6011			displayMaster.sort(function (x, y) {
6012				return x<y ? -1 : x>y ? 1 : 0;
6013			});
6014		}
6015	
6016		if (col === undefined) {
6017			// Tell the draw function that we have sorted the data
6018			oSettings.bSorted = true;
6019			oSettings.sortDetails = aSort;
6020	
6021			_fnCallbackFire( oSettings, null, 'order', [oSettings, aSort] );
6022		}
6023	
6024		return displayMaster;
6025	}
6026	
6027	
6028	/**
6029	 * Function to run on user sort request
6030	 *  @param {object} settings dataTables settings object
6031	 *  @param {node} attachTo node to attach the handler to
6032	 *  @param {int} colIdx column sorting index
6033	 *  @param {int} addIndex Counter
6034	 *  @param {boolean} [shift=false] Shift click add
6035	 *  @param {function} [callback] callback function
6036	 *  @memberof DataTable#oApi
6037	 */
6038	function _fnSortAdd ( settings, colIdx, addIndex, shift )
6039	{
6040		var col = settings.aoColumns[ colIdx ];
6041		var sorting = settings.aaSorting;
6042		var asSorting = col.asSorting;
6043		var nextSortIdx;
6044		var next = function ( a, overflow ) {
6045			var idx = a._idx;
6046			if ( idx === undefined ) {
6047				idx = asSorting.indexOf(a[1]);
6048			}
6049	
6050			return idx+1 < asSorting.length ?
6051				idx+1 :
6052				overflow ?
6053					null :
6054					0;
6055		};
6056	
6057		if ( ! col.bSortable ) {
6058			return false;
6059		}
6060	
6061		// Convert to 2D array if needed
6062		if ( typeof sorting[0] === 'number' ) {
6063			sorting = settings.aaSorting = [ sorting ];
6064		}
6065	
6066		// If appending the sort then we are multi-column sorting
6067		if ( (shift || addIndex) && settings.oFeatures.bSortMulti ) {
6068			// Are we already doing some kind of sort on this column?
6069			var sortIdx = _pluck(sorting, '0').indexOf(colIdx);
6070	
6071			if ( sortIdx !== -1 ) {
6072				// Yes, modify the sort
6073				nextSortIdx = next( sorting[sortIdx], true );
6074	
6075				if ( nextSortIdx === null && sorting.length === 1 ) {
6076					nextSortIdx = 0; // can't remove sorting completely
6077				}
6078	
6079				if ( nextSortIdx === null ) {
6080					sorting.splice( sortIdx, 1 );
6081				}
6082				else {
6083					sorting[sortIdx][1] = asSorting[ nextSortIdx ];
6084					sorting[sortIdx]._idx = nextSortIdx;
6085				}
6086			}
6087			else if (shift) {
6088				// No sort on this column yet, being added by shift click
6089				// add it as itself
6090				sorting.push( [ colIdx, asSorting[0], 0 ] );
6091				sorting[sorting.length-1]._idx = 0;
6092			}
6093			else {
6094				// No sort on this column yet, being added from a colspan
6095				// so add with same direction as first column
6096				sorting.push( [ colIdx, sorting[0][1], 0 ] );
6097				sorting[sorting.length-1]._idx = 0;
6098			}
6099		}
6100		else if ( sorting.length && sorting[0][0] == colIdx ) {
6101			// Single column - already sorting on this column, modify the sort
6102			nextSortIdx = next( sorting[0] );
6103	
6104			sorting.length = 1;
6105			sorting[0][1] = asSorting[ nextSortIdx ];
6106			sorting[0]._idx = nextSortIdx;
6107		}
6108		else {
6109			// Single column - sort only on this column
6110			sorting.length = 0;
6111			sorting.push( [ colIdx, asSorting[0] ] );
6112			sorting[0]._idx = 0;
6113		}
6114	}
6115	
6116	
6117	/**
6118	 * Set the sorting classes on table's body, Note: it is safe to call this function
6119	 * when bSort and bSortClasses are false
6120	 *  @param {object} oSettings dataTables settings object
6121	 *  @memberof DataTable#oApi
6122	 */
6123	function _fnSortingClasses( settings )
6124	{
6125		var oldSort = settings.aLastSort;
6126		var sortClass = settings.oClasses.order.position;
6127		var sort = _fnSortFlatten( settings );
6128		var features = settings.oFeatures;
6129		var i, ien, colIdx;
6130	
6131		if ( features.bSort && features.bSortClasses ) {
6132			// Remove old sorting classes
6133			for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
6134				colIdx = oldSort[i].src;
6135	
6136				// Remove column sorting
6137				$( _pluck( settings.aoData, 'anCells', colIdx ) )
6138					.removeClass( sortClass + (i<2 ? i+1 : 3) );
6139			}
6140	
6141			// Add new column sorting
6142			for ( i=0, ien=sort.length ; i<ien ; i++ ) {
6143				colIdx = sort[i].src;
6144	
6145				$( _pluck( settings.aoData, 'anCells', colIdx ) )
6146					.addClass( sortClass + (i<2 ? i+1 : 3) );
6147			}
6148		}
6149	
6150		settings.aLastSort = sort;
6151	}
6152	
6153	
6154	// Get the data to sort a column, be it from cache, fresh (populating the
6155	// cache), or from a sort formatter
6156	function _fnSortData( settings, colIdx )
6157	{
6158		// Custom sorting function - provided by the sort data type
6159		var column = settings.aoColumns[ colIdx ];
6160		var customSort = DataTable.ext.order[ column.sSortDataType ];
6161		var customData;
6162	
6163		if ( customSort ) {
6164			customData = customSort.call( settings.oInstance, settings, colIdx,
6165				_fnColumnIndexToVisible( settings, colIdx )
6166			);
6167		}
6168	
6169		// Use / populate cache
6170		var row, cellData;
6171		var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
6172		var data = settings.aoData;
6173	
6174		for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) {
6175			// Sparse array
6176			if (! data[rowIdx]) {
6177				continue;
6178			}
6179	
6180			row = data[rowIdx];
6181	
6182			if ( ! row._aSortData ) {
6183				row._aSortData = [];
6184			}
6185	
6186			if ( ! row._aSortData[colIdx] || customSort ) {
6187				cellData = customSort ?
6188					customData[rowIdx] : // If there was a custom sort function, use data from there
6189					_fnGetCellData( settings, rowIdx, colIdx, 'sort' );
6190	
6191				row._aSortData[ colIdx ] = formatter ?
6192					formatter( cellData, settings ) :
6193					cellData;
6194			}
6195		}
6196	}
6197	
6198	
6199	/**
6200	 * State information for a table
6201	 *
6202	 * @param {*} settings
6203	 * @returns State object
6204	 */
6205	function _fnSaveState ( settings )
6206	{
6207		if (settings._bLoadingState) {
6208			return;
6209		}
6210	
6211		// Sort state saving uses [[idx, order]] structure.
6212		var sorting = [];
6213		_fnSortResolve(settings, sorting, settings.aaSorting );
6214	
6215		/* Store the interesting variables */
6216		var columns = settings.aoColumns;
6217		var state = {
6218			time:    +new Date(),
6219			start:   settings._iDisplayStart,
6220			length:  settings._iDisplayLength,
6221			order:   sorting.map(function (sort) {
6222				// If a column name is available, use it
6223				return columns[sort[0]] && columns[sort[0]].sName
6224					? [ columns[sort[0]].sName, sort[1] ]
6225					: sort.slice();
6226			} ),
6227			search:  $.extend({}, settings.oPreviousSearch),
6228			columns: settings.aoColumns.map( function ( col, i ) {
6229				return {
6230					name: col.sName,
6231					visible: col.bVisible,
6232					search: $.extend({}, settings.aoPreSearchCols[i])
6233				};
6234			} )
6235		};
6236	
6237		settings.oSavedState = state;
6238		_fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
6239		
6240		if ( settings.oFeatures.bStateSave && !settings.bDestroying )
6241		{
6242			settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
6243		}	
6244	}
6245	
6246	
6247	/**
6248	 * Attempt to load a saved table state
6249	 *  @param {object} oSettings dataTables settings object
6250	 *  @param {object} oInit DataTables init object so we can override settings
6251	 *  @param {function} callback Callback to execute when the state has been loaded
6252	 *  @memberof DataTable#oApi
6253	 */
6254	function _fnLoadState ( settings, init, callback )
6255	{
6256		if ( ! settings.oFeatures.bStateSave ) {
6257			callback();
6258			return;
6259		}
6260	
6261		var loaded = function(state) {
6262			_fnImplementState(settings, state, callback);
6263		}
6264	
6265		var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );
6266	
6267		if ( state !== undefined ) {
6268			_fnImplementState( settings, state, callback );
6269		}
6270		// otherwise, wait for the loaded callback to be executed
6271	
6272		return true;
6273	}
6274	
6275	function _fnImplementState ( settings, s, callback) {
6276		var i, ien;
6277		var columns = settings.aoColumns;
6278		var currentNames = _pluck(settings.aoColumns, 'sName');
6279	
6280		settings._bLoadingState = true;
6281	
6282		// When StateRestore was introduced the state could now be implemented at any time
6283		// Not just initialisation. To do this an api instance is required in some places
6284		var api = settings._bInitComplete ? new DataTable.Api(settings) : null;
6285	
6286		if ( ! s || ! s.time ) {
6287			settings._bLoadingState = false;
6288			callback();
6289			return;
6290		}
6291	
6292		// Reject old data
6293		var duration = settings.iStateDuration;
6294		if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
6295			settings._bLoadingState = false;
6296			callback();
6297			return;
6298		}
6299	
6300		// Allow custom and plug-in manipulation functions to alter the saved data set and
6301		// cancelling of loading by returning false
6302		var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] );
6303		if ( abStateLoad.indexOf(false) !== -1 ) {
6304			settings._bLoadingState = false;
6305			callback();
6306			return;
6307		}
6308	
6309		// Store the saved state so it might be accessed at any time
6310		settings.oLoadedState = $.extend( true, {}, s );
6311	
6312		// This is needed for ColReorder, which has to happen first to allow all
6313		// the stored indexes to be usable. It is not publicly documented.
6314		_fnCallbackFire( settings, null, 'stateLoadInit', [settings, s], true );
6315	
6316		// Page Length
6317		if ( s.length !== undefined ) {
6318			// If already initialised just set the value directly so that the select element is also updated
6319			if (api) {
6320				api.page.len(s.length)
6321			}
6322			else {
6323				settings._iDisplayLength   = s.length;
6324			}
6325		}
6326	
6327		// Restore key features
6328		if ( s.start !== undefined ) {
6329			if(api === null) {
6330				settings._iDisplayStart    = s.start;
6331				settings.iInitDisplayStart = s.start;
6332			}
6333			else {
6334				_fnPageChange(settings, s.start/settings._iDisplayLength);
6335			}
6336		}
6337	
6338		// Order
6339		if ( s.order !== undefined ) {
6340			settings.aaSorting = [];
6341			$.each( s.order, function ( i, col ) {
6342				var set = [ col[0], col[1] ];
6343	
6344				// A column name was stored and should be used for restore
6345				if (typeof col[0] === 'string') {
6346					var idx = currentNames.indexOf(col[0]);
6347	
6348					// Find the name from the current list of column names, or fallback to index 0
6349					set[0] = idx >= 0
6350						? idx
6351						: 0;
6352				}
6353				else if (set[0] >= columns.length) {
6354					// If a column name, but it is out of bounds, set to 0
6355					set[0] = 0;
6356				}
6357	
6358				settings.aaSorting.push(set);
6359			} );
6360		}
6361	
6362		// Search
6363		if ( s.search !== undefined ) {
6364			$.extend( settings.oPreviousSearch, s.search );
6365		}
6366	
6367		// Columns
6368		if ( s.columns ) {
6369			var set = s.columns;
6370			var incoming = _pluck(s.columns, 'name');
6371	
6372			// Check if it is a 2.2 style state object with a `name` property for the columns, and if
6373			// the name was defined. If so, then create a new array that will map the state object
6374			// given, to the current columns (don't bother if they are already matching tho).
6375			if (incoming.join('').length && incoming.join('') !== currentNames.join('')) {
6376				set = [];
6377	
6378				// For each column, try to find the name in the incoming array
6379				for (i=0 ; i<currentNames.length ; i++) {
6380					if (currentNames[i] != '') {
6381						var idx = incoming.indexOf(currentNames[i]);
6382	
6383						if (idx >= 0) {
6384							set.push(s.columns[idx]);
6385						}
6386						else {
6387							// No matching column name in the state's columns, so this might be a new
6388							// column and thus can't have a state already.
6389							set.push({});
6390						}
6391					}
6392					else {
6393						// If no name, but other columns did have a name, then there is no knowing
6394						// where this one came from originally so it can't be restored.
6395						set.push({});
6396					}
6397				}
6398			}
6399	
6400			// If the number of columns to restore is different from current, then all bets are off.
6401			if (set.length === columns.length) {
6402				for ( i=0, ien=set.length ; i<ien ; i++ ) {
6403					var col = set[i];
6404	
6405					// Visibility
6406					if ( col.visible !== undefined ) {
6407						// If the api is defined, the table has been initialised so we need to use it rather than internal settings
6408						if (api) {
6409							// Don't redraw the columns on every iteration of this loop, we will do this at the end instead
6410							api.column(i).visible(col.visible, false);
6411						}
6412						else {
6413							columns[i].bVisible = col.visible;
6414						}
6415					}
6416	
6417					// Search
6418					if ( col.search !== undefined ) {
6419						$.extend( settings.aoPreSearchCols[i], col.search );
6420					}
6421				}
6422	
6423				// If the api is defined then we need to adjust the columns once the visibility has been changed
6424				if (api) {
6425					api.columns.adjust();
6426				}
6427			}
6428		}
6429	
6430		settings._bLoadingState = false;
6431		_fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] );
6432		callback();
6433	}
6434	
6435	/**
6436	 * Log an error message
6437	 *  @param {object} settings dataTables settings object
6438	 *  @param {int} level log error messages, or display them to the user
6439	 *  @param {string} msg error message
6440	 *  @param {int} tn Technical note id to get more information about the error.
6441	 *  @memberof DataTable#oApi
6442	 */
6443	function _fnLog( settings, level, msg, tn )
6444	{
6445		msg = 'DataTables warning: '+
6446			(settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
6447	
6448		if ( tn ) {
6449			msg += '. For more information about this error, please see '+
6450			'https://datatables.net/tn/'+tn;
6451		}
6452	
6453		if ( ! level  ) {
6454			// Backwards compatibility pre 1.10
6455			var ext = DataTable.ext;
6456			var type = ext.sErrMode || ext.errMode;
6457	
6458			if ( settings ) {
6459				_fnCallbackFire( settings, null, 'dt-error', [ settings, tn, msg ], true );
6460			}
6461	
6462			if ( type == 'alert' ) {
6463				alert( msg );
6464			}
6465			else if ( type == 'throw' ) {
6466				throw new Error(msg);
6467			}
6468			else if ( typeof type == 'function' ) {
6469				type( settings, tn, msg );
6470			}
6471		}
6472		else if ( window.console && console.log ) {
6473			console.log( msg );
6474		}
6475	}
6476	
6477	
6478	/**
6479	 * See if a property is defined on one object, if so assign it to the other object
6480	 *  @param {object} ret target object
6481	 *  @param {object} src source object
6482	 *  @param {string} name property
6483	 *  @param {string} [mappedName] name to map too - optional, name used if not given
6484	 *  @memberof DataTable#oApi
6485	 */
6486	function _fnMap( ret, src, name, mappedName )
6487	{
6488		if ( Array.isArray( name ) ) {
6489			$.each( name, function (i, val) {
6490				if ( Array.isArray( val ) ) {
6491					_fnMap( ret, src, val[0], val[1] );
6492				}
6493				else {
6494					_fnMap( ret, src, val );
6495				}
6496			} );
6497	
6498			return;
6499		}
6500	
6501		if ( mappedName === undefined ) {
6502			mappedName = name;
6503		}
6504	
6505		if ( src[name] !== undefined ) {
6506			ret[mappedName] = src[name];
6507		}
6508	}
6509	
6510	
6511	/**
6512	 * Extend objects - very similar to jQuery.extend, but deep copy objects, and
6513	 * shallow copy arrays. The reason we need to do this, is that we don't want to
6514	 * deep copy array init values (such as aaSorting) since the dev wouldn't be
6515	 * able to override them, but we do want to deep copy arrays.
6516	 *  @param {object} out Object to extend
6517	 *  @param {object} extender Object from which the properties will be applied to
6518	 *      out
6519	 *  @param {boolean} breakRefs If true, then arrays will be sliced to take an
6520	 *      independent copy with the exception of the `data` or `aaData` parameters
6521	 *      if they are present. This is so you can pass in a collection to
6522	 *      DataTables and have that used as your data source without breaking the
6523	 *      references
6524	 *  @returns {object} out Reference, just for convenience - out === the return.
6525	 *  @memberof DataTable#oApi
6526	 *  @todo This doesn't take account of arrays inside the deep copied objects.
6527	 */
6528	function _fnExtend( out, extender, breakRefs )
6529	{
6530		var val;
6531	
6532		for ( var prop in extender ) {
6533			if ( Object.prototype.hasOwnProperty.call(extender, prop) ) {
6534				val = extender[prop];
6535	
6536				if ( $.isPlainObject( val ) ) {
6537					if ( ! $.isPlainObject( out[prop] ) ) {
6538						out[prop] = {};
6539					}
6540					$.extend( true, out[prop], val );
6541				}
6542				else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) {
6543					out[prop] = val.slice();
6544				}
6545				else {
6546					out[prop] = val;
6547				}
6548			}
6549		}
6550	
6551		return out;
6552	}
6553	
6554	
6555	/**
6556	 * Bind an event handers to allow a click or return key to activate the callback.
6557	 * This is good for accessibility since a return on the keyboard will have the
6558	 * same effect as a click, if the element has focus.
6559	 *  @param {element} n Element to bind the action to
6560	 *  @param {object|string} selector Selector (for delegated events) or data object
6561	 *   to pass to the triggered function
6562	 *  @param {function} fn Callback function for when the event is triggered
6563	 *  @memberof DataTable#oApi
6564	 */
6565	function _fnBindAction( n, selector, fn )
6566	{
6567		$(n)
6568			.on( 'click.DT', selector, function (e) {
6569				fn(e);
6570			} )
6571			.on( 'keypress.DT', selector, function (e){
6572				if ( e.which === 13 ) {
6573					e.preventDefault();
6574					fn(e);
6575				}
6576			} )
6577			.on( 'selectstart.DT', selector, function () {
6578				// Don't want a double click resulting in text selection
6579				return false;
6580			} );
6581	}
6582	
6583	
6584	/**
6585	 * Register a callback function. Easily allows a callback function to be added to
6586	 * an array store of callback functions that can then all be called together.
6587	 *  @param {object} settings dataTables settings object
6588	 *  @param {string} store Name of the array storage for the callbacks in oSettings
6589	 *  @param {function} fn Function to be called back
6590	 *  @memberof DataTable#oApi
6591	 */
6592	function _fnCallbackReg( settings, store, fn )
6593	{
6594		if ( fn ) {
6595			settings[store].push(fn);
6596		}
6597	}
6598	
6599	
6600	/**
6601	 * Fire callback functions and trigger events. Note that the loop over the
6602	 * callback array store is done backwards! Further note that you do not want to
6603	 * fire off triggers in time sensitive applications (for example cell creation)
6604	 * as its slow.
6605	 *  @param {object} settings dataTables settings object
6606	 *  @param {string} callbackArr Name of the array storage for the callbacks in
6607	 *      oSettings
6608	 *  @param {string} eventName Name of the jQuery custom event to trigger. If
6609	 *      null no trigger is fired
6610	 *  @param {array} args Array of arguments to pass to the callback function /
6611	 *      trigger
6612	 *  @param {boolean} [bubbles] True if the event should bubble
6613	 *  @memberof DataTable#oApi
6614	 */
6615	function _fnCallbackFire( settings, callbackArr, eventName, args, bubbles )
6616	{
6617		var ret = [];
6618	
6619		if ( callbackArr ) {
6620			ret = settings[callbackArr].slice().reverse().map( function (val) {
6621				return val.apply( settings.oInstance, args );
6622			} );
6623		}
6624	
6625		if ( eventName !== null) {
6626			var e = $.Event( eventName+'.dt' );
6627			var table = $(settings.nTable);
6628			
6629			// Expose the DataTables API on the event object for easy access
6630			e.dt = settings.api;
6631	
6632			table[bubbles ?  'trigger' : 'triggerHandler']( e, args );
6633	
6634			// If not yet attached to the document, trigger the event
6635			// on the body directly to sort of simulate the bubble
6636			if (bubbles && table.parents('body').length === 0) {
6637				$('body').trigger( e, args );
6638			}
6639	
6640			ret.push( e.result );
6641		}
6642	
6643		return ret;
6644	}
6645	
6646	
6647	function _fnLengthOverflow ( settings )
6648	{
6649		var
6650			start = settings._iDisplayStart,
6651			end = settings.fnDisplayEnd(),
6652			len = settings._iDisplayLength;
6653	
6654		/* If we have space to show extra rows (backing up from the end point - then do so */
6655		if ( start >= end )
6656		{
6657			start = end - len;
6658		}
6659	
6660		// Keep the start record on the current page
6661		start -= (start % len);
6662	
6663		if ( len === -1 || start < 0 )
6664		{
6665			start = 0;
6666		}
6667	
6668		settings._iDisplayStart = start;
6669	}
6670	
6671	
6672	function _fnRenderer( settings, type )
6673	{
6674		var renderer = settings.renderer;
6675		var host = DataTable.ext.renderer[type];
6676	
6677		if ( $.isPlainObject( renderer ) && renderer[type] ) {
6678			// Specific renderer for this type. If available use it, otherwise use
6679			// the default.
6680			return host[renderer[type]] || host._;
6681		}
6682		else if ( typeof renderer === 'string' ) {
6683			// Common renderer - if there is one available for this type use it,
6684			// otherwise use the default
6685			return host[renderer] || host._;
6686		}
6687	
6688		// Use the default
6689		return host._;
6690	}
6691	
6692	
6693	/**
6694	 * Detect the data source being used for the table. Used to simplify the code
6695	 * a little (ajax) and to make it compress a little smaller.
6696	 *
6697	 *  @param {object} settings dataTables settings object
6698	 *  @returns {string} Data source
6699	 *  @memberof DataTable#oApi
6700	 */
6701	function _fnDataSource ( settings )
6702	{
6703		if ( settings.oFeatures.bServerSide ) {
6704			return 'ssp';
6705		}
6706		else if ( settings.ajax ) {
6707			return 'ajax';
6708		}
6709		return 'dom';
6710	}
6711	
6712	/**
6713	 * Common replacement for language strings
6714	 *
6715	 * @param {*} settings DT settings object
6716	 * @param {*} str String with values to replace
6717	 * @param {*} entries Plural number for _ENTRIES_ - can be undefined
6718	 * @returns String
6719	 */
6720	function _fnMacros ( settings, str, entries )
6721	{
6722		// When infinite scrolling, we are always starting at 1. _iDisplayStart is
6723		// used only internally
6724		var
6725			formatter  = settings.fnFormatNumber,
6726			start      = settings._iDisplayStart+1,
6727			len        = settings._iDisplayLength,
6728			vis        = settings.fnRecordsDisplay(),
6729			max        = settings.fnRecordsTotal(),
6730			all        = len === -1;
6731	
6732		return str.
6733			replace(/_START_/g, formatter.call( settings, start ) ).
6734			replace(/_END_/g,   formatter.call( settings, settings.fnDisplayEnd() ) ).
6735			replace(/_MAX_/g,   formatter.call( settings, max ) ).
6736			replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
6737			replace(/_PAGE_/g,  formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
6738			replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) ).
6739			replace(/_ENTRIES_/g, settings.api.i18n('entries', '', entries) ).
6740			replace(/_ENTRIES-MAX_/g, settings.api.i18n('entries', '', max) ).
6741			replace(/_ENTRIES-TOTAL_/g, settings.api.i18n('entries', '', vis) );
6742	}
6743	
6744	/**
6745	 * Add elements to an array as quickly as possible, but stack stafe.
6746	 *
6747	 * @param {*} arr Array to add the data to
6748	 * @param {*} data Data array that is to be added
6749	 * @returns 
6750	 */
6751	function _fnArrayApply(arr, data) {
6752		if (! data) {
6753			return;
6754		}
6755	
6756		// Chrome can throw a max stack error if apply is called with
6757		// too large an array, but apply is faster.
6758		if (data.length < 10000) {
6759			arr.push.apply(arr, data);
6760		}
6761		else {
6762			for (i=0 ; i<data.length ; i++) {
6763				arr.push(data[i]);
6764			}
6765		}
6766	}
6767	
6768	
6769	
6770	/**
6771	 * Computed structure of the DataTables API, defined by the options passed to
6772	 * `DataTable.Api.register()` when building the API.
6773	 *
6774	 * The structure is built in order to speed creation and extension of the Api
6775	 * objects since the extensions are effectively pre-parsed.
6776	 *
6777	 * The array is an array of objects with the following structure, where this
6778	 * base array represents the Api prototype base:
6779	 *
6780	 *     [
6781	 *       {
6782	 *         name:      'data'                -- string   - Property name
6783	 *         val:       function () {},       -- function - Api method (or undefined if just an object
6784	 *         methodExt: [ ... ],              -- array    - Array of Api object definitions to extend the method result
6785	 *         propExt:   [ ... ]               -- array    - Array of Api object definitions to extend the property
6786	 *       },
6787	 *       {
6788	 *         name:     'row'
6789	 *         val:       {},
6790	 *         methodExt: [ ... ],
6791	 *         propExt:   [
6792	 *           {
6793	 *             name:      'data'
6794	 *             val:       function () {},
6795	 *             methodExt: [ ... ],
6796	 *             propExt:   [ ... ]
6797	 *           },
6798	 *           ...
6799	 *         ]
6800	 *       }
6801	 *     ]
6802	 *
6803	 * @type {Array}
6804	 * @ignore
6805	 */
6806	var __apiStruct = [];
6807	
6808	
6809	/**
6810	 * `Array.prototype` reference.
6811	 *
6812	 * @type object
6813	 * @ignore
6814	 */
6815	var __arrayProto = Array.prototype;
6816	
6817	
6818	/**
6819	 * Abstraction for `context` parameter of the `Api` constructor to allow it to
6820	 * take several different forms for ease of use.
6821	 *
6822	 * Each of the input parameter types will be converted to a DataTables settings
6823	 * object where possible.
6824	 *
6825	 * @param  {string|node|jQuery|object} mixed DataTable identifier. Can be one
6826	 *   of:
6827	 *
6828	 *   * `string` - jQuery selector. Any DataTables' matching the given selector
6829	 *     with be found and used.
6830	 *   * `node` - `TABLE` node which has already been formed into a DataTable.
6831	 *   * `jQuery` - A jQuery object of `TABLE` nodes.
6832	 *   * `object` - DataTables settings object
6833	 *   * `DataTables.Api` - API instance
6834	 * @return {array|null} Matching DataTables settings objects. `null` or
6835	 *   `undefined` is returned if no matching DataTable is found.
6836	 * @ignore
6837	 */
6838	var _toSettings = function ( mixed )
6839	{
6840		var idx, jq;
6841		var settings = DataTable.settings;
6842		var tables = _pluck(settings, 'nTable');
6843	
6844		if ( ! mixed ) {
6845			return [];
6846		}
6847		else if ( mixed.nTable && mixed.oFeatures ) {
6848			// DataTables settings object
6849			return [ mixed ];
6850		}
6851		else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
6852			// Table node
6853			idx = tables.indexOf(mixed);
6854			return idx !== -1 ? [ settings[idx] ] : null;
6855		}
6856		else if ( mixed && typeof mixed.settings === 'function' ) {
6857			return mixed.settings().toArray();
6858		}
6859		else if ( typeof mixed === 'string' ) {
6860			// jQuery selector
6861			jq = $(mixed).get();
6862		}
6863		else if ( mixed instanceof $ ) {
6864			// jQuery object (also DataTables instance)
6865			jq = mixed.get();
6866		}
6867	
6868		if ( jq ) {
6869			return settings.filter(function (v, idx) {
6870				return jq.includes(tables[idx]);
6871			});
6872		}
6873	};
6874	
6875	
6876	/**
6877	 * DataTables API class - used to control and interface with  one or more
6878	 * DataTables enhanced tables.
6879	 *
6880	 * The API class is heavily based on jQuery, presenting a chainable interface
6881	 * that you can use to interact with tables. Each instance of the API class has
6882	 * a "context" - i.e. the tables that it will operate on. This could be a single
6883	 * table, all tables on a page or a sub-set thereof.
6884	 *
6885	 * Additionally the API is designed to allow you to easily work with the data in
6886	 * the tables, retrieving and manipulating it as required. This is done by
6887	 * presenting the API class as an array like interface. The contents of the
6888	 * array depend upon the actions requested by each method (for example
6889	 * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6890	 * return an array of objects or arrays depending upon your table's
6891	 * configuration). The API object has a number of array like methods (`push`,
6892	 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
6893	 * `unique` etc) to assist your working with the data held in a table.
6894	 *
6895	 * Most methods (those which return an Api instance) are chainable, which means
6896	 * the return from a method call also has all of the methods available that the
6897	 * top level object had. For example, these two calls are equivalent:
6898	 *
6899	 *     // Not chained
6900	 *     api.row.add( {...} );
6901	 *     api.draw();
6902	 *
6903	 *     // Chained
6904	 *     api.row.add( {...} ).draw();
6905	 *
6906	 * @class DataTable.Api
6907	 * @param {array|object|string|jQuery} context DataTable identifier. This is
6908	 *   used to define which DataTables enhanced tables this API will operate on.
6909	 *   Can be one of:
6910	 *
6911	 *   * `string` - jQuery selector. Any DataTables' matching the given selector
6912	 *     with be found and used.
6913	 *   * `node` - `TABLE` node which has already been formed into a DataTable.
6914	 *   * `jQuery` - A jQuery object of `TABLE` nodes.
6915	 *   * `object` - DataTables settings object
6916	 * @param {array} [data] Data to initialise the Api instance with.
6917	 *
6918	 * @example
6919	 *   // Direct initialisation during DataTables construction
6920	 *   var api = $('#example').DataTable();
6921	 *
6922	 * @example
6923	 *   // Initialisation using a DataTables jQuery object
6924	 *   var api = $('#example').dataTable().api();
6925	 *
6926	 * @example
6927	 *   // Initialisation as a constructor
6928	 *   var api = new DataTable.Api( 'table.dataTable' );
6929	 */
6930	_Api = function ( context, data )
6931	{
6932		if ( ! (this instanceof _Api) ) {
6933			return new _Api( context, data );
6934		}
6935	
6936		var i;
6937		var settings = [];
6938		var ctxSettings = function ( o ) {
6939			var a = _toSettings( o );
6940			if ( a ) {
6941				settings.push.apply( settings, a );
6942			}
6943		};
6944	
6945		if ( Array.isArray( context ) ) {
6946			for ( i=0 ; i<context.length ; i++ ) {
6947				ctxSettings( context[i] );
6948			}
6949		}
6950		else {
6951			ctxSettings( context );
6952		}
6953	
6954		// Remove duplicates
6955		this.context = settings.length > 1
6956			? _unique( settings )
6957			: settings;
6958	
6959		// Initial data
6960		_fnArrayApply(this, data);
6961	
6962		// selector
6963		this.selector = {
6964			rows: null,
6965			cols: null,
6966			opts: null
6967		};
6968	
6969		_Api.extend( this, this, __apiStruct );
6970	};
6971	
6972	DataTable.Api = _Api;
6973	
6974	// Don't destroy the existing prototype, just extend it. Required for jQuery 2's
6975	// isPlainObject.
6976	$.extend( _Api.prototype, {
6977		any: function ()
6978		{
6979			return this.count() !== 0;
6980		},
6981	
6982		context: [], // array of table settings objects
6983	
6984		count: function ()
6985		{
6986			return this.flatten().length;
6987		},
6988	
6989		each: function ( fn )
6990		{
6991			for ( var i=0, ien=this.length ; i<ien; i++ ) {
6992				fn.call( this, this[i], i, this );
6993			}
6994	
6995			return this;
6996		},
6997	
6998		eq: function ( idx )
6999		{
7000			var ctx = this.context;
7001	
7002			return ctx.length > idx ?
7003				new _Api( ctx[idx], this[idx] ) :
7004				null;
7005		},
7006	
7007		filter: function ( fn )
7008		{
7009			var a = __arrayProto.filter.call( this, fn, this );
7010	
7011			return new _Api( this.context, a );
7012		},
7013	
7014		flatten: function ()
7015		{
7016			var a = [];
7017	
7018			return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
7019		},
7020	
7021		get: function ( idx )
7022		{
7023			return this[ idx ];
7024		},
7025	
7026		join:    __arrayProto.join,
7027	
7028		includes: function ( find ) {
7029			return this.indexOf( find ) === -1 ? false : true;
7030		},
7031	
7032		indexOf: __arrayProto.indexOf,
7033	
7034		iterator: function ( flatten, type, fn, alwaysNew ) {
7035			var
7036				a = [], ret,
7037				i, ien, j, jen,
7038				context = this.context,
7039				rows, items, item,
7040				selector = this.selector;
7041	
7042			// Argument shifting
7043			if ( typeof flatten === 'string' ) {
7044				alwaysNew = fn;
7045				fn = type;
7046				type = flatten;
7047				flatten = false;
7048			}
7049	
7050			for ( i=0, ien=context.length ; i<ien ; i++ ) {
7051				var apiInst = new _Api( context[i] );
7052	
7053				if ( type === 'table' ) {
7054					ret = fn.call( apiInst, context[i], i );
7055	
7056					if ( ret !== undefined ) {
7057						a.push( ret );
7058					}
7059				}
7060				else if ( type === 'columns' || type === 'rows' ) {
7061					// this has same length as context - one entry for each table
7062					ret = fn.call( apiInst, context[i], this[i], i );
7063	
7064					if ( ret !== undefined ) {
7065						a.push( ret );
7066					}
7067				}
7068				else if ( type === 'every' || type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
7069					// columns and rows share the same structure.
7070					// 'this' is an array of column indexes for each context
7071					items = this[i];
7072	
7073					if ( type === 'column-rows' ) {
7074						rows = _selector_row_indexes( context[i], selector.opts );
7075					}
7076	
7077					for ( j=0, jen=items.length ; j<jen ; j++ ) {
7078						item = items[j];
7079	
7080						if ( type === 'cell' ) {
7081							ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
7082						}
7083						else {
7084							ret = fn.call( apiInst, context[i], item, i, j, rows );
7085						}
7086	
7087						if ( ret !== undefined ) {
7088							a.push( ret );
7089						}
7090					}
7091				}
7092			}
7093	
7094			if ( a.length || alwaysNew ) {
7095				var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
7096				var apiSelector = api.selector;
7097				apiSelector.rows = selector.rows;
7098				apiSelector.cols = selector.cols;
7099				apiSelector.opts = selector.opts;
7100				return api;
7101			}
7102			return this;
7103		},
7104	
7105		lastIndexOf: __arrayProto.lastIndexOf,
7106	
7107		length:  0,
7108	
7109		map: function ( fn )
7110		{
7111			var a = __arrayProto.map.call( this, fn, this );
7112	
7113			return new _Api( this.context, a );
7114		},
7115	
7116		pluck: function ( prop )
7117		{
7118			var fn = DataTable.util.get(prop);
7119	
7120			return this.map( function ( el ) {
7121				return fn(el);
7122			} );
7123		},
7124	
7125		pop:     __arrayProto.pop,
7126	
7127		push:    __arrayProto.push,
7128	
7129		reduce: __arrayProto.reduce,
7130	
7131		reduceRight: __arrayProto.reduceRight,
7132	
7133		reverse: __arrayProto.reverse,
7134	
7135		// Object with rows, columns and opts
7136		selector: null,
7137	
7138		shift:   __arrayProto.shift,
7139	
7140		slice: function () {
7141			return new _Api( this.context, this );
7142		},
7143	
7144		sort:    __arrayProto.sort,
7145	
7146		splice:  __arrayProto.splice,
7147	
7148		toArray: function ()
7149		{
7150			return __arrayProto.slice.call( this );
7151		},
7152	
7153		to$: function ()
7154		{
7155			return $( this );
7156		},
7157	
7158		toJQuery: function ()
7159		{
7160			return $( this );
7161		},
7162	
7163		unique: function ()
7164		{
7165			return new _Api( this.context, _unique(this.toArray()) );
7166		},
7167	
7168		unshift: __arrayProto.unshift
7169	} );
7170	
7171	
7172	function _api_scope( scope, fn, struc ) {
7173		return function () {
7174			var ret = fn.apply( scope || this, arguments );
7175	
7176			// Method extension
7177			_Api.extend( ret, ret, struc.methodExt );
7178			return ret;
7179		};
7180	}
7181	
7182	function _api_find( src, name ) {
7183		for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7184			if ( src[i].name === name ) {
7185				return src[i];
7186			}
7187		}
7188		return null;
7189	}
7190	
7191	window.__apiStruct = __apiStruct;
7192	
7193	_Api.extend = function ( scope, obj, ext )
7194	{
7195		// Only extend API instances and static properties of the API
7196		if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
7197			return;
7198		}
7199	
7200		var
7201			i, ien,
7202			struct;
7203	
7204		for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7205			struct = ext[i];
7206	
7207			if (struct.name === '__proto__') {
7208				continue;
7209			}
7210	
7211			// Value
7212			obj[ struct.name ] = struct.type === 'function' ?
7213				_api_scope( scope, struct.val, struct ) :
7214				struct.type === 'object' ?
7215					{} :
7216					struct.val;
7217	
7218			obj[ struct.name ].__dt_wrapper = true;
7219	
7220			// Property extension
7221			_Api.extend( scope, obj[ struct.name ], struct.propExt );
7222		}
7223	};
7224	
7225	//     [
7226	//       {
7227	//         name:      'data'                -- string   - Property name
7228	//         val:       function () {},       -- function - Api method (or undefined if just an object
7229	//         methodExt: [ ... ],              -- array    - Array of Api object definitions to extend the method result
7230	//         propExt:   [ ... ]               -- array    - Array of Api object definitions to extend the property
7231	//       },
7232	//       {
7233	//         name:     'row'
7234	//         val:       {},
7235	//         methodExt: [ ... ],
7236	//         propExt:   [
7237	//           {
7238	//             name:      'data'
7239	//             val:       function () {},
7240	//             methodExt: [ ... ],
7241	//             propExt:   [ ... ]
7242	//           },
7243	//           ...
7244	//         ]
7245	//       }
7246	//     ]
7247	
7248	
7249	_Api.register = _api_register = function ( name, val )
7250	{
7251		if ( Array.isArray( name ) ) {
7252			for ( var j=0, jen=name.length ; j<jen ; j++ ) {
7253				_Api.register( name[j], val );
7254			}
7255			return;
7256		}
7257	
7258		var
7259			i, ien,
7260			heir = name.split('.'),
7261			struct = __apiStruct,
7262			key, method;
7263	
7264		for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7265			method = heir[i].indexOf('()') !== -1;
7266			key = method ?
7267				heir[i].replace('()', '') :
7268				heir[i];
7269	
7270			var src = _api_find( struct, key );
7271			if ( ! src ) {
7272				src = {
7273					name:      key,
7274					val:       {},
7275					methodExt: [],
7276					propExt:   [],
7277					type:      'object'
7278				};
7279				struct.push( src );
7280			}
7281	
7282			if ( i === ien-1 ) {
7283				src.val = val;
7284				src.type = typeof val === 'function' ?
7285					'function' :
7286					$.isPlainObject( val ) ?
7287						'object' :
7288						'other';
7289			}
7290			else {
7291				struct = method ?
7292					src.methodExt :
7293					src.propExt;
7294			}
7295		}
7296	};
7297	
7298	_Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7299		_Api.register( pluralName, val );
7300	
7301		_Api.register( singularName, function () {
7302			var ret = val.apply( this, arguments );
7303	
7304			if ( ret === this ) {
7305				// Returned item is the API instance that was passed in, return it
7306				return this;
7307			}
7308			else if ( ret instanceof _Api ) {
7309				// New API instance returned, want the value from the first item
7310				// in the returned array for the singular result.
7311				return ret.length ?
7312					Array.isArray( ret[0] ) ?
7313						new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
7314						ret[0] :
7315					undefined;
7316			}
7317	
7318			// Non-API return - just fire it back
7319			return ret;
7320		} );
7321	};
7322	
7323	
7324	/**
7325	 * Selector for HTML tables. Apply the given selector to the give array of
7326	 * DataTables settings objects.
7327	 *
7328	 * @param {string|integer} [selector] jQuery selector string or integer
7329	 * @param  {array} Array of DataTables settings objects to be filtered
7330	 * @return {array}
7331	 * @ignore
7332	 */
7333	var __table_selector = function ( selector, a )
7334	{
7335		if ( Array.isArray(selector) ) {
7336			var result = [];
7337	
7338			selector.forEach(function (sel) {
7339				var inner = __table_selector(sel, a);
7340	
7341				_fnArrayApply(result, inner);
7342			});
7343	
7344			return result.filter( function (item) {
7345				return item;
7346			});
7347		}
7348	
7349		// Integer is used to pick out a table by index
7350		if ( typeof selector === 'number' ) {
7351			return [ a[ selector ] ];
7352		}
7353	
7354		// Perform a jQuery selector on the table nodes
7355		var nodes = a.map( function (el) {
7356			return el.nTable;
7357		} );
7358	
7359		return $(nodes)
7360			.filter( selector )
7361			.map( function () {
7362				// Need to translate back from the table node to the settings
7363				var idx = nodes.indexOf(this);
7364				return a[ idx ];
7365			} )
7366			.toArray();
7367	};
7368	
7369	
7370	
7371	/**
7372	 * Context selector for the API's context (i.e. the tables the API instance
7373	 * refers to.
7374	 *
7375	 * @name    DataTable.Api#tables
7376	 * @param {string|integer} [selector] Selector to pick which tables the iterator
7377	 *   should operate on. If not given, all tables in the current context are
7378	 *   used. This can be given as a jQuery selector (for example `':gt(0)'`) to
7379	 *   select multiple tables or as an integer to select a single table.
7380	 * @returns {DataTable.Api} Returns a new API instance if a selector is given.
7381	 */
7382	_api_register( 'tables()', function ( selector ) {
7383		// A new instance is created if there was a selector specified
7384		return selector !== undefined && selector !== null ?
7385			new _Api( __table_selector( selector, this.context ) ) :
7386			this;
7387	} );
7388	
7389	
7390	_api_register( 'table()', function ( selector ) {
7391		var tables = this.tables( selector );
7392		var ctx = tables.context;
7393	
7394		// Truncate to the first matched table
7395		return ctx.length ?
7396			new _Api( ctx[0] ) :
7397			tables;
7398	} );
7399	
7400	// Common methods, combined to reduce size
7401	[
7402		['nodes', 'node', 'nTable'],
7403		['body', 'body', 'nTBody'],
7404		['header', 'header', 'nTHead'],
7405		['footer', 'footer', 'nTFoot'],
7406	].forEach(function (item) {
7407		_api_registerPlural(
7408			'tables().' + item[0] + '()',
7409			'table().' + item[1] + '()' ,
7410			function () {
7411				return this.iterator( 'table', function ( ctx ) {
7412					return ctx[item[2]];
7413				}, 1 );
7414			}
7415		);
7416	});
7417	
7418	// Structure methods
7419	[
7420		['header', 'aoHeader'],
7421		['footer', 'aoFooter'],
7422	].forEach(function (item) {
7423		_api_register( 'table().' + item[0] + '.structure()' , function (selector) {
7424			var indexes = this.columns(selector).indexes().flatten();
7425			var ctx = this.context[0];
7426			
7427			return _fnHeaderLayout(ctx, ctx[item[1]], indexes);
7428		} );
7429	})
7430	
7431	
7432	_api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7433		return this.iterator( 'table', function ( ctx ) {
7434			return ctx.nTableWrapper;
7435		}, 1 );
7436	} );
7437	
7438	_api_register( 'tables().every()', function ( fn ) {
7439		var that = this;
7440	
7441		return this.iterator('table', function (s, i) {
7442			fn.call(that.table(i), i);
7443		});
7444	});
7445	
7446	_api_register( 'caption()', function ( value, side ) {
7447		var context = this.context;
7448	
7449		// Getter - return existing node's content
7450		if ( value === undefined ) {
7451			var caption = context[0].captionNode;
7452	
7453			return caption && context.length ?
7454				caption.innerHTML : 
7455				null;
7456		}
7457	
7458		return this.iterator( 'table', function ( ctx ) {
7459			var table = $(ctx.nTable);
7460			var caption = $(ctx.captionNode);
7461			var container = $(ctx.nTableWrapper);
7462	
7463			// Create the node if it doesn't exist yet
7464			if ( ! caption.length ) {
7465				caption = $('<caption/>').html( value );
7466				ctx.captionNode = caption[0];
7467	
7468				// If side isn't set, we need to insert into the document to let the
7469				// CSS decide so we can read it back, otherwise there is no way to
7470				// know if the CSS would put it top or bottom for scrolling
7471				if (! side) {
7472					table.prepend(caption);
7473	
7474					side = caption.css('caption-side');
7475				}
7476			}
7477	
7478			caption.html( value );
7479	
7480			if ( side ) {
7481				caption.css( 'caption-side', side );
7482				caption[0]._captionSide = side;
7483			}
7484	
7485			if (container.find('div.dataTables_scroll').length) {
7486				var selector = (side === 'top' ? 'Head' : 'Foot');
7487	
7488				container.find('div.dataTables_scroll'+ selector +' table').prepend(caption);
7489			}
7490			else {
7491				table.prepend(caption);
7492			}
7493		}, 1 );
7494	} );
7495	
7496	_api_register( 'caption.node()', function () {
7497		var ctx = this.context;
7498	
7499		return ctx.length ? ctx[0].captionNode : null;
7500	} );
7501	
7502	
7503	/**
7504	 * Redraw the tables in the current context.
7505	 */
7506	_api_register( 'draw()', function ( paging ) {
7507		return this.iterator( 'table', function ( settings ) {
7508			if ( paging === 'page' ) {
7509				_fnDraw( settings );
7510			}
7511			else {
7512				if ( typeof paging === 'string' ) {
7513					paging = paging === 'full-hold' ?
7514						false :
7515						true;
7516				}
7517	
7518				_fnReDraw( settings, paging===false );
7519			}
7520		} );
7521	} );
7522	
7523	
7524	
7525	/**
7526	 * Get the current page index.
7527	 *
7528	 * @return {integer} Current page index (zero based)
7529	 *//**
7530	 * Set the current page.
7531	 *
7532	 * Note that if you attempt to show a page which does not exist, DataTables will
7533	 * not throw an error, but rather reset the paging.
7534	 *
7535	 * @param {integer|string} action The paging action to take. This can be one of:
7536	 *  * `integer` - The page index to jump to
7537	 *  * `string` - An action to take:
7538	 *    * `first` - Jump to first page.
7539	 *    * `next` - Jump to the next page
7540	 *    * `previous` - Jump to previous page
7541	 *    * `last` - Jump to the last page.
7542	 * @returns {DataTables.Api} this
7543	 */
7544	_api_register( 'page()', function ( action ) {
7545		if ( action === undefined ) {
7546			return this.page.info().page; // not an expensive call
7547		}
7548	
7549		// else, have an action to take on all tables
7550		return this.iterator( 'table', function ( settings ) {
7551			_fnPageChange( settings, action );
7552		} );
7553	} );
7554	
7555	
7556	/**
7557	 * Paging information for the first table in the current context.
7558	 *
7559	 * If you require paging information for another table, use the `table()` method
7560	 * with a suitable selector.
7561	 *
7562	 * @return {object} Object with the following properties set:
7563	 *  * `page` - Current page index (zero based - i.e. the first page is `0`)
7564	 *  * `pages` - Total number of pages
7565	 *  * `start` - Display index for the first record shown on the current page
7566	 *  * `end` - Display index for the last record shown on the current page
7567	 *  * `length` - Display length (number of records). Note that generally `start
7568	 *    + length = end`, but this is not always true, for example if there are
7569	 *    only 2 records to show on the final page, with a length of 10.
7570	 *  * `recordsTotal` - Full data set length
7571	 *  * `recordsDisplay` - Data set length once the current filtering criterion
7572	 *    are applied.
7573	 */
7574	_api_register( 'page.info()', function () {
7575		if ( this.context.length === 0 ) {
7576			return undefined;
7577		}
7578	
7579		var
7580			settings   = this.context[0],
7581			start      = settings._iDisplayStart,
7582			len        = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
7583			visRecords = settings.fnRecordsDisplay(),
7584			all        = len === -1;
7585	
7586		return {
7587			"page":           all ? 0 : Math.floor( start / len ),
7588			"pages":          all ? 1 : Math.ceil( visRecords / len ),
7589			"start":          start,
7590			"end":            settings.fnDisplayEnd(),
7591			"length":         len,
7592			"recordsTotal":   settings.fnRecordsTotal(),
7593			"recordsDisplay": visRecords,
7594			"serverSide":     _fnDataSource( settings ) === 'ssp'
7595		};
7596	} );
7597	
7598	
7599	/**
7600	 * Get the current page length.
7601	 *
7602	 * @return {integer} Current page length. Note `-1` indicates that all records
7603	 *   are to be shown.
7604	 *//**
7605	 * Set the current page length.
7606	 *
7607	 * @param {integer} Page length to set. Use `-1` to show all records.
7608	 * @returns {DataTables.Api} this
7609	 */
7610	_api_register( 'page.len()', function ( len ) {
7611		// Note that we can't call this function 'length()' because `length`
7612		// is a Javascript property of functions which defines how many arguments
7613		// the function expects.
7614		if ( len === undefined ) {
7615			return this.context.length !== 0 ?
7616				this.context[0]._iDisplayLength :
7617				undefined;
7618		}
7619	
7620		// else, set the page length
7621		return this.iterator( 'table', function ( settings ) {
7622			_fnLengthChange( settings, len );
7623		} );
7624	} );
7625	
7626	
7627	
7628	var __reload = function ( settings, holdPosition, callback ) {
7629		// Use the draw event to trigger a callback
7630		if ( callback ) {
7631			var api = new _Api( settings );
7632	
7633			api.one( 'draw', function () {
7634				callback( api.ajax.json() );
7635			} );
7636		}
7637	
7638		if ( _fnDataSource( settings ) == 'ssp' ) {
7639			_fnReDraw( settings, holdPosition );
7640		}
7641		else {
7642			_fnProcessingDisplay( settings, true );
7643	
7644			// Cancel an existing request
7645			var xhr = settings.jqXHR;
7646			if ( xhr && xhr.readyState !== 4 ) {
7647				xhr.abort();
7648			}
7649	
7650			// Trigger xhr
7651			_fnBuildAjax( settings, {}, function( json ) {
7652				_fnClearTable( settings );
7653	
7654				var data = _fnAjaxDataSrc( settings, json );
7655				for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7656					_fnAddData( settings, data[i] );
7657				}
7658	
7659				_fnReDraw( settings, holdPosition );
7660				_fnInitComplete( settings );
7661				_fnProcessingDisplay( settings, false );
7662			} );
7663		}
7664	};
7665	
7666	
7667	/**
7668	 * Get the JSON response from the last Ajax request that DataTables made to the
7669	 * server. Note that this returns the JSON from the first table in the current
7670	 * context.
7671	 *
7672	 * @return {object} JSON received from the server.
7673	 */
7674	_api_register( 'ajax.json()', function () {
7675		var ctx = this.context;
7676	
7677		if ( ctx.length > 0 ) {
7678			return ctx[0].json;
7679		}
7680	
7681		// else return undefined;
7682	} );
7683	
7684	
7685	/**
7686	 * Get the data submitted in the last Ajax request
7687	 */
7688	_api_register( 'ajax.params()', function () {
7689		var ctx = this.context;
7690	
7691		if ( ctx.length > 0 ) {
7692			return ctx[0].oAjaxData;
7693		}
7694	
7695		// else return undefined;
7696	} );
7697	
7698	
7699	/**
7700	 * Reload tables from the Ajax data source. Note that this function will
7701	 * automatically re-draw the table when the remote data has been loaded.
7702	 *
7703	 * @param {boolean} [reset=true] Reset (default) or hold the current paging
7704	 *   position. A full re-sort and re-filter is performed when this method is
7705	 *   called, which is why the pagination reset is the default action.
7706	 * @returns {DataTables.Api} this
7707	 */
7708	_api_register( 'ajax.reload()', function ( callback, resetPaging ) {
7709		return this.iterator( 'table', function (settings) {
7710			__reload( settings, resetPaging===false, callback );
7711		} );
7712	} );
7713	
7714	
7715	/**
7716	 * Get the current Ajax URL. Note that this returns the URL from the first
7717	 * table in the current context.
7718	 *
7719	 * @return {string} Current Ajax source URL
7720	 *//**
7721	 * Set the Ajax URL. Note that this will set the URL for all tables in the
7722	 * current context.
7723	 *
7724	 * @param {string} url URL to set.
7725	 * @returns {DataTables.Api} this
7726	 */
7727	_api_register( 'ajax.url()', function ( url ) {
7728		var ctx = this.context;
7729	
7730		if ( url === undefined ) {
7731			// get
7732			if ( ctx.length === 0 ) {
7733				return undefined;
7734			}
7735			ctx = ctx[0];
7736	
7737			return $.isPlainObject( ctx.ajax ) ?
7738				ctx.ajax.url :
7739				ctx.ajax;
7740		}
7741	
7742		// set
7743		return this.iterator( 'table', function ( settings ) {
7744			if ( $.isPlainObject( settings.ajax ) ) {
7745				settings.ajax.url = url;
7746			}
7747			else {
7748				settings.ajax = url;
7749			}
7750		} );
7751	} );
7752	
7753	
7754	/**
7755	 * Load data from the newly set Ajax URL. Note that this method is only
7756	 * available when `ajax.url()` is used to set a URL. Additionally, this method
7757	 * has the same effect as calling `ajax.reload()` but is provided for
7758	 * convenience when setting a new URL. Like `ajax.reload()` it will
7759	 * automatically redraw the table once the remote data has been loaded.
7760	 *
7761	 * @returns {DataTables.Api} this
7762	 */
7763	_api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
7764		// Same as a reload, but makes sense to present it for easy access after a
7765		// url change
7766		return this.iterator( 'table', function ( ctx ) {
7767			__reload( ctx, resetPaging===false, callback );
7768		} );
7769	} );
7770	
7771	
7772	
7773	
7774	var _selector_run = function ( type, selector, selectFn, settings, opts )
7775	{
7776		var
7777			out = [], res,
7778			a, i, ien, j, jen,
7779			selectorType = typeof selector;
7780	
7781		// Can't just check for isArray here, as an API or jQuery instance might be
7782		// given with their array like look
7783		if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7784			selector = [ selector ];
7785		}
7786	
7787		for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7788			// Only split on simple strings - complex expressions will be jQuery selectors
7789			a = selector[i] && selector[i].split && ! selector[i].match(/[[(:]/) ?
7790				selector[i].split(',') :
7791				[ selector[i] ];
7792	
7793			for ( j=0, jen=a.length ; j<jen ; j++ ) {
7794				res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] );
7795	
7796				// Remove empty items
7797				res = res.filter( function (item) {
7798					return item !== null && item !== undefined;
7799				});
7800	
7801				if ( res && res.length ) {
7802					out = out.concat( res );
7803				}
7804			}
7805		}
7806	
7807		// selector extensions
7808		var ext = _ext.selector[ type ];
7809		if ( ext.length ) {
7810			for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7811				out = ext[i]( settings, opts, out );
7812			}
7813		}
7814	
7815		return _unique( out );
7816	};
7817	
7818	
7819	var _selector_opts = function ( opts )
7820	{
7821		if ( ! opts ) {
7822			opts = {};
7823		}
7824	
7825		// Backwards compatibility for 1.9- which used the terminology filter rather
7826		// than search
7827		if ( opts.filter && opts.search === undefined ) {
7828			opts.search = opts.filter;
7829		}
7830	
7831		return $.extend( {
7832			search: 'none',
7833			order: 'current',
7834			page: 'all'
7835		}, opts );
7836	};
7837	
7838	
7839	// Reduce the API instance to the first item found
7840	var _selector_first = function ( old )
7841	{
7842		var inst = new _Api(old.context[0]);
7843	
7844		// Use a push rather than passing to the constructor, since it will
7845		// merge arrays down automatically, which isn't what is wanted here
7846		if (old.length) {
7847			inst.push( old[0] );
7848		}
7849	
7850		inst.selector = old.selector;
7851	
7852		// Limit to a single row / column / cell
7853		if (inst.length && inst[0].length > 1) {
7854			inst[0].splice(1);
7855		}
7856	
7857		return inst;
7858	};
7859	
7860	
7861	var _selector_row_indexes = function ( settings, opts )
7862	{
7863		var
7864			i, ien, tmp, a=[],
7865			displayFiltered = settings.aiDisplay,
7866			displayMaster = settings.aiDisplayMaster;
7867	
7868		var
7869			search = opts.search,  // none, applied, removed
7870			order  = opts.order,   // applied, current, index (original - compatibility with 1.9)
7871			page   = opts.page;    // all, current
7872	
7873		if ( _fnDataSource( settings ) == 'ssp' ) {
7874			// In server-side processing mode, most options are irrelevant since
7875			// rows not shown don't exist and the index order is the applied order
7876			// Removed is a special case - for consistency just return an empty
7877			// array
7878			return search === 'removed' ?
7879				[] :
7880				_range( 0, displayMaster.length );
7881		}
7882	
7883		if ( page == 'current' ) {
7884			// Current page implies that order=current and filter=applied, since it is
7885			// fairly senseless otherwise, regardless of what order and search actually
7886			// are
7887			for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
7888				a.push( displayFiltered[i] );
7889			}
7890		}
7891		else if ( order == 'current' || order == 'applied' ) {
7892			if ( search == 'none') {
7893				a = displayMaster.slice();
7894			}
7895			else if ( search == 'applied' ) {
7896				a = displayFiltered.slice();
7897			}
7898			else if ( search == 'removed' ) {
7899				// O(n+m) solution by creating a hash map
7900				var displayFilteredMap = {};
7901	
7902				for ( i=0, ien=displayFiltered.length ; i<ien ; i++ ) {
7903					displayFilteredMap[displayFiltered[i]] = null;
7904				}
7905	
7906				displayMaster.forEach(function (item) {
7907					if (! Object.prototype.hasOwnProperty.call(displayFilteredMap, item)) {
7908						a.push(item);
7909					}
7910				});
7911			}
7912		}
7913		else if ( order == 'index' || order == 'original' ) {
7914			for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
7915				if (! settings.aoData[i]) {
7916					continue;
7917				}
7918	
7919				if ( search == 'none' ) {
7920					a.push( i );
7921				}
7922				else { // applied | removed
7923					tmp = displayFiltered.indexOf(i);
7924	
7925					if ((tmp === -1 && search == 'removed') ||
7926						(tmp >= 0   && search == 'applied') )
7927					{
7928						a.push( i );
7929					}
7930				}
7931			}
7932		}
7933		else if ( typeof order === 'number' ) {
7934			// Order the rows by the given column
7935			var ordered = _fnSort(settings, order, 'asc');
7936	
7937			if (search === 'none') {
7938				a = ordered;
7939			}
7940			else { // applied | removed
7941				for (i=0; i<ordered.length; i++) {
7942					tmp = displayFiltered.indexOf(ordered[i]);
7943	
7944					if ((tmp === -1 && search == 'removed') ||
7945						(tmp >= 0   && search == 'applied') )
7946					{
7947						a.push( ordered[i] );
7948					}
7949				}
7950			}
7951		}
7952	
7953		return a;
7954	};
7955	
7956	
7957	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7958	 * Rows
7959	 *
7960	 * {}          - no selector - use all available rows
7961	 * {integer}   - row aoData index
7962	 * {node}      - TR node
7963	 * {string}    - jQuery selector to apply to the TR elements
7964	 * {array}     - jQuery array of nodes, or simply an array of TR nodes
7965	 *
7966	 */
7967	var __row_selector = function ( settings, selector, opts )
7968	{
7969		var rows;
7970		var run = function ( sel ) {
7971			var selInt = _intVal( sel );
7972			var aoData = settings.aoData;
7973	
7974			// Short cut - selector is a number and no options provided (default is
7975			// all records, so no need to check if the index is in there, since it
7976			// must be - dev error if the index doesn't exist).
7977			if ( selInt !== null && ! opts ) {
7978				return [ selInt ];
7979			}
7980	
7981			if ( ! rows ) {
7982				rows = _selector_row_indexes( settings, opts );
7983			}
7984	
7985			if ( selInt !== null && rows.indexOf(selInt) !== -1 ) {
7986				// Selector - integer
7987				return [ selInt ];
7988			}
7989			else if ( sel === null || sel === undefined || sel === '' ) {
7990				// Selector - none
7991				return rows;
7992			}
7993	
7994			// Selector - function
7995			if ( typeof sel === 'function' ) {
7996				return rows.map( function (idx) {
7997					var row = aoData[ idx ];
7998					return sel( idx, row._aData, row.nTr ) ? idx : null;
7999				} );
8000			}
8001	
8002			// Selector - node
8003			if ( sel.nodeName ) {
8004				var rowIdx = sel._DT_RowIndex;  // Property added by DT for fast lookup
8005				var cellIdx = sel._DT_CellIndex;
8006	
8007				if ( rowIdx !== undefined ) {
8008					// Make sure that the row is actually still present in the table
8009					return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ?
8010						[ rowIdx ] :
8011						[];
8012				}
8013				else if ( cellIdx ) {
8014					return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ?
8015						[ cellIdx.row ] :
8016						[];
8017				}
8018				else {
8019					var host = $(sel).closest('*[data-dt-row]');
8020					return host.length ?
8021						[ host.data('dt-row') ] :
8022						[];
8023				}
8024			}
8025	
8026			// ID selector. Want to always be able to select rows by id, regardless
8027			// of if the tr element has been created or not, so can't rely upon
8028			// jQuery here - hence a custom implementation. This does not match
8029			// Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
8030			// but to select it using a CSS selector engine (like Sizzle or
8031			// querySelect) it would need to need to be escaped for some characters.
8032			// DataTables simplifies this for row selectors since you can select
8033			// only a row. A # indicates an id any anything that follows is the id -
8034			// unescaped.
8035			if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
8036				// get row index from id
8037				var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
8038				if ( rowObj !== undefined ) {
8039					return [ rowObj.idx ];
8040				}
8041	
8042				// need to fall through to jQuery in case there is DOM id that
8043				// matches
8044			}
8045			
8046			// Get nodes in the order from the `rows` array with null values removed
8047			var nodes = _removeEmpty(
8048				_pluck_order( settings.aoData, rows, 'nTr' )
8049			);
8050	
8051			// Selector - jQuery selector string, array of nodes or jQuery object/
8052			// As jQuery's .filter() allows jQuery objects to be passed in filter,
8053			// it also allows arrays, so this will cope with all three options
8054			return $(nodes)
8055				.filter( sel )
8056				.map( function () {
8057					return this._DT_RowIndex;
8058				} )
8059				.toArray();
8060		};
8061	
8062		var matched = _selector_run( 'row', selector, run, settings, opts );
8063	
8064		if (opts.order === 'current' || opts.order === 'applied') {
8065			_fnSortDisplay(settings, matched);
8066		}
8067	
8068		return matched;
8069	};
8070	
8071	
8072	_api_register( 'rows()', function ( selector, opts ) {
8073		// argument shifting
8074		if ( selector === undefined ) {
8075			selector = '';
8076		}
8077		else if ( $.isPlainObject( selector ) ) {
8078			opts = selector;
8079			selector = '';
8080		}
8081	
8082		opts = _selector_opts( opts );
8083	
8084		var inst = this.iterator( 'table', function ( settings ) {
8085			return __row_selector( settings, selector, opts );
8086		}, 1 );
8087	
8088		// Want argument shifting here and in __row_selector?
8089		inst.selector.rows = selector;
8090		inst.selector.opts = opts;
8091	
8092		return inst;
8093	} );
8094	
8095	_api_register( 'rows().nodes()', function () {
8096		return this.iterator( 'row', function ( settings, row ) {
8097			return settings.aoData[ row ].nTr || undefined;
8098		}, 1 );
8099	} );
8100	
8101	_api_register( 'rows().data()', function () {
8102		return this.iterator( true, 'rows', function ( settings, rows ) {
8103			return _pluck_order( settings.aoData, rows, '_aData' );
8104		}, 1 );
8105	} );
8106	
8107	_api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
8108		return this.iterator( 'row', function ( settings, row ) {
8109			var r = settings.aoData[ row ];
8110			return type === 'search' ? r._aFilterData : r._aSortData;
8111		}, 1 );
8112	} );
8113	
8114	_api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
8115		return this.iterator( 'row', function ( settings, row ) {
8116			_fnInvalidate( settings, row, src );
8117		} );
8118	} );
8119	
8120	_api_registerPlural( 'rows().indexes()', 'row().index()', function () {
8121		return this.iterator( 'row', function ( settings, row ) {
8122			return row;
8123		}, 1 );
8124	} );
8125	
8126	_api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
8127		var a = [];
8128		var context = this.context;
8129	
8130		// `iterator` will drop undefined values, but in this case we want them
8131		for ( var i=0, ien=context.length ; i<ien ; i++ ) {
8132			for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
8133				var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
8134				a.push( (hash === true ? '#' : '' )+ id );
8135			}
8136		}
8137	
8138		return new _Api( context, a );
8139	} );
8140	
8141	_api_registerPlural( 'rows().remove()', 'row().remove()', function () {
8142		this.iterator( 'row', function ( settings, row ) {
8143			var data = settings.aoData;
8144			var rowData = data[ row ];
8145	
8146			// Delete from the display arrays
8147			var idx = settings.aiDisplayMaster.indexOf(row);
8148			if (idx !== -1) {
8149				settings.aiDisplayMaster.splice(idx, 1);
8150			}
8151	
8152			// For server-side processing tables - subtract the deleted row from the count
8153			if ( settings._iRecordsDisplay > 0 ) {
8154				settings._iRecordsDisplay--;
8155			}
8156	
8157			// Check for an 'overflow' they case for displaying the table
8158			_fnLengthOverflow( settings );
8159	
8160			// Remove the row's ID reference if there is one
8161			var id = settings.rowIdFn( rowData._aData );
8162			if ( id !== undefined ) {
8163				delete settings.aIds[ id ];
8164			}
8165	
8166			data[row] = null;
8167		} );
8168	
8169		return this;
8170	} );
8171	
8172	
8173	_api_register( 'rows.add()', function ( rows ) {
8174		var newRows = this.iterator( 'table', function ( settings ) {
8175				var row, i, ien;
8176				var out = [];
8177	
8178				for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8179					row = rows[i];
8180	
8181					if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8182						out.push( _fnAddTr( settings, row )[0] );
8183					}
8184					else {
8185						out.push( _fnAddData( settings, row ) );
8186					}
8187				}
8188	
8189				return out;
8190			}, 1 );
8191	
8192		// Return an Api.rows() extended instance, so rows().nodes() etc can be used
8193		var modRows = this.rows( -1 );
8194		modRows.pop();
8195		_fnArrayApply(modRows, newRows);
8196	
8197		return modRows;
8198	} );
8199	
8200	
8201	
8202	
8203	
8204	/**
8205	 *
8206	 */
8207	_api_register( 'row()', function ( selector, opts ) {
8208		return _selector_first( this.rows( selector, opts ) );
8209	} );
8210	
8211	
8212	_api_register( 'row().data()', function ( data ) {
8213		var ctx = this.context;
8214	
8215		if ( data === undefined ) {
8216			// Get
8217			return ctx.length && this.length && this[0].length ?
8218				ctx[0].aoData[ this[0] ]._aData :
8219				undefined;
8220		}
8221	
8222		// Set
8223		var row = ctx[0].aoData[ this[0] ];
8224		row._aData = data;
8225	
8226		// If the DOM has an id, and the data source is an array
8227		if ( Array.isArray( data ) && row.nTr && row.nTr.id ) {
8228			_fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id );
8229		}
8230	
8231		// Automatically invalidate
8232		_fnInvalidate( ctx[0], this[0], 'data' );
8233	
8234		return this;
8235	} );
8236	
8237	
8238	_api_register( 'row().node()', function () {
8239		var ctx = this.context;
8240	
8241		if (ctx.length && this.length && this[0].length) {
8242			var row = ctx[0].aoData[ this[0] ];
8243	
8244			if (row && row.nTr) {
8245				return row.nTr;
8246			}
8247		}
8248	
8249		return null;
8250	} );
8251	
8252	
8253	_api_register( 'row.add()', function ( row ) {
8254		// Allow a jQuery object to be passed in - only a single row is added from
8255		// it though - the first element in the set
8256		if ( row instanceof $ && row.length ) {
8257			row = row[0];
8258		}
8259	
8260		var rows = this.iterator( 'table', function ( settings ) {
8261			if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8262				return _fnAddTr( settings, row )[0];
8263			}
8264			return _fnAddData( settings, row );
8265		} );
8266	
8267		// Return an Api.rows() extended instance, with the newly added row selected
8268		return this.row( rows[0] );
8269	} );
8270	
8271	
8272	$(document).on('plugin-init.dt', function (e, context) {
8273		var api = new _Api( context );
8274	
8275		api.on( 'stateSaveParams.DT', function ( e, settings, d ) {
8276			// This could be more compact with the API, but it is a lot faster as a simple
8277			// internal loop
8278			var idFn = settings.rowIdFn;
8279			var rows = settings.aiDisplayMaster;
8280			var ids = [];
8281	
8282			for (var i=0 ; i<rows.length ; i++) {
8283				var rowIdx = rows[i];
8284				var data = settings.aoData[rowIdx];
8285	
8286				if (data._detailsShow) {
8287					ids.push( '#' + idFn(data._aData) );
8288				}
8289			}
8290	
8291			d.childRows = ids;
8292		});
8293	
8294		// For future state loads (e.g. with StateRestore)
8295		api.on( 'stateLoaded.DT', function (e, settings, state) {
8296			__details_state_load( api, state );
8297		});
8298	
8299		// And the initial load state
8300		__details_state_load( api, api.state.loaded() );
8301	});
8302	
8303	var __details_state_load = function (api, state)
8304	{
8305		if ( state && state.childRows ) {
8306			api
8307				.rows( state.childRows.map(function (id) {
8308					// Escape any `:` characters from the row id. Accounts for
8309					// already escaped characters.
8310					return id.replace(/([^:\\]*(?:\\.[^:\\]*)*):/g, "$1\\:");
8311				}) )
8312				.every( function () {
8313					_fnCallbackFire( api.settings()[0], null, 'requestChild', [ this ] )
8314				});
8315		}
8316	}
8317	
8318	var __details_add = function ( ctx, row, data, klass )
8319	{
8320		// Convert to array of TR elements
8321		var rows = [];
8322		var addRow = function ( r, k ) {
8323			// Recursion to allow for arrays of jQuery objects
8324			if ( Array.isArray( r ) || r instanceof $ ) {
8325				for ( var i=0, ien=r.length ; i<ien ; i++ ) {
8326					addRow( r[i], k );
8327				}
8328				return;
8329			}
8330	
8331			// If we get a TR element, then just add it directly - up to the dev
8332			// to add the correct number of columns etc
8333			if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
8334				r.setAttribute( 'data-dt-row', row.idx );
8335				rows.push( r );
8336			}
8337			else {
8338				// Otherwise create a row with a wrapper
8339				var created = $('<tr><td></td></tr>')
8340					.attr( 'data-dt-row', row.idx )
8341					.addClass( k );
8342				
8343				$('td', created)
8344					.addClass( k )
8345					.html( r )[0].colSpan = _fnVisbleColumns( ctx );
8346	
8347				rows.push( created[0] );
8348			}
8349		};
8350	
8351		addRow( data, klass );
8352	
8353		if ( row._details ) {
8354			row._details.detach();
8355		}
8356	
8357		row._details = $(rows);
8358	
8359		// If the children were already shown, that state should be retained
8360		if ( row._detailsShow ) {
8361			row._details.insertAfter( row.nTr );
8362		}
8363	};
8364	
8365	
8366	// Make state saving of child row details async to allow them to be batch processed
8367	var __details_state = DataTable.util.throttle(
8368		function (ctx) {
8369			_fnSaveState( ctx[0] )
8370		},
8371		500
8372	);
8373	
8374	
8375	var __details_remove = function ( api, idx )
8376	{
8377		var ctx = api.context;
8378	
8379		if ( ctx.length ) {
8380			var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
8381	
8382			if ( row && row._details ) {
8383				row._details.remove();
8384	
8385				row._detailsShow = undefined;
8386				row._details = undefined;
8387				$( row.nTr ).removeClass( 'dt-hasChild' );
8388				__details_state( ctx );
8389			}
8390		}
8391	};
8392	
8393	
8394	var __details_display = function ( api, show ) {
8395		var ctx = api.context;
8396	
8397		if ( ctx.length && api.length ) {
8398			var row = ctx[0].aoData[ api[0] ];
8399	
8400			if ( row._details ) {
8401				row._detailsShow = show;
8402	
8403				if ( show ) {
8404					row._details.insertAfter( row.nTr );
8405					$( row.nTr ).addClass( 'dt-hasChild' );
8406				}
8407				else {
8408					row._details.detach();
8409					$( row.nTr ).removeClass( 'dt-hasChild' );
8410				}
8411	
8412				_fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] )
8413	
8414				__details_events( ctx[0] );
8415				__details_state( ctx );
8416			}
8417		}
8418	};
8419	
8420	
8421	var __details_events = function ( settings )
8422	{
8423		var api = new _Api( settings );
8424		var namespace = '.dt.DT_details';
8425		var drawEvent = 'draw'+namespace;
8426		var colvisEvent = 'column-sizing'+namespace;
8427		var destroyEvent = 'destroy'+namespace;
8428		var data = settings.aoData;
8429	
8430		api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
8431	
8432		if ( _pluck( data, '_details' ).length > 0 ) {
8433			// On each draw, insert the required elements into the document
8434			api.on( drawEvent, function ( e, ctx ) {
8435				if ( settings !== ctx ) {
8436					return;
8437				}
8438	
8439				api.rows( {page:'current'} ).eq(0).each( function (idx) {
8440					// Internal data grab
8441					var row = data[ idx ];
8442	
8443					if ( row._detailsShow ) {
8444						row._details.insertAfter( row.nTr );
8445					}
8446				} );
8447			} );
8448	
8449			// Column visibility change - update the colspan
8450			api.on( colvisEvent, function ( e, ctx ) {
8451				if ( settings !== ctx ) {
8452					return;
8453				}
8454	
8455				// Update the colspan for the details rows (note, only if it already has
8456				// a colspan)
8457				var row, visible = _fnVisbleColumns( ctx );
8458	
8459				for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8460					row = data[i];
8461	
8462					if ( row && row._details ) {
8463						row._details.each(function () {
8464							var el = $(this).children('td');
8465	
8466							if (el.length == 1) {
8467								el.attr('colspan', visible);
8468							}
8469						});
8470					}
8471				}
8472			} );
8473	
8474			// Table destroyed - nuke any child rows
8475			api.on( destroyEvent, function ( e, ctx ) {
8476				if ( settings !== ctx ) {
8477					return;
8478				}
8479	
8480				for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8481					if ( data[i] && data[i]._details ) {
8482						__details_remove( api, i );
8483					}
8484				}
8485			} );
8486		}
8487	};
8488	
8489	// Strings for the method names to help minification
8490	var _emp = '';
8491	var _child_obj = _emp+'row().child';
8492	var _child_mth = _child_obj+'()';
8493	
8494	// data can be:
8495	//  tr
8496	//  string
8497	//  jQuery or array of any of the above
8498	_api_register( _child_mth, function ( data, klass ) {
8499		var ctx = this.context;
8500	
8501		if ( data === undefined ) {
8502			// get
8503			return ctx.length && this.length && ctx[0].aoData[ this[0] ]
8504				? ctx[0].aoData[ this[0] ]._details
8505				: undefined;
8506		}
8507		else if ( data === true ) {
8508			// show
8509			this.child.show();
8510		}
8511		else if ( data === false ) {
8512			// remove
8513			__details_remove( this );
8514		}
8515		else if ( ctx.length && this.length ) {
8516			// set
8517			__details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8518		}
8519	
8520		return this;
8521	} );
8522	
8523	
8524	_api_register( [
8525		_child_obj+'.show()',
8526		_child_mth+'.show()' // only when `child()` was called with parameters (without
8527	], function () {         // it returns an object and this method is not executed)
8528		__details_display( this, true );
8529		return this;
8530	} );
8531	
8532	
8533	_api_register( [
8534		_child_obj+'.hide()',
8535		_child_mth+'.hide()' // only when `child()` was called with parameters (without
8536	], function () {         // it returns an object and this method is not executed)
8537		__details_display( this, false );
8538		return this;
8539	} );
8540	
8541	
8542	_api_register( [
8543		_child_obj+'.remove()',
8544		_child_mth+'.remove()' // only when `child()` was called with parameters (without
8545	], function () {           // it returns an object and this method is not executed)
8546		__details_remove( this );
8547		return this;
8548	} );
8549	
8550	
8551	_api_register( _child_obj+'.isShown()', function () {
8552		var ctx = this.context;
8553	
8554		if ( ctx.length && this.length && ctx[0].aoData[ this[0] ] ) {
8555			// _detailsShown as false or undefined will fall through to return false
8556			return ctx[0].aoData[ this[0] ]._detailsShow || false;
8557		}
8558		return false;
8559	} );
8560	
8561	
8562	
8563	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8564	 * Columns
8565	 *
8566	 * {integer}           - column index (>=0 count from left, <0 count from right)
8567	 * "{integer}:visIdx"  - visible column index (i.e. translate to column index)  (>=0 count from left, <0 count from right)
8568	 * "{integer}:visible" - alias for {integer}:visIdx  (>=0 count from left, <0 count from right)
8569	 * "{string}:name"     - column name
8570	 * "{string}"          - jQuery selector on column header nodes
8571	 *
8572	 */
8573	
8574	// can be an array of these items, comma separated list, or an array of comma
8575	// separated lists
8576	
8577	var __re_column_selector = /^([^:]+)?:(name|title|visIdx|visible)$/;
8578	
8579	
8580	// r1 and r2 are redundant - but it means that the parameters match for the
8581	// iterator callback in columns().data()
8582	var __columnData = function ( settings, column, r1, r2, rows, type ) {
8583		var a = [];
8584		for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
8585			a.push( _fnGetCellData( settings, rows[row], column, type ) );
8586		}
8587		return a;
8588	};
8589	
8590	
8591	var __column_header = function ( settings, column, row ) {
8592		var header = settings.aoHeader;
8593		var target = row !== undefined
8594			? row
8595			: settings.bSortCellsTop // legacy support
8596				? 0
8597				: header.length - 1;
8598	
8599		return header[target][column].cell;
8600	};
8601	
8602	var __column_selector = function ( settings, selector, opts )
8603	{
8604		var
8605			columns = settings.aoColumns,
8606			names = _pluck( columns, 'sName' ),
8607			titles = _pluck( columns, 'sTitle' ),
8608			cells = DataTable.util.get('[].[].cell')(settings.aoHeader),
8609			nodes = _unique( _flatten([], cells) );
8610		
8611		var run = function ( s ) {
8612			var selInt = _intVal( s );
8613	
8614			// Selector - all
8615			if ( s === '' ) {
8616				return _range( columns.length );
8617			}
8618	
8619			// Selector - index
8620			if ( selInt !== null ) {
8621				return [ selInt >= 0 ?
8622					selInt : // Count from left
8623					columns.length + selInt // Count from right (+ because its a negative value)
8624				];
8625			}
8626	
8627			// Selector = function
8628			if ( typeof s === 'function' ) {
8629				var rows = _selector_row_indexes( settings, opts );
8630	
8631				return columns.map(function (col, idx) {
8632					return s(
8633							idx,
8634							__columnData( settings, idx, 0, 0, rows ),
8635							__column_header( settings, idx )
8636						) ? idx : null;
8637				});
8638			}
8639	
8640			// jQuery or string selector
8641			var match = typeof s === 'string' ?
8642				s.match( __re_column_selector ) :
8643				'';
8644	
8645			if ( match ) {
8646				switch( match[2] ) {
8647					case 'visIdx':
8648					case 'visible':
8649						// Selector is a column index
8650						if (match[1] && match[1].match(/^\d+$/)) {
8651							var idx = parseInt( match[1], 10 );
8652	
8653							// Visible index given, convert to column index
8654							if ( idx < 0 ) {
8655								// Counting from the right
8656								var visColumns = columns.map( function (col,i) {
8657									return col.bVisible ? i : null;
8658								} );
8659								return [ visColumns[ visColumns.length + idx ] ];
8660							}
8661							// Counting from the left
8662							return [ _fnVisibleToColumnIndex( settings, idx ) ];
8663						}
8664						
8665						return columns.map( function (col, idx) {
8666							// Not visible, can't match
8667							if (! col.bVisible) {
8668								return null;
8669							}
8670	
8671							// Selector
8672							if (match[1]) {
8673								return $(nodes[idx]).filter(match[1]).length > 0 ? idx : null;
8674							}
8675	
8676							// `:visible` on its own
8677							return idx;
8678						} );
8679	
8680					case 'name':
8681						// match by name. `names` is column index complete and in order
8682						return names.map( function (name, i) {
8683							return name === match[1] ? i : null;
8684						} );
8685	
8686					case 'title':
8687						// match by column title
8688						return titles.map( function (title, i) {
8689							return title === match[1] ? i : null;
8690						} );
8691	
8692					default:
8693						return [];
8694				}
8695			}
8696	
8697			// Cell in the table body
8698			if ( s.nodeName && s._DT_CellIndex ) {
8699				return [ s._DT_CellIndex.column ];
8700			}
8701	
8702			// jQuery selector on the TH elements for the columns
8703			var jqResult = $( nodes )
8704				.filter( s )
8705				.map( function () {
8706					return _fnColumnsFromHeader( this ); // `nodes` is column index complete and in order
8707				} )
8708				.toArray()
8709				.sort(function (a, b) {
8710					return a - b;
8711				});
8712	
8713			if ( jqResult.length || ! s.nodeName ) {
8714				return jqResult;
8715			}
8716	
8717			// Otherwise a node which might have a `dt-column` data attribute, or be
8718			// a child or such an element
8719			var host = $(s).closest('*[data-dt-column]');
8720			return host.length ?
8721				[ host.data('dt-column') ] :
8722				[];
8723		};
8724	
8725		return _selector_run( 'column', selector, run, settings, opts );
8726	};
8727	
8728	
8729	var __setColumnVis = function ( settings, column, vis ) {
8730		var
8731			cols = settings.aoColumns,
8732			col  = cols[ column ],
8733			data = settings.aoData,
8734			cells, i, ien, tr;
8735	
8736		// Get
8737		if ( vis === undefined ) {
8738			return col.bVisible;
8739		}
8740	
8741		// Set
8742		// No change
8743		if ( col.bVisible === vis ) {
8744			return false;
8745		}
8746	
8747		if ( vis ) {
8748			// Insert column
8749			// Need to decide if we should use appendChild or insertBefore
8750			var insertBefore = _pluck(cols, 'bVisible').indexOf(true, column+1);
8751	
8752			for ( i=0, ien=data.length ; i<ien ; i++ ) {
8753				if (data[i]) {
8754					tr = data[i].nTr;
8755					cells = data[i].anCells;
8756	
8757					if ( tr ) {
8758						// insertBefore can act like appendChild if 2nd arg is null
8759						tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
8760					}
8761				}
8762			}
8763		}
8764		else {
8765			// Remove column
8766			$( _pluck( settings.aoData, 'anCells', column ) ).detach();
8767		}
8768	
8769		// Common actions
8770		col.bVisible = vis;
8771	
8772		_colGroup(settings);
8773		
8774		return true;
8775	};
8776	
8777	
8778	_api_register( 'columns()', function ( selector, opts ) {
8779		// argument shifting
8780		if ( selector === undefined ) {
8781			selector = '';
8782		}
8783		else if ( $.isPlainObject( selector ) ) {
8784			opts = selector;
8785			selector = '';
8786		}
8787	
8788		opts = _selector_opts( opts );
8789	
8790		var inst = this.iterator( 'table', function ( settings ) {
8791			return __column_selector( settings, selector, opts );
8792		}, 1 );
8793	
8794		// Want argument shifting here and in _row_selector?
8795		inst.selector.cols = selector;
8796		inst.selector.opts = opts;
8797	
8798		return inst;
8799	} );
8800	
8801	_api_registerPlural( 'columns().header()', 'column().header()', function ( row ) {
8802		return this.iterator( 'column', function (settings, column) {
8803			return __column_header(settings, column, row);
8804		}, 1 );
8805	} );
8806	
8807	_api_registerPlural( 'columns().footer()', 'column().footer()', function ( row ) {
8808		return this.iterator( 'column', function ( settings, column ) {
8809			var footer = settings.aoFooter;
8810	
8811			if (! footer.length) {
8812				return null;
8813			}
8814	
8815			return settings.aoFooter[row !== undefined ? row : 0][column].cell;
8816		}, 1 );
8817	} );
8818	
8819	_api_registerPlural( 'columns().data()', 'column().data()', function () {
8820		return this.iterator( 'column-rows', __columnData, 1 );
8821	} );
8822	
8823	_api_registerPlural( 'columns().render()', 'column().render()', function ( type ) {
8824		return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8825			return __columnData( settings, column, i, j, rows, type );
8826		}, 1 );
8827	} );
8828	
8829	_api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8830		return this.iterator( 'column', function ( settings, column ) {
8831			return settings.aoColumns[column].mData;
8832		}, 1 );
8833	} );
8834	
8835	_api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8836		return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8837			return _pluck_order( settings.aoData, rows,
8838				type === 'search' ? '_aFilterData' : '_aSortData', column
8839			);
8840		}, 1 );
8841	} );
8842	
8843	_api_registerPlural( 'columns().init()', 'column().init()', function () {
8844		return this.iterator( 'column', function ( settings, column ) {
8845			return settings.aoColumns[column];
8846		}, 1 );
8847	} );
8848	
8849	_api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8850		return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8851			return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8852		}, 1 );
8853	} );
8854	
8855	_api_registerPlural( 'columns().titles()', 'column().title()', function (title, row) {
8856		return this.iterator( 'column', function ( settings, column ) {
8857			// Argument shifting
8858			if (typeof title === 'number') {
8859				row = title;
8860				title = undefined;
8861			}
8862	
8863			var span = $('span.dt-column-title', this.column(column).header(row));
8864	
8865			if (title !== undefined) {
8866				span.html(title);
8867				return this;
8868			}
8869	
8870			return span.html();
8871		}, 1 );
8872	} );
8873	
8874	_api_registerPlural( 'columns().types()', 'column().type()', function () {
8875		return this.iterator( 'column', function ( settings, column ) {
8876			var type = settings.aoColumns[column].sType;
8877	
8878			// If the type was invalidated, then resolve it. This actually does
8879			// all columns at the moment. Would only happen once if getting all
8880			// column's data types.
8881			if (! type) {
8882				_fnColumnTypes(settings);
8883			}
8884	
8885			return type;
8886		}, 1 );
8887	} );
8888	
8889	_api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
8890		var that = this;
8891		var changed = [];
8892		var ret = this.iterator( 'column', function ( settings, column ) {
8893			if ( vis === undefined ) {
8894				return settings.aoColumns[ column ].bVisible;
8895			} // else
8896			
8897			if (__setColumnVis( settings, column, vis )) {
8898				changed.push(column);
8899			}
8900		} );
8901	
8902		// Group the column visibility changes
8903		if ( vis !== undefined ) {
8904			this.iterator( 'table', function ( settings ) {
8905				// Redraw the header after changes
8906				_fnDrawHead( settings, settings.aoHeader );
8907				_fnDrawHead( settings, settings.aoFooter );
8908		
8909				// Update colspan for no records display. Child rows and extensions will use their own
8910				// listeners to do this - only need to update the empty table item here
8911				if ( ! settings.aiDisplay.length ) {
8912					$(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings));
8913				}
8914		
8915				_fnSaveState( settings );
8916	
8917				// Second loop once the first is done for events
8918				that.iterator( 'column', function ( settings, column ) {
8919					if (changed.includes(column)) {
8920						_fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
8921					}
8922				} );
8923	
8924				if ( changed.length && (calc === undefined || calc) ) {
8925					that.columns.adjust();
8926				}
8927			});
8928		}
8929	
8930		return ret;
8931	} );
8932	
8933	_api_registerPlural( 'columns().widths()', 'column().width()', function () {
8934		// Injects a fake row into the table for just a moment so the widths can
8935		// be read, regardless of colspan in the header and rows being present in
8936		// the body
8937		var columns = this.columns(':visible').count();
8938		var row = $('<tr>').html('<td>' + Array(columns).join('</td><td>') + '</td>');
8939	
8940		$(this.table().body()).append(row);
8941	
8942		var widths = row.children().map(function () {
8943			return $(this).outerWidth();
8944		});
8945	
8946		row.remove();
8947		
8948		return this.iterator( 'column', function ( settings, column ) {
8949			var visIdx = _fnColumnIndexToVisible( settings, column );
8950	
8951			return visIdx !== null ? widths[visIdx] : 0;
8952		}, 1);
8953	} );
8954	
8955	_api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8956		return this.iterator( 'column', function ( settings, column ) {
8957			return type === 'visible' ?
8958				_fnColumnIndexToVisible( settings, column ) :
8959				column;
8960		}, 1 );
8961	} );
8962	
8963	_api_register( 'columns.adjust()', function () {
8964		return this.iterator( 'table', function ( settings ) {
8965			// Force a column sizing to happen with a manual call - otherwise it can skip
8966			// if the size hasn't changed
8967			settings.containerWidth = -1;
8968	
8969			_fnAdjustColumnSizing( settings );
8970		}, 1 );
8971	} );
8972	
8973	_api_register( 'column.index()', function ( type, idx ) {
8974		if ( this.context.length !== 0 ) {
8975			var ctx = this.context[0];
8976	
8977			if ( type === 'fromVisible' || type === 'toData' ) {
8978				return _fnVisibleToColumnIndex( ctx, idx );
8979			}
8980			else if ( type === 'fromData' || type === 'toVisible' ) {
8981				return _fnColumnIndexToVisible( ctx, idx );
8982			}
8983		}
8984	} );
8985	
8986	_api_register( 'column()', function ( selector, opts ) {
8987		return _selector_first( this.columns( selector, opts ) );
8988	} );
8989	
8990	var __cell_selector = function ( settings, selector, opts )
8991	{
8992		var data = settings.aoData;
8993		var rows = _selector_row_indexes( settings, opts );
8994		var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
8995		var allCells = $(_flatten( [], cells ));
8996		var row;
8997		var columns = settings.aoColumns.length;
8998		var a, i, ien, j, o, host;
8999	
9000		var run = function ( s ) {
9001			var fnSelector = typeof s === 'function';
9002	
9003			if ( s === null || s === undefined || fnSelector ) {
9004				// All cells and function selectors
9005				a = [];
9006	
9007				for ( i=0, ien=rows.length ; i<ien ; i++ ) {
9008					row = rows[i];
9009	
9010					for ( j=0 ; j<columns ; j++ ) {
9011						o = {
9012							row: row,
9013							column: j
9014						};
9015	
9016						if ( fnSelector ) {
9017							// Selector - function
9018							host = data[ row ];
9019	
9020							if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
9021								a.push( o );
9022							}
9023						}
9024						else {
9025							// Selector - all
9026							a.push( o );
9027						}
9028					}
9029				}
9030	
9031				return a;
9032			}
9033			
9034			// Selector - index
9035			if ( $.isPlainObject( s ) ) {
9036				// Valid cell index and its in the array of selectable rows
9037				return s.column !== undefined && s.row !== undefined && rows.indexOf(s.row) !== -1 ?
9038					[s] :
9039					[];
9040			}
9041	
9042			// Selector - jQuery filtered cells
9043			var jqResult = allCells
9044				.filter( s )
9045				.map( function (i, el) {
9046					return { // use a new object, in case someone changes the values
9047						row:    el._DT_CellIndex.row,
9048						column: el._DT_CellIndex.column
9049					};
9050				} )
9051				.toArray();
9052	
9053			if ( jqResult.length || ! s.nodeName ) {
9054				return jqResult;
9055			}
9056	
9057			// Otherwise the selector is a node, and there is one last option - the
9058			// element might be a child of an element which has dt-row and dt-column
9059			// data attributes
9060			host = $(s).closest('*[data-dt-row]');
9061			return host.length ?
9062				[ {
9063					row: host.data('dt-row'),
9064					column: host.data('dt-column')
9065				} ] :
9066				[];
9067		};
9068	
9069		return _selector_run( 'cell', selector, run, settings, opts );
9070	};
9071	
9072	
9073	
9074	
9075	_api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
9076		// Argument shifting
9077		if ( $.isPlainObject( rowSelector ) ) {
9078			// Indexes
9079			if ( rowSelector.row === undefined ) {
9080				// Selector options in first parameter
9081				opts = rowSelector;
9082				rowSelector = null;
9083			}
9084			else {
9085				// Cell index objects in first parameter
9086				opts = columnSelector;
9087				columnSelector = null;
9088			}
9089		}
9090		if ( $.isPlainObject( columnSelector ) ) {
9091			opts = columnSelector;
9092			columnSelector = null;
9093		}
9094	
9095		// Cell selector
9096		if ( columnSelector === null || columnSelector === undefined ) {
9097			return this.iterator( 'table', function ( settings ) {
9098				return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
9099			} );
9100		}
9101	
9102		// The default built in options need to apply to row and columns
9103		var internalOpts = opts ? {
9104			page: opts.page,
9105			order: opts.order,
9106			search: opts.search
9107		} : {};
9108	
9109		// Row + column selector
9110		var columns = this.columns( columnSelector, internalOpts );
9111		var rows = this.rows( rowSelector, internalOpts );
9112		var i, ien, j, jen;
9113	
9114		var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) {
9115			var a = [];
9116	
9117			for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
9118				for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
9119					a.push( {
9120						row:    rows[idx][i],
9121						column: columns[idx][j]
9122					} );
9123				}
9124			}
9125	
9126			return a;
9127		}, 1 );
9128	
9129		// There is currently only one extension which uses a cell selector extension
9130		// It is a _major_ performance drag to run this if it isn't needed, so this is
9131		// an extension specific check at the moment
9132		var cells = opts && opts.selected ?
9133			this.cells( cellsNoOpts, opts ) :
9134			cellsNoOpts;
9135	
9136		$.extend( cells.selector, {
9137			cols: columnSelector,
9138			rows: rowSelector,
9139			opts: opts
9140		} );
9141	
9142		return cells;
9143	} );
9144	
9145	
9146	_api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
9147		return this.iterator( 'cell', function ( settings, row, column ) {
9148			var data = settings.aoData[ row ];
9149	
9150			return data && data.anCells ?
9151				data.anCells[ column ] :
9152				undefined;
9153		}, 1 );
9154	} );
9155	
9156	
9157	_api_register( 'cells().data()', function () {
9158		return this.iterator( 'cell', function ( settings, row, column ) {
9159			return _fnGetCellData( settings, row, column );
9160		}, 1 );
9161	} );
9162	
9163	
9164	_api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
9165		type = type === 'search' ? '_aFilterData' : '_aSortData';
9166	
9167		return this.iterator( 'cell', function ( settings, row, column ) {
9168			return settings.aoData[ row ][ type ][ column ];
9169		}, 1 );
9170	} );
9171	
9172	
9173	_api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
9174		return this.iterator( 'cell', function ( settings, row, column ) {
9175			return _fnGetCellData( settings, row, column, type );
9176		}, 1 );
9177	} );
9178	
9179	
9180	_api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
9181		return this.iterator( 'cell', function ( settings, row, column ) {
9182			return {
9183				row: row,
9184				column: column,
9185				columnVisible: _fnColumnIndexToVisible( settings, column )
9186			};
9187		}, 1 );
9188	} );
9189	
9190	
9191	_api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
9192		return this.iterator( 'cell', function ( settings, row, column ) {
9193			_fnInvalidate( settings, row, src, column );
9194		} );
9195	} );
9196	
9197	
9198	
9199	_api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
9200		return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
9201	} );
9202	
9203	
9204	_api_register( 'cell().data()', function ( data ) {
9205		var ctx = this.context;
9206		var cell = this[0];
9207	
9208		if ( data === undefined ) {
9209			// Get
9210			return ctx.length && cell.length ?
9211				_fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
9212				undefined;
9213		}
9214	
9215		// Set
9216		_fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
9217		_fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
9218	
9219		return this;
9220	} );
9221	
9222	
9223	
9224	/**
9225	 * Get current ordering (sorting) that has been applied to the table.
9226	 *
9227	 * @returns {array} 2D array containing the sorting information for the first
9228	 *   table in the current context. Each element in the parent array represents
9229	 *   a column being sorted upon (i.e. multi-sorting with two columns would have
9230	 *   2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
9231	 *   the column index that the sorting condition applies to, the second is the
9232	 *   direction of the sort (`desc` or `asc`) and, optionally, the third is the
9233	 *   index of the sorting order from the `column.sorting` initialisation array.
9234	 *//**
9235	 * Set the ordering for the table.
9236	 *
9237	 * @param {integer} order Column index to sort upon.
9238	 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
9239	 * @returns {DataTables.Api} this
9240	 *//**
9241	 * Set the ordering for the table.
9242	 *
9243	 * @param {array} order 1D array of sorting information to be applied.
9244	 * @param {array} [...] Optional additional sorting conditions
9245	 * @returns {DataTables.Api} this
9246	 *//**
9247	 * Set the ordering for the table.
9248	 *
9249	 * @param {array} order 2D array of sorting information to be applied.
9250	 * @returns {DataTables.Api} this
9251	 */
9252	_api_register( 'order()', function ( order, dir ) {
9253		var ctx = this.context;
9254		var args = Array.prototype.slice.call( arguments );
9255	
9256		if ( order === undefined ) {
9257			// get
9258			return ctx.length !== 0 ?
9259				ctx[0].aaSorting :
9260				undefined;
9261		}
9262	
9263		// set
9264		if ( typeof order === 'number' ) {
9265			// Simple column / direction passed in
9266			order = [ [ order, dir ] ];
9267		}
9268		else if ( args.length > 1 ) {
9269			// Arguments passed in (list of 1D arrays)
9270			order = args;
9271		}
9272		// otherwise a 2D array was passed in
9273	
9274		return this.iterator( 'table', function ( settings ) {
9275			settings.aaSorting = Array.isArray(order) ? order.slice() : order;
9276		} );
9277	} );
9278	
9279	
9280	/**
9281	 * Attach a sort listener to an element for a given column
9282	 *
9283	 * @param {node|jQuery|string} node Identifier for the element(s) to attach the
9284	 *   listener to. This can take the form of a single DOM node, a jQuery
9285	 *   collection of nodes or a jQuery selector which will identify the node(s).
9286	 * @param {integer} column the column that a click on this node will sort on
9287	 * @param {function} [callback] callback function when sort is run
9288	 * @returns {DataTables.Api} this
9289	 */
9290	_api_register( 'order.listener()', function ( node, column, callback ) {
9291		return this.iterator( 'table', function ( settings ) {
9292			_fnSortAttachListener(settings, node, {}, column, callback);
9293		} );
9294	} );
9295	
9296	
9297	_api_register( 'order.fixed()', function ( set ) {
9298		if ( ! set ) {
9299			var ctx = this.context;
9300			var fixed = ctx.length ?
9301				ctx[0].aaSortingFixed :
9302				undefined;
9303	
9304			return Array.isArray( fixed ) ?
9305				{ pre: fixed } :
9306				fixed;
9307		}
9308	
9309		return this.iterator( 'table', function ( settings ) {
9310			settings.aaSortingFixed = $.extend( true, {}, set );
9311		} );
9312	} );
9313	
9314	
9315	// Order by the selected column(s)
9316	_api_register( [
9317		'columns().order()',
9318		'column().order()'
9319	], function ( dir ) {
9320		var that = this;
9321	
9322		if ( ! dir ) {
9323			return this.iterator( 'column', function ( settings, idx ) {
9324				var sort = _fnSortFlatten( settings );
9325	
9326				for ( var i=0, ien=sort.length ; i<ien ; i++ ) {
9327					if ( sort[i].col === idx ) {
9328						return sort[i].dir;
9329					}
9330				}
9331	
9332				return null;
9333			}, 1 );
9334		}
9335		else {
9336			return this.iterator( 'table', function ( settings, i ) {
9337				settings.aaSorting = that[i].map( function (col) {
9338					return [ col, dir ];
9339				} );
9340			} );
9341		}
9342	} );
9343	
9344	_api_registerPlural('columns().orderable()', 'column().orderable()', function ( directions ) {
9345		return this.iterator( 'column', function ( settings, idx ) {
9346			var col = settings.aoColumns[idx];
9347	
9348			return directions ?
9349				col.asSorting :
9350				col.bSortable;
9351		}, 1 );
9352	} );
9353	
9354	
9355	_api_register( 'processing()', function ( show ) {
9356		return this.iterator( 'table', function ( ctx ) {
9357			_fnProcessingDisplay( ctx, show );
9358		} );
9359	} );
9360	
9361	
9362	_api_register( 'search()', function ( input, regex, smart, caseInsen ) {
9363		var ctx = this.context;
9364	
9365		if ( input === undefined ) {
9366			// get
9367			return ctx.length !== 0 ?
9368				ctx[0].oPreviousSearch.search :
9369				undefined;
9370		}
9371	
9372		// set
9373		return this.iterator( 'table', function ( settings ) {
9374			if ( ! settings.oFeatures.bFilter ) {
9375				return;
9376			}
9377	
9378			if (typeof regex === 'object') {
9379				// New style options to pass to the search builder
9380				_fnFilterComplete( settings, $.extend( settings.oPreviousSearch, regex, {
9381					search: input
9382				} ) );
9383			}
9384			else {
9385				// Compat for the old options
9386				_fnFilterComplete( settings, $.extend( settings.oPreviousSearch, {
9387					search: input,
9388					regex:  regex === null ? false : regex,
9389					smart:  smart === null ? true  : smart,
9390					caseInsensitive: caseInsen === null ? true : caseInsen
9391				} ) );
9392			}
9393		} );
9394	} );
9395	
9396	_api_register( 'search.fixed()', function ( name, search ) {
9397		var ret = this.iterator( true, 'table', function ( settings ) {
9398			var fixed = settings.searchFixed;
9399	
9400			if (! name) {
9401				return Object.keys(fixed)
9402			}
9403			else if (search === undefined) {
9404				return fixed[name];
9405			}
9406			else if (search === null) {
9407				delete fixed[name];
9408			}
9409			else {
9410				fixed[name] = search;
9411			}
9412	
9413			return this;
9414		} );
9415	
9416		return name !== undefined && search === undefined
9417			? ret[0]
9418			: ret;
9419	} );
9420	
9421	_api_registerPlural(
9422		'columns().search()',
9423		'column().search()',
9424		function ( input, regex, smart, caseInsen ) {
9425			return this.iterator( 'column', function ( settings, column ) {
9426				var preSearch = settings.aoPreSearchCols;
9427	
9428				if ( input === undefined ) {
9429					// get
9430					return preSearch[ column ].search;
9431				}
9432	
9433				// set
9434				if ( ! settings.oFeatures.bFilter ) {
9435					return;
9436				}
9437	
9438				if (typeof regex === 'object') {
9439					// New style options to pass to the search builder
9440					$.extend( preSearch[ column ], regex, {
9441						search: input
9442					} );
9443				}
9444				else {
9445					// Old style (with not all options available)
9446					$.extend( preSearch[ column ], {
9447						search: input,
9448						regex:  regex === null ? false : regex,
9449						smart:  smart === null ? true  : smart,
9450						caseInsensitive: caseInsen === null ? true : caseInsen
9451					} );
9452				}
9453	
9454				_fnFilterComplete( settings, settings.oPreviousSearch );
9455			} );
9456		}
9457	);
9458	
9459	_api_register([
9460			'columns().search.fixed()',
9461			'column().search.fixed()'
9462		],
9463		function ( name, search ) {
9464			var ret = this.iterator( true, 'column', function ( settings, colIdx ) {
9465				var fixed = settings.aoColumns[colIdx].searchFixed;
9466	
9467				if (! name) {
9468					return Object.keys(fixed)
9469				}
9470				else if (search === undefined) {
9471					return fixed[name];
9472				}
9473				else if (search === null) {
9474					delete fixed[name];
9475				}
9476				else {
9477					fixed[name] = search;
9478				}
9479	
9480				return this;
9481			} );
9482	
9483			return name !== undefined && search === undefined
9484				? ret[0]
9485				: ret;
9486		}
9487	);
9488	/*
9489	 * State API methods
9490	 */
9491	
9492	_api_register( 'state()', function ( set, ignoreTime ) {
9493		// getter
9494		if ( ! set ) {
9495			return this.context.length ?
9496				this.context[0].oSavedState :
9497				null;
9498		}
9499	
9500		var setMutate = $.extend( true, {}, set );
9501	
9502		// setter
9503		return this.iterator( 'table', function ( settings ) {
9504			if ( ignoreTime !== false ) {
9505				setMutate.time = +new Date() + 100;
9506			}
9507	
9508			_fnImplementState( settings, setMutate, function(){} );
9509		} );
9510	} );
9511	
9512	
9513	_api_register( 'state.clear()', function () {
9514		return this.iterator( 'table', function ( settings ) {
9515			// Save an empty object
9516			settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
9517		} );
9518	} );
9519	
9520	
9521	_api_register( 'state.loaded()', function () {
9522		return this.context.length ?
9523			this.context[0].oLoadedState :
9524			null;
9525	} );
9526	
9527	
9528	_api_register( 'state.save()', function () {
9529		return this.iterator( 'table', function ( settings ) {
9530			_fnSaveState( settings );
9531		} );
9532	} );
9533	
9534	// Can be assigned in DateTable.use() - note luxon and moment vars are in helpers.js
9535	var __bootstrap;
9536	var __foundation;
9537	
9538	/**
9539	 * Set the libraries that DataTables uses, or the global objects.
9540	 * Note that the arguments can be either way around (legacy support)
9541	 * and the second is optional. See docs.
9542	 */
9543	DataTable.use = function (arg1, arg2) {
9544		// Reverse arguments for legacy support
9545		var module = typeof arg1 === 'string'
9546			? arg2
9547			: arg1;
9548		var type = typeof arg2 === 'string'
9549			? arg2
9550			: arg1;
9551	
9552		// Getter
9553		if (module === undefined && typeof type === 'string') {
9554			switch (type) {
9555				case 'lib':
9556				case 'jq':
9557					return $;
9558	
9559				case 'win':
9560					return window;
9561	
9562				case 'datetime':
9563					return DataTable.DateTime;
9564	
9565				case 'luxon':
9566					return __luxon;
9567	
9568				case 'moment':
9569					return __moment;
9570	
9571				case 'bootstrap':
9572					// Use local if set, otherwise try window, which could be undefined
9573					return __bootstrap || window.bootstrap;
9574	
9575				case 'foundation':
9576					// Ditto
9577					return __foundation || window.Foundation;
9578	
9579				default:
9580					return null;
9581			}
9582		}
9583	
9584		// Setter
9585		if (type === 'lib' || type === 'jq' || (module && module.fn && module.fn.jquery)) {
9586			$ = module;
9587		}
9588		else if (type === 'win' || (module && module.document)) {
9589			window = module;
9590			document = module.document;
9591		}
9592		else if (type === 'datetime' || (module && module.type === 'DateTime')) {
9593			DataTable.DateTime = module;
9594		}
9595		else if (type === 'luxon' || (module && module.FixedOffsetZone)) {
9596			__luxon = module;
9597		}
9598		else if (type === 'moment' || (module && module.isMoment)) {
9599			__moment = module;
9600		}
9601		else if (type === 'bootstrap' || (module && module.Modal && module.Modal.NAME === 'modal'))
9602		{
9603			// This is currently for BS5 only. BS3/4 attach to jQuery, so no need to use `.use()`
9604			__bootstrap = module;
9605		}
9606		else if (type === 'foundation' || (module && module.Reveal)) {
9607			__foundation = module;
9608		}
9609	}
9610	
9611	/**
9612	 * CommonJS factory function pass through. This will check if the arguments
9613	 * given are a window object or a jQuery object. If so they are set
9614	 * accordingly.
9615	 * @param {*} root Window
9616	 * @param {*} jq jQUery
9617	 * @returns {boolean} Indicator
9618	 */
9619	DataTable.factory = function (root, jq) {
9620		var is = false;
9621	
9622		// Test if the first parameter is a window object
9623		if (root && root.document) {
9624			window = root;
9625			document = root.document;
9626		}
9627	
9628		// Test if the second parameter is a jQuery object
9629		if (jq && jq.fn && jq.fn.jquery) {
9630			$ = jq;
9631			is = true;
9632		}
9633	
9634		return is;
9635	}
9636	
9637	/**
9638	 * Provide a common method for plug-ins to check the version of DataTables being
9639	 * used, in order to ensure compatibility.
9640	 *
9641	 *  @param {string} version Version string to check for, in the format "X.Y.Z".
9642	 *    Note that the formats "X" and "X.Y" are also acceptable.
9643	 *  @param {string} [version2=current DataTables version] As above, but optional.
9644	 *   If not given the current DataTables version will be used.
9645	 *  @returns {boolean} true if this version of DataTables is greater or equal to
9646	 *    the required version, or false if this version of DataTales is not
9647	 *    suitable
9648	 *  @static
9649	 *  @dtopt API-Static
9650	 *
9651	 *  @example
9652	 *    alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
9653	 */
9654	DataTable.versionCheck = function( version, version2 )
9655	{
9656		var aThis = version2 ?
9657			version2.split('.') :
9658			DataTable.version.split('.');
9659		var aThat = version.split('.');
9660		var iThis, iThat;
9661	
9662		for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
9663			iThis = parseInt( aThis[i], 10 ) || 0;
9664			iThat = parseInt( aThat[i], 10 ) || 0;
9665	
9666			// Parts are the same, keep comparing
9667			if (iThis === iThat) {
9668				continue;
9669			}
9670	
9671			// Parts are different, return immediately
9672			return iThis > iThat;
9673		}
9674	
9675		return true;
9676	};
9677	
9678	
9679	/**
9680	 * Check if a `<table>` node is a DataTable table already or not.
9681	 *
9682	 *  @param {node|jquery|string} table Table node, jQuery object or jQuery
9683	 *      selector for the table to test. Note that if more than more than one
9684	 *      table is passed on, only the first will be checked
9685	 *  @returns {boolean} true the table given is a DataTable, or false otherwise
9686	 *  @static
9687	 *  @dtopt API-Static
9688	 *
9689	 *  @example
9690	 *    if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
9691	 *      $('#example').dataTable();
9692	 *    }
9693	 */
9694	DataTable.isDataTable = function ( table )
9695	{
9696		var t = $(table).get(0);
9697		var is = false;
9698	
9699		if ( table instanceof DataTable.Api ) {
9700			return true;
9701		}
9702	
9703		$.each( DataTable.settings, function (i, o) {
9704			var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
9705			var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
9706	
9707			if ( o.nTable === t || head === t || foot === t ) {
9708				is = true;
9709			}
9710		} );
9711	
9712		return is;
9713	};
9714	
9715	
9716	/**
9717	 * Get all DataTable tables that have been initialised - optionally you can
9718	 * select to get only currently visible tables.
9719	 *
9720	 *  @param {boolean} [visible=false] Flag to indicate if you want all (default)
9721	 *    or visible tables only.
9722	 *  @returns {array} Array of `table` nodes (not DataTable instances) which are
9723	 *    DataTables
9724	 *  @static
9725	 *  @dtopt API-Static
9726	 *
9727	 *  @example
9728	 *    $.each( $.fn.dataTable.tables(true), function () {
9729	 *      $(table).DataTable().columns.adjust();
9730	 *    } );
9731	 */
9732	DataTable.tables = function ( visible )
9733	{
9734		var api = false;
9735	
9736		if ( $.isPlainObject( visible ) ) {
9737			api = visible.api;
9738			visible = visible.visible;
9739		}
9740	
9741		var a = DataTable.settings
9742			.filter( function (o) {
9743				return !visible || (visible && $(o.nTable).is(':visible')) 
9744					? true
9745					: false;
9746			} )
9747			.map( function (o) {
9748				return o.nTable;
9749			});
9750	
9751		return api ?
9752			new _Api( a ) :
9753			a;
9754	};
9755	
9756	
9757	/**
9758	 * Convert from camel case parameters to Hungarian notation. This is made public
9759	 * for the extensions to provide the same ability as DataTables core to accept
9760	 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
9761	 * parameters.
9762	 *
9763	 *  @param {object} src The model object which holds all parameters that can be
9764	 *    mapped.
9765	 *  @param {object} user The object to convert from camel case to Hungarian.
9766	 *  @param {boolean} force When set to `true`, properties which already have a
9767	 *    Hungarian value in the `user` object will be overwritten. Otherwise they
9768	 *    won't be.
9769	 */
9770	DataTable.camelToHungarian = _fnCamelToHungarian;
9771	
9772	
9773	
9774	/**
9775	 *
9776	 */
9777	_api_register( '$()', function ( selector, opts ) {
9778		var
9779			rows   = this.rows( opts ).nodes(), // Get all rows
9780			jqRows = $(rows);
9781	
9782		return $( [].concat(
9783			jqRows.filter( selector ).toArray(),
9784			jqRows.find( selector ).toArray()
9785		) );
9786	} );
9787	
9788	
9789	// jQuery functions to operate on the tables
9790	$.each( [ 'on', 'one', 'off' ], function (i, key) {
9791		_api_register( key+'()', function ( /* event, handler */ ) {
9792			var args = Array.prototype.slice.call(arguments);
9793	
9794			// Add the `dt` namespace automatically if it isn't already present
9795			args[0] = args[0].split( /\s/ ).map( function ( e ) {
9796				return ! e.match(/\.dt\b/) ?
9797					e+'.dt' :
9798					e;
9799				} ).join( ' ' );
9800	
9801			var inst = $( this.tables().nodes() );
9802			inst[key].apply( inst, args );
9803			return this;
9804		} );
9805	} );
9806	
9807	
9808	_api_register( 'clear()', function () {
9809		return this.iterator( 'table', function ( settings ) {
9810			_fnClearTable( settings );
9811		} );
9812	} );
9813	
9814	
9815	_api_register( 'error()', function (msg) {
9816		return this.iterator( 'table', function ( settings ) {
9817			_fnLog( settings, 0, msg );
9818		} );
9819	} );
9820	
9821	
9822	_api_register( 'settings()', function () {
9823		return new _Api( this.context, this.context );
9824	} );
9825	
9826	
9827	_api_register( 'init()', function () {
9828		var ctx = this.context;
9829		return ctx.length ? ctx[0].oInit : null;
9830	} );
9831	
9832	
9833	_api_register( 'data()', function () {
9834		return this.iterator( 'table', function ( settings ) {
9835			return _pluck( settings.aoData, '_aData' );
9836		} ).flatten();
9837	} );
9838	
9839	
9840	_api_register( 'trigger()', function ( name, args, bubbles ) {
9841		return this.iterator( 'table', function ( settings ) {
9842			return _fnCallbackFire( settings, null, name, args, bubbles );
9843		} ).flatten();
9844	} );
9845	
9846	
9847	_api_register( 'ready()', function ( fn ) {
9848		var ctx = this.context;
9849	
9850		// Get status of first table
9851		if (! fn) {
9852			return ctx.length
9853				? (ctx[0]._bInitComplete || false)
9854				: null;
9855		}
9856	
9857		// Function to run either once the table becomes ready or
9858		// immediately if it is already ready.
9859		return this.tables().every(function () {
9860			var api = this;
9861	
9862			if (this.context[0]._bInitComplete) {
9863				fn.call(api);
9864			}
9865			else {
9866				this.on('init.dt.DT', function () {
9867					fn.call(api);
9868				});
9869			}
9870		} );
9871	} );
9872	
9873	
9874	_api_register( 'destroy()', function ( remove ) {
9875		remove = remove || false;
9876	
9877		return this.iterator( 'table', function ( settings ) {
9878			var classes   = settings.oClasses;
9879			var table     = settings.nTable;
9880			var tbody     = settings.nTBody;
9881			var thead     = settings.nTHead;
9882			var tfoot     = settings.nTFoot;
9883			var jqTable   = $(table);
9884			var jqTbody   = $(tbody);
9885			var jqWrapper = $(settings.nTableWrapper);
9886			var rows      = settings.aoData.map( function (r) { return r ? r.nTr : null; } );
9887			var orderClasses = classes.order;
9888	
9889			// Flag to note that the table is currently being destroyed - no action
9890			// should be taken
9891			settings.bDestroying = true;
9892	
9893			// Fire off the destroy callbacks for plug-ins etc
9894			_fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings], true );
9895	
9896			// If not being removed from the document, make all columns visible
9897			if ( ! remove ) {
9898				new _Api( settings ).columns().visible( true );
9899			}
9900	
9901			// Container width change listener
9902			if (settings.resizeObserver) {
9903				settings.resizeObserver.disconnect();
9904			}
9905	
9906			// Blitz all `DT` namespaced events (these are internal events, the
9907			// lowercase, `dt` events are user subscribed and they are responsible
9908			// for removing them
9909			jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
9910			$(window).off('.DT-'+settings.sInstance);
9911	
9912			// When scrolling we had to break the table up - restore it
9913			if ( table != thead.parentNode ) {
9914				jqTable.children('thead').detach();
9915				jqTable.append( thead );
9916			}
9917	
9918			if ( tfoot && table != tfoot.parentNode ) {
9919				jqTable.children('tfoot').detach();
9920				jqTable.append( tfoot );
9921			}
9922	
9923			// Clean up the header
9924			$(thead).find('span.dt-column-order').remove();
9925			$(thead).find('span.dt-column-title').each(function () {
9926				var title = $(this).html();
9927				$(this).parent().append(title);
9928				$(this).remove();
9929			});
9930	
9931			settings.colgroup.remove();
9932	
9933			settings.aaSorting = [];
9934			settings.aaSortingFixed = [];
9935			_fnSortingClasses( settings );
9936	
9937			$(jqTable).find('th, td').removeClass(
9938				$.map(DataTable.ext.type.className, function (v) {
9939					return v;
9940				}).join(' ')
9941			);
9942	
9943			$('th, td', thead)
9944				.removeClass(
9945					orderClasses.none + ' ' +
9946					orderClasses.canAsc + ' ' +
9947					orderClasses.canDesc + ' ' +
9948					orderClasses.isAsc + ' ' +
9949					orderClasses.isDesc
9950				)
9951				.css('width', '')
9952				.removeAttr('data-dt-column')
9953				.removeAttr('aria-sort');
9954	
9955			// Add the TR elements back into the table in their original order
9956			jqTbody.children().detach();
9957			jqTbody.append( rows );
9958	
9959			var orig = settings.nTableWrapper.parentNode;
9960			var insertBefore = settings.nTableWrapper.nextSibling;
9961	
9962			// Remove the DataTables generated nodes, events and classes
9963			var removedMethod = remove ? 'remove' : 'detach';
9964			jqTable[ removedMethod ]();
9965			jqWrapper[ removedMethod ]();
9966	
9967			// If we need to reattach the table to the document
9968			if ( ! remove && orig ) {
9969				// insertBefore acts like appendChild if !arg[1]
9970				orig.insertBefore( table, insertBefore );
9971	
9972				// Restore the width of the original table - was read from the style property,
9973				// so we can restore directly to that
9974				jqTable
9975					.css( 'width', settings.sDestroyWidth )
9976					.removeClass( classes.table );
9977			}
9978	
9979			/* Remove the settings object from the settings array */
9980			var idx = DataTable.settings.indexOf(settings);
9981			if ( idx !== -1 ) {
9982				DataTable.settings.splice( idx, 1 );
9983			}
9984		} );
9985	} );
9986	
9987	
9988	// Add the `every()` method for rows, columns and cells in a compact form
9989	$.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
9990		_api_register( type+'s().every()', function ( fn ) {
9991			var opts = this.selector.opts;
9992			var api = this;
9993			var inst;
9994			var counter = 0;
9995	
9996			return this.iterator( 'every', function ( settings, selectedIdx, tableIdx ) {
9997				inst = api[ type ](selectedIdx, opts);
9998	
9999				if (type === 'cell') {
10000					fn.call(inst, inst[0][0].row, inst[0][0].column, tableIdx, counter);
10001				}
10002				else {
10003					fn.call(inst, selectedIdx, tableIdx, counter);
10004				}
10005	
10006				counter++;
10007			} );
10008		} );
10009	} );
10010	
10011	
10012	// i18n method for extensions to be able to use the language object from the
10013	// DataTable
10014	_api_register( 'i18n()', function ( token, def, plural ) {
10015		var ctx = this.context[0];
10016		var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
10017	
10018		if ( resolved === undefined ) {
10019			resolved = def;
10020		}
10021	
10022		if ( $.isPlainObject( resolved ) ) {
10023			resolved = plural !== undefined && resolved[ plural ] !== undefined ?
10024				resolved[ plural ] :
10025				resolved._;
10026		}
10027	
10028		return typeof resolved === 'string'
10029			? resolved.replace( '%d', plural ) // nb: plural might be undefined,
10030			: resolved;
10031	} );
10032	
10033	/**
10034	 * Version string for plug-ins to check compatibility. Allowed format is
10035	 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
10036	 * only for non-release builds. See https://semver.org/ for more information.
10037	 *  @member
10038	 *  @type string
10039	 *  @default Version number
10040	 */
10041	DataTable.version = "2.2.2";
10042	
10043	/**
10044	 * Private data store, containing all of the settings objects that are
10045	 * created for the tables on a given page.
10046	 *
10047	 * Note that the `DataTable.settings` object is aliased to
10048	 * `jQuery.fn.dataTableExt` through which it may be accessed and
10049	 * manipulated, or `jQuery.fn.dataTable.settings`.
10050	 *  @member
10051	 *  @type array
10052	 *  @default []
10053	 *  @private
10054	 */
10055	DataTable.settings = [];
10056	
10057	/**
10058	 * Object models container, for the various models that DataTables has
10059	 * available to it. These models define the objects that are used to hold
10060	 * the active state and configuration of the table.
10061	 *  @namespace
10062	 */
10063	DataTable.models = {};
10064	
10065	
10066	
10067	/**
10068	 * Template object for the way in which DataTables holds information about
10069	 * search information for the global filter and individual column filters.
10070	 *  @namespace
10071	 */
10072	DataTable.models.oSearch = {
10073		/**
10074		 * Flag to indicate if the filtering should be case insensitive or not
10075		 */
10076		"caseInsensitive": true,
10077	
10078		/**
10079		 * Applied search term
10080		 */
10081		"search": "",
10082	
10083		/**
10084		 * Flag to indicate if the search term should be interpreted as a
10085		 * regular expression (true) or not (false) and therefore and special
10086		 * regex characters escaped.
10087		 */
10088		"regex": false,
10089	
10090		/**
10091		 * Flag to indicate if DataTables is to use its smart filtering or not.
10092		 */
10093		"smart": true,
10094	
10095		/**
10096		 * Flag to indicate if DataTables should only trigger a search when
10097		 * the return key is pressed.
10098		 */
10099		"return": false
10100	};
10101	
10102	
10103	
10104	
10105	/**
10106	 * Template object for the way in which DataTables holds information about
10107	 * each individual row. This is the object format used for the settings
10108	 * aoData array.
10109	 *  @namespace
10110	 */
10111	DataTable.models.oRow = {
10112		/**
10113		 * TR element for the row
10114		 */
10115		"nTr": null,
10116	
10117		/**
10118		 * Array of TD elements for each row. This is null until the row has been
10119		 * created.
10120		 */
10121		"anCells": null,
10122	
10123		/**
10124		 * Data object from the original data source for the row. This is either
10125		 * an array if using the traditional form of DataTables, or an object if
10126		 * using mData options. The exact type will depend on the passed in
10127		 * data from the data source, or will be an array if using DOM a data
10128		 * source.
10129		 */
10130		"_aData": [],
10131	
10132		/**
10133		 * Sorting data cache - this array is ostensibly the same length as the
10134		 * number of columns (although each index is generated only as it is
10135		 * needed), and holds the data that is used for sorting each column in the
10136		 * row. We do this cache generation at the start of the sort in order that
10137		 * the formatting of the sort data need be done only once for each cell
10138		 * per sort. This array should not be read from or written to by anything
10139		 * other than the master sorting methods.
10140		 */
10141		"_aSortData": null,
10142	
10143		/**
10144		 * Per cell filtering data cache. As per the sort data cache, used to
10145		 * increase the performance of the filtering in DataTables
10146		 */
10147		"_aFilterData": null,
10148	
10149		/**
10150		 * Filtering data cache. This is the same as the cell filtering cache, but
10151		 * in this case a string rather than an array. This is easily computed with
10152		 * a join on `_aFilterData`, but is provided as a cache so the join isn't
10153		 * needed on every search (memory traded for performance)
10154		 */
10155		"_sFilterRow": null,
10156	
10157		/**
10158		 * Denote if the original data source was from the DOM, or the data source
10159		 * object. This is used for invalidating data, so DataTables can
10160		 * automatically read data from the original source, unless uninstructed
10161		 * otherwise.
10162		 */
10163		"src": null,
10164	
10165		/**
10166		 * Index in the aoData array. This saves an indexOf lookup when we have the
10167		 * object, but want to know the index
10168		 */
10169		"idx": -1,
10170	
10171		/**
10172		 * Cached display value
10173		 */
10174		displayData: null
10175	};
10176	
10177	
10178	/**
10179	 * Template object for the column information object in DataTables. This object
10180	 * is held in the settings aoColumns array and contains all the information that
10181	 * DataTables needs about each individual column.
10182	 *
10183	 * Note that this object is related to {@link DataTable.defaults.column}
10184	 * but this one is the internal data store for DataTables's cache of columns.
10185	 * It should NOT be manipulated outside of DataTables. Any configuration should
10186	 * be done through the initialisation options.
10187	 *  @namespace
10188	 */
10189	DataTable.models.oColumn = {
10190		/**
10191		 * Column index.
10192		 */
10193		"idx": null,
10194	
10195		/**
10196		 * A list of the columns that sorting should occur on when this column
10197		 * is sorted. That this property is an array allows multi-column s
10197orting
10198		 * to be defined for a column (for example first name / last name columns
10199		 * would benefit from this). The values are integers pointing to the
10200		 * columns to be sorted on (typically it will be a single integer pointing
10201		 * at itself, but that doesn't need to be the case).
10202		 */
10203		"aDataSort": null,
10204	
10205		/**
10206		 * Define the sorting directions that are applied to the column, in sequence
10207		 * as the column is repeatedly sorted upon - i.e. the first value is used
10208		 * as the sorting direction when the column if first sorted (clicked on).
10209		 * Sort it again (click again) and it will move on to the next index.
10210		 * Repeat until loop.
10211		 */
10212		"asSorting": null,
10213	
10214		/**
10215		 * Flag to indicate if the column is searchable, and thus should be included
10216		 * in the filtering or not.
10217		 */
10218		"bSearchable": null,
10219	
10220		/**
10221		 * Flag to indicate if the column is sortable or not.
10222		 */
10223		"bSortable": null,
10224	
10225		/**
10226		 * Flag to indicate if the column is currently visible in the table or not
10227		 */
10228		"bVisible": null,
10229	
10230		/**
10231		 * Store for manual type assignment using the `column.type` option. This
10232		 * is held in store so we can manipulate the column's `sType` property.
10233		 */
10234		"_sManualType": null,
10235	
10236		/**
10237		 * Flag to indicate if HTML5 data attributes should be used as the data
10238		 * source for filtering or sorting. True is either are.
10239		 */
10240		"_bAttrSrc": false,
10241	
10242		/**
10243		 * Developer definable function that is called whenever a cell is created (Ajax source,
10244		 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
10245		 * allowing you to modify the DOM element (add background colour for example) when the
10246		 * element is available.
10247		 */
10248		"fnCreatedCell": null,
10249	
10250		/**
10251		 * Function to get data from a cell in a column. You should <b>never</b>
10252		 * access data directly through _aData internally in DataTables - always use
10253		 * the method attached to this property. It allows mData to function as
10254		 * required. This function is automatically assigned by the column
10255		 * initialisation method
10256		 */
10257		"fnGetData": null,
10258	
10259		/**
10260		 * Function to set data for a cell in the column. You should <b>never</b>
10261		 * set the data directly to _aData internally in DataTables - always use
10262		 * this method. It allows mData to function as required. This function
10263		 * is automatically assigned by the column initialisation method
10264		 */
10265		"fnSetData": null,
10266	
10267		/**
10268		 * Property to read the value for the cells in the column from the data
10269		 * source array / object. If null, then the default content is used, if a
10270		 * function is given then the return from the function is used.
10271		 */
10272		"mData": null,
10273	
10274		/**
10275		 * Partner property to mData which is used (only when defined) to get
10276		 * the data - i.e. it is basically the same as mData, but without the
10277		 * 'set' option, and also the data fed to it is the result from mData.
10278		 * This is the rendering method to match the data method of mData.
10279		 */
10280		"mRender": null,
10281	
10282		/**
10283		 * The class to apply to all TD elements in the table's TBODY for the column
10284		 */
10285		"sClass": null,
10286	
10287		/**
10288		 * When DataTables calculates the column widths to assign to each column,
10289		 * it finds the longest string in each column and then constructs a
10290		 * temporary table and reads the widths from that. The problem with this
10291		 * is that "mmm" is much wider then "iiii", but the latter is a longer
10292		 * string - thus the calculation can go wrong (doing it properly and putting
10293		 * it into an DOM object and measuring that is horribly(!) slow). Thus as
10294		 * a "work around" we provide this option. It will append its value to the
10295		 * text that is found to be the longest string for the column - i.e. padding.
10296		 */
10297		"sContentPadding": null,
10298	
10299		/**
10300		 * Allows a default value to be given for a column's data, and will be used
10301		 * whenever a null data source is encountered (this can be because mData
10302		 * is set to null, or because the data source itself is null).
10303		 */
10304		"sDefaultContent": null,
10305	
10306		/**
10307		 * Name for the column, allowing reference to the column by name as well as
10308		 * by index (needs a lookup to work by name).
10309		 */
10310		"sName": null,
10311	
10312		/**
10313		 * Custom sorting data type - defines which of the available plug-ins in
10314		 * afnSortData the custom sorting will use - if any is defined.
10315		 */
10316		"sSortDataType": 'std',
10317	
10318		/**
10319		 * Class to be applied to the header element when sorting on this column
10320		 */
10321		"sSortingClass": null,
10322	
10323		/**
10324		 * Title of the column - what is seen in the TH element (nTh).
10325		 */
10326		"sTitle": null,
10327	
10328		/**
10329		 * Column sorting and filtering type
10330		 */
10331		"sType": null,
10332	
10333		/**
10334		 * Width of the column
10335		 */
10336		"sWidth": null,
10337	
10338		/**
10339		 * Width of the column when it was first "encountered"
10340		 */
10341		"sWidthOrig": null,
10342	
10343		/** Cached string which is the longest in the column */
10344		maxLenString: null,
10345	
10346		/**
10347		 * Store for named searches
10348		 */
10349		searchFixed: null
10350	};
10351	
10352	
10353	/*
10354	 * Developer note: The properties of the object below are given in Hungarian
10355	 * notation, that was used as the interface for DataTables prior to v1.10, however
10356	 * from v1.10 onwards the primary interface is camel case. In order to avoid
10357	 * breaking backwards compatibility utterly with this change, the Hungarian
10358	 * version is still, internally the primary interface, but is is not documented
10359	 * - hence the @name tags in each doc comment. This allows a Javascript function
10360	 * to create a map from Hungarian notation to camel case (going the other direction
10361	 * would require each property to be listed, which would add around 3K to the size
10362	 * of DataTables, while this method is about a 0.5K hit).
10363	 *
10364	 * Ultimately this does pave the way for Hungarian notation to be dropped
10365	 * completely, but that is a massive amount of work and will break current
10366	 * installs (therefore is on-hold until v2).
10367	 */
10368	
10369	/**
10370	 * Initialisation options that can be given to DataTables at initialisation
10371	 * time.
10372	 *  @namespace
10373	 */
10374	DataTable.defaults = {
10375		/**
10376		 * An array of data to use for the table, passed in at initialisation which
10377		 * will be used in preference to any data which is already in the DOM. This is
10378		 * particularly useful for constructing tables purely in Javascript, for
10379		 * example with a custom Ajax call.
10380		 */
10381		"aaData": null,
10382	
10383	
10384		/**
10385		 * If ordering is enabled, then DataTables will perform a first pass sort on
10386		 * initialisation. You can define which column(s) the sort is performed
10387		 * upon, and the sorting direction, with this variable. The `sorting` array
10388		 * should contain an array for each column to be sorted initially containing
10389		 * the column's index and a direction string ('asc' or 'desc').
10390		 */
10391		"aaSorting": [[0,'asc']],
10392	
10393	
10394		/**
10395		 * This parameter is basically identical to the `sorting` parameter, but
10396		 * cannot be overridden by user interaction with the table. What this means
10397		 * is that you could have a column (visible or hidden) which the sorting
10398		 * will always be forced on first - any sorting after that (from the user)
10399		 * will then be performed as required. This can be useful for grouping rows
10400		 * together.
10401		 */
10402		"aaSortingFixed": [],
10403	
10404	
10405		/**
10406		 * DataTables can be instructed to load data to display in the table from a
10407		 * Ajax source. This option defines how that Ajax call is made and where to.
10408		 *
10409		 * The `ajax` property has three different modes of operation, depending on
10410		 * how it is defined. These are:
10411		 *
10412		 * * `string` - Set the URL from where the data should be loaded from.
10413		 * * `object` - Define properties for `jQuery.ajax`.
10414		 * * `function` - Custom data get function
10415		 *
10416		 * `string`
10417		 * --------
10418		 *
10419		 * As a string, the `ajax` property simply defines the URL from which
10420		 * DataTables will load data.
10421		 *
10422		 * `object`
10423		 * --------
10424		 *
10425		 * As an object, the parameters in the object are passed to
10426		 * [jQuery.ajax](https://api.jquery.com/jQuery.ajax/) allowing fine control
10427		 * of the Ajax request. DataTables has a number of default parameters which
10428		 * you can override using this option. Please refer to the jQuery
10429		 * documentation for a full description of the options available, although
10430		 * the following parameters provide additional options in DataTables or
10431		 * require special consideration:
10432		 *
10433		 * * `data` - As with jQuery, `data` can be provided as an object, but it
10434		 *   can also be used as a function to manipulate the data DataTables sends
10435		 *   to the server. The function takes a single parameter, an object of
10436		 *   parameters with the values that DataTables has readied for sending. An
10437		 *   object may be returned which will be merged into the DataTables
10438		 *   defaults, or you can add the items to the object that was passed in and
10439		 *   not return anything from the function. This supersedes `fnServerParams`
10440		 *   from DataTables 1.9-.
10441		 *
10442		 * * `dataSrc` - By default DataTables will look for the property `data` (or
10443		 *   `aaData` for compatibility with DataTables 1.9-) when obtaining data
10444		 *   from an Ajax source or for server-side processing - this parameter
10445		 *   allows that property to be changed. You can use Javascript dotted
10446		 *   object notation to get a data source for multiple levels of nesting, or
10447		 *   it my be used as a function. As a function it takes a single parameter,
10448		 *   the JSON returned from the server, which can be manipulated as
10449		 *   required, with the returned value being that used by DataTables as the
10450		 *   data source for the table.
10451		 *
10452		 * * `success` - Should not be overridden it is used internally in
10453		 *   DataTables. To manipulate / transform the data returned by the server
10454		 *   use `ajax.dataSrc`, or use `ajax` as a function (see below).
10455		 *
10456		 * `function`
10457		 * ----------
10458		 *
10459		 * As a function, making the Ajax call is left up to yourself allowing
10460		 * complete control of the Ajax request. Indeed, if desired, a method other
10461		 * than Ajax could be used to obtain the required data, such as Web storage
10462		 * or an AIR database.
10463		 *
10464		 * The function is given four parameters and no return is required. The
10465		 * parameters are:
10466		 *
10467		 * 1. _object_ - Data to send to the server
10468		 * 2. _function_ - Callback function that must be executed when the required
10469		 *    data has been obtained. That data should be passed into the callback
10470		 *    as the only parameter
10471		 * 3. _object_ - DataTables settings object for the table
10472		 */
10473		"ajax": null,
10474	
10475	
10476		/**
10477		 * This parameter allows you to readily specify the entries in the length drop
10478		 * down menu that DataTables shows when pagination is enabled. It can be
10479		 * either a 1D array of options which will be used for both the displayed
10480		 * option and the value, or a 2D array which will use the array in the first
10481		 * position as the value, and the array in the second position as the
10482		 * displayed options (useful for language strings such as 'All').
10483		 *
10484		 * Note that the `pageLength` property will be automatically set to the
10485		 * first value given in this array, unless `pageLength` is also provided.
10486		 */
10487		"aLengthMenu": [ 10, 25, 50, 100 ],
10488	
10489	
10490		/**
10491		 * The `columns` option in the initialisation parameter allows you to define
10492		 * details about the way individual columns behave. For a full list of
10493		 * column options that can be set, please see
10494		 * {@link DataTable.defaults.column}. Note that if you use `columns` to
10495		 * define your columns, you must have an entry in the array for every single
10496		 * column that you have in your table (these can be null if you don't which
10497		 * to specify any options).
10498		 */
10499		"aoColumns": null,
10500	
10501		/**
10502		 * Very similar to `columns`, `columnDefs` allows you to target a specific
10503		 * column, multiple columns, or all columns, using the `targets` property of
10504		 * each object in the array. This allows great flexibility when creating
10505		 * tables, as the `columnDefs` arrays can be of any length, targeting the
10506		 * columns you specifically want. `columnDefs` may use any of the column
10507		 * options available: {@link DataTable.defaults.column}, but it _must_
10508		 * have `targets` defined in each object in the array. Values in the `targets`
10509		 * array may be:
10510		 *   <ul>
10511		 *     <li>a string - class name will be matched on the TH for the column</li>
10512		 *     <li>0 or a positive integer - column index counting from the left</li>
10513		 *     <li>a negative integer - column index counting from the right</li>
10514		 *     <li>the string "_all" - all columns (i.e. assign a default)</li>
10515		 *   </ul>
10516		 */
10517		"aoColumnDefs": null,
10518	
10519	
10520		/**
10521		 * Basically the same as `search`, this parameter defines the individual column
10522		 * filtering state at initialisation time. The array must be of the same size
10523		 * as the number of columns, and each element be an object with the parameters
10524		 * `search` and `escapeRegex` (the latter is optional). 'null' is also
10525		 * accepted and the default will be used.
10526		 */
10527		"aoSearchCols": [],
10528	
10529	
10530		/**
10531		 * Enable or disable automatic column width calculation. This can be disabled
10532		 * as an optimisation (it takes some time to calculate the widths) if the
10533		 * tables widths are passed in using `columns`.
10534		 */
10535		"bAutoWidth": true,
10536	
10537	
10538		/**
10539		 * Deferred rendering can provide DataTables with a huge speed boost when you
10540		 * are using an Ajax or JS data source for the table. This option, when set to
10541		 * true, will cause DataTables to defer the creation of the table elements for
10542		 * each row until they are needed for a draw - saving a significant amount of
10543		 * time.
10544		 */
10545		"bDeferRender": true,
10546	
10547	
10548		/**
10549		 * Replace a DataTable which matches the given selector and replace it with
10550		 * one which has the properties of the new initialisation object passed. If no
10551		 * table matches the selector, then the new DataTable will be constructed as
10552		 * per normal.
10553		 */
10554		"bDestroy": false,
10555	
10556	
10557		/**
10558		 * Enable or disable filtering of data. Filtering in DataTables is "smart" in
10559		 * that it allows the end user to input multiple words (space separated) and
10560		 * will match a row containing those words, even if not in the order that was
10561		 * specified (this allow matching across multiple columns). Note that if you
10562		 * wish to use filtering in DataTables this must remain 'true' - to remove the
10563		 * default filtering input box and retain filtering abilities, please use
10564		 * {@link DataTable.defaults.dom}.
10565		 */
10566		"bFilter": true,
10567	
10568		/**
10569		 * Used only for compatiblity with DT1
10570		 * @deprecated
10571		 */
10572		"bInfo": true,
10573	
10574		/**
10575		 * Used only for compatiblity with DT1
10576		 * @deprecated
10577		 */
10578		"bLengthChange": true,
10579	
10580		/**
10581		 * Enable or disable pagination.
10582		 */
10583		"bPaginate": true,
10584	
10585	
10586		/**
10587		 * Enable or disable the display of a 'processing' indicator when the table is
10588		 * being processed (e.g. a sort). This is particularly useful for tables with
10589		 * large amounts of data where it can take a noticeable amount of time to sort
10590		 * the entries.
10591		 */
10592		"bProcessing": false,
10593	
10594	
10595		/**
10596		 * Retrieve the DataTables object for the given selector. Note that if the
10597		 * table has already been initialised, this parameter will cause DataTables
10598		 * to simply return the object that has already been set up - it will not take
10599		 * account of any changes you might have made to the initialisation object
10600		 * passed to DataTables (setting this parameter to true is an acknowledgement
10601		 * that you understand this). `destroy` can be used to reinitialise a table if
10602		 * you need.
10603		 */
10604		"bRetrieve": false,
10605	
10606	
10607		/**
10608		 * When vertical (y) scrolling is enabled, DataTables will force the height of
10609		 * the table's viewport to the given height at all times (useful for layout).
10610		 * However, this can look odd when filtering data down to a small data set,
10611		 * and the footer is left "floating" further down. This parameter (when
10612		 * enabled) will cause DataTables to collapse the table's viewport down when
10613		 * the result set will fit within the given Y height.
10614		 */
10615		"bScrollCollapse": false,
10616	
10617	
10618		/**
10619		 * Configure DataTables to use server-side processing. Note that the
10620		 * `ajax` parameter must also be given in order to give DataTables a
10621		 * source to obtain the required data for each draw.
10622		 */
10623		"bServerSide": false,
10624	
10625	
10626		/**
10627		 * Enable or disable sorting of columns. Sorting of individual columns can be
10628		 * disabled by the `sortable` option for each column.
10629		 */
10630		"bSort": true,
10631	
10632	
10633		/**
10634		 * Enable or display DataTables' ability to sort multiple columns at the
10635		 * same time (activated by shift-click by the user).
10636		 */
10637		"bSortMulti": true,
10638	
10639	
10640		/**
10641		 * Allows control over whether DataTables should use the top (true) unique
10642		 * cell that is found for a single column, or the bottom (false - default).
10643		 * This is useful when using complex headers.
10644		 */
10645		"bSortCellsTop": null,
10646	
10647	
10648		/**
10649		 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10650		 * `sorting\_3` to the columns which are currently being sorted on. This is
10651		 * presented as a feature switch as it can increase processing time (while
10652		 * classes are removed and added) so for large data sets you might want to
10653		 * turn this off.
10654		 */
10655		"bSortClasses": true,
10656	
10657	
10658		/**
10659		 * Enable or disable state saving. When enabled HTML5 `localStorage` will be
10660		 * used to save table display information such as pagination information,
10661		 * display length, filtering and sorting. As such when the end user reloads
10662		 * the page the display display will match what thy had previously set up.
10663		 */
10664		"bStateSave": false,
10665	
10666	
10667		/**
10668		 * This function is called when a TR element is created (and all TD child
10669		 * elements have been inserted), or registered if using a DOM source, allowing
10670		 * manipulation of the TR element (adding classes etc).
10671		 */
10672		"fnCreatedRow": null,
10673	
10674	
10675		/**
10676		 * This function is called on every 'draw' event, and allows you to
10677		 * dynamically modify any aspect you want about the created DOM.
10678		 */
10679		"fnDrawCallback": null,
10680	
10681	
10682		/**
10683		 * Identical to fnHeaderCallback() but for the table footer this function
10684		 * allows you to modify the table footer on every 'draw' event.
10685		 */
10686		"fnFooterCallback": null,
10687	
10688	
10689		/**
10690		 * When rendering large numbers in the information element for the table
10691		 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
10692		 * to have a comma separator for the 'thousands' units (e.g. 1 million is
10693		 * rendered as "1,000,000") to help readability for the end user. This
10694		 * function will override the default method DataTables uses.
10695		 */
10696		"fnFormatNumber": function ( toFormat ) {
10697			return toFormat.toString().replace(
10698				/\B(?=(\d{3})+(?!\d))/g,
10699				this.oLanguage.sThousands
10700			);
10701		},
10702	
10703	
10704		/**
10705		 * This function is called on every 'draw' event, and allows you to
10706		 * dynamically modify the header row. This can be used to calculate and
10707		 * display useful information about the table.
10708		 */
10709		"fnHeaderCallback": null,
10710	
10711	
10712		/**
10713		 * The information element can be used to convey information about the current
10714		 * state of the table. Although the internationalisation options presented by
10715		 * DataTables are quite capable of dealing with most customisations, there may
10716		 * be times where you wish to customise the string further. This callback
10717		 * allows you to do exactly that.
10718		 */
10719		"fnInfoCallback": null,
10720	
10721	
10722		/**
10723		 * Called when the table has been initialised. Normally DataTables will
10724		 * initialise sequentially and there will be no need for this function,
10725		 * however, this does not hold true when using external language information
10726		 * since that is obtained using an async XHR call.
10727		 */
10728		"fnInitComplete": null,
10729	
10730	
10731		/**
10732		 * Called at the very start of each table draw and can be used to cancel the
10733		 * draw by returning false, any other return (including undefined) results in
10734		 * the full draw occurring).
10735		 */
10736		"fnPreDrawCallback": null,
10737	
10738	
10739		/**
10740		 * This function allows you to 'post process' each row after it have been
10741		 * generated for each table draw, but before it is rendered on screen. This
10742		 * function might be used for setting the row class name etc.
10743		 */
10744		"fnRowCallback": null,
10745	
10746	
10747		/**
10748		 * Load the table state. With this function you can define from where, and how, the
10749		 * state of a table is loaded. By default DataTables will load from `localStorage`
10750		 * but you might wish to use a server-side database or cookies.
10751		 */
10752		"fnStateLoadCallback": function ( settings ) {
10753			try {
10754				return JSON.parse(
10755					(settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
10756						'DataTables_'+settings.sInstance+'_'+location.pathname
10757					)
10758				);
10759			} catch (e) {
10760				return {};
10761			}
10762		},
10763	
10764	
10765		/**
10766		 * Callback which allows modification of the saved state prior to loading that state.
10767		 * This callback is called when the table is loading state from the stored data, but
10768		 * prior to the settings object being modified by the saved state. Note that for
10769		 * plug-in authors, you should use the `stateLoadParams` event to load parameters for
10770		 * a plug-in.
10771		 */
10772		"fnStateLoadParams": null,
10773	
10774	
10775		/**
10776		 * Callback that is called when the state has been loaded from the state saving method
10777		 * and the DataTables settings object has been modified as a result of the loaded state.
10778		 */
10779		"fnStateLoaded": null,
10780	
10781	
10782		/**
10783		 * Save the table state. This function allows you to define where and how the state
10784		 * information for the table is stored By default DataTables will use `localStorage`
10785		 * but you might wish to use a server-side database or cookies.
10786		 */
10787		"fnStateSaveCallback": function ( settings, data ) {
10788			try {
10789				(settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
10790					'DataTables_'+settings.sInstance+'_'+location.pathname,
10791					JSON.stringify( data )
10792				);
10793			} catch (e) {
10794				// noop
10795			}
10796		},
10797	
10798	
10799		/**
10800		 * Callback which allows modification of the state to be saved. Called when the table
10801		 * has changed state a new state save is required. This method allows modification of
10802		 * the state saving object prior to actually doing the save, including addition or
10803		 * other state properties or modification. Note that for plug-in authors, you should
10804		 * use the `stateSaveParams` event to save parameters for a plug-in.
10805		 */
10806		"fnStateSaveParams": null,
10807	
10808	
10809		/**
10810		 * Duration for which the saved state information is considered valid. After this period
10811		 * has elapsed the state will be returned to the default.
10812		 * Value is given in seconds.
10813		 */
10814		"iStateDuration": 7200,
10815	
10816	
10817		/**
10818		 * Number of rows to display on a single page when using pagination. If
10819		 * feature enabled (`lengthChange`) then the end user will be able to override
10820		 * this to a custom setting using a pop-up menu.
10821		 */
10822		"iDisplayLength": 10,
10823	
10824	
10825		/**
10826		 * Define the starting point for data display when using DataTables with
10827		 * pagination. Note that this parameter is the number of records, rather than
10828		 * the page number, so if you have 10 records per page and want to start on
10829		 * the third page, it should be "20".
10830		 */
10831		"iDisplayStart": 0,
10832	
10833	
10834		/**
10835		 * By default DataTables allows keyboard navigation of the table (sorting, paging,
10836		 * and filtering) by adding a `tabindex` attribute to the required elements. This
10837		 * allows you to tab through the controls and press the enter key to activate them.
10838		 * The tabindex is default 0, meaning that the tab follows the flow of the document.
10839		 * You can overrule this using this parameter if you wish. Use a value of -1 to
10840		 * disable built-in keyboard navigation.
10841		 */
10842		"iTabIndex": 0,
10843	
10844	
10845		/**
10846		 * Classes that DataTables assigns to the various components and features
10847		 * that it adds to the HTML table. This allows classes to be configured
10848		 * during initialisation in addition to through the static
10849		 * {@link DataTable.ext.oStdClasses} object).
10850		 */
10851		"oClasses": {},
10852	
10853	
10854		/**
10855		 * All strings that DataTables uses in the user interface that it creates
10856		 * are defined in this object, allowing you to modified them individually or
10857		 * completely replace them all as required.
10858		 */
10859		"oLanguage": {
10860			/**
10861			 * Strings that are used for WAI-ARIA labels and controls only (these are not
10862			 * actually visible on the page, but will be read by screenreaders, and thus
10863			 * must be internationalised as well).
10864			 */
10865			"oAria": {
10866				/**
10867				 * ARIA label that is added to the table headers when the column may be sorted
10868				 */
10869				"orderable": ": Activate to sort",
10870	
10871				/**
10872				 * ARIA label that is added to the table headers when the column is currently being sorted
10873				 */
10874				"orderableReverse": ": Activate to invert sorting",
10875	
10876				/**
10877				 * ARIA label that is added to the table headers when the column is currently being 
10878				 * sorted and next step is to remove sorting
10879				 */
10880				"orderableRemove": ": Activate to remove sorting",
10881	
10882				paginate: {
10883					first: 'First',
10884					last: 'Last',
10885					next: 'Next',
10886					previous: 'Previous',
10887					number: ''
10888				}
10889			},
10890	
10891			/**
10892			 * Pagination string used by DataTables for the built-in pagination
10893			 * control types.
10894			 */
10895			"oPaginate": {
10896				/**
10897				 * Label and character for first page button («)
10898				 */
10899				"sFirst": "\u00AB",
10900	
10901				/**
10902				 * Last page button (»)
10903				 */
10904				"sLast": "\u00BB",
10905	
10906				/**
10907				 * Next page button (›)
10908				 */
10909				"sNext": "\u203A",
10910	
10911				/**
10912				 * Previous page button (‹)
10913				 */
10914				"sPrevious": "\u2039",
10915			},
10916	
10917			/**
10918			 * Plural object for the data type the table is showing
10919			 */
10920			entries: {
10921				_: "entries",
10922				1: "entry"
10923			},
10924	
10925			/**
10926			 * This string is shown in preference to `zeroRecords` when the table is
10927			 * empty of data (regardless of filtering). Note that this is an optional
10928			 * parameter - if it is not given, the value of `zeroRecords` will be used
10929			 * instead (either the default or given value).
10930			 */
10931			"sEmptyTable": "No data available in table",
10932	
10933	
10934			/**
10935			 * This string gives information to the end user about the information
10936			 * that is current on display on the page. The following tokens can be
10937			 * used in the string and will be dynamically replaced as the table
10938			 * display updates. This tokens can be placed anywhere in the string, or
10939			 * removed as needed by the language requires:
10940			 *
10941			 * * `\_START\_` - Display index of the first record on the current page
10942			 * * `\_END\_` - Display index of the last record on the current page
10943			 * * `\_TOTAL\_` - Number of records in the table after filtering
10944			 * * `\_MAX\_` - Number of records in the table without filtering
10945			 * * `\_PAGE\_` - Current page number
10946			 * * `\_PAGES\_` - Total number of pages of data in the table
10947			 */
10948			"sInfo": "Showing _START_ to _END_ of _TOTAL_ _ENTRIES-TOTAL_",
10949	
10950	
10951			/**
10952			 * Display information string for when the table is empty. Typically the
10953			 * format of this string should match `info`.
10954			 */
10955			"sInfoEmpty": "Showing 0 to 0 of 0 _ENTRIES-TOTAL_",
10956	
10957	
10958			/**
10959			 * When a user filters the information in a table, this string is appended
10960			 * to the information (`info`) to give an idea of how strong the filtering
10961			 * is. The variable _MAX_ is dynamically updated.
10962			 */
10963			"sInfoFiltered": "(filtered from _MAX_ total _ENTRIES-MAX_)",
10964	
10965	
10966			/**
10967			 * If can be useful to append extra information to the info string at times,
10968			 * and this variable does exactly that. This information will be appended to
10969			 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
10970			 * being used) at all times.
10971			 */
10972			"sInfoPostFix": "",
10973	
10974	
10975			/**
10976			 * This decimal place operator is a little different from the other
10977			 * language options since DataTables doesn't output floating point
10978			 * numbers, so it won't ever use this for display of a number. Rather,
10979			 * what this parameter does is modify the sort methods of the table so
10980			 * that numbers which are in a format which has a character other than
10981			 * a period (`.`) as a decimal place will be sorted numerically.
10982			 *
10983			 * Note that numbers with different decimal places cannot be shown in
10984			 * the same table and still be sortable, the table must be consistent.
10985			 * However, multiple different tables on the page can use different
10986			 * decimal place characters.
10987			 */
10988			"sDecimal": "",
10989	
10990	
10991			/**
10992			 * DataTables has a build in number formatter (`formatNumber`) which is
10993			 * used to format large numbers that are used in the table information.
10994			 * By default a comma is used, but this can be trivially changed to any
10995			 * character you wish with this parameter.
10996			 */
10997			"sThousands": ",",
10998	
10999	
11000			/**
11001			 * Detail the action that will be taken when the drop down menu for the
11002			 * pagination length option is changed. The '_MENU_' variable is replaced
11003			 * with a default select list of 10, 25, 50 and 100, and can be replaced
11004			 * with a custom select box if required.
11005			 */
11006			"sLengthMenu": "_MENU_ _ENTRIES_ per page",
11007	
11008	
11009			/**
11010			 * When using Ajax sourced data and during the first draw when DataTables is
11011			 * gathering the data, this message is shown in an empty row in the table to
11012			 * indicate to the end user the the data is being loaded. Note that this
11013			 * parameter is not used when loading data by server-side processing, just
11014			 * Ajax sourced data with client-side processing.
11015			 */
11016			"sLoadingRecords": "Loading...",
11017	
11018	
11019			/**
11020			 * Text which is displayed when the table is processing a user action
11021			 * (usually a sort command or similar).
11022			 */
11023			"sProcessing": "",
11024	
11025	
11026			/**
11027			 * Details the actions that will be taken when the user types into the
11028			 * filtering input text box. The variable "_INPUT_", if used in the string,
11029			 * is replaced with the HTML text box for the filtering input allowing
11030			 * control over where it appears in the string. If "_INPUT_" is not given
11031			 * then the input box is appended to the string automatically.
11032			 */
11033			"sSearch": "Search:",
11034	
11035	
11036			/**
11037			 * Assign a `placeholder` attribute to the search `input` element
11038			 *  @type string
11039			 *  @default 
11040			 *
11041			 *  @dtopt Language
11042			 *  @name DataTable.defaults.language.searchPlaceholder
11043			 */
11044			"sSearchPlaceholder": "",
11045	
11046	
11047			/**
11048			 * All of the language information can be stored in a file on the
11049			 * server-side, which DataTables will look up if this parameter is passed.
11050			 * It must store the URL of the language file, which is in a JSON format,
11051			 * and the object has the same properties as the oLanguage object in the
11052			 * initialiser object (i.e. the above parameters). Please refer to one of
11053			 * the example language files to see how this works in action.
11054			 */
11055			"sUrl": "",
11056	
11057	
11058			/**
11059			 * Text shown inside the table records when the is no information to be
11060			 * displayed after filtering. `emptyTable` is shown when there is simply no
11061			 * information in the table at all (regardless of filtering).
11062			 */
11063			"sZeroRecords": "No matching records found"
11064		},
11065	
11066	
11067		/** The initial data order is reversed when `desc` ordering */
11068		orderDescReverse: true,
11069	
11070	
11071		/**
11072		 * This parameter allows you to have define the global filtering state at
11073		 * initialisation time. As an object the `search` parameter must be
11074		 * defined, but all other parameters are optional. When `regex` is true,
11075		 * the search string will be treated as a regular expression, when false
11076		 * (default) it will be treated as a straight string. When `smart`
11077		 * DataTables will use it's smart filtering methods (to word match at
11078		 * any point in the data), when false this will not be done.
11079		 */
11080		"oSearch": $.extend( {}, DataTable.models.oSearch ),
11081	
11082	
11083		/**
11084		 * Table and control layout. This replaces the legacy `dom` option.
11085		 */
11086		layout: {
11087			topStart: 'pageLength',
11088			topEnd: 'search',
11089			bottomStart: 'info',
11090			bottomEnd: 'paging'
11091		},
11092	
11093	
11094		/**
11095		 * Legacy DOM layout option
11096		 */
11097		"sDom": null,
11098	
11099	
11100		/**
11101		 * Search delay option. This will throttle full table searches that use the
11102		 * DataTables provided search input element (it does not effect calls to
11103		 * `dt-api search()`, providing a delay before the search is made.
11104		 */
11105		"searchDelay": null,
11106	
11107	
11108		/**
11109		 * DataTables features six different built-in options for the buttons to
11110		 * display for pagination control:
11111		 *
11112		 * * `numbers` - Page number buttons only
11113		 * * `simple` - 'Previous' and 'Next' buttons only
11114		 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
11115		 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
11116		 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
11117		 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
11118		 */
11119		"sPaginationType": "",
11120	
11121	
11122		/**
11123		 * Enable horizontal scrolling. When a table is too wide to fit into a
11124		 * certain layout, or you have a large number of columns in the table, you
11125		 * can enable x-scrolling to show the table in a viewport, which can be
11126		 * scrolled. This property can be `true` which will allow the table to
11127		 * scroll horizontally when needed, or any CSS unit, or a number (in which
11128		 * case it will be treated as a pixel measurement). Setting as simply `true`
11129		 * is recommended.
11130		 */
11131		"sScrollX": "",
11132	
11133	
11134		/**
11135		 * This property can be used to force a DataTable to use more width than it
11136		 * might otherwise do when x-scrolling is enabled. For example if you have a
11137		 * table which requires to be well spaced, this parameter is useful for
11138		 * "over-sizing" the table, and thus forcing scrolling. This property can by
11139		 * any CSS unit, or a number (in which case it will be treated as a pixel
11140		 * measurement).
11141		 */
11142		"sScrollXInner": "",
11143	
11144	
11145		/**
11146		 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
11147		 * to the given height, and enable scrolling for any data which overflows the
11148		 * current viewport. This can be used as an alternative to paging to display
11149		 * a lot of data in a small area (although paging and scrolling can both be
11150		 * enabled at the same time). This property can be any CSS unit, or a number
11151		 * (in which case it will be treated as a pixel measurement).
11152		 */
11153		"sScrollY": "",
11154	
11155	
11156		/**
11157		 * __Deprecated__ The functionality provided by this parameter has now been
11158		 * superseded by that provided through `ajax`, which should be used instead.
11159		 *
11160		 * Set the HTTP method that is used to make the Ajax call for server-side
11161		 * processing or Ajax sourced data.
11162		 */
11163		"sServerMethod": "GET",
11164	
11165	
11166		/**
11167		 * DataTables makes use of renderers when displaying HTML elements for
11168		 * a table. These renderers can be added or modified by plug-ins to
11169		 * generate suitable mark-up for a site. For example the Bootstrap
11170		 * integration plug-in for DataTables uses a paging button renderer to
11171		 * display pagination buttons in the mark-up required by Bootstrap.
11172		 *
11173		 * For further information about the renderers available see
11174		 * DataTable.ext.renderer
11175		 */
11176		"renderer": null,
11177	
11178	
11179		/**
11180		 * Set the data property name that DataTables should use to get a row's id
11181		 * to set as the `id` property in the node.
11182		 */
11183		"rowId": "DT_RowId",
11184	
11185	
11186		/**
11187		 * Caption value
11188		 */
11189		"caption": null,
11190	
11191	
11192		/**
11193		 * For server-side processing - use the data from the DOM for the first draw
11194		 */
11195		iDeferLoading: null
11196	};
11197	
11198	_fnHungarianMap( DataTable.defaults );
11199	
11200	
11201	
11202	/*
11203	 * Developer note - See note in model.defaults.js about the use of Hungarian
11204	 * notation and camel case.
11205	 */
11206	
11207	/**
11208	 * Column options that can be given to DataTables at initialisation time.
11209	 *  @namespace
11210	 */
11211	DataTable.defaults.column = {
11212		/**
11213		 * Define which column(s) an order will occur on for this column. This
11214		 * allows a column's ordering to take multiple columns into account when
11215		 * doing a sort or use the data from a different column. For example first
11216		 * name / last name columns make sense to do a multi-column sort over the
11217		 * two columns.
11218		 */
11219		"aDataSort": null,
11220		"iDataSort": -1,
11221	
11222		ariaTitle: '',
11223	
11224	
11225		/**
11226		 * You can control the default ordering direction, and even alter the
11227		 * behaviour of the sort handler (i.e. only allow ascending ordering etc)
11228		 * using this parameter.
11229		 */
11230		"asSorting": [ 'asc', 'desc', '' ],
11231	
11232	
11233		/**
11234		 * Enable or disable filtering on the data in this column.
11235		 */
11236		"bSearchable": true,
11237	
11238	
11239		/**
11240		 * Enable or disable ordering on this column.
11241		 */
11242		"bSortable": true,
11243	
11244	
11245		/**
11246		 * Enable or disable the display of this column.
11247		 */
11248		"bVisible": true,
11249	
11250	
11251		/**
11252		 * Developer definable function that is called whenever a cell is created (Ajax source,
11253		 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
11254		 * allowing you to modify the DOM element (add background colour for example) when the
11255		 * element is available.
11256		 */
11257		"fnCreatedCell": null,
11258	
11259	
11260		/**
11261		 * This property can be used to read data from any data source property,
11262		 * including deeply nested objects / properties. `data` can be given in a
11263		 * number of different ways which effect its behaviour:
11264		 *
11265		 * * `integer` - treated as an array index for the data source. This is the
11266		 *   default that DataTables uses (incrementally increased for each column).
11267		 * * `string` - read an object property from the data source. There are
11268		 *   three 'special' options that can be used in the string to alter how
11269		 *   DataTables reads the data from the source object:
11270		 *    * `.` - Dotted Javascript notation. Just as you use a `.` in
11271		 *      Javascript to read from nested objects, so to can the options
11272		 *      specified in `data`. For example: `browser.version` or
11273		 *      `browser.name`. If your object parameter name contains a period, use
11274		 *      `\\` to escape it - i.e. `first\\.name`.
11275		 *    * `[]` - Array notation. DataTables can automatically combine data
11276		 *      from and array source, joining the data with the characters provided
11277		 *      between the two brackets. For example: `name[, ]` would provide a
11278		 *      comma-space separated list from the source array. If no characters
11279		 *      are provided between the brackets, the original array source is
11280		 *      returned.
11281		 *    * `()` - Function notation. Adding `()` to the end of a parameter will
11282		 *      execute a function of the name given. For example: `browser()` for a
11283		 *      simple function on the data source, `browser.version()` for a
11284		 *      function in a nested property or even `browser().version` to get an
11285		 *      object property if the function called returns an object. Note that
11286		 *      function notation is recommended for use in `render` rather than
11287		 *      `data` as it is much simpler to use as a renderer.
11288		 * * `null` - use the original data source for the row rather than plucking
11289		 *   data directly from it. This action has effects on two other
11290		 *   initialisation options:
11291		 *    * `defaultContent` - When null is given as the `data` option and
11292		 *      `defaultContent` is specified for the column, the value defined by
11293		 *      `defaultContent` will be used for the cell.
11294		 *    * `render` - When null is used for the `data` option and the `render`
11295		 *      option is specified for the column, the whole data source for the
11296		 *      row is used for the renderer.
11297		 * * `function` - the function given will be executed whenever DataTables
11298		 *   needs to set or get the data for a cell in the column. The function
11299		 *   takes three parameters:
11300		 *    * Parameters:
11301		 *      * `{array|object}` The data source for the row
11302		 *      * `{string}` The type call data requested - this will be 'set' when
11303		 *        setting data or 'filter', 'display', 'type', 'sort' or undefined
11304		 *        when gathering data. Note that when `undefined` is given for the
11305		 *        type DataTables expects to get the raw data for the object back<
11306		 *      * `{*}` Data to set when the second parameter is 'set'.
11307		 *    * Return:
11308		 *      * The return value from the function is not required when '
11308set' is
11309		 *        the type of call, but otherwise the return is what will be used
11310		 *        for the data requested.
11311		 *
11312		 * Note that `data` is a getter and setter option. If you just require
11313		 * formatting of data for output, you will likely want to use `render` which
11314		 * is simply a getter and thus simpler to use.
11315		 *
11316		 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
11317		 * name change reflects the flexibility of this property and is consistent
11318		 * with the naming of mRender. If 'mDataProp' is given, then it will still
11319		 * be used by DataTables, as it automatically maps the old name to the new
11320		 * if required.
11321		 */
11322		"mData": null,
11323	
11324	
11325		/**
11326		 * This property is the rendering partner to `data` and it is suggested that
11327		 * when you want to manipulate data for display (including filtering,
11328		 * sorting etc) without altering the underlying data for the table, use this
11329		 * property. `render` can be considered to be the the read only companion to
11330		 * `data` which is read / write (then as such more complex). Like `data`
11331		 * this option can be given in a number of different ways to effect its
11332		 * behaviour:
11333		 *
11334		 * * `integer` - treated as an array index for the data source. This is the
11335		 *   default that DataTables uses (incrementally increased for each column).
11336		 * * `string` - read an object property from the data source. There are
11337		 *   three 'special' options that can be used in the string to alter how
11338		 *   DataTables reads the data from the source object:
11339		 *    * `.` - Dotted Javascript notation. Just as you use a `.` in
11340		 *      Javascript to read from nested objects, so to can the options
11341		 *      specified in `data`. For example: `browser.version` or
11342		 *      `browser.name`. If your object parameter name contains a period, use
11343		 *      `\\` to escape it - i.e. `first\\.name`.
11344		 *    * `[]` - Array notation. DataTables can automatically combine data
11345		 *      from and array source, joining the data with the characters provided
11346		 *      between the two brackets. For example: `name[, ]` would provide a
11347		 *      comma-space separated list from the source array. If no characters
11348		 *      are provided between the brackets, the original array source is
11349		 *      returned.
11350		 *    * `()` - Function notation. Adding `()` to the end of a parameter will
11351		 *      execute a function of the name given. For example: `browser()` for a
11352		 *      simple function on the data source, `browser.version()` for a
11353		 *      function in a nested property or even `browser().version` to get an
11354		 *      object property if the function called returns an object.
11355		 * * `object` - use different data for the different data types requested by
11356		 *   DataTables ('filter', 'display', 'type' or 'sort'). The property names
11357		 *   of the object is the data type the property refers to and the value can
11358		 *   defined using an integer, string or function using the same rules as
11359		 *   `render` normally does. Note that an `_` option _must_ be specified.
11360		 *   This is the default value to use if you haven't specified a value for
11361		 *   the data type requested by DataTables.
11362		 * * `function` - the function given will be executed whenever DataTables
11363		 *   needs to set or get the data for a cell in the column. The function
11364		 *   takes three parameters:
11365		 *    * Parameters:
11366		 *      * {array|object} The data source for the row (based on `data`)
11367		 *      * {string} The type call data requested - this will be 'filter',
11368		 *        'display', 'type' or 'sort'.
11369		 *      * {array|object} The full data source for the row (not based on
11370		 *        `data`)
11371		 *    * Return:
11372		 *      * The return value from the function is what will be used for the
11373		 *        data requested.
11374		 */
11375		"mRender": null,
11376	
11377	
11378		/**
11379		 * Change the cell type created for the column - either TD cells or TH cells. This
11380		 * can be useful as TH cells have semantic meaning in the table body, allowing them
11381		 * to act as a header for a row (you may wish to add scope='row' to the TH elements).
11382		 */
11383		"sCellType": "td",
11384	
11385	
11386		/**
11387		 * Class to give to each cell in this column.
11388		 */
11389		"sClass": "",
11390	
11391		/**
11392		 * When DataTables calculates the column widths to assign to each column,
11393		 * it finds the longest string in each column and then constructs a
11394		 * temporary table and reads the widths from that. The problem with this
11395		 * is that "mmm" is much wider then "iiii", but the latter is a longer
11396		 * string - thus the calculation can go wrong (doing it properly and putting
11397		 * it into an DOM object and measuring that is horribly(!) slow). Thus as
11398		 * a "work around" we provide this option. It will append its value to the
11399		 * text that is found to be the longest string for the column - i.e. padding.
11400		 * Generally you shouldn't need this!
11401		 */
11402		"sContentPadding": "",
11403	
11404	
11405		/**
11406		 * Allows a default value to be given for a column's data, and will be used
11407		 * whenever a null data source is encountered (this can be because `data`
11408		 * is set to null, or because the data source itself is null).
11409		 */
11410		"sDefaultContent": null,
11411	
11412	
11413		/**
11414		 * This parameter is only used in DataTables' server-side processing. It can
11415		 * be exceptionally useful to know what columns are being displayed on the
11416		 * client side, and to map these to database fields. When defined, the names
11417		 * also allow DataTables to reorder information from the server if it comes
11418		 * back in an unexpected order (i.e. if you switch your columns around on the
11419		 * client-side, your server-side code does not also need updating).
11420		 */
11421		"sName": "",
11422	
11423	
11424		/**
11425		 * Defines a data source type for the ordering which can be used to read
11426		 * real-time information from the table (updating the internally cached
11427		 * version) prior to ordering. This allows ordering to occur on user
11428		 * editable elements such as form inputs.
11429		 */
11430		"sSortDataType": "std",
11431	
11432	
11433		/**
11434		 * The title of this column.
11435		 */
11436		"sTitle": null,
11437	
11438	
11439		/**
11440		 * The type allows you to specify how the data for this column will be
11441		 * ordered. Four types (string, numeric, date and html (which will strip
11442		 * HTML tags before ordering)) are currently available. Note that only date
11443		 * formats understood by Javascript's Date() object will be accepted as type
11444		 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
11445		 * 'numeric', 'date' or 'html' (by default). Further types can be adding
11446		 * through plug-ins.
11447		 */
11448		"sType": null,
11449	
11450	
11451		/**
11452		 * Defining the width of the column, this parameter may take any CSS value
11453		 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
11454		 * been given a specific width through this interface ensuring that the table
11455		 * remains readable.
11456		 */
11457		"sWidth": null
11458	};
11459	
11460	_fnHungarianMap( DataTable.defaults.column );
11461	
11462	
11463	
11464	/**
11465	 * DataTables settings object - this holds all the information needed for a
11466	 * given table, including configuration, data and current application of the
11467	 * table options. DataTables does not have a single instance for each DataTable
11468	 * with the settings attached to that instance, but rather instances of the
11469	 * DataTable "class" are created on-the-fly as needed (typically by a
11470	 * $().dataTable() call) and the settings object is then applied to that
11471	 * instance.
11472	 *
11473	 * Note that this object is related to {@link DataTable.defaults} but this
11474	 * one is the internal data store for DataTables's cache of columns. It should
11475	 * NOT be manipulated outside of DataTables. Any configuration should be done
11476	 * through the initialisation options.
11477	 */
11478	DataTable.models.oSettings = {
11479		/**
11480		 * Primary features of DataTables and their enablement state.
11481		 */
11482		"oFeatures": {
11483	
11484			/**
11485			 * Flag to say if DataTables should automatically try to calculate the
11486			 * optimum table and columns widths (true) or not (false).
11487			 * Note that this parameter will be set by the initialisation routine. To
11488			 * set a default use {@link DataTable.defaults}.
11489			 */
11490			"bAutoWidth": null,
11491	
11492			/**
11493			 * Delay the creation of TR and TD elements until they are actually
11494			 * needed by a driven page draw. This can give a significant speed
11495			 * increase for Ajax source and Javascript source data, but makes no
11496			 * difference at all for DOM and server-side processing tables.
11497			 * Note that this parameter will be set by the initialisation routine. To
11498			 * set a default use {@link DataTable.defaults}.
11499			 */
11500			"bDeferRender": null,
11501	
11502			/**
11503			 * Enable filtering on the table or not. Note that if this is disabled
11504			 * then there is no filtering at all on the table, including fnFilter.
11505			 * To just remove the filtering input use sDom and remove the 'f' option.
11506			 * Note that this parameter will be set by the initialisation routine. To
11507			 * set a default use {@link DataTable.defaults}.
11508			 */
11509			"bFilter": null,
11510	
11511			/**
11512			 * Used only for compatiblity with DT1
11513			 * @deprecated
11514			 */
11515			"bInfo": true,
11516	
11517			/**
11518			 * Used only for compatiblity with DT1
11519			 * @deprecated
11520			 */
11521			"bLengthChange": true,
11522	
11523			/**
11524			 * Pagination enabled or not. Note that if this is disabled then length
11525			 * changing must also be disabled.
11526			 * Note that this parameter will be set by the initialisation routine. To
11527			 * set a default use {@link DataTable.defaults}.
11528			 */
11529			"bPaginate": null,
11530	
11531			/**
11532			 * Processing indicator enable flag whenever DataTables is enacting a
11533			 * user request - typically an Ajax request for server-side processing.
11534			 * Note that this parameter will be set by the initialisation routine. To
11535			 * set a default use {@link DataTable.defaults}.
11536			 */
11537			"bProcessing": null,
11538	
11539			/**
11540			 * Server-side processing enabled flag - when enabled DataTables will
11541			 * get all data from the server for every draw - there is no filtering,
11542			 * sorting or paging done on the client-side.
11543			 * Note that this parameter will be set by the initialisation routine. To
11544			 * set a default use {@link DataTable.defaults}.
11545			 */
11546			"bServerSide": null,
11547	
11548			/**
11549			 * Sorting enablement flag.
11550			 * Note that this parameter will be set by the initialisation routine. To
11551			 * set a default use {@link DataTable.defaults}.
11552			 */
11553			"bSort": null,
11554	
11555			/**
11556			 * Multi-column sorting
11557			 * Note that this parameter will be set by the initialisation routine. To
11558			 * set a default use {@link DataTable.defaults}.
11559			 */
11560			"bSortMulti": null,
11561	
11562			/**
11563			 * Apply a class to the columns which are being sorted to provide a
11564			 * visual highlight or not. This can slow things down when enabled since
11565			 * there is a lot of DOM interaction.
11566			 * Note that this parameter will be set by the initialisation routine. To
11567			 * set a default use {@link DataTable.defaults}.
11568			 */
11569			"bSortClasses": null,
11570	
11571			/**
11572			 * State saving enablement flag.
11573			 * Note that this parameter will be set by the initialisation routine. To
11574			 * set a default use {@link DataTable.defaults}.
11575			 */
11576			"bStateSave": null
11577		},
11578	
11579	
11580		/**
11581		 * Scrolling settings for a table.
11582		 */
11583		"oScroll": {
11584			/**
11585			 * When the table is shorter in height than sScrollY, collapse the
11586			 * table container down to the height of the table (when true).
11587			 * Note that this parameter will be set by the initialisation routine. To
11588			 * set a default use {@link DataTable.defaults}.
11589			 */
11590			"bCollapse": null,
11591	
11592			/**
11593			 * Width of the scrollbar for the web-browser's platform. Calculated
11594			 * during table initialisation.
11595			 */
11596			"iBarWidth": 0,
11597	
11598			/**
11599			 * Viewport width for horizontal scrolling. Horizontal scrolling is
11600			 * disabled if an empty string.
11601			 * Note that this parameter will be set by the initialisation routine. To
11602			 * set a default use {@link DataTable.defaults}.
11603			 */
11604			"sX": null,
11605	
11606			/**
11607			 * Width to expand the table to when using x-scrolling. Typically you
11608			 * should not need to use this.
11609			 * Note that this parameter will be set by the initialisation routine. To
11610			 * set a default use {@link DataTable.defaults}.
11611			 *  @deprecated
11612			 */
11613			"sXInner": null,
11614	
11615			/**
11616			 * Viewport height for vertical scrolling. Vertical scrolling is disabled
11617			 * if an empty string.
11618			 * Note that this parameter will be set by the initialisation routine. To
11619			 * set a default use {@link DataTable.defaults}.
11620			 */
11621			"sY": null
11622		},
11623	
11624		/**
11625		 * Language information for the table.
11626		 */
11627		"oLanguage": {
11628			/**
11629			 * Information callback function. See
11630			 * {@link DataTable.defaults.fnInfoCallback}
11631			 */
11632			"fnInfoCallback": null
11633		},
11634	
11635		/**
11636		 * Browser support parameters
11637		 */
11638		"oBrowser": {
11639			/**
11640			 * Determine if the vertical scrollbar is on the right or left of the
11641			 * scrolling container - needed for rtl language layout, although not
11642			 * all browsers move the scrollbar (Safari).
11643			 */
11644			"bScrollbarLeft": false,
11645	
11646			/**
11647			 * Browser scrollbar width
11648			 */
11649			"barWidth": 0
11650		},
11651	
11652	
11653		"ajax": null,
11654	
11655	
11656		/**
11657		 * Array referencing the nodes which are used for the features. The
11658		 * parameters of this object match what is allowed by sDom - i.e.
11659		 *   <ul>
11660		 *     <li>'l' - Length changing</li>
11661		 *     <li>'f' - Filtering input</li>
11662		 *     <li>'t' - The table!</li>
11663		 *     <li>'i' - Information</li>
11664		 *     <li>'p' - Pagination</li>
11665		 *     <li>'r' - pRocessing</li>
11666		 *   </ul>
11667		 */
11668		"aanFeatures": [],
11669	
11670		/**
11671		 * Store data information - see {@link DataTable.models.oRow} for detailed
11672		 * information.
11673		 */
11674		"aoData": [],
11675	
11676		/**
11677		 * Array of indexes which are in the current display (after filtering etc)
11678		 */
11679		"aiDisplay": [],
11680	
11681		/**
11682		 * Array of indexes for display - no filtering
11683		 */
11684		"aiDisplayMaster": [],
11685	
11686		/**
11687		 * Map of row ids to data indexes
11688		 */
11689		"aIds": {},
11690	
11691		/**
11692		 * Store information about each column that is in use
11693		 */
11694		"aoColumns": [],
11695	
11696		/**
11697		 * Store information about the table's header
11698		 */
11699		"aoHeader": [],
11700	
11701		/**
11702		 * Store information about the table's footer
11703		 */
11704		"aoFooter": [],
11705	
11706		/**
11707		 * Store the applied global search information in case we want to force a
11708		 * research or compare the old search to a new one.
11709		 * Note that this parameter will be set by the initialisation routine. To
11710		 * set a default use {@link DataTable.defaults}.
11711		 */
11712		"oPreviousSearch": {},
11713	
11714		/**
11715		 * Store for named searches
11716		 */
11717		searchFixed: {},
11718	
11719		/**
11720		 * Store the applied search for each column - see
11721		 * {@link DataTable.models.oSearch} for the format that is used for the
11722		 * filtering information for each column.
11723		 */
11724		"aoPreSearchCols": [],
11725	
11726		/**
11727		 * Sorting that is applied to the table. Note that the inner arrays are
11728		 * used in the following manner:
11729		 * <ul>
11730		 *   <li>Index 0 - column number</li>
11731		 *   <li>Index 1 - current sorting direction</li>
11732		 * </ul>
11733		 * Note that this parameter will be set by the initialisation routine. To
11734		 * set a default use {@link DataTable.defaults}.
11735		 */
11736		"aaSorting": null,
11737	
11738		/**
11739		 * Sorting that is always applied to the table (i.e. prefixed in front of
11740		 * aaSorting).
11741		 * Note that this parameter will be set by the initialisation routine. To
11742		 * set a default use {@link DataTable.defaults}.
11743		 */
11744		"aaSortingFixed": [],
11745	
11746		/**
11747		 * If restoring a table - we should restore its width
11748		 */
11749		"sDestroyWidth": 0,
11750	
11751		/**
11752		 * Callback functions array for every time a row is inserted (i.e. on a draw).
11753		 */
11754		"aoRowCallback": [],
11755	
11756		/**
11757		 * Callback functions for the header on each draw.
11758		 */
11759		"aoHeaderCallback": [],
11760	
11761		/**
11762		 * Callback function for the footer on each draw.
11763		 */
11764		"aoFooterCallback": [],
11765	
11766		/**
11767		 * Array of callback functions for draw callback functions
11768		 */
11769		"aoDrawCallback": [],
11770	
11771		/**
11772		 * Array of callback functions for row created function
11773		 */
11774		"aoRowCreatedCallback": [],
11775	
11776		/**
11777		 * Callback functions for just before the table is redrawn. A return of
11778		 * false will be used to cancel the draw.
11779		 */
11780		"aoPreDrawCallback": [],
11781	
11782		/**
11783		 * Callback functions for when the table has been initialised.
11784		 */
11785		"aoInitComplete": [],
11786	
11787	
11788		/**
11789		 * Callbacks for modifying the settings to be stored for state saving, prior to
11790		 * saving state.
11791		 */
11792		"aoStateSaveParams": [],
11793	
11794		/**
11795		 * Callbacks for modifying the settings that have been stored for state saving
11796		 * prior to using the stored values to restore the state.
11797		 */
11798		"aoStateLoadParams": [],
11799	
11800		/**
11801		 * Callbacks for operating on the settings object once the saved state has been
11802		 * loaded
11803		 */
11804		"aoStateLoaded": [],
11805	
11806		/**
11807		 * Cache the table ID for quick access
11808		 */
11809		"sTableId": "",
11810	
11811		/**
11812		 * The TABLE node for the main table
11813		 */
11814		"nTable": null,
11815	
11816		/**
11817		 * Permanent ref to the thead element
11818		 */
11819		"nTHead": null,
11820	
11821		/**
11822		 * Permanent ref to the tfoot element - if it exists
11823		 */
11824		"nTFoot": null,
11825	
11826		/**
11827		 * Permanent ref to the tbody element
11828		 */
11829		"nTBody": null,
11830	
11831		/**
11832		 * Cache the wrapper node (contains all DataTables controlled elements)
11833		 */
11834		"nTableWrapper": null,
11835	
11836		/**
11837		 * Indicate if all required information has been read in
11838		 */
11839		"bInitialised": false,
11840	
11841		/**
11842		 * Information about open rows. Each object in the array has the parameters
11843		 * 'nTr' and 'nParent'
11844		 */
11845		"aoOpenRows": [],
11846	
11847		/**
11848		 * Dictate the positioning of DataTables' control elements - see
11849		 * {@link DataTable.model.oInit.sDom}.
11850		 * Note that this parameter will be set by the initialisation routine. To
11851		 * set a default use {@link DataTable.defaults}.
11852		 */
11853		"sDom": null,
11854	
11855		/**
11856		 * Search delay (in mS)
11857		 */
11858		"searchDelay": null,
11859	
11860		/**
11861		 * Which type of pagination should be used.
11862		 * Note that this parameter will be set by the initialisation routine. To
11863		 * set a default use {@link DataTable.defaults}.
11864		 */
11865		"sPaginationType": "two_button",
11866	
11867		/**
11868		 * Number of paging controls on the page. Only used for backwards compatibility
11869		 */
11870		pagingControls: 0,
11871	
11872		/**
11873		 * The state duration (for `stateSave`) in seconds.
11874		 * Note that this parameter will be set by the initialisation routine. To
11875		 * set a default use {@link DataTable.defaults}.
11876		 */
11877		"iStateDuration": 0,
11878	
11879		/**
11880		 * Array of callback functions for state saving. Each array element is an
11881		 * object with the following parameters:
11882		 *   <ul>
11883		 *     <li>function:fn - function to call. Takes two parameters, oSettings
11884		 *       and the JSON string to save that has been thus far created. Returns
11885		 *       a JSON string to be inserted into a json object
11886		 *       (i.e. '"param": [ 0, 1, 2]')</li>
11887		 *     <li>string:sName - name of callback</li>
11888		 *   </ul>
11889		 */
11890		"aoStateSave": [],
11891	
11892		/**
11893		 * Array of callback functions for state loading. Each array element is an
11894		 * object with the following parameters:
11895		 *   <ul>
11896		 *     <li>function:fn - function to call. Takes two parameters, oSettings
11897		 *       and the object stored. May return false to cancel state loading</li>
11898		 *     <li>string:sName - name of callback</li>
11899		 *   </ul>
11900		 */
11901		"aoStateLoad": [],
11902	
11903		/**
11904		 * State that was saved. Useful for back reference
11905		 */
11906		"oSavedState": null,
11907	
11908		/**
11909		 * State that was loaded. Useful for back reference
11910		 */
11911		"oLoadedState": null,
11912	
11913		/**
11914		 * Note if draw should be blocked while getting data
11915		 */
11916		"bAjaxDataGet": true,
11917	
11918		/**
11919		 * The last jQuery XHR object that was used for server-side data gathering.
11920		 * This can be used for working with the XHR information in one of the
11921		 * callbacks
11922		 */
11923		"jqXHR": null,
11924	
11925		/**
11926		 * JSON returned from the server in the last Ajax request
11927		 */
11928		"json": undefined,
11929	
11930		/**
11931		 * Data submitted as part of the last Ajax request
11932		 */
11933		"oAjaxData": undefined,
11934	
11935		/**
11936		 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
11937		 * required).
11938		 * Note that this parameter will be set by the initialisation routine. To
11939		 * set a default use {@link DataTable.defaults}.
11940		 */
11941		"sServerMethod": null,
11942	
11943		/**
11944		 * Format numbers for display.
11945		 * Note that this parameter will be set by the initialisation routine. To
11946		 * set a default use {@link DataTable.defaults}.
11947		 */
11948		"fnFormatNumber": null,
11949	
11950		/**
11951		 * List of options that can be used for the user selectable length menu.
11952		 * Note that this parameter will be set by the initialisation routine. To
11953		 * set a default use {@link DataTable.defaults}.
11954		 */
11955		"aLengthMenu": null,
11956	
11957		/**
11958		 * Counter for the draws that the table does. Also used as a tracker for
11959		 * server-side processing
11960		 */
11961		"iDraw": 0,
11962	
11963		/**
11964		 * Indicate if a redraw is being done - useful for Ajax
11965		 */
11966		"bDrawing": false,
11967	
11968		/**
11969		 * Draw index (iDraw) of the last error when parsing the returned data
11970		 */
11971		"iDrawError": -1,
11972	
11973		/**
11974		 * Paging display length
11975		 */
11976		"_iDisplayLength": 10,
11977	
11978		/**
11979		 * Paging start point - aiDisplay index
11980		 */
11981		"_iDisplayStart": 0,
11982	
11983		/**
11984		 * Server-side processing - number of records in the result set
11985		 * (i.e. before filtering), Use fnRecordsTotal rather than
11986		 * this property to get the value of the number of records, regardless of
11987		 * the server-side processing setting.
11988		 */
11989		"_iRecordsTotal": 0,
11990	
11991		/**
11992		 * Server-side processing - number of records in the current display set
11993		 * (i.e. after filtering). Use fnRecordsDisplay rather than
11994		 * this property to get the value of the number of records, regardless of
11995		 * the server-side processing setting.
11996		 */
11997		"_iRecordsDisplay": 0,
11998	
11999		/**
12000		 * The classes to use for the table
12001		 */
12002		"oClasses": {},
12003	
12004		/**
12005		 * Flag attached to the settings object so you can check in the draw
12006		 * callback if filtering has been done in the draw. Deprecated in favour of
12007		 * events.
12008		 *  @deprecated
12009		 */
12010		"bFiltered": false,
12011	
12012		/**
12013		 * Flag attached to the settings object so you can check in the draw
12014		 * callback if sorting has been done in the draw. Deprecated in favour of
12015		 * events.
12016		 *  @deprecated
12017		 */
12018		"bSorted": false,
12019	
12020		/**
12021		 * Indicate that if multiple rows are in the header and there is more than
12022		 * one unique cell per column, if the top one (true) or bottom one (false)
12023		 * should be used for sorting / title by DataTables.
12024		 * Note that this parameter will be set by the initialisation routine. To
12025		 * set a default use {@link DataTable.defaults}.
12026		 */
12027		"bSortCellsTop": null,
12028	
12029		/**
12030		 * Initialisation object that is used for the table
12031		 */
12032		"oInit": null,
12033	
12034		/**
12035		 * Destroy callback functions - for plug-ins to attach themselves to the
12036		 * destroy so they can clean up markup and events.
12037		 */
12038		"aoDestroyCallback": [],
12039	
12040	
12041		/**
12042		 * Get the number of records in the current record set, before filtering
12043		 */
12044		"fnRecordsTotal": function ()
12045		{
12046			return _fnDataSource( this ) == 'ssp' ?
12047				this._iRecordsTotal * 1 :
12048				this.aiDisplayMaster.length;
12049		},
12050	
12051		/**
12052		 * Get the number of records in the current record set, after filtering
12053		 */
12054		"fnRecordsDisplay": function ()
12055		{
12056			return _fnDataSource( this ) == 'ssp' ?
12057				this._iRecordsDisplay * 1 :
12058				this.aiDisplay.length;
12059		},
12060	
12061		/**
12062		 * Get the display end point - aiDisplay index
12063		 */
12064		"fnDisplayEnd": function ()
12065		{
12066			var
12067				len      = this._iDisplayLength,
12068				start    = this._iDisplayStart,
12069				calc     = start + len,
12070				records  = this.aiDisplay.length,
12071				features = this.oFeatures,
12072				paginate = features.bPaginate;
12073	
12074			if ( features.bServerSide ) {
12075				return paginate === false || len === -1 ?
12076					start + records :
12077					Math.min( start+len, this._iRecordsDisplay );
12078			}
12079			else {
12080				return ! paginate || calc>records || len===-1 ?
12081					records :
12082					calc;
12083			}
12084		},
12085	
12086		/**
12087		 * The DataTables object for this table
12088		 */
12089		"oInstance": null,
12090	
12091		/**
12092		 * Unique identifier for each instance of the DataTables object. If there
12093		 * is an ID on the table node, then it takes that value, otherwise an
12094		 * incrementing internal counter is used.
12095		 */
12096		"sInstance": null,
12097	
12098		/**
12099		 * tabindex attribute value that is added to DataTables control elements, allowing
12100		 * keyboard navigation of the table and its controls.
12101		 */
12102		"iTabIndex": 0,
12103	
12104		/**
12105		 * DIV container for the footer scrolling table if scrolling
12106		 */
12107		"nScrollHead": null,
12108	
12109		/**
12110		 * DIV container for the footer scrolling table if scrolling
12111		 */
12112		"nScrollFoot": null,
12113	
12114		/**
12115		 * Last applied sort
12116		 */
12117		"aLastSort": [],
12118	
12119		/**
12120		 * Stored plug-in instances
12121		 */
12122		"oPlugins": {},
12123	
12124		/**
12125		 * Function used to get a row's id from the row's data
12126		 */
12127		"rowIdFn": null,
12128	
12129		/**
12130		 * Data location where to store a row's id
12131		 */
12132		"rowId": null,
12133	
12134		caption: '',
12135	
12136		captionNode: null,
12137	
12138		colgroup: null,
12139	
12140		/** Delay loading of data */
12141		deferLoading: null,
12142	
12143		/** Allow auto type detection */
12144		typeDetect: true,
12145	
12146		/** ResizeObserver for the container div */
12147		resizeObserver: null,
12148	
12149		/** Keep a record of the last size of the container, so we can skip duplicates */
12150		containerWidth: -1
12151	};
12152	
12153	/**
12154	 * Extension object for DataTables that is used to provide all extension
12155	 * options.
12156	 *
12157	 * Note that the `DataTable.ext` object is available through
12158	 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
12159	 * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
12160	 *  @namespace
12161	 *  @extends DataTable.models.ext
12162	 */
12163	
12164	
12165	var extPagination = DataTable.ext.pager;
12166	
12167	// Paging buttons configuration
12168	$.extend( extPagination, {
12169		simple: function () {
12170			return [ 'previous', 'next' ];
12171		},
12172	
12173		full: function () {
12174			return [ 'first', 'previous', 'next', 'last' ];
12175		},
12176	
12177		numbers: function () {
12178			return [ 'numbers' ];
12179		},
12180	
12181		simple_numbers: function () {
12182			return [ 'previous', 'numbers', 'next' ];
12183		},
12184	
12185		full_numbers: function () {
12186			return [ 'first', 'previous', 'numbers', 'next', 'last' ];
12187		},
12188	
12189		first_last: function () {
12190			return ['first', 'last'];
12191		},
12192	
12193		first_last_numbers: function () {
12194			return ['first', 'numbers', 'last'];
12195		},
12196	
12197		// For testing and plug-ins to use
12198		_numbers: _pagingNumbers,
12199	
12200		// Number of number buttons - legacy, use `numbers` option for paging feature
12201		numbers_length: 7
12202	} );
12203	
12204	
12205	$.extend( true, DataTable.ext.renderer, {
12206		pagingButton: {
12207			_: function (settings, buttonType, content, active, disabled) {
12208				var classes = settings.oClasses.paging;
12209				var btnClasses = [classes.button];
12210				var btn;
12211	
12212				if (active) {
12213					btnClasses.push(classes.active);
12214				}
12215	
12216				if (disabled) {
12217					btnClasses.push(classes.disabled)
12218				}
12219	
12220				if (buttonType === 'ellipsis') {
12221					btn = $('<span class="ellipsis"></span>').html(content)[0];
12222				}
12223				else {
12224					btn = $('<button>', {
12225						class: btnClasses.join(' '),
12226						role: 'link',
12227						type: 'button'
12228					}).html(content);
12229				}
12230	
12231				return {
12232					display: btn,
12233					clicker: btn
12234				}
12235			}
12236		},
12237	
12238		pagingContainer: {
12239			_: function (settings, buttons) {
12240				// No wrapping element - just append directly to the host
12241				return buttons;
12242			}
12243		}
12244	} );
12245	
12246	// Common function to remove new lines, strip HTML and diacritic control
12247	var _filterString = function (stripHtml, normalize) {
12248		return function (str) {
12249			if (_empty(str) || typeof str !== 'string') {
12250				return str;
12251			}
12252	
12253			str = str.replace( _re_new_lines, " " );
12254	
12255			if (stripHtml) {
12256				str = _stripHtml(str);
12257			}
12258	
12259			if (normalize) {
12260				str = _normalize(str, false);
12261			}
12262	
12263			return str;
12264		};
12265	}
12266	
12267	/*
12268	 * Public helper functions. These aren't used internally by DataTables, or
12269	 * called by any of the options passed into DataTables, but they can be used
12270	 * externally by developers working with DataTables. They are helper functions
12271	 * to make working with DataTables a little bit easier.
12272	 */
12273	
12274	/**
12275	 * Common logic for moment, luxon or a date action.
12276	 *
12277	 * Happens after __mldObj, so don't need to call `resolveWindowsLibs` again
12278	 */
12279	function __mld( dtLib, momentFn, luxonFn, dateFn, arg1 ) {
12280		if (__moment) {
12281			return dtLib[momentFn]( arg1 );
12282		}
12283		else if (__luxon) {
12284			return dtLib[luxonFn]( arg1 );
12285		}
12286		
12287		return dateFn ? dtLib[dateFn]( arg1 ) : dtLib;
12288	}
12289	
12290	
12291	var __mlWarning = false;
12292	var __luxon; // Can be assigned in DateTable.use()
12293	var __moment; // Can be assigned in DateTable.use()
12294	
12295	/**
12296	 * 
12297	 */
12298	function resolveWindowLibs() {
12299		if (window.luxon && ! __luxon) {
12300			__luxon = window.luxon;
12301		}
12302		
12303		if (window.moment && ! __moment) {
12304			__moment = window.moment;
12305		}
12306	}
12307	
12308	function __mldObj (d, format, locale) {
12309		var dt;
12310	
12311		resolveWindowLibs();
12312	
12313		if (__moment) {
12314			dt = __moment.utc( d, format, locale, true );
12315	
12316			if (! dt.isValid()) {
12317				return null;
12318			}
12319		}
12320		else if (__luxon) {
12321			dt = format && typeof d === 'string'
12322				? __luxon.DateTime.fromFormat( d, format )
12323				: __luxon.DateTime.fromISO( d );
12324	
12325			if (! dt.isValid) {
12326				return null;
12327			}
12328	
12329			dt = dt.setLocale(locale);
12330		}
12331		else if (! format) {
12332			// No format given, must be ISO
12333			dt = new Date(d);
12334		}
12335		else {
12336			if (! __mlWarning) {
12337				alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17');
12338			}
12339	
12340			__mlWarning = true;
12341		}
12342	
12343		return dt;
12344	}
12345	
12346	// Wrapper for date, datetime and time which all operate the same way with the exception of
12347	// the output string for auto locale support
12348	function __mlHelper (localeString) {
12349		return function ( from, to, locale, def ) {
12350			// Luxon and Moment support
12351			// Argument shifting
12352			if ( arguments.length === 0 ) {
12353				locale = 'en';
12354				to = null; // means toLocaleString
12355				from = null; // means iso8601
12356			}
12357			else if ( arguments.length === 1 ) {
12358				locale = 'en';
12359				to = from;
12360				from = null;
12361			}
12362			else if ( arguments.length === 2 ) {
12363				locale = to;
12364				to = from;
12365				from = null;
12366			}
12367	
12368			var typeName = 'datetime' + (to ? '-' + to : '');
12369	
12370			// Add type detection and sorting specific to this date format - we need to be able to identify
12371			// date type columns as such, rather than as numbers in extensions. Hence the need for this.
12372			if (! DataTable.ext.type.order[typeName + '-pre']) {
12373				DataTable.type(typeName, {
12374					detect: function (d) {
12375						// The renderer will give the value to type detect as the type!
12376						return d === typeName ? typeName : false;
12377					},
12378					order: {
12379						pre: function (d) {
12380							// The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a
12381							// `valueOf` which gives milliseconds epoch
12382							return d.valueOf();
12383						}
12384					},
12385					className: 'dt-right'
12386				});
12387			}
12388		
12389			return function ( d, type ) {
12390				// Allow for a default value
12391				if (d === null || d === undefined) {
12392					if (def === '--now') {
12393						// We treat everything as UTC further down, so no changes are
12394						// made, as such need to get the local date / time as if it were
12395						// UTC
12396						var local = new Date();
12397						d = new Date( Date.UTC(
12398							local.getFullYear(), local.getMonth(), local.getDate(),
12399							local.getHours(), local.getMinutes(), local.getSeconds()
12400						) );
12401					}
12402					else {
12403						d = '';
12404					}
12405				}
12406	
12407				if (type === 'type') {
12408					// Typing uses the type name for fast matching
12409					return typeName;
12410				}
12411	
12412				if (d === '') {
12413					return type !== 'sort'
12414						? ''
12415						: __mldObj('0000-01-01 00:00:00', null, locale);
12416				}
12417	
12418				// Shortcut. If `from` and `to` are the same, we are using the renderer to
12419				// format for ordering, not display - its already in the display format.
12420				if ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) {
12421					return d;
12422				}
12423	
12424				var dt = __mldObj(d, from, locale);
12425	
12426				if (dt === null) {
12427					return d;
12428				}
12429	
12430				if (type === 'sort') {
12431					return dt;
12432				}
12433				
12434				var formatted = to === null
12435					? __mld(dt, 'toDate', 'toJSDate', '')[localeString]()
12436					: __mld(dt, 'format', 'toFormat', 'toISOString', to);
12437	
12438				// XSS protection
12439				return type === 'display' ?
12440					_escapeHtml( formatted ) :
12441					formatted;
12442			};
12443		}
12444	}
12445	
12446	// Based on locale, determine standard number formatting
12447	// Fallback for legacy browsers is US English
12448	var __thousands = ',';
12449	var __decimal = '.';
12450	
12451	if (window.Intl !== undefined) {
12452		try {
12453			var num = new Intl.NumberFormat().formatToParts(100000.1);
12454		
12455			for (var i=0 ; i<num.length ; i++) {
12456				if (num[i].type === 'group') {
12457					__thousands = num[i].value;
12458				}
12459				else if (num[i].type === 'decimal') {
12460					__decimal = num[i].value;
12461				}
12462			}
12463		}
12464		catch (e) {
12465			// noop
12466		}
12467	}
12468	
12469	// Formatted date time detection - use by declaring the formats you are going to use
12470	DataTable.datetime = function ( format, locale ) {
12471		var typeName = 'datetime-' + format;
12472	
12473		if (! locale) {
12474			locale = 'en';
12475		}
12476	
12477		if (! DataTable.ext.type.order[typeName]) {
12478			DataTable.type(typeName, {
12479				detect: function (d) {
12480					var dt = __mldObj(d, format, locale);
12481					return d === '' || dt ? typeName : false;
12482				},
12483				order: {
12484					pre: function (d) {
12485						return __mldObj(d, format, locale) || 0;
12486					}
12487				},
12488				className: 'dt-right'
12489			});
12490		}
12491	}
12492	
12493	/**
12494	 * Helpers for `columns.render`.
12495	 *
12496	 * The options defined here can be used with the `columns.render` initialisation
12497	 * option to provide a display renderer. The following functions are defined:
12498	 *
12499	 * * `moment` - Uses the MomentJS library to convert from a given format into another.
12500	 * This renderer has three overloads:
12501	 *   * 1 parameter:
12502	 *     * `string` - Format to convert to (assumes input is ISO8601 and locale is `en`)
12503	 *   * 2 parameters:
12504	 *     * `string` - Format to convert from
12505	 *     * `string` - Format to convert to. Assumes `en` locale
12506	 *   * 3 parameters:
12507	 *     * `string` - Format to convert from
12508	 *     * `string` - Format to convert to
12509	 *     * `string` - Locale
12510	 * * `number` - Will format numeric data (defined by `columns.data`) for
12511	 *   display, retaining the original unformatted data for sorting and filtering.
12512	 *   It takes 5 parameters:
12513	 *   * `string` - Thousands grouping separator
12514	 *   * `string` - Decimal point indicator
12515	 *   * `integer` - Number of decimal points to show
12516	 *   * `string` (optional) - Prefix.
12517	 *   * `string` (optional) - Postfix (/suffix).
12518	 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
12519	 *   parameters.
12520	 *
12521	 * @example
12522	 *   // Column definition using the number renderer
12523	 *   {
12524	 *     data: "salary",
12525	 *     render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
12526	 *   }
12527	 *
12528	 * @namespace
12529	 */
12530	DataTable.render = {
12531		date: __mlHelper('toLocaleDateString'),
12532		datetime: __mlHelper('toLocaleString'),
12533		time: __mlHelper('toLocaleTimeString'),
12534		number: function ( thousands, decimal, precision, prefix, postfix ) {
12535			// Auto locale detection
12536			if (thousands === null || thousands === undefined) {
12537				thousands = __thousands;
12538			}
12539	
12540			if (decimal === null || decimal === undefined) {
12541				decimal = __decimal;
12542			}
12543	
12544			return {
12545				display: function ( d ) {
12546					if ( typeof d !== 'number' && typeof d !== 'string' ) {
12547						return d;
12548					}
12549	
12550					if (d === '' || d === null) {
12551						return d;
12552					}
12553	
12554					var negative = d < 0 ? '-' : '';
12555					var flo = parseFloat( d );
12556					var abs = Math.abs(flo);
12557	
12558					// Scientific notation for large and small numbers
12559					if (abs >= 100000000000 || (abs < 0.0001 && abs !== 0) ) {
12560						var exp = flo.toExponential(precision).split(/e\+?/);
12561						return exp[0] + ' x 10<sup>' + exp[1] + '</sup>';
12562					}
12563	
12564					// If NaN then there isn't much formatting that we can do - just
12565					// return immediately, escaping any HTML (this was supposed to
12566					// be a number after all)
12567					if ( isNaN( flo ) ) {
12568						return _escapeHtml( d );
12569					}
12570	
12571					flo = flo.toFixed( precision );
12572					d = Math.abs( flo );
12573	
12574					var intPart = parseInt( d, 10 );
12575					var floatPart = precision ?
12576						decimal+(d - intPart).toFixed( precision ).substring( 2 ):
12577						'';
12578	
12579					// If zero, then can't have a negative prefix
12580					if (intPart === 0 && parseFloat(floatPart) === 0) {
12581						negative = '';
12582					}
12583	
12584					return negative + (prefix||'') +
12585						intPart.toString().replace(
12586							/\B(?=(\d{3})+(?!\d))/g, thousands
12587						) +
12588						floatPart +
12589						(postfix||'');
12590				}
12591			};
12592		},
12593	
12594		text: function () {
12595			return {
12596				display: _escapeHtml,
12597				filter: _escapeHtml
12598			};
12599		}
12600	};
12601	
12602	
12603	var _extTypes = DataTable.ext.type;
12604	
12605	// Get / set type
12606	DataTable.type = function (name, prop, val) {
12607		if (! prop) {
12608			return {
12609				className: _extTypes.className[name],
12610				detect: _extTypes.detect.find(function (fn) {
12611					return fn._name === name;
12612				}),
12613				order: {
12614					pre: _extTypes.order[name + '-pre'],
12615					asc: _extTypes.order[name + '-asc'],
12616					desc: _extTypes.order[name + '-desc']
12617				},
12618				render: _extTypes.render[name],
12619				search: _extTypes.search[name]
12620			};
12621		}
12622	
12623		var setProp = function(prop, propVal) {
12624			_extTypes[prop][name] = propVal;
12625		};
12626		var setDetect = function (detect) {
12627			// `detect` can be a function or an object - we set a name
12628			// property for either - that is used for the detection
12629			Object.defineProperty(detect, "_name", {value: name});
12630	
12631			var idx = _extTypes.detect.findIndex(function (item) {
12632				return item._name === name;
12633			});
12634	
12635			if (idx === -1) {
12636				_extTypes.detect.unshift(detect);
12637			}
12638			else {
12639				_extTypes.detect.splice(idx, 1, detect);
12640			}
12641		};
12642		var setOrder = function (obj) {
12643			_extTypes.order[name + '-pre'] = obj.pre; // can be undefined
12644			_extTypes.order[name + '-asc'] = obj.asc; // can be undefined
12645			_extTypes.order[name + '-desc'] = obj.desc; // can be undefined
12646		};
12647	
12648		// prop is optional
12649		if (val === undefined) {
12650			val = prop;
12651			prop = null;
12652		}
12653	
12654		if (prop === 'className') {
12655			setProp('className', val);
12656		}
12657		else if (prop === 'detect') {
12658			setDetect(val);
12659		}
12660		else if (prop === 'order') {
12661			setOrder(val);
12662		}
12663		else if (prop === 'render') {
12664			setProp('render', val);
12665		}
12666		else if (prop === 'search') {
12667			setProp('search', val);
12668		}
12669		else if (! prop) {
12670			if (val.className) {
12671				setProp('className', val.className);
12672			}
12673	
12674			if (val.detect !== undefined) {
12675				setDetect(val.detect);
12676			}
12677	
12678			if (val.order) {
12679				setOrder(val.order);
12680			}
12681	
12682			if (val.render !== undefined) {
12683				setProp('render', val.render);
12684			}
12685	
12686			if (val.search !== undefined) {
12687				setProp('search', val.search);
12688			}
12689		}
12690	}
12691	
12692	// Get a list of types
12693	DataTable.types = function () {
12694		return _extTypes.detect.map(function (fn) {
12695			return fn._name;
12696		});
12697	};
12698	
12699	var __diacriticSort = function (a, b) {
12700		a = a !== null && a !== undefined ? a.toString().toLowerCase() : '';
12701		b = b !== null && b !== undefined ? b.toString().toLowerCase() : '';
12702	
12703		// Checked for `navigator.languages` support in `oneOf` so this code can't execute in old
12704		// Safari and thus can disable this check
12705		// eslint-disable-next-line compat/compat
12706		return a.localeCompare(b, navigator.languages[0] || navigator.language, {
12707			numeric: true,
12708			ignorePunctuation: true,
12709		});
12710	}
12711	
12712	var __diacriticHtmlSort = function (a, b) {
12713		a = _stripHtml(a);
12714		b = _stripHtml(b);
12715	
12716		return __diacriticSort(a, b);
12717	}
12718	
12719	//
12720	// Built in data types
12721	//
12722	
12723	DataTable.type('string', {
12724		detect: function () {
12725			return 'string';
12726		},
12727		order: {
12728			pre: function ( a ) {
12729				// This is a little complex, but faster than always calling toString,
12730				// http://jsperf.com/tostring-v-check
12731				return _empty(a) && typeof a !== 'boolean' ?
12732					'' :
12733					typeof a === 'string' ?
12734						a.toLowerCase() :
12735						! a.toString ?
12736							'' :
12737							a.toString();
12738			}
12739		},
12740		search: _filterString(false, true)
12741	});
12742	
12743	DataTable.type('string-utf8', {
12744		detect: {
12745			allOf: function ( d ) {
12746				return true;
12747			},
12748			oneOf: function ( d ) {
12749				// At least one data point must contain a non-ASCII character
12750				// This line will also check if navigator.languages is supported or not. If not (Safari 10.0-)
12751				// this data type won't be supported.
12752				// eslint-disable-next-line compat/compat
12753				return ! _empty( d ) && navigator.languages && typeof d === 'string' && d.match(/[^\x00-\x7F]/);
12754			}
12755		},
12756		order: {
12757			asc: __diacriticSort,
12758			desc: function (a, b) {
12759				return __diacriticSort(a, b) * -1;
12760			}
12761		},
12762		search: _filterString(false, true)
12763	});
12764	
12765	
12766	DataTable.type('html', {
12767		detect: {
12768			allOf: function ( d ) {
12769				return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1);
12770			},
12771			oneOf: function ( d ) {
12772				// At least one data point must contain a `<`
12773				return ! _empty( d ) && typeof d === 'string' && d.indexOf('<') !== -1;
12774			}
12775		},
12776		order: {
12777			pre: function ( a ) {
12778				return _empty(a) ?
12779					'' :
12780					a.replace ?
12781						_stripHtml(a).trim().toLowerCase() :
12782						a+'';
12783			}
12784		},
12785		search: _filterString(true, true)
12786	});
12787	
12788	
12789	DataTable.type('html-utf8', {
12790		detect: {
12791			allOf: function ( d ) {
12792				return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1);
12793			},
12794			oneOf: function ( d ) {
12795				// At least one data point must contain a `<` and a non-ASCII character
12796				// eslint-disable-next-line compat/compat
12797				return navigator.languages &&
12798					! _empty( d ) &&
12799					typeof d === 'string' &&
12800					d.indexOf('<') !== -1 &&
12801					typeof d === 'string' && d.match(/[^\x00-\x7F]/);
12802			}
12803		},
12804		order: {
12805			asc: __diacriticHtmlSort,
12806			desc: function (a, b) {
12807				return __diacriticHtmlSort(a, b) * -1;
12808			}
12809		},
12810		search: _filterString(true, true)
12811	});
12812	
12813	
12814	DataTable.type('date', {
12815		className: 'dt-type-date',
12816		detect: {
12817			allOf: function ( d ) {
12818				// V8 tries _very_ hard to make a string passed into `Date.parse()`
12819				// valid, so we need to use a regex to restrict date formats. Use a
12820				// plug-in for anything other than ISO8601 style strings
12821				if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
12822					return null;
12823				}
12824				var parsed = Date.parse(d);
12825				return (parsed !== null && !isNaN(parsed)) || _empty(d);
12826			},
12827			oneOf: function ( d ) {
12828				// At least one entry must be a date or a string with a date
12829				return (d instanceof Date) || (typeof d === 'string' && _re_date.test(d));
12830			}
12831		},
12832		order: {
12833			pre: function ( d ) {
12834				var ts = Date.parse( d );
12835				return isNaN(ts) ? -Infinity : ts;
12836			}
12837		}
12838	});
12839	
12840	
12841	DataTable.type('html-num-fmt', {
12842		className: 'dt-type-numeric',
12843		detect: {
12844			allOf: function ( d, settings ) {
12845				var decimal = settings.oLanguage.sDecimal;
12846				return _htmlNumeric( d, decimal, true, false );
12847			},
12848			oneOf: function (d, settings) {
12849				// At least one data point must contain a numeric value
12850				var decimal = settings.oLanguage.sDecimal;
12851				return _htmlNumeric( d, decimal, true, false );
12852			}
12853		},
12854		order: {
12855			pre: function ( d, s ) {
12856				var dp = s.oLanguage.sDecimal;
12857				return __numericReplace( d, dp, _re_html, _re_formatted_numeric );
12858			}
12859		},
12860		search: _filterString(true, true)
12861	});
12862	
12863	
12864	DataTable.type('html-num', {
12865		className: 'dt-type-numeric',
12866		detect: {
12867			allOf: function ( d, settings ) {
12868				var decimal = settings.oLanguage.sDecimal;
12869				return _htmlNumeric( d, decimal, false, true );
12870			},
12871			oneOf: function (d, settings) {
12872				// At least one data point must contain a numeric value
12873				var decimal = settings.oLanguage.sDecimal;
12874				return _htmlNumeric( d, decimal, false, false );
12875			}
12876		},
12877		order: {
12878			pre: function ( d, s ) {
12879				var dp = s.oLanguage.sDecimal;
12880				return __numericReplace( d, dp, _re_html );
12881			}
12882		},
12883		search: _filterString(true, true)
12884	});
12885	
12886	
12887	DataTable.type('num-fmt', {
12888		className: 'dt-type-numeric',
12889		detect: {
12890			allOf: function ( d, settings ) {
12891				var decimal = settings.oLanguage.sDecimal;
12892				return _isNumber( d, decimal, true, true );
12893			},
12894			oneOf: function (d, settings) {
12895				// At least one data point must contain a numeric value
12896				var decimal = settings.oLanguage.sDecimal;
12897				return _isNumber( d, decimal, true, false );
12898			}
12899		},
12900		order: {
12901			pre: function ( d, s ) {
12902				var dp = s.oLanguage.sDecimal;
12903				return __numericReplace( d, dp, _re_formatted_numeric );
12904			}
12905		}
12906	});
12907	
12908	
12909	DataTable.type('num', {
12910		className: 'dt-type-numeric',
12911		detect: {
12912			allOf: function ( d, settings ) {
12913				var decimal = settings.oLanguage.sDecimal;
12914				return _isNumber( d, decimal, false, true );
12915			},
12916			oneOf: function (d, settings) {
12917				// At least one data point must contain a numeric value
12918				var decimal = settings.oLanguage.sDecimal;
12919				return _isNumber( d, decimal, false, false );
12920			}
12921		},
12922		order: {
12923			pre: function (d, s) {
12924				var dp = s.oLanguage.sDecimal;
12925				return __numericReplace( d, dp );
12926			}
12927		}
12928	});
12929	
12930	
12931	
12932	
12933	var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
12934		if ( d !== 0 && (!d || d === '-') ) {
12935			return -Infinity;
12936		}
12937		
12938		var type = typeof d;
12939	
12940		if (type === 'number' || type === 'bigint') {
12941			return d;
12942		}
12943	
12944		// If a decimal place other than `.` is used, it needs to be given to the
12945		// function so we can detect it and replace with a `.` which is the only
12946		// decimal place Javascript recognises - it is not locale aware.
12947		if ( decimalPlace ) {
12948			d = _numToDecimal( d, decimalPlace );
12949		}
12950	
12951		if ( d.replace ) {
12952			if ( re1 ) {
12953				d = d.replace( re1, '' );
12954			}
12955	
12956			if ( re2 ) {
12957				d = d.replace( re2, '' );
12958			}
12959		}
12960	
12961		return d * 1;
12962	};
12963	
12964	
12965	$.extend( true, DataTable.ext.renderer, {
12966		footer: {
12967			_: function ( settings, cell, classes ) {
12968				cell.addClass(classes.tfoot.cell);
12969			}
12970		},
12971	
12972		header: {
12973			_: function ( settings, cell, classes ) {
12974				cell.addClass(classes.thead.cell);
12975	
12976				if (! settings.oFeatures.bSort) {
12977					cell.addClass(classes.order.none);
12978				}
12979	
12980				var legacyTop = settings.bSortCellsTop;
12981				var headerRows = cell.closest('thead').find('tr');
12982				var rowIdx = cell.parent().index();
12983	
12984				// Conditions to not apply the ordering icons
12985				if (
12986					// Cells and rows which have the attribute to disable the icons
12987					cell.attr('data-dt-order') === 'disable' ||
12988					cell.parent().attr('data-dt-order') === 'disable' ||
12989	
12990					// Legacy support for `orderCellsTop`. If it is set, then cells
12991					// which are not in the top or bottom row of the header (depending
12992					// on the value) do not get the sorting classes applied to them
12993					(legacyTop === true && rowIdx !== 0) ||
12994					(legacyTop === false && rowIdx !== headerRows.length - 1)
12995				) {
12996					return;
12997				}
12998	
12999				// No additional mark-up required
13000				// Attach a sort listener to update on sort - note that using the
13001				// `DT` namespace will allow the event to be removed automatically
13002				// on destroy, while the `dt` namespaced event is the one we are
13003				// listening for
13004				$(settings.nTable).on( 'order.dt.DT column-visibility.dt.DT', function ( e, ctx ) {
13005					if ( settings !== ctx ) { // need to check this this is the host
13006						return;               // table, not a nested one
13007					}
13008	
13009					var sorting = ctx.sortDetails;
13010	
13011					if (! sorting) {
13012						return;
13013					}
13014	
13015					var i;
13016					var orderClasses = classes.order;
13017					var columns = ctx.api.columns( cell );
13018					var col = settings.aoColumns[columns.flatten()[0]];
13019					var orderable = columns.orderable().includes(true);
13020					var ariaType = '';
13021					var indexes = columns.indexes();
13022					var sortDirs = columns.orderable(true).flatten();
13023					var orderedColumns = _pluck(sorting, 'col');
13024					var tabIndex = settings.iTabIndex;
13025	
13026					cell
13027						.removeClass(
13028							orderClasses.isAsc +' '+
13029							orderClasses.isDesc
13030						)
13031						.toggleClass( orderClasses.none, ! orderable )
13032						.toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') )
13033						.toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') );
13034	
13035					// Determine if all of the columns that this cell covers are included in the
13036					// current ordering
13037					var isOrdering = true;
13038					
13039					for (i=0; i<indexes.length; i++) {
13040						if (! orderedColumns.includes(indexes[i])) {
13041							isOrdering = false;
13042						}
13043					}
13044	
13045					if ( isOrdering ) {
13046						// Get the ordering direction for the columns under this cell
13047						// Note that it is possible for a cell to be asc and desc sorting
13048						// (column spanning cells)
13049						var orderDirs = columns.order();
13050	
13051						cell.addClass(
13052							orderDirs.includes('asc') ? orderClasses.isAsc : '' +
13053							orderDirs.includes('desc') ? orderClasses.isDesc : ''
13054						);
13055					}
13056	
13057					// Find the first visible column that has ordering applied to it - it get's
13058					// the aria information, as the ARIA spec says that only one column should
13059					// be marked with aria-sort
13060					var firstVis = -1; // column index
13061	
13062					for (i=0; i<orderedColumns.length; i++) {
13063						if (settings.aoColumns[orderedColumns[i]].bVisible) {
13064							firstVis = orderedColumns[i];
13065							break;
13066						}
13067					}
13068	
13069					if (indexes[0] == firstVis) {
13070						var firstSort = sorting[0];
13071						var sortOrder = col.asSorting;
13072	
13073						cell.attr('aria-sort', firstSort.dir === 'asc' ? 'ascending' : 'descending');
13074	
13075						// Determine if the next click will remove sorting or change the sort
13076						ariaType = ! sortOrder[firstSort.index + 1] ? 'Remove' : 'Reverse';
13077					}
13078					else {
13079						cell.removeAttr('aria-sort');
13080					}
13081	
13082					// Make the headers tab-able for keyboard navigation
13083					if (orderable) {
13084						var orderSpan = cell.find('.dt-column-order');
13085						
13086						orderSpan
13087							.attr('role', 'button')
13088							.attr('aria-label', orderable
13089								? col.ariaTitle + ctx.api.i18n('oAria.orderable' + ariaType)
13090								: col.ariaTitle
13091							);
13092	
13093						if (tabIndex !== -1) {
13094							orderSpan.attr('tabindex', tabIndex);
13095						}
13096					}
13097				} );
13098			}
13099		},
13100	
13101		layout: {
13102			_: function ( settings, container, items ) {
13103				var classes = settings.oClasses.layout;
13104				var row = $('<div/>')
13105					.attr('id', items.id || null)
13106					.addClass(items.className || classes.row)
13107					.appendTo( container );
13108	
13109				DataTable.ext.renderer.layout._forLayoutRow(items, function (key, val) {
13110					if (key === 'id' || key === 'className') {
13111						return;
13112					}
13113	
13114					var klass = '';
13115	
13116					if (val.table) {
13117						row.addClass(classes.tableRow);
13118						klass += classes.tableCell + ' ';
13119					}
13120	
13121					if (key === 'start') {
13122						klass += classes.start;
13123					}
13124					else if (key === 'end') {
13125						klass += classes.end;
13126					}
13127					else {
13128						klass += classes.full;
13129					}
13130	
13131					$('<div/>')
13132						.attr({
13133							id: val.id || null,
13134							"class": val.className
13135								? val.className
13136								: classes.cell + ' ' + klass
13137						})
13138						.append( val.contents )
13139						.appendTo( row );
13140				});
13141			},
13142	
13143			// Shared for use by the styling frameworks
13144			_forLayoutRow: function (items, fn) {
13145				// As we are inserting dom elements, we need start / end in a
13146				// specific order, this function is used for sorting the layout
13147				// keys.
13148				var layoutEnum = function (x) {
13149					switch (x) {
13150						case '': return 0;
13151						case 'start': return 1;
13152						case 'end': return 2;
13153						default: return 3;
13154					}
13155				};
13156	
13157				Object
13158					.keys(items)
13159					.sort(function (a, b) {
13160						return layoutEnum(a) - layoutEnum(b);
13161					})
13162					.forEach(function (key) {
13163						fn(key, items[key]);
13164					});
13165			}
13166		}
13167	} );
13168	
13169	
13170	DataTable.feature = {};
13171	
13172	// Third parameter is internal only!
13173	DataTable.feature.register = function ( name, cb, legacy ) {
13174		DataTable.ext.features[ name ] = cb;
13175	
13176		if (legacy) {
13177			_ext.feature.push({
13178				cFeature: legacy,
13179				fnInit: cb
13180			});
13181		}
13182	};
13183	
13184	function _divProp(el, prop, val) {
13185		if (val) {
13186			el[prop] = val;
13187		}
13188	}
13189	
13190	DataTable.feature.register( 'div', function ( settings, opts ) {
13191		var n = $('<div>')[0];
13192	
13193		if (opts) {
13194			_divProp(n, 'className', opts.className);
13195			_divProp(n, 'id', opts.id);
13196			_divProp(n, 'innerHTML', opts.html);
13197			_divProp(n, 'textContent', opts.text);
13198		}
13199	
13200		return n;
13201	} );
13202	
13203	DataTable.feature.register( 'info', function ( settings, opts ) {
13204		// For compatibility with the legacy `info` top level option
13205		if (! settings.oFeatures.bInfo) {
13206			return null;
13207		}
13208	
13209		var
13210			lang  = settings.oLanguage,
13211			tid = settings.sTableId,
13212			n = $('<div/>', {
13213				'class': settings.oClasses.info.container,
13214			} );
13215	
13216		opts = $.extend({
13217			callback: lang.fnInfoCallback,
13218			empty: lang.sInfoEmpty,
13219			postfix: lang.sInfoPostFix,
13220			search: lang.sInfoFiltered,
13221			text: lang.sInfo,
13222		}, opts);
13223	
13224	
13225		// Update display on each draw
13226		settings.aoDrawCallback.push(function (s) {
13227			_fnUpdateInfo(s, opts, n);
13228		});
13229	
13230		// For the first info display in the table, we add a callback and aria information.
13231		if (! settings._infoEl) {
13232			n.attr({
13233				'aria-live': 'polite',
13234				id: tid+'_info',
13235				role: 'status'
13236			});
13237	
13238			// Table is described by our info div
13239			$(settings.nTable).attr( 'aria-describedby', tid+'_info' );
13240	
13241			settings._infoEl = n;
13242		}
13243	
13244		return n;
13245	}, 'i' );
13246	
13247	/**
13248	 * Update the information elements in the display
13249	 *  @param {object} settings dataTables settings object
13250	 *  @memberof DataTable#oApi
13251	 */
13252	function _fnUpdateInfo ( settings, opts, node )
13253	{
13254		var
13255			start = settings._iDisplayStart+1,
13256			end   = settings.fnDisplayEnd(),
13257			max   = settings.fnRecordsTotal(),
13258			total = settings.fnRecordsDisplay(),
13259			out   = total
13260				? opts.text
13261				: opts.empty;
13262	
13263		if ( total !== max ) {
13264			// Record set after filtering
13265			out += ' ' + opts.search;
13266		}
13267	
13268		// Convert the macros
13269		out += opts.postfix;
13270		out = _fnMacros( settings, out );
13271	
13272		if ( opts.callback ) {
13273			out = opts.callback.call( settings.oInstance,
13274				settings, start, end, max, total, out
13275			);
13276		}
13277	
13278		node.html( out );
13279	
13280		_fnCallbackFire(settings, null, 'info', [settings, node[0], out]);
13281	}
13282	
13283	var __searchCounter = 0;
13284	
13285	// opts
13286	// - text
13287	// - placeholder
13288	DataTable.feature.register( 'search', function ( settings, opts ) {
13289		// Don't show the input if filtering isn't available on the table
13290		if (! settings.oFeatures.bFilter) {
13291			return null;
13292		}
13293	
13294		var classes = settings.oClasses.search;
13295		var tableId = settings.sTableId;
13296		var language = settings.oLanguage;
13297		var previousSearch = settings.oPreviousSearch;
13298		var input = '<input type="search" class="'+classes.input+'"/>';
13299	
13300		opts = $.extend({
13301			placeholder: language.sSearchPlaceholder,
13302			processing: false,
13303			text: language.sSearch
13304		}, opts);
13305	
13306		// The _INPUT_ is optional - is appended if not present
13307		if (opts.text.indexOf('_INPUT_') === -1) {
13308			opts.text += '_INPUT_';
13309		}
13310	
13311		opts.text = _fnMacros(settings, opts.text);
13312	
13313		// We can put the <input> outside of the label if it is at the start or end
13314		// which helps improve accessability (not all screen readers like implicit
13315		// for elements).
13316		var end = opts.text.match(/_INPUT_$/);
13317		var start = opts.text.match(/^_INPUT_/);
13318		var removed = opts.text.replace(/_INPUT_/, '');
13319		var str = '<label>' + opts.text + '</label>';
13320	
13321		if (start) {
13322			str = '_INPUT_<label>' + removed + '</label>';
13323		}
13324		else if (end) {
13325			str = '<label>' + removed + '</label>_INPUT_';
13326		}
13327	
13328		var filter = $('<div>')
13329			.addClass(classes.container)
13330			.append(str.replace(/_INPUT_/, input));
13331	
13332		// add for and id to label and input
13333		filter.find('label').attr('for', 'dt-search-' + __searchCounter);
13334		filter.find('input').attr('id', 'dt-search-' + __searchCounter);
13335		__searchCounter++;
13336	
13337		var searchFn = function(event) {
13338			var val = this.value;
13339	
13340			if(previousSearch.return && event.key !== "Enter") {
13341				return;
13342			}
13343	
13344			/* Now do the filter */
13345			if ( val != previousSearch.search ) {
13346				_fnProcessingRun(settings, opts.processing, function () {
13347					previousSearch.search = val;
13348			
13349					_fnFilterComplete( settings, previousSearch );
13350			
13351					// Need to redraw, without resorting
13352					settings._iDisplayStart = 0;
13353					_fnDraw( settings );
13354				});
13355			}
13356		};
13357	
13358		var searchDelay = settings.searchDelay !== null ?
13359			settings.searchDelay :
13360			0;
13361	
13362		var jqFilter = $('input', filter)
13363			.val( previousSearch.search )
13364			.attr( 'placeholder', opts.placeholder )
13365			.on(
13366				'keyup.DT search.DT input.DT paste.DT cut.DT',
13367				searchDelay ?
13368					DataTable.util.debounce( searchFn, searchDelay ) :
13369					searchFn
13370			)
13371			.on( 'mouseup.DT', function(e) {
13372				// Edge fix! Edge 17 does not trigger anything other than mouse events when clicking
13373				// on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn`
13374				// checks the value to see if it has changed. In other browsers it won't have.
13375				setTimeout( function () {
13376					searchFn.call(jqFilter[0], e);
13377				}, 10);
13378			} )
13379			.on( 'keypress.DT', function(e) {
13380				/* Prevent form submission */
13381				if ( e.keyCode == 13 ) {
13382					return false;
13383				}
13384			} )
13385			.attr('aria-controls', tableId);
13386	
13387		// Update the input elements whenever the table is filtered
13388		$(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
13389			if ( settings === s && jqFilter[0] !== document.activeElement ) {
13390				jqFilter.val( typeof previousSearch.search !== 'function'
13391					? previousSearch.search
13392					: ''
13393				);
13394			}
13395		} );
13396	
13397		return filter;
13398	}, 'f' );
13399	
13400	// opts
13401	// - type - button configuration
13402	// - buttons - number of buttons to show - must be odd
13403	DataTable.feature.register( 'paging', function ( settings, opts ) {
13404		// Don't show the paging input if the table doesn't have paging enabled
13405		if (! settings.oFeatures.bPaginate) {
13406			return null;
13407		}
13408	
13409		opts = $.extend({
13410			buttons: DataTable.ext.pager.numbers_length,
13411			type: settings.sPaginationType,
13412			boundaryNumbers: true,
13413			firstLast: true,
13414			previousNext: true,
13415			numbers: true
13416		}, opts);
13417	
13418		var host = $('<div/>')
13419			.addClass(settings.oClasses.paging.container + (opts.type ? ' paging_' + opts.type : ''))
13420			.append(
13421				$('<nav>')
13422					.attr('aria-label', 'pagination')
13423					.addClass(settings.oClasses.paging.nav)
13424			);
13425		var draw = function () {
13426			_pagingDraw(settings, host.children(), opts);
13427		};
13428	
13429		settings.aoDrawCallback.push(draw);
13430	
13431		// Responsive redraw of paging control
13432		$(settings.nTable).on('column-sizing.dt.DT', draw);
13433	
13434		return host;
13435	}, 'p' );
13436	
13437	/**
13438	 * Dynamically create the button type array based on the configuration options.
13439	 * This will only happen if the paging type is not defined.
13440	 */
13441	function _pagingDynamic(opts) {
13442		var out = [];
13443	
13444		if (opts.numbers) {
13445			out.push('numbers');
13446		}
13447	
13448		if (opts.previousNext) {
13449			out.unshift('previous');
13450			out.push('next');
13451		}
13452	
13453		if (opts.firstLast) {
13454			out.unshift('first');
13455			out.push('last');
13456		}
13457	
13458		return out;
13459	}
13460	
13461	function _pagingDraw(settings, host, opts) {
13462		if (! settings._bInitComplete) {
13463			return;
13464		}
13465	
13466		var
13467			plugin = opts.type
13468				? DataTable.ext.pager[ opts.type ]
13469				: _pagingDynamic,
13470			aria = settings.oLanguage.oAria.paginate || {},
13471			start      = settings._iDisplayStart,
13472			len        = settings._iDisplayLength,
13473			visRecords = settings.fnRecordsDisplay(),
13474			all        = len === -1,
13475			page = all ? 0 : Math.ceil( start / len ),
13476			pages = all ? 1 : Math.ceil( visRecords / len ),
13477			buttons = [],
13478			buttonEls = [],
13479			buttonsNested = plugin(opts)
13480				.map(function (val) {
13481					return val === 'numbers'
13482						? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers)
13483						: val;
13484				});
13485	
13486		// .flat() would be better, but not supported in old Safari
13487		buttons = buttons.concat.apply(buttons, buttonsNested);
13488	
13489		for (var i=0 ; i<buttons.length ; i++) {
13490			var button = buttons[i];
13491	
13492			var btnInfo = _pagingButtonInfo(settings, button, page, pages);
13493			var btn = _fnRenderer( settings, 'pagingButton' )(
13494				settings,
13495				button,
13496				btnInfo.display,
13497				btnInfo.active,
13498				btnInfo.disabled
13499			);
13500	
13501			var ariaLabel = typeof button === 'string'
13502				? aria[ button ]
13503				: aria.number
13504					? aria.number + (button+1)
13505					: null;
13506	
13507			// Common attributes
13508			$(btn.clicker).attr({
13509				'aria-controls': settings.sTableId,
13510				'aria-disabled': btnInfo.disabled ? 'true' : null,
13511				'aria-current': btnInfo.active ? 'page' : null,
13512				'aria-label': ariaLabel,
13513				'data-dt-idx': button,
13514				'tabIndex': btnInfo.disabled
13515					? -1
13516					: settings.iTabIndex && btn.clicker[0].nodeName.toLowerCase() !== 'span'
13517						? settings.iTabIndex
13518						: null, // `0` doesn't need a tabIndex since it is the default
13519			});
13520	
13521			if (typeof button !== 'number') {
13522				$(btn.clicker).addClass(button);
13523			}
13524	
13525			_fnBindAction(
13526				btn.clicker, {action: button}, function(e) {
13527					e.preventDefault();
13528	
13529					_fnPageChange( settings, e.data.action, true );
13530				}
13531			);
13532	
13533			buttonEls.push(btn.display);
13534		}
13535	
13536		var wrapped = _fnRenderer(settings, 'pagingContainer')(
13537			settings, buttonEls
13538		);
13539	
13540		var activeEl = host.find(document.activeElement).data('dt-idx');
13541	
13542		host.empty().append(wrapped);
13543	
13544		if ( activeEl !== undefined ) {
13545			host.find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
13546		}
13547	
13548		// Responsive - check if the buttons are over two lines based on the
13549		// height of the buttons and the container.
13550		if (buttonEls.length) {
13551			var outerHeight = $(buttonEls[0]).outerHeight();
13552		
13553			if (
13554				opts.buttons > 1 && // prevent infinite
13555				outerHeight > 0 && // will be 0 if hidden
13556				$(host).height() >= (outerHeight * 2) - 10
13557			) {
13558				_pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 }));
13559			}
13560		}
13561	}
13562	
13563	/**
13564	 * Get properties for a button based on the current paging state of the table
13565	 *
13566	 * @param {*} settings DT settings object
13567	 * @param {*} button The button type in question
13568	 * @param {*} page Table's current page
13569	 * @param {*} pages Number of pages
13570	 * @returns Info object
13571	 */
13572	function _pagingButtonInfo(settings, button, page, pages) {
13573		var lang = settings.oLanguage.oPaginate;
13574		var o = {
13575			display: '',
13576			active: false,
13577			disabled: false
13578		};
13579	
13580		switch ( button ) {
13581			case 'ellipsis':
13582				o.display = '&#x2026;';
13583				break;
13584	
13585			case 'first':
13586				o.display = lang.sFirst;
13587	
13588				if (page === 0) {
13589					o.disabled = true;
13590				}
13591				break;
13592	
13593			case 'previous':
13594				o.display = lang.sPrevious;
13595	
13596				if ( page === 0 ) {
13597					o.disabled = true;
13598				}
13599				break;
13600	
13601			case 'next':
13602				o.display = lang.sNext;
13603	
13604				if ( pages === 0 || page === pages-1 ) {
13605					o.disabled = true;
13606				}
13607				break;
13608	
13609			case 'last':
13610				o.display = lang.sLast;
13611	
13612				if ( pages === 0 || page === pages-1 ) {
13613					o.disabled = true;
13614				}
13615				break;
13616	
13617			default:
13618				if ( typeof button === 'number' ) {
13619					o.display = settings.fnFormatNumber( button + 1 );
13620					
13621					if (page === button) {
13622						o.active = true;
13623					}
13624				}
13625				break;
13626		}
13627	
13628		return o;
13629	}
13630	
13631	/**
13632	 * Compute what number buttons to show in the paging control
13633	 *
13634	 * @param {*} page Current page
13635	 * @param {*} pages Total number of pages
13636	 * @param {*} buttons Target number of number buttons
13637	 * @param {boolean} addFirstLast Indicate if page 1 and end should be included
13638	 * @returns Buttons to show
13639	 */
13640	function _pagingNumbers ( page, pages, buttons, addFirstLast ) {
13641		var
13642			numbers = [],
13643			half = Math.floor(buttons / 2),
13644			before = addFirstLast ? 2 : 1,
13645			after = addFirstLast ? 1 : 0;
13646	
13647		if ( pages <= buttons ) {
13648			numbers = _range(0, pages);
13649		}
13650		else if (buttons === 1) {
13651			// Single button - current page only
13652			numbers = [page];
13653		}
13654		else if (buttons === 3) {
13655			// Special logic for just three buttons
13656			if (page <= 1) {
13657				numbers = [0, 1, 'ellipsis'];
13658			}
13659			else if (page >= pages - 2) {
13660				numbers = _range(pages-2, pages);
13661				numbers.unshift('ellipsis');
13662			}
13663			else {
13664				numbers = ['ellipsis', page, 'ellipsis'];
13665			}
13666		}
13667		else if ( page <= half ) {
13668			numbers = _range(0, buttons-before);
13669			numbers.push('ellipsis');
13670	
13671			if (addFirstLast) {
13672				numbers.push(pages-1);
13673			}
13674		}
13675		else if ( page >= pages - 1 - half ) {
13676			numbers = _range(pages-(buttons-before), pages);
13677			numbers.unshift('ellipsis');
13678	
13679			if (addFirstLast) {
13680				numbers.unshift(0);
13681			}
13682		}
13683		else {
13684			numbers = _range(page-half+before, page+half-after);
13685			numbers.push('ellipsis');
13686			numbers.unshift('ellipsis');
13687	
13688			if (addFirstLast) {
13689				numbers.push(pages-1);
13690				numbers.unshift(0);
13691			}
13692		}
13693	
13694		return numbers;
13695	}
13696	
13697	var __lengthCounter = 0;
13698	
13699	// opts
13700	// - menu
13701	// - text
13702	DataTable.feature.register( 'pageLength', function ( settings, opts ) {
13703		var features = settings.oFeatures;
13704	
13705		// For compatibility with the legacy `pageLength` top level option
13706		if (! features.bPaginate || ! features.bLengthChange) {
13707			return null;
13708		}
13709	
13710		opts = $.extend({
13711			menu: settings.aLengthMenu,
13712			text: settings.oLanguage.sLengthMenu
13713		}, opts);
13714	
13715		var
13716			classes  = settings.oClasses.length,
13717			tableId  = settings.sTableId,
13718			menu     = opts.menu,
13719			lengths  = [],
13720			language = [],
13721			i;
13722	
13723		// Options can be given in a number of ways
13724		if (Array.isArray( menu[0] )) {
13725			// Old 1.x style - 2D array
13726			lengths = menu[0];
13727			language = menu[1];
13728		}
13729		else {
13730			for ( i=0 ; i<menu.length ; i++ ) {
13731				// An object with different label and value
13732				if ($.isPlainObject(menu[i])) {
13733					lengths.push(menu[i].value);
13734					language.push(menu[i].label);
13735				}
13736				else {
13737					// Or just a number to display and use
13738					lengths.push(menu[i]);
13739					language.push(menu[i]);
13740				}
13741			}
13742		}
13743	
13744		// We can put the <select> outside of the label if it is at the start or
13745		// end which helps improve accessability (not all screen readers like
13746		// implicit for elements).
13747		var end = opts.text.match(/_MENU_$/);
13748		var start = opts.text.match(/^_MENU_/);
13749		var removed = opts.text.replace(/_MENU_/, '');
13750		var str = '<label>' + opts.text + '</label>';
13751	
13752		if (start) {
13753			str = '_MENU_<label>' + removed + '</label>';
13754		}
13755		else if (end) {
13756			str = '<label>' + removed + '</label>_MENU_';
13757		}
13758	
13759		// Wrapper element - use a span as a holder for where the select will go
13760		var tmpId = 'tmp-' + (+new Date())
13761		var div = $('<div/>')
13762			.addClass( classes.container )
13763			.append(
13764				str.replace( '_MENU_', '<span id="'+tmpId+'"></span>' )
13765			);
13766	
13767		// Save text node content for macro updating
13768		var textNodes = [];
13769		Array.prototype.slice.call(div.find('label')[0].childNodes).forEach(function (el) {
13770			if (el.nodeType === Node.TEXT_NODE) {
13771				textNodes.push({
13772					el: el,
13773					text: el.textContent
13774				});
13775			}
13776		});
13777	
13778		// Update the label text in case it has an entries value
13779		var updateEntries = function (len) {
13780			textNodes.forEach(function (node) {
13781				node.el.textContent = _fnMacros(settings, node.text, len);
13782			});
13783		}
13784	
13785		// Next, the select itself, along with the options
13786		var select = $('<select/>', {
13787			'aria-controls': tableId,
13788			'class':         classes.select
13789		} );
13790	
13791		for ( i=0 ; i<lengths.length ; i++ ) {
13792			select[0][ i ] = new Option(
13793				typeof language[i] === 'number' ?
13794					settings.fnFormatNumber( language[i] ) :
13795					language[i],
13796				lengths[i]
13797			);
13798		}
13799	
13800		// add for and id to label and input
13801		div.find('label').attr('for', 'dt-length-' + __lengthCounter);
13802		select.attr('id', 'dt-length-' + __lengthCounter);
13803		__lengthCounter++;
13804	
13805		// Swap in the select list
13806		div.find('#' + tmpId).replaceWith(select);
13807	
13808		// Can't use `select` variable as user might provide their own and the
13809		// reference is broken by the use of outerHTML
13810		$('select', div)
13811			.val( settings._iDisplayLength )
13812			.on( 'change.DT', function() {
13813				_fnLengthChange( settings, $(this).val() );
13814				_fnDraw( settings );
13815			} );
13816	
13817		// Update node value whenever anything changes the table's length
13818		$(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
13819			if ( settings === s ) {
13820				$('select', div).val( len );
13821	
13822				// Resolve plurals in the text for the new length
13823				updateEntries(len);
13824			}
13825		} );
13826	
13827		updateEntries(settings._iDisplayLength);
13828	
13829		return div;
13830	}, 'l' );
13831	
13832	// jQuery access
13833	$.fn.dataTable = DataTable;
13834	
13835	// Provide access to the host jQuery object (circular reference)
13836	DataTable.$ = $;
13837	
13838	// Legacy aliases
13839	$.fn.dataTableSettings = DataTable.settings;
13840	$.fn.dataTableExt = DataTable.ext;
13841	
13842	// With a capital `D` we return a DataTables API instance rather than a
13843	// jQuery object
13844	$.fn.DataTable = function ( opts ) {
13845		return $(this).dataTable( opts ).api();
13846	};
13847	
13848	// All properties that are available to $.fn.dataTable should also be
13849	// available on $.fn.DataTable
13850	$.each( DataTable, function ( prop, val ) {
13851		$.fn.DataTable[ prop ] = val;
13852	} );
13853
13854	return DataTable;
13855}));
13856
13857
13858/*! DataTables styling integration
13859 * © SpryMedia Ltd - datatables.net/license
13860 */
13861
13862(function( factory ){
13863	if ( typeof define === 'function' && define.amd ) {
13864		// AMD
13865		define( ['jquery', 'datatables.net'], function ( $ ) {
13866			return factory( $, window, document );
13867		} );
13868	}
13869	else if ( typeof exports === 'object' ) {
13870		// CommonJS
13871		var jq = require('jquery');
13872		var cjsRequires = function (root, $) {
13873			if ( ! $.fn.dataTable ) {
13874				require('datatables.net')(root, $);
13875			}
13876		};
13877
13878		if (typeof window === 'undefined') {
13879			module.exports = function (root, $) {
13880				if ( ! root ) {
13881					// CommonJS environments without a window global must pass a
13882					// root. This will give an error otherwise
13883					root = window;
13884				}
13885
13886				if ( ! $ ) {
13887					$ = jq( root );
13888				}
13889
13890				cjsRequires( root, $ );
13891				return factory( $, root, root.document );
13892			};
13893		}
13894		else {
13895			cjsRequires( window, jq );
13896			module.exports = factory( jq, window, window.document );
13897		}
13898	}
13899	else {
13900		// Browser
13901		factory( jQuery, window, document );
13902	}
13903}(function( $, window, document ) {
13904'use strict';
13905var DataTable = $.fn.dataTable;
13906
13907
13908
13909
13910
13911return DataTable;
13912}));
13913
13914
13915
13916/*!
13917 * jScrollPane - v2.0.19 - 2013-11-16
13918 * http://jscrollpane.kelvinluck.com/
13919 *
13920 * Copyright (c) 2013 Kelvin Luck
13921 * Dual licensed under the MIT or GPL licenses.
13922 */
13923! function(a, b, c) {
13924    a.fn.jScrollPane = function(d) {
13925        function e(d, e) {
13926            function f(b) {
13927                var e, h, j, l, m, n, q = !1,
13928                    r = !1;
13929                if (P = b, Q === c) m = d.scrollTop(), n = d.scrollLeft(), d.css({
13930                    overflow: "hidden",
13931                    padding: 0
13932                }), R = d.innerWidth() + tb, S = d.innerHeight(), d.width(R), Q = a('<div class="jspPane" />').css("padding", sb).append(d.children()), T = a('<div class="jspContainer" />').css({
13933                    width: R + "px",
13934                    height: S + "px"
13935                }).append(Q).appendTo(d);
13936                else {
13937                    if (d.css("width", ""), q = P.stickToBottom && C(), r = P.stickToRight && D(), l = d.innerWidth() + tb != R || d.outerHeight() != S, l && (R = d.innerWidth() + tb, S = d.innerHeight(), T.css({
13938                            width: R + "px",
13939                            height: S + "px"
13940                        })), !l && ub == U && Q.outerHeight() == V) return d.width(R), void 0;
13941                    ub = U, Q.css("width", ""), d.width(R), T.find(">.jspVerticalBar,>.jspHorizontalBar").remove().end()
13942                }
13943                Q.css("overflow", "auto"), U = b.contentWidth ? b.contentWidth : Q[0].scrollWidth, V = Q[0].scrollHeight, Q.css("overflow", ""), W = U / R, X = V / S, Y = X > 1, Z = W > 1, Z || Y ? (d.addClass("jspScrollable"), e = P.maintainPosition && (ab || db), e && (h = A(), j = B()), g(), i(), k(), e && (y(r ? U - R : h, !1), x(q ? V - S : j, !1)), H(), E(), N(), P.enableKeyboardNavigation && J(), P.clickOnTrack && o(), L(), P.hijackInternalLinks && M()) : (d.removeClass("jspScrollable"), Q.css({
13944                    top: 0,
13945                    left: 0,
13946                    width: T.width() - tb
13947                }), F(), I(), K(), p()), P.autoReinitialise && !rb ? rb = setInterval(function() {
13948                    f(P)
13949                }, P.autoReinitialiseDelay) : !P.autoReinitialise && rb && clearInterval(rb), m && d.scrollTop(0) && x(m, !1), n && d.scrollLeft(0) && y(n, !1), d.trigger("jsp-initialised", [Z || Y])
13950            }
13951
13952            function g() {
13953                Y && (T.append(a('<div class="jspVerticalBar" />').append(a('<div class="jspCap jspCapTop" />'), a('<div class="jspTrack" />').append(a('<div class="jspDrag" />').append(a('<div class="jspDragTop" />'), a('<div class="jspDragBottom" />'))), a('<div class="jspCap jspCapBottom" />'))), eb = T.find(">.jspVerticalBar"), fb = eb.find(">.jspTrack"), $ = fb.find(">
vendor: 5,041 bytes, lines 13953-14029
13953.jspDrag"), P.showArrows && (jb = a('<a class="jspArrow jspArrowUp" />').bind("mousedown.jsp", m(0, -1)).bind("click.jsp", G), kb = a('<a class="jspArrow jspArrowDown" />').bind("mousedown.jsp", m(0, 1)).bind("click.jsp", G), P.arrowScrollOnHover && (jb.bind("mouseover.jsp", m(0, -1, jb)), kb.bind("mouseover.jsp", m(0, 1, kb))), l(fb, P.verticalArrowPositions, jb, kb)), hb = S, T.find(">.jspVerticalBar>.jspCap:visible,>.jspVerticalBar>.jspArrow").each(function() {
13954                    hb -= a(this).outerHeight()
13955                }), $.hover(function() {
13956                    $.addClass("jspHover")
13957                }, function() {
13958                    $.removeClass("jspHover")
13959                }).bind("mousedown.jsp", function(b) {
13960                    a("html").bind("dragstart.jsp selectstart.jsp", G), $.addClass("jspActive");
13961                    var c = b.pageY - $.position().top;
13962                    return a("html").bind("mousemove.jsp", function(a) {
13963                        r(a.pageY - c, !1)
13964                    }).bind("mouseup.jsp mouseleave.jsp", q), !1
13965                }), h())
13966            }
13967
13968            function h() {
13969                fb.height(hb + "px"), ab = 0, gb = P.verticalGutter + fb.outerWidth(), Q.width(R - gb - tb);
13970                try {
13971                    0 === eb.position().left && Q.css("margin-left", gb + "px")
13972                } catch (a) {}
13973            }
13974
13975            function i() {
13976                Z && (T.append(a('<div class="jspHorizontalBar" />').append(a('<div class="jspCap jspCapLeft" />'), a('<div class="jspTrack" />').append(a('<div class="jspDrag" />').append(a('<div class="jspDragLeft" />'), a('<div class="jspDragRight" />'))), a('<div class="jspCap jspCapRight" />'))), lb = T.find(">.jspHorizontalBar"), mb = lb.find(">.jspTrack"), bb = mb.find(">.jspDrag"), P.showArrows && (pb = a('<a class="jspArrow jspArrowLeft" />').bind("mousedown.jsp", m(-1, 0)).bind("click.jsp", G), qb = a('<a class="jspArrow jspArrowRight" />').bind("mousedown.jsp", m(1, 0)).bind("click.jsp", G), P.arrowScrollOnHover && (pb.bind("mouseover.jsp", m(-1, 0, pb)), qb.bind("mouseover.jsp", m(1, 0, qb))), l(mb, P.horizontalArrowPositions, pb, qb)), bb.hover(function() {
13977                    bb.addClass("jspHover")
13978                }, function() {
13979                    bb.removeClass("jspHover")
13980                }).bind("mousedown.jsp", function(b) {
13981                    a("html").bind("dragstart.jsp selectstart.jsp", G), bb.addClass("jspActive");
13982                    var c = b.pageX - bb.position().left;
13983                    return a("html").bind("mousemove.jsp", function(a) {
13984                        t(a.pageX - c, !1)
13985                    }).bind("mouseup.jsp mouseleave.jsp", q), !1
13986                }), nb = T.innerWidth(), j())
13987            }
13988
13989            function j() {
13990                T.find(">.jspHorizontalBar>.jspCap:visible,>.jspHorizontalBar>.jspArrow").each(function() {
13991                    nb -= a(this).outerWidth()
13992                }), mb.width(nb + "px"), db = 0
13993            }
13994
13995            function k() {
13996                if (Z && Y) {
13997                    var b = mb.outerHeight(),
13998                        c = fb.outerWidth();
13999                    hb -= b, a(lb).find(">.jspCap:visible,>.jspArrow").each(function() {
14000                        nb += a(this).outerWidth()
14001                    }), nb -= c, S -= c, R -= b, mb.parent().append(a('<div class="jspCorner" />').css("width", b + "px")), h(), j()
14002                }
14003                Z && Q.width(T.outerWidth() - tb + "px"), V = Q.outerHeight(), X = V / S, Z && (ob = Math.ceil(1 / W * nb), ob > P.horizontalDragMaxWidth ? ob = P.horizontalDragMaxWidth : ob < P.horizontalDragMinWidth && (ob = P.horizontalDragMinWidth), bb.width(ob + "px"), cb = nb - ob, u(db)), Y && (ib = Math.ceil(1 / X * hb), ib > P.verticalDragMaxHeight ? ib = P.verticalDragMaxHeight : ib < P.verticalDragMinHeight && (ib = P.verticalDragMinHeight), $.height(ib + "px"), _ = hb - ib, s(ab))
14004            }
14005
14006            function l(a, b, c, d) {
14007                var e, f = "before",
14008                    g = "after";
14009                "os" == b && (b = /Mac/.test(navigator.platform) ? "after" : "split"), b == f ? g = b : b == g && (f = b, e = c, c = d, d = e), a[f](c)[g](d)
14010            }
14011
14012            function m(a, b, c) {
14013                return function() {
14014                    return n(a, b, this, c), this.blur(), !1
14015                }
14016            }
14017
14018            function n(b, c, d, e) {
14019                d = a(d).addClass("jspActive");
14020                var f, g, h = !0,
14021                    i = function() {
14022                        0 !== b && vb.scrollByX(b * P.arrowButtonSpeed), 0 !== c && vb.scrollByY(c * P.arrowButtonSpeed), g = setTimeout(i, h ? P.initialDelay : P.arrowRepeatFreq), h = !1
14023                    };
14024                i(), f = e ? "mouseout.jsp" : "mouseup.jsp", e = e || a("html"), e.bind(f, function() {
14025                    d.removeClass("jspActive"), g && clearTimeout(g), g = null, e.unbind(f)
14026                })
14027            }
14028
14029            function o() {
14030                p(), Y && fb.bind("mousedown.jsp", function(b) {
14031                    if (b.originalTarget === c || b.originalTarget == b.currentTarget) {
14032                        var d, e = a(this),
14033                            f = e.offset(),
14034                            g = b.pageY - f.top - ab,
14035                            h = !0,
14036                            i = function() {
14037                                var a = e.offset(),
14038                                    c = b.pageY - a.top - ib / 2,
14039                                    f = S * P.scrollPagePercent,
14040                                    k = _ * f / (V - S);
14041                                if (0 > g) ab - k > c ? vb.scrollByY(-f) : r(c);
14042                                else {
14043                                    if (!(g > 0)) return j(), void 0;
14044                                    c > ab + k ? vb.scrollByY(f) : r(c)
14045                                }
14046                                d = setTimeout(i, h ? P.initialDelay : P.trackClickRepeatFreq), h = !1
14047                            },
14048                            j = function() {
14049                                d && clearTimeout(d), d = null, a(document).unbind("mouseup.jsp", j)
14050                            };
14051                        return i(), a(document).bind("mouseup.jsp", j), !1
14052                    }
14053                }), Z && mb.bind("mousedown.jsp", function(b) {
14054                    if (b.originalTarget === c || b.originalTarget == b.currentTarget) {
14055                        var d, e = a(this),
14056                            f = e.offset(),
14057                            g = b.pageX - f.left - db,
14058                            h = !0,
14059                            i = function() {
14060                                var a = e.offset(),
14061                                    c = b.pageX - a.left - ob / 2,
14062                                    f = R * P.scrollPagePercent,
14063                                    k = cb * f / (U - R);
14064                                if (0 > g) db - k > c ? vb.scrollByX(-f) : t(c);
14065                                else {
14066                                    if (!(g > 0)) return j(), void 0;
14067                                    c > db + k ? vb.scrollByX(f) : t(c)
14068                                }
14069                                d = setTimeout(i, h ? P.initialDelay : P.trackClickRepeatFreq), h = !1
14070                            },
14071                            j = function() {
14072                                d && clearTimeout(d), d = null, a(document).unbind("mouseup.jsp", j)
14073                            };
14074                        return i(), a(document).bind("mouseup.jsp", j), !1
14075                    }
14076                })
14077            }
14078
14079            function p() {
14080                mb && mb.unbind("mousedown.jsp"), fb && fb.unbind("mousedown.jsp")
14081            }
14082
14083            function q() {
14084                a("html").unbind("dragstart.jsp selectstart.jsp mousemove.jsp mouseup.jsp mouseleave.jsp"), $ && $.removeClass("jspActive"), bb && bb.removeClass("jspActive")
14085            }
14086
14087            function r(a, b) {
14088                Y && (0 > a ? a = 0 : a > _ && (a = _), b === c && (b = P.animateScroll), b ? vb.animate($, "top", a, s) : ($.css("top", a), s(a)))
14089            }
14090
14091            function s(a) {
14092                a === c && (a = $.position().top), T.scrollTop(0), ab = a;
14093                var b = 0 === ab,
14094                    e = ab == _,
14095                    f = a / _,
14096                    g = -f * (V - S);
14097                (wb != b || yb != e) && (wb = b, yb = e, d.trigger("jsp-arrow-change", [wb, yb, xb, zb])), v(b, e), Q.css("top", g), d.trigger("jsp-scroll-y", [-g, b, e]).trigger("scroll")
14098            }
14099
14100            function t(a, b) {
14101                Z && (0 > a ? a = 0 : a > cb && (a = cb), b === c && (b = P.animateScroll), b ? vb.animate(bb, "left", a, u) : (bb.css("left", a), u(a)))
14102            }
14103
14104            function u(a) {
14105                a === c && (a = bb.position().left), T.scrollTop(0), db = a;
14106                var b = 0 === db,
14107                    e = db == cb,
14108                    f = a / cb,
14109                    g = -f * (U - R);
14110                (xb != b || zb != e) && (xb = b, zb = e, d.trigger("jsp-arrow-change", [wb, yb, xb, zb])), w(b, e), Q.css("left", g), d.trigger("jsp-scroll-x", [-g, b, e]).trigger("scroll")
14111            }
14112
14113            function v(a, b) {
14114                P.showArrows && (jb[a ? "addClass" : "removeClass"]("jspDisabled"), kb[b ? "addClass" : "removeClass"]("jspDisabled"))
14115            }
14116
14117            function w(a, b) {
14118                P.showArrows && (pb[a ? "addClass" : "removeClass"]("jspDisabled"), qb[b ? "addClass" : "removeClass"]("jspDisabled"))
14119            }
14120
14121            function x(a, b) {
14122                var c = a / (V - S);
14123                r(c * _, b)
14124            }
14125
14126            function y(a, b) {
14127                var c = a / (U - R);
14128                t(c * cb, b)
14129            }
14130
14131            function z(b, c, d) {
14132                var e, f, g, h, i, j, k, l, m, n = 0,
14133                    o = 0;
14134                try {
14135                    e = a(b)
14136                } catch (p) {
14137                    return
14138                }
14139                for (f = e.outerHeight(), g = e.outerWidth(), T.scrollTop(0), T.scrollLeft(0); !e.is(".jspPane");)
14140                    if (n += e.position().top, o += e.position().left, e = e.offsetParent(), /^body|html$/i.test(e[0].nodeName)) return;
14141                h = B(), j = h + S, h > n || c ? l = n - P.horizontalGutter : n + f > j && (l = n - S + f + P.horizontalGutter), isNaN(l) || x(l, d), i = A(), k = i + R, i > o || c ? m = o - P.horizontalGutter : o + g > k && (m = o - R + g + P.horizontalGutter), isNaN(m) || y(m, d)
14142            }
14143
14144            function A() {
14145                return -Q.position().left
14146            }
14147
14148            function B() {
14149                return -Q.position().top
14150            }
14151
14152            function C() {
14153                var a = V - S;
14154                return a > 20 && a - B() < 10
14155            }
14156
14157            function D() {
14158                var a = U - R;
14159                return a > 20 && a - A() < 10
14160            }
14161
14162            function E() {
14163                T.unbind(Bb).bind(Bb, function(a, b, c, d) {
14164                    var e = db,
14165                        f = ab,
14166                        g = a.deltaFactor || P.mouseWheelSpeed;
14167                    return vb.scrollBy(c * g, -d * g, !1), e == db && f == ab
14168                })
14169            }
14170
14171            function F() {
14172                T.unbind(Bb)
14173            }
14174
14175            function G() {
14176                return !1
14177            }
14178
14179            function H() {
14180                Q.find(":input,a").unbind("focus.jsp").bind("focus.jsp", function(a) {
14181                    z(a.target, !1)
14182                })
14183            }
14184
14185            function I() {
14186                Q.find(":input,a").unbind("focus.jsp")
14187            }
14188
14189            function J() {
14190                function b() {
14191                    var a = db,
14192                        b = ab;
14193                    switch (c) {
14194                        case 40:
14195                            vb.scrollByY(P.keyboardSpeed, !1);
14196                            break;
14197                        case 38:
14198                            vb.scrollByY(-P.keyboardSpeed, !1);
14199                            break;
14200                        case 34:
14201                        case 32:
14202                            vb.scrollByY(S * P.scrollPagePercent, !1);
14203                            break;
14204                        case 33:
14205                            vb.scrollByY(-S * P.scrollPagePercent, !1);
14206                            break;
14207                        case 39:
14208                            vb.scrollByX(P.keyboardSpeed, !1);
14209                            break;
14210                        case 37:
14211                            vb.scrollByX(-P.keyboardSpeed, !1)
14212                    }
14213                    return e = a != db || b != ab
14214                }
14215                var c, e, f = [];
14216                Z && f.push(lb[0]), Y && f.push(eb[0]), Q.focus(function() {
14217                    d.focus()
14218                }
14218), d.attr("tabindex", 0).unbind("keydown.jsp keypress.jsp").bind("keydown.jsp", function(d) {
14219                    if (d.target === this || f.length && a(d.target).closest(f).length) {
14220                        var g = db,
14221                            h = ab;
14222                        switch (d.keyCode) {
14223                            case 40:
14224                            case 38:
14225                            case 34:
14226                            case 32:
14227                            case 33:
14228                            case 39:
14229                            case 37:
14230                                c = d.keyCode, b();
14231                                break;
14232                            case 35:
14233                                x(V - S), c = null;
14234                                break;
14235                            case 36:
14236                                x(0), c = null
14237                        }
14238                        return e = d.keyCode == c && g != db || h != ab, !e
14239                    }
14240                }).bind("keypress.jsp", function(a) {
14241                    return a.keyCode == c && b(), !e
14242                }), P.hideFocus ? (d.css("outline", "none"), "hideFocus" in T[0] && d.attr("hideFocus", !0)) : (d.css("outline", ""), "hideFocus" in T[0] && d.attr("hideFocus", !1))
14243            }
14244
14245            function K() {
14246                d.attr("tabindex", "-1").removeAttr("tabindex").unbind("keydown.jsp keypress.jsp")
14247            }
14248
14249            function L() {
14250                if (location.hash && location.hash.length > 1) {
14251                    var b, c, d = escape(location.hash.substr(1));
14252                    try {
14253                        b = a("#" + d + ', a[name="' + d + '"]')
14254                    } catch (e) {
14255                        return
14256                    }
14257                    b.length && Q.find(d) && (0 === T.scrollTop() ? c = setInterval(function() {
14258                        T.scrollTop() > 0 && (z(b, !0), a(document).scrollTop(T.position().top), clearInterval(c))
14259                    }, 50) : (z(b, !0), a(document).scrollTop(T.position().top)))
14260                }
14261            }
14262
14263            function M() {
14264                a(document.body).data("jspHijack") || (a(document.body).data("jspHijack", !0), a(document.body).delegate("a[href*=#]", "click", function(c) {
14265                    var d, e, f, g, h, i, j = this.href.substr(0, this.href.indexOf("#")),
14266                        k = location.href;
14267                    if (-1 !== location.href.indexOf("#") && (k = location.href.substr(0, location.href.indexOf("#"))), j === k) {
14268                        d = escape(this.href.substr(this.href.indexOf("#") + 1));
14269                        try {
14270                            e = a("#" + d + ', a[name="' + d + '"]')
14271                        } catch (l) {
14272                            return
14273                        }
14274                        e.length && (f = e.closest(".jspScrollable"), g = f.data("jsp"), g.scrollToElement(e, !0), f[0].scrollIntoView && (h = a(b).scrollTop(), i = e.offset().top, (h > i || i > h + a(b).height()) && f[0].scrollIntoView()), c.preventDefault())
14275                    }
14276                }))
14277            }
14278
14279            function N() {
14280                var a, b, c, d, e, f = !1;
14281                T.unbind("touchstart.jsp touchmove.jsp touchend.jsp click.jsp-touchclick").bind("touchstart.jsp", function(g) {
14282                    var h = g.originalEvent.touches[0];
14283                    a = A(), b = B(), c = h.pageX, d = h.pageY, e = !1, f = !0
14284                }).bind("touchmove.jsp", function(g) {
14285                    if (f) {
14286                        var h = g.originalEvent.touches[0],
14287                            i = db,
14288                            j = ab;
14289                        return vb.scrollTo(a + c - h.pageX, b + d - h.pageY), e = e || Math.abs(c - h.pageX) > 5 || Math.abs(d - h.pageY) > 5, i == db && j == ab
14290                    }
14291                }).bind("touchend.jsp", function() {
14292                    f = !1
14293                }).bind("click.jsp-touchclick", function() {
14294                    return e ? (e = !1, !1) : void 0
14295                })
14296            }
14297
14298            function O() {
14299                var a = B(),
14300                    b = A();
14301                d.removeClass("jspScrollable").unbind(".jsp"), d.replaceWith(Ab.append(Q.children())), Ab.scrollTop(a), Ab.scrollLeft(b), rb && clearInterval(rb)
14302            }
14303            var P, Q, R, S, T, U, V, W, X, Y, Z, $, _, ab, bb, cb, db, eb, fb, gb, hb, ib, jb, kb, lb, mb, nb, ob, pb, qb, rb, sb, tb, ub, vb = this,
14304                wb = !0,
14305                xb = !0,
14306                yb = !1,
14307                zb = !1,
14308                Ab = d.clone(!1, !1).empty(),
14309                Bb = a.fn.mwheelIntent ? "mwheelIntent.jsp" : "mousewheel.jsp";
14310            "border-box" === d.css("box-sizing") ? (sb = 0, tb = 0) : (sb = d.css("paddingTop") + " " + d.
14310css("paddingRight") + " " + d.css("paddingBottom") + " " + d.css("paddingLeft"), tb = (parseInt(d.css("paddingLeft"), 10) || 0) + (parseInt(d.css("paddingRight"), 10) || 0)), a.extend(vb, {
14311                reinitialise: function(b) {
14312                    b = a.extend({}, P, b), f(b)
14313                },
14314                scrollToElement: function(a, b, c) {
14315                    z(a, b, c)
14316                },
14317                scrollTo: function(a, b, c) {
14318                    y(a, c), x(b, c)
14319                },
14320                scrollToX: function(a, b) {
14321                    y(a, b)
14322                },
14323                scrollToY: function(a, b) {
14324                    x(a, b)
14325                },
14326                scrollToPercentX: function(a, b) {
14327                    y(a * (U - R), b)
14328                },
14329                scrollToPercentY: function(a, b) {
14330                    x(a * (V - S), b)
14331                },
14332                scrollBy: function(a, b, c) {
14333                    vb.scrollByX(a, c), vb.scrollByY(b, c)
14334                },
14335                scrollByX: function(a, b) {
14336                    var c = A() + Math[0 > a ? "floor" : "ceil"](a),
14337                        d = c / (U - R);
14338                    t(d * cb, b)
14339                },
14340                scrollByY: function(a, b) {
14341                    var c = B() + Math[0 > a ? "floor" : "ceil"](a),
14342                        d = c / (V - S);
14343                    r(d * _, b)
14344                },
14345                positionDragX: function(a, b) {
14346                    t(a, b)
14347                },
14348                positionDragY: function(a, b) {
14349                    r(a, b)
14350                },
14351                animate: function(a, b, c, d) {
14352                    var e = {};
14353                    e[b] = c, a.animate(e, {
14354                        duration: P.animateDuration,
14355                        easing: P.animateEase,
14356                        queue: !1,
14357                        step: d
14358                    })
14359                },
14360                getContentPositionX: function() {
14361                    return A()
14362                },
14363                getContentPositionY: function() {
14364                    return B()
14365                },
14366                getContentWidth: function() {
14367                    return U
14368                },
14369                getContentHeight: function() {
14370                    return V
14371                },
14372                getPercentScrolledX: function() {
14373                    return A() / (U - R)
14374                },
14375                getPercentScrolledY: function() {
14376                    return B() / (V - S)
14377                },
14378                getIsScrollableH: function() {
14379                    return Z
14380                },
14381                getIsScrollableV: function() {
14382                    return Y
14383                },
14384                getContentPane: function() {
14385                    return Q
14386                },
14387                scrollToBottom: function(a) {
14388                    r(_, a)
14389                },
14390                hijackInternalLinks: a.noop,
14391                destroy: function() {
14392                    O()
14393                }
14394            }), f(e)
14395        }
14396        return d = a.extend({}, a.fn.jScrollPane.defaults, d), a.each(["arrowButtonSpeed", "trackClickSpeed", "keyboardSpeed"], function() {
14397            d[this] = d[this] || d.speed
14398        }), this.each(function() {
14399            var b = a(this),
14400                c = b.data("jsp");
14401            c ? c.reinitialise(d) : (a("script", b).filter('[type="text/javascript"],:not([type])').remove(), c = new e(b, d), b.data("jsp", c))
14402        })
14403    }, a.fn.jScrollPane.defaults = {
14404        showArrows: !1,
14405        maintainPosition: !0,
14406        stickToBottom: !1,
14407        stickToRight: !1,
14408        clickOnTrack: !0,
14409        autoReinitialise: !1,
14410        autoReinitialiseDelay: 500,
14411        verticalDragMinHeight: 0,
14412        verticalDragMaxHeight: 99999,
14413        horizontalDragMinWidth: 0,
14414        horizontalDragMaxWidth: 99999,
14415        contentWidth: c,
14416        animateScroll: !1,
14417        animateDuration: 300,
14418        animateEase: "linear",
14419        hijackInternalLinks: !1,
14420        verticalGutter: 4,
14421        horizontalGutter: 4,
14422        mouseWheelSpeed: 3,
14423        arrowButtonSpeed: 0,
14424        arrowRepeatFreq: 50,
14425        arrowScrollOnHover: !1,
14426        trackClickSpeed: 0,
14427        trackClickRepeatFreq: 70,
14428        verticalArrowPositions: "split",
14429        horizontalArrowPositions: "split",
14430        enableKeyboardNavigation: !0,
14431        hideFocus: !1,
14432        keyboardSpeed: 0,
14433        initialDelay: 300,
14434        speed: 30,
14435        scrollPagePercent: .8
14436    }
14437}(jQuery, this);
14438/*
14439 OverlappingMarkerSpiderfier
14440https://github.com/jawj/OverlappingMarkerSpiderfier
14441Copyright (c) 2011 - 2017 George MacKerron
14442Released under the MIT licence: http://opensource.org/licenses/mit-license
14443Note: The Google Maps API v3 must be included *before* this code
14444*/
14445(function(){var m,t,w,y,u,z={}.hasOwnProperty,A=[].slice;this.OverlappingMarkerSpiderfier=function(){function r(a,d){var b,f,e;this.map=a;null==d&&(d={});null==this.constructor.N&&(this.constructor.N=!0,h=google.maps,l=h.event,p=h.MapTypeId,c.keepSpiderfied=!1,c.ignoreMapClick=!1,c.markersWontHide=!1,c.markersWontMove=!1,c.basicFormatEvents=!1,c.nearbyDistance=20,c.circleSpiralSwitchover=9,c.circleFootSeparation=23,c.circleStartAngle=x
14445/12,c.spiralFootSeparation=26,c.spiralLengthStart=11,c.spiralLengthFactor=
144464,c.spiderfiedZIndex=h.Marker.MAX_ZINDEX+2E4,c.highlightedLegZIndex=h.Marker.MAX_ZINDEX+1E4,c.usualLegZIndex=h.Marker.MAX_ZINDEX+1,c.legWeight=1.5,c.legColors={usual:{},highlighted:{}},e=c.legColors.usual,f=c.legColors.highlighted,e[p.HYBRID]=e[p.SATELLITE]="#fff",f[p.HYBRID]=f[p.SATELLITE]="#f00",e[p.TERRAIN]=e[p.ROADMAP]="#444",f[p.TERRAIN]=f[p.ROADMAP]="#f00",this.constructor.j=function(a){return this.setMap(a)},this.constructor.j.prototype=new h.OverlayView,this.constructor.j.prototype.draw=function(){});
14447for(b in d)z.call(d,b)&&(f=d[b],this[b]=f);this.g=new this.constructor.j(this.map);this.C();this.c={};this.B=this.l=null;this.addListener("click",function(a,b){return l.trigger(a,"spider_click",b)});this.addListener("format",function(a,b){return l.trigger(a,"spider_format",b)});this.ignoreMapClick||l.addListener(this.map,"click",function(a){return function(){return a.unspiderfy()}}(this));l.addListener(this.map,"maptypeid_changed",function(a){return function(){return a.unspiderfy()}}(this));l.addListener(this.map,
14448"zoom_changed",function(a){return function(){a.unspiderfy();if(!a.basicFormatEvents)return a.h()}}(this))}var l,h,m,v,p,c,t,x,u;c=r.prototype;t=[r,c];m=0;for(v=t.length;m<v;m++)u=t[m],u.VERSION="1.0.3";x=2*Math.PI;h=l=p=null;r.markerStatus={SPIDERFIED:"SPIDERFIED",SPIDERFIABLE:"SPIDERFIABLE",UNSPIDERFIABLE:"UNSPIDERFIABLE",UNSPIDERFIED:"UNSPIDERFIED"};c.C=function(){this.a=[];this.s=[]};c.addMarker=function(a,d){a.setMap(this.map);return this.trackMarker(a,d)};c.trackMarker=function(a,d){var b;if(null!=
14449a._oms)return this;a._oms=!0;b=[l.addListener(a,"click",function(b){return function(d){return b.V(a,d)}}(this))];this.markersWontHide||b.push(l.addListener(a,"visible_changed",function(b){return function(){return b.D(a,!1)}}(this)));this.markersWontMove||b.push(l.addListener(a,"position_changed",function(b){return function(){return b.D(a,!0)}}(this)));null!=d&&b.push(l.addListener(a,"spider_click",d));this.s.push(b);this.a.push(a);this.basicFormatEvents?this.trigger("format",a,this.constructor.markerStatus.UNSPIDERFIED):
14450(this.trigger("format",a,this.constructor.markerStatus.UNSPIDERFIABLE),this.h());return this};c.D=function(a,d){if(!this.J&&!this.K)return null==a._omsData||!d&&a.getVisible()||this.unspiderfy(d?a:null),this.h()};c.getMarkers=function(){return this.a.slice(0)};c.removeMarker=function(a){this.forgetMarker(a);return a.setMap(null)};c.forgetMarker=function(a){var d,b,f,e,g;null!=a._omsData&&this.unspiderfy();d=this.A(this.a,a);if(0>d)return this;g=this.s.splice(d,1)[0];b=0;for(f=g.length;b<f;b++)e=g[b],
14451l.removeListener(e);delete a._oms;this.a.splice(d,1);this.h();return this};c.removeAllMarkers=c.clearMarkers=function(){var a,d,b,f;f=this.getMarkers();this.forgetAllMarkers();a=0;for(d=f.length;a<d;a++)b=f[a],b.setMap(null);return this};c.forgetAllMarkers=function(){var a,d,b,f,e,g,c,q;this.unspiderfy();q=this.a;a=d=0;for(b=q.length;d<b;a=++d){g=q[a];e=this.s[a];c=0;for(a=e.length;c<a;c++)f=e[c],l.removeListener(f);delete g._oms}this.C();return this};c.addListener=function(a,d){var b;(null!=(b=this.c)[a]?
14452b[a]:b[a]=[]).push(d);return this};c.removeListener=function(a,d){var b;b=this.A(this.c[a],d);0>b||this.c[a].splice(b,1);return this};c.clearListeners=function(a){this.c[a]=[];return this};c.trigger=function(){var a,d,b,f,e,g;d=arguments[0];a=2<=arguments.length?A.call(arguments,1):[];d=null!=(b=this.c[d])?b:[];g=[];f=0;for(e=d.length;f<e;f++)b=d[f],g.push(b.apply(null,a));return g};c.L=function(a,d){var b,f,e,g,c;g=this.circleFootSeparation*(2+a)/x;f=x/a;c=[];for(b=e=0;0<=a?e<a:e>a;b=0<=a?++e:--e)b=
14453this.circleStartAngle+b*f,c.push(new h.Point(d.x+g*Math.cos(b),d.y+g*Math.sin(b)));return c};c.M=function(a,d){var b,f,e,c,k;c=this.spiralLengthStart;b=0;k=[];for(f=e=0;0<=a?e<a:e>a;f=0<=a?++e:--e)b+=this.spiralFootSeparation/c+5E-4*f,f=new h.Point(d.x+c*Math.cos(b),d.y+c*Math.sin(b)),c+=x*this.spiralLengthFactor/b,k.push(f);return k};c.V=function(a,d){var b,f,e,c,k,q,n,l,h;(q=null!=a._omsData)&&this.keepSpiderfied||this.unspiderfy();if(q||this.map.getStreetView().getVisible()||"GoogleEarthAPI"===
14454this.map.getMapTypeId())return this.trigger("click",a,d);q=[];n=[];b=this.nearbyDistance;l=b*b;k=this.f(a.position);h=this.a;b=0;for(f=h.length;b<f;b++)e=h[b],null!=e.map&&e.getVisible()&&(c=this.f(e.position),this.i(c,k)<l?q.push({R:e,G:c}):n.push(e));return 1===q.length?this.trigger("click",a,d):this.W(q,n)};c.markersNearMarker=function(a,d){var b,f,e,c,k,q,n,l,h,m;null==d&&(d=!1);if(null==this.g.getProjection())throw"Must wait for 'idle' event on map before calling markersNearMarker";b=this.nearbyDistance;
14455n=b*b;k=this.f(a.position);q=[];l=this.a;b=0;for(f=l.length;b<f&&!(e=l[b],e!==a&&null!=e.map&&e.getVisible()&&(c=this.f(null!=(h=null!=(m=e._omsData)?m.v:void 0)?h:e.position),this.i(c,k)<n&&(q.push(e),d)));b++);return q};c.F=function(){var a,d,b,f,e,c,k,l,n,h,m;if(null==this.g.getProjection())throw"Must wait for 'idle' event on map before calling markersNearAnyOtherMar
14455ker";n=this.nearbyDistance;n*=n;var p;e=this.a;p=[];h=0;for(d=e.length;h<d;h++)f=e[h],p.push({H:this.f(null!=(a=null!=(b=f._omsData)?
14456b.v:void 0)?a:f.position),b:!1});h=this.a;a=b=0;for(f=h.length;b<f;a=++b)if(d=h[a],null!=d.getMap()&&d.getVisible()&&(c=p[a],!c.b))for(m=this.a,d=l=0,e=m.length;l<e;d=++l)if(k=m[d],d!==a&&null!=k.getMap()&&k.getVisible()&&(k=p[d],(!(d<a)||k.b)&&this.i(c.H,k.H)<n)){c.b=k.b=!0;break}return p};c.markersNearAnyOtherMarker=function(){var a,d,b,c,e,g,k;e=this.F();g=this.a;k=[];a=d=0;for(b=g.length;d<b;a=++d)c=g[a],e[a].b&&k.push(c);return k};c.setImmediate=function(a){return window.setTimeout(a,0)};c.h=
14457function(){if(!this.basicFormatEvents&&null==this.l)return this.l=this.setImmediate(function(a){return function(){a.l=null;return null!=a.g.getProjection()?a.w():null!=a.B?void 0:a.B=l.addListenerOnce(a.map,"idle",function(){return a.w()})}}(this))};c.w=function(){var a,d,b,c,e,g,k;if(this.basicFormatEvents){e=[];d=0;for(b=markers.length;d<b;d++)c=markers[d],a=null!=c._omsData?"SPIDERFIED":"UNSPIDERFIED",e.push(this.trigger("format",c,this.constructor.markerStatus[a]));return e}e=this.F();g=this.a;
14458k=[];a=b=0;for(d=g.length;b<d;a=++b)c=g[a],a=null!=c._omsData?"SPIDERFIED":e[a].b?"SPIDERFIABLE":"UNSPIDERFIABLE",k.push(this.trigger("format",c,this.constructor.markerStatus[a]));return k};c.P=function(a){return{m:function(d){return function(){return a._omsData.o.setOptions({strokeColor:d.legColors.highlighted[d.map.mapTypeId],zIndex:d.highlightedLegZIndex})}}(this),u:function(d){return function(){return a._omsData.o.setOptions({strokeColor:d.legColors.usual[d.map.mapTypeId],zIndex:d.usualLegZIndex})}}(this)}};
14459c.W=function(a,d){var b,c,e,g,k,q,n,m,p,r;this.J=!0;r=a.length;b=this.T(function(){var b,d,c;c=[];b=0;for(d=a.length;b<d;b++)m=a[b],c.push(m.G);return c}());g=r>=this.circleSpiralSwitchover?this.M(r,b).reverse():this.L(r,b);b=function(){var b,d,f;f=[];
14459b=0;for(d=g.length;b<d;b++)e=g[b],c=this.U(e),p=this.S(a,function(a){return function(b){return a.i(b.G,e)}}(this)),n=p.R,q=new h.Polyline({map:this.map,path:[n.position,c],strokeColor:this.legColors.usual[this.map.mapTypeId],strokeWeight:this.legWeight,
14460zIndex:this.usualLegZIndex}),n._omsData={v:n.getPosition(),X:n.getZIndex(),o:q},this.legColors.highlighted[this.map.mapTypeId]!==this.legColors.usual[this.map.mapTypeId]&&(k=this.P(n),n._omsData.O={m:l.addListener(n,"mouseover",k.m),u:l.addListener(n,"mouseout",k.u)}),this.trigger("format",n,this.constructor.markerStatus.SPIDERFIED),n.setPosition(c),n.setZIndex(Math.round(this.spiderfiedZIndex+e.y)),f.push(n);return f}.call(this);delete this.J;this.I=!0;return this.trigger("spiderfy",b,d)};c.unspiderfy=
14461function(a){var d,b,c,e,g,k,h;null==a&&(a=null);if(null==this.I)return this;this.K=!0;h=[];g=[];k=this.a;d=0;for(b=k.length;d<b;d++)e=k[d],null!=e._omsData?(e._omsData.o.setMap(null),e!==a&&e.setPosition(e._omsData.v),e.setZIndex(e._omsData.X),c=e._omsData.O,null!=c&&(l.removeListener(c.m),l.removeListener(c.u)),delete e._omsData,e!==a&&(c=this.basicFormatEvents?"UNSPIDERFIED":"SPIDERFIABLE",this.trigger("format",e,this.constructor.markerStatus[c])),h.push(e)):g.push(e);delete this.K;delete this.I;
14462this.trigger("unspiderfy",h,g);return this};c.i=function(a,d){var b,c;b=a.x-d.x;c=a.y-d.y;return b*b+c*c};c.T=function(a){var c,b,f,e,g;c=e=g=0;for(b=a.length;c<b;c++)f=a[c],e+=f.x,g+=f.y;a=a.length;return new h.Point(e/a,g/a)};c.f=function(a){return this.g.getProjection().fromLatLngToDivPixel(a)};c.U=function(a){return this.g.getProjection().fromDivPixelToLatLng(a)};c.S=function(a,c){var b,d,e,g,k,h;e=k=0;for(h=a.length;k<h;e=++k)if(g=a[e],g=c(g),"undefined"===typeof b||null===b||g<d)d=g,b=e;return a.splice(b,
144631)[0]};c.A=function(a,c){var b,d,e,g;if(null!=a.indexOf)return a.indexOf(c);b=d=0;for(e=a.length;d<e;b=++d)if(g=a[b],g===c)return b;return-1};return r}();t=/(\?.*(&|&amp;)|\?)spiderfier_callback=(\w+)/;m=document.currentScript;null==m&&(m=function(){var m,l,h,w,v;h=document.getElementsByTagName("script");v=[];m=0;for(l=h.length;m<l;m++)u=h[m],null!=(w=u.getAttribute("src"))&&w.match(t)&&v.push(u);return v}()[0]);if(null!=m&&(m=null!=(w=m.getAttribute("src"))?null!=(y=w.match(t))?y[3]:void 0:void 0)&&
14464"function"===typeof window[m])window[m]();"function"===typeof window.spiderfier_callback&&window.spiderfier_callback()}).call(this);
14465/* Thu 11 May 2017 09:00:27 BST */
14466/*  jQuery.print, version 1.0.3
14467 *  (c) Sathvik Ponangi, Doers' Guild
14468 * Licence: CC-By (http://creativecommons.org/licenses/by/3.0/)
14469 *--------------------------------------------------------------------------*/
14470
14471(function($) {
14472    "use strict";
14473    // A nice closure for our definitions
14474
14475    function getjQueryObject(string) {
14476        // Make string a vaild jQuery thing
14477        var jqObj = $("");
14478        try {
14479            jqObj = $(string).clone();
14480        } catch (e) {
14481            jqObj = $("<span />").html(string);
14482        }
14483        return jqObj;
14484    }
14485
14486    function isNode(o) {
14487        /* http://stackoverflow.com/a/384380/937891 */
14488        return !!(typeof Node === "object" ? o instanceof Node : o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string");
14489    }
14490
14491
14492    $.print = $.fn.print = function() {
14493        // Print a given set of elements
14494
14495        var options, $this, self = this;
14496
14497        // console.log("Printing", this, arguments);
14498
14499        if (self instanceof $) {
14500            // Get the node if it is a jQuery object
14501            self = self.get(0);
14502        }
14503
14504        if (isNode(self)) {
14505            // If `this` is a HTML element, i.e. for
14506            // $(selector).print()
14507            $this = $(self);
14508            if (arguments.length > 0) {
14509                options = arguments[0];
14510            }
14511        } else {
14512            if (arguments.length > 0) {
14513                // $.print(selector,options)
14514                $this = $(arguments[0]);
14515                if (isNode($this[0])) {
14516                    if (arguments.length > 1) {
14517                        options = arguments[1];
14518                    }
14519                } else {
14520                    // $.print(options)
14521                    options = arguments[0];
14522                    $this = $("html");
14523                }
14524            } else {
14525                // $.print()
14526                $this = $("html");
14527            }
14528        }
14529
14530        // Default options
14531        var defaults = {
14532            globalStyles: true,
14533            mediaPrint: false,
14534            stylesheet: null,
14535            noPrintSelector: ".no-print",
14536            iframe: true,
14537            append: null,
14538            prepend: null
14539        };
14540        // Merge with user-options
14541        options = $.extend({}, defaults, (options || {}));
14542
14543        var $styles = $("");
14544        if (options.globalStyles) {
14545            // Apply the stlyes from the current sheet to the printed page
14546            $styles = $("style, link, meta, title");
14547        } else if (options.mediaPrint) {
14548            // Apply the media-print stylesheet
14549            $styles = $("link[media=print]");
14550        }
14551        if (options.stylesheet) {
14552            // Add a custom stylesheet if given
14553            $styles = $.merge($styles, $('<link rel="stylesheet" href="' + options.stylesheet + '">'));
14554        }
14555
14556        // Create a copy of the element to print
14557        var copy = $this.clone();
14558        // Wrap it in a span to get the HTML markup string
14559        copy = $("<span/>").append(copy);
14560        // Remove unwanted elements
14561        copy.find(options.noPrintSelector).remove();
14562        // Add in the styles
14563        copy.append($styles.clone());
14564        // Appedned content
14565        copy.append(getjQueryObject(options.append));
14566        // Prepended content
14567        copy.prepend(getjQueryObject(options.prepend));
14568        // Get the HTML markup string
14569        var content = copy.html();
14570        // Destroy the copy
14571        copy.remove();
14572
14573        var w, wdoc;
14574        if (options.iframe) {
14575            // Use an iframe for printing
14576            try {
14577                var $iframe = $(options.iframe + "");
14578                var iframeCount = $iframe.length;
14579                if (iframeCount === 0) {
14580                    // Create a new iFrame if none is given
14581                    $iframe = $('<iframe height="0" width="0" border="0" wmode="Opaque"/>').prependTo('body').css({
14582                        "position": "absolute",
14583                        "top": -999,
14584                        "left": -999
14585                    });
14586                }
14587                w = $iframe.get(0);
14588                w = w.contentWindow || w.contentDocument || w;
14589                wdoc = w.document || w.contentDocument || w;
14590                wdoc.open();
14591                wdoc.write(content);
14592                wdoc.close();
14593                setTimeout(function() {
14594                    // Fix for IE : Allow it to render the iframe
14595                    w.focus();
14596                    w.print();
14597                    setTimeout(function() {
14598                        // Fix for IE
14599                        if (iframeCount === 0) {
14600                            // Destroy the iframe if created here
14601                            $iframe.remove();
14602                        }
14603                    }, 100);
14604                }, 250);
14605            } catch (e) {
14606                // Use the pop-up method if iframe fails for some reason
14607                console.error("Failed to print from iframe", e.stack, e.message);
14608                w = window.open();
14609                w.document.write(content);
14610                w.document.close();
14611                w.focus();
14612                w.print();
14613                w.close();
14614            }
14615        } else {
14616            // Use a new window for printing
14617            w = window.open();
14618            w.document.write(content);
14619            w.document.close();
14620            w.focus();
14621            w.print();
14622            w.close();
14623        }
14624        return this;
14625    };
14626
14627})(jQuery);
14628/**
14629 * @name MarkerClustererPlus for Google Maps V3
14630 * @version 2.1.2 [May 28, 2014]
14631 * @author Gary Little
14632 * @fileoverview
14633 * The library creates and manages per-zoom-level clusters for large amounts of markers.
14634 * <p>
14635 * This is an enhanced V3 implementation of the
14636 * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/"
14637 * >V2 MarkerClusterer</a> by Xiaoxi Wu. It is based on the
14638 * <a href="http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerclusterer/"
14639 * >V3 MarkerClusterer</a> port by Luke Mahe. MarkerClustererPlus was created by Gary Little.
14640 * <p>
14641 * v2.0 release: MarkerClustererPlus v2.0 is backward compatible with MarkerClusterer v1.0. It
14642 *  adds support for the <code>ignoreHidden</code>, <code>title</code>, <code>batchSizeIE</code>,
14643 *  and <code>calculator</code> properties as well as support for four more events. It also allows
14644 *  greater control over the styling of the text that appears on the cluster marker. The
14645 *  documentation has been significantly improved and the overall code has been simplified and
14646 *  polished. Very large numbers of markers can now be managed without causing Javascript timeout
14647 *  errors on Internet Explorer. Note that the name of the <code>clusterclick</code> event has been
14648 *  deprecated. The new name is <code>click</code>, so please change your application code now.
14649 */
14650
14651/**
14652 * Licensed under the Apache License, Version 2.0 (the "License");
14653 * you may not use this file except in compliance with the License.
14654 * You may obtain a copy of the License at
14655 *
14656 *     http://www.apache.org/licenses/LICENSE-2.0
14657 *
14658 * Unless required by applicable law or agreed to in writing, software
14659 * distributed under the License is distributed on an "AS IS" BASIS,
14660 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14661 * See the License for the specific language governing permissions and
14662 * limitations under the License.
14663 */
14664
14665
14666/**
14667 * @name ClusterIconStyle
14668 * @class This class represents the object for values in the <code>styles</code> array passed
14669 *  to the {@link MarkerClusterer} constructor. The element in this array that is used to
14670 *  style the cluster icon is determined by calling the <code>calculator</code> function.
14671 *
14672 * @property {string} url The URL of the cluster icon image file. Required.
14673 * @property {number} height The display height (in pixels) of the cluster icon. Required.
14674 * @property {number} width The display width (in pixels) of the cluster icon. Required.
14675 * @property {Array} [anchorText] The position (in pixels) from the center of the cluster icon to
14676 *  where the text label is to be centered and drawn. The format is <code>[yoffset, xoffset]</code>
14677 *  where <code>yoffset</code> increases as you go down from center and <code>xoffset</code>
14678 *  increases to the right of center. The default is <code>[0, 0]</code>.
14679 * @property {Array} [anchorIcon] The anchor position (in pixels) of the cluster icon. This is the
14680 *  spot on the cluster icon that is to be aligned with the cluster position. The format is
14681 *  <code>[yoffset, xoffset]</code> where <code>yoffset</code> increases as you go down and
14682 *  <code>xoffset</code> increases to the right of the top-left corner of the icon. The default
14683 *  anchor position is the center of the cluster icon.
14684 * @property {string} [textColor="black"] The color of the label text shown on the
14685 *  cluster icon.
14686 * @property {number} [textSize=11] The size (in pixels) of the label text shown on the
14687 *  cluster icon.
14688 * @property {string} [textDecoration="none"] The value of the CSS <code>text-decoration</code>
14689 *  property for the label text shown on the cluster icon.
14690 * @property {string} [fontWeight="bold"] The value of the CSS <code>font-weight</code>
14691 *  property for the label text shown on the cluster icon.
14692 * @property {string} [fontStyle="normal"] The value of the CSS <code>font-style</code>
14693 *  property for the label text shown on the cluster icon.
14694 * @property {string} [fontFamily="Arial,sans-serif"] The value of the CSS <code>font-family</code>
14695 *  property for the label text shown on the cluster icon.
14696 * @property {string} [backgroundPosition="0 0"] The position of the cluster icon image
14697 *  within the image defined by <code>url</code>. The format is <code>"xpos ypos"</code>
14698 *  (the same format as for the CSS <code>background-position</code> property). You must set
14699 *  this property appropriately when the image defined by <code>url</code> represents a sprite
14700 *  containing multiple images. Note that the position <i>must</i> be specified in px units.
14701 */
14702/**
14703 * @name ClusterIconInfo
14704 * @class This class is an object containing general information about a cluster icon. This is
14705 *  the object that a <code>calculator</code> function returns.
14706 *
14707 * @property {string} text The text of the label to be shown on the cluster icon.
14708 * @property {number} index The index plus 1 of the element in the <code>styles</code>
14709 *  array to be used to style the cluster icon.
14710 * @property {string} title The tooltip to display when the mouse moves over the cluster icon.
14711 *  If this value is <code>undefined</code> or <code>""</code>, <code>title</code> is set to the
14712 *  value of the <code>title</code> property passed to the MarkerClusterer.
14713 */
14714/**
14715 * A cluster icon.
14716 *
14717 * @constructor
14718 * @extends google.maps.OverlayView
14719 * @param {Cluster} cluster The cluster with which the icon is to be associated.
14720 * @param {Array} [styles] An array of {@link ClusterIconStyle} defining the cluster icons
14721 *  to use for various cluster sizes.
14722 * @private
14723 */
14724function ClusterIcon(cluster, styles) {
14725    cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView);
14726
14727    this.cluster_ = cluster;
14728    this.className_ = cluster.getMarkerClusterer().getClusterClass();
14729    this.styles_ = styles;
14730    this.center_ = null;
14731    this.div_ = null;
14732    this.sums_ = null;
14733    this.visible_ = false;
14734
14735    this.setMap(cluster.getMap()); // Note: this causes onAdd to be called
14736}
14737
14738
14739/**
14740 * Adds the icon to the DOM.
14741 */
14742ClusterIcon.prototype.onAdd = function() {
14743    var cClusterIcon = this;
14744    var cMouseDownInCluster;
14745    var cDraggingMapByCluster;
14746
14747    this.div_ = document.createElement("div");
14748    this.div_.className = this.className_;
14749    if (this.visible_) {
14750        this.show();
14751    }
14752
14753    this.getPanes().overlayMouseTarget.appendChild(this.div_);
14754
14755    // Fix for Issue 157
14756    this.boundsChangedListener_ = google.maps.event.addListener(this.getMap(), "bounds_changed", function() {
14757        cDraggingMapByCluster = cMouseDownInCluster;
14758    });
14759
14760    google.maps.event.addDomListener(this.div_, "mousedown", function() {
14761        cMouseDownInCluster = true;
14762        cDraggingMapByCluster = false;
14763    });
14764
14765    google.maps.event.addDomListener(this.div_, "click", function(e) {
14766        cMouseDownInCluster = false;
14767        if (!cDraggingMapByCluster) {
14768            var theBounds;
14769            var mz;
14770            var mc = cClusterIcon.cluster_.getMarkerClusterer();
14771            /**
14772             * This event is fired when a cluster marker is clicked.
14773             * @name MarkerClusterer#click
14774             * @param {Cluster} c The cluster that was clicked.
14775             * @event
14776             */
14777            google.maps.event.trigger(mc, "click", cClusterIcon.cluster_);
14778            google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name
14779
14780            // The default click handler follows. Disable it by setting
14781            // the zoomOnClick property to false.
14782            if (mc.getZoomOnClick()) {
14783                // Zoom into the cluster.
14784                mz = mc.getMaxZoom();
14785                theBounds = cClusterIcon.cluster_.getBounds();
14786                mc.getMap().fitBounds(theBounds);
14787                // There is a fix for Issue 170 here:
14788                setTimeout(function() {
14789                    mc.getMap().fitBounds(theBounds);
14790                    // Don't zoom beyond the max zoom level
14791                    if (mz !== null && (mc.getMap().getZoom() > mz)) {
14792                        mc.getMap().setZoom(mz + 1);
14793                    }
14794                }, 100);
14795            }
14796
14797            // Prevent event propagation to the map:
14798            e.cancelBubble = true;
14799            if (e.stopPropagation) {
14800                e.stopPropagation();
14801            }
14802        }
14803    });
14804
14805    google.maps.event.addDomListener(this.div_, "mouseover", function() {
14806        var mc = cClusterIcon.cluster_.getMarkerClusterer();
14807        /**
14808         * This event is fired when the mouse moves over a cluster marker.
14809         * @name MarkerClusterer#mouseover
14810         * @param {Cluster} c The cluster that the mouse moved over.
14811         * @event
14812         */
14813        google.maps.event.trigger(mc, "mouseover", cClusterIcon.cluster_);
14814    });
14815
14816    google.maps.event.addDomListener(this.div_, "mouseout", function() {
14817        var mc = cClusterIcon.cluster_.getMarkerClusterer();
14818        /**
14819         * This event is fired when the mouse moves out of a cluster marker.
14820         * @name MarkerClusterer#mouseout
14821         * @param {Cluster} c The cluster that the mouse moved out of.
14822         * @event
14823         */
14824        google.maps.event.trigger(mc, "mouseout", cClusterIcon.cluster_);
14825    });
14826};
14827
14828
14829/**
14830 * Removes the icon from the DOM.
14831 */
14832ClusterIcon.prototype.onRemove = function() {
14833    if (this.div_ && this.div_.parentNode) {
14834        this.hide();
14835        google.maps.event.removeListener(this.boundsChangedListener_);
14836        google.maps.event.clearInstanceListeners(this.div_);
14837        this.div_.parentNode.removeChild(this.div_);
14838        this.div_ = null;
14839    }
14840};
14841
14842
14843/**
14844 * Draws the icon.
14845 */
14846ClusterIcon.prototype.draw = function() {
14847    if (this.visible_) {
14848        var pos = this.getPosFromLatLng_(this.center_);
14849        this.div_.style.top = pos.y + "px";
14850        this.div_.style.left = pos.x + "px";
14851    }
14852};
14853
14854
14855/**
14856 * Hides the icon.
14857 */
14858ClusterIcon.prototype.hide = function() {
14859    if (this.div_) {
14860        this.div_.style.display = "none";
14861    }
14862    this.visible_ = false;
14863};
14864
14865
14866/**
14867 * Positions and shows the icon.
14868 */
14869ClusterIcon.prototype.show = function() {
14870    if (this.div_) {
14871        var img = "";
14872        // NOTE: values must be specified in px units
14873        var bp = this.backgroundPosition_.split(" ");
14874        var spriteH = parseInt(bp[0].replace(/^\s+|\s+$/g, ""), 10);
14875        var spriteV = parseInt(bp[1].replace(/^\s+|\s+$/g, ""), 10);
14876        var pos = this.getPosFromLatLng_(this.center_);
14877        this.div_.style.cssText = this.createCss(pos);
14878        img = "<img src='" + this.url_ + "' style='position: absolute; top: " + spriteV + "px; left: " + spriteH + "px; ";
14879        if (!this.cluster_.getMarkerClusterer().enableRetinaIcons_) {
14880            img += "clip: rect(" + (-1 * spriteV) + "px, " + ((-1 * spriteH) + this.width_) + "px, " +
14881                ((-1 * spriteV) + this.height_) + "px, " + (-1 * spriteH) + "px);";
14882        }
14883        img += "'>";
14884        this.div_.innerHTML = img + "<div style='" +
14885            "position: absolute;" +
14886            "top: " + this.anchorText_[0] + "px;" +
14887            "left: " + this.anchorText_[1] + "px;" +
14888            "color: " + this.textColor_ + ";" +
14889            "font-size: " + this.textSize_ + "px;" +
14890            "font-family: " + this.fontFamily_ + ";" +
14891            "font-weight: " + this.fontWeight_ + ";" +
14892            "font-style: " + this.fontStyle_ + ";" +
14893            "text-decoration: " + this.textDecoration_ + ";" +
14894            "text-align: center;" +
14895            "width: " + this.width_ + "px;" +
14896            "line-height:" + this.height_ + "px;" +
14897            "'>" + this.sums_.text + "</div>";
14898        if (typeof this.sums_.title === "undefined" || this.sums_.title === "") {
14899            this.div_.title = this.cluster_.getMarkerClusterer().getTitle();
14900        } else {
14901            this.div_.title = this.sums_.title;
14902        }
14903        this.div_.style.display = "";
14904    }
14905    this.visible_ = true;
14906};
14907
14908
14909/**
14910 * Sets the icon styles to the appropriate element in the styles array.
14911 *
14912 * @param {ClusterIconInfo} sums The icon label text and styles index.
14913 */
14914ClusterIcon.prototype.useStyle = function(sums) {
14915    this.sums_ = sums;
14916    var index = Math.max(0, sums.index - 1);
14917    index = Math.min(this.styles_.length - 1, index);
14918    var style = this.styles_[index];
14919    this.url_ = style.url;
14920    this.height_ = style.height;
14921    this.width_ = style.width;
14922    this.anchorText_ = style.anchorText || [0, 0];
14923    this.anchorIcon_ = style.anchorIcon || [parseInt(this.height_ / 2, 10), parseInt(this.width_ / 2, 10)];
14924    this.textColor_ = style.textColor || "black";
14925    this.textSize_ = style.textSize || 11;
14926    this.textDecoration_ = style.textDecoration || "none";
14927    this.fontWeight_ = style.fontWeight || "bold";
14928    this.fontStyle_ = style.fontStyle || "normal";
14929    this.fontFamily_ = style.fontFamily || "Arial,sans-serif";
14930    this.backgroundPosition_ = style.backgroundPosition || "0 0";
14931};
14932
14933
14934/**
14935 * Sets the position at which to center the icon.
14936 *
14937 * @param {google.maps.LatLng} center The latlng to set as the center.
14938 */
14939ClusterIcon.prototype.setCenter = function(center) {
14940    this.center_ = center;
14941};
14942
14943
14944/**
14945 * Creates the cssText style parameter based on the position of the icon.
14946 *
14947 * @param {google.maps.Point} pos The position of the icon.
14948 * @return {string} The CSS style text.
14949 */
14950ClusterIcon.prototype.createCss = function(pos) {
14951    var style = [];
14952    style.push("cursor: pointer;");
14953    style.push("position: absolute; top: " + pos.y + "px; left: " + pos.x + "px;");
14954    style.push("width: " + this.width_ + "px; height: " + this.height_ + "px;");
14955    return style.join("");
14956};
14957
14958
14959/**
14960 * Returns the position at which to place the DIV depending on the latlng.
14961 *
14962 * @param {google.maps.LatLng} latlng The position in latlng.
14963 * @return {google.maps.Point} The position in pixels.
14964 */
14965ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) {
14966    var pos = this.getProjection().fromLatLngToDivPixel(latlng);
14967    pos.x -= this.anchorIcon_[1];
14968    pos.y -= this.anchorIcon_[0];
14969    pos.x = parseInt(pos.x, 10);
14970    pos.y = parseInt(pos.y, 10);
14971    return pos;
14972};
14973
14974
14975/**
14976 * Creates a single cluster that manages a group of proximate markers.
14977 *  Used internally, do not call this constructor directly.
14978 * @constructor
14979 * @param {MarkerClusterer} mc The <code>MarkerClusterer</code> object with which this
14980 *  cluster is associated.
14981 */
14982function Cluster(mc) {
14983    this.markerClusterer_ = mc;
14984    this.map_ = mc.getMap();
14985    this.gridSize_ = mc.getGridSize();
14986    this.minClusterSize_ = mc.getMinimumClusterSize();
14987    this.averageCenter_ = mc.getAverageCenter();
14988    this.markers_ = [];
14989    this.center_ = null;
14990    this.bounds_ = null;
14991    this.clusterIcon_ = new ClusterIcon(this, mc.getStyles());
14992}
14993
14994
14995/**
14996 * Returns the number of markers managed by the cluster. You can call this from
14997 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler
14998 * for the <code>MarkerClusterer</code> object.
14999 *
15000 * @return {number} The number of markers in the cluster.
15001 */
15002Cluster.prototype.getSize = function() {
15003    return this.markers_.length;
15004};
15005
15006
15007/**
15008 * Returns the array of markers managed by the cluster. You can call this from
15009 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler
15010 * for the <code>MarkerClusterer</code> object.
15011 *
15012 * @return {Array} The array of markers in the cluster.
15013 */
15014Cluster.prototype.getMarkers = function() {
15015    return this.markers_;
15016};
15017
15018
15019/**
15020 * Returns the center of the cluster. You can call this from
15021 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler
15022 * for the <code>MarkerClusterer</code> object.
15023 *
15024 * @return {google.maps.LatLng} The center of the cluster.
15025 */
15026Cluster.prototype.getCenter = function() {
15027    return this.center_;
15028};
15029
15030
15031/**
15032 * Returns the map with which the cluster is associated.
15033 *
15034 * @return {google.maps.Map} The map.
15035 * @ignore
15036 */
15037Cluster.prototype.getMap = function() {
15038    return this.map_;
15039};
15040
15041
15042/**
15043 * Returns the <code>MarkerClusterer</code> object with which the cluster is associated.
15044 *
15045 * @return {MarkerClusterer} The associated marker clusterer.
15046 * @ignore
15047 */
15048Cluster.prototype.getMarkerClusterer = function() {
15049    return this.markerClusterer_;
15050};
15051
15052
15053/**
15054 * Returns the bounds of the cluster.
15055 *
15056 * @return {google.maps.LatLngBounds} the cluster bounds.
15057 * @ignore
15058 */
15059Cluster.prototype.getBounds = function() {
15060    var i;
15061    var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
15062    var markers = this.getMarkers();
15063    for (i = 0; i < markers.length; i++) {
15064        bounds.extend(markers[i].getPosition());
15065    }
15066    return bounds;
15067};
15068
15069
15070/**
15071 * Removes the cluster from the map.
15072 *
15073 * @ignore
15074 */
15075Cluster.prototype.remove = function() {
15076    this.clusterIcon_.setMap(null);
15077    this.markers_ = [];
15078    delete this.markers_;
15079};
15080
15081
15082/**
15083 * Adds a marker to the cluster.
15084 *
15085 * @param {google.maps.Marker} marker The marker to be added.
15086 * @return {boolean} True if the marker was added.
15087 * @ignore
15088 */
15089Cluster.prototype.addMarker = function(marker) {
15090    var i;
15091    var mCount;
15092    var mz;
15093
15094    if (this.isMarkerAlreadyAdded_(marker)) {
15095        return false;
15096    }
15097
15098    if (!this.center_) {
15099        this.center_ = marker.getPosition();
15100        this.calculateBounds_();
15101    } else {
15102        if (this.averageCenter_) {
15103            var l = this.markers_.length + 1;
15104            var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l;
15105            var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l;
15106            this.center_ = new google.maps.LatLng(lat, lng);
15107            this.calculateBounds_();
15108        }
15109    }
15110
15111    marker.isAdded = true;
15112    this.markers_.push(marker);
15113
15114    mCount = this.markers_.length;
15115    mz = this.markerClusterer_.getMaxZoom();
15116    if (mz !== null && this.map_.getZoom() > mz) {
15117        // Zoomed in past max zoom, so show the marker.
15118        if (marker.getMap() !== this.map_) {
15119            marker.setMap(this.map_);
15120        }
15121    } else if (mCount < this.minClusterSize_) {
15122        // Min cluster size not reached so show the marker.
15123        if (marker.getMap() !== this.map_) {
15124            marker.setMap(this.map_);
15125        }
15126    } else if (mCount === this.minClusterSize_) {
15127        // Hide the markers that were showing.
15128        for (i = 0; i < mCount; i++) {
15129            this.markers_[i].setMap(null);
15130        }
15131    } else {
15132        marker.setMap(null);
15133    }
15134
15135    this.updateIcon_();
15136    return true;
15137};
15138
15139
15140/**
15141 * Determines if a marker lies within the cluster's bounds.
15142 *
15143 * @param {google.maps.Marker} marker The marker to check.
15144 * @return {boolean} True if the marker lies in the bounds.
15145 * @ignore
15146 */
15147Cluster.prototype.isMarkerInClusterBounds = function(marker) {
15148    return this.bounds_.contains(marker.getPosition());
15149};
15150
15151
15152/**
15153 * Calculates the extended bounds of the cluster with the grid.
15154 */
15155Cluster.prototype.calculateBounds_ = function() {
15156    var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
15157    this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);
15158};
15159
15160
15161/**
15162 * Updates the cluster icon.
15163 */
15164Cluster.prototype.updateIcon_ = function() {
15165    var mCount = this.markers_.length;
15166    var mz = this.markerClusterer_.getMaxZoom();
15167
15168    if (mz !== null && this.map_.getZoom() > mz) {
15169        this.clusterIcon_.hide();
15170        return;
15171    }
15172
15173    if (mCount < this.minClusterSize_) {
15174        // Min cluster size not yet reached.
15175        this.clusterIcon_.hide();
15176        return;
15177    }
15178
15179    var numStyles = this.markerClusterer_.getStyles().length;
15180    var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles);
15181    this.clusterIcon_.setCenter(this.center_);
15182    this.clusterIcon_.useStyle(sums);
15183    this.clusterIcon_.show();
15184};
15185
15186
15187/**
15188 * Determines if a marker has already been added to the cluster.
15189 *
15190 * @param {google.maps.Marker} marker The marker to check.
15191 * @return {boolean} True if the marker has already been added.
15192 */
15193Cluster.prototype.isMarkerAlreadyAdded_ = function(marker) {
15194    var i;
15195    if (this.markers_.indexOf) {
15196        return this.markers_.indexOf(marker) !== -1;
15197    } else {
15198        for (i = 0; i < this.markers_.length; i++) {
15199            if (marker === this.markers_[i]) {
15200                return true;
15201            }
15202        }
15203    }
15204    return false;
15205};
15206
15207
15208/**
15209 * @name MarkerClustererOptions
15210 * @class This class represents the optional parameter passed to
15211 *  the {@link MarkerClusterer} constructor.
15212 * @property {number} [gridSize=60] The grid size of a cluster in pixels. The grid is a square.
15213 * @property {number} [maxZoom=null] The maximum zoom level at which clustering is enabled or
15214 *  <code>null</code> if clustering is to be enabled at all zoom levels.
15215 * @property {boolean} [zoomOnClick=true] Whether to zoom the map when a cluster marker is
15216 *  clicked. You may want to set this to <code>false</code> if you have installed a handler
15217 *  for the <code>click</code> event and it deals with zooming on its own.
15218 * @property {boolean} [averageCenter=false] Whether the position of a cluster marker should be
15219 *  the average position of all markers in the cluster. If set to <code>false</code>, the
15220 *  cluster marker is positioned at the location of the first marker added to the cluster.
15221 * @property {number} [minimumClusterSize=2] The minimum number of markers needed in a cluster
15222 *  before the markers are hidden and a cluster marker appears.
15223 * @property {boolean} [ignoreHidden=false] Whether to ignore hidden markers in clusters. You
15224 *  may want to set this to <code>true</code> to ensure that hidden markers are not included
15225 *  in the marker count that appears on a cluster marker (this count is the value of the
15226 *  <code>text</code> property of the result returned by the default <code>calculator</code>).
15227 *  If set to <code>true</code> and you change the visibility of a marker being clustered, be
15228 *  sure to also call <code>MarkerClusterer.repaint()</code>.
15229 * @property {string} [title=""] The tooltip to display when the mouse moves over a cluster
15230 *  marker. (Alternatively, you can use a custom <code>calculator</code> function to specify a
15231 *  different tooltip for each cluster marker.)
15232 * @property {function} [calculator=MarkerClusterer.CALCULATOR] The function used to determine
15233 *  the text to be displayed on a cluster marker and the index indicating which style to use
15234 *  for the cluster marker. The input parameters for the function are (1) the array of markers
15235 *  represented by a cluster marker and (2) the number of cluster icon styles. It returns a
15236 *  {@link ClusterIconInfo} object. The default <code>calculator</code> returns a
15237 *  <code>text</code> property which is the number of markers in the cluster and an
15238 *  <code>index</code> property which is one higher than the lowest integer such that
15239 *  <code>10^i</code> exceeds the number of markers in the cluster, or the size of the styles
15240 *  array, whichever is less. The <code>styles</code> array element used has an index of
15241 *  <code>index</code> minus 1. For example, the default <code>calculator</code> returns a
15242 *  <code>text</code> value of <code>"125"</code> and an <code>index</code> of <code>3</code>
15243 *  for a cluster icon representing 125 markers so the element used in the <code>styles</code>
15244 *  array is <code>2</code>. A <code>calculator</code> may also return a <code>title</code>
15245 *  property that contains the text of the tooltip to be used for the cluster marker. If
15246 *   <code>title</code> is not defined, the tooltip is set to the value of the <code>title</code>
15247 *   property for the MarkerClusterer.
15248 * @property {string} [clusterClass="cluster"] The name of the CSS class defining general styles
15249 *  for the cluster markers. Use this class to define CSS styles that are not set up by the code
15250 *  that processes the <code>styles</code> array.
15251 * @property {Array} [styles] An array of {@link ClusterIconStyle} elements defining the styles
15252 *  of the cluster markers to be used. The element to be used to style a given cluster marker
15253 *  is determined by the function defined by the <code>calculator</code> property.
15254 *  The default is an array of {@link ClusterIconStyle} elements whose properties are derived
15255 *  from the values for <code>imagePath</code>, <code>imageExtension</code>, and
15256 *  <code>imageSizes</code>.
15257 * @property {boolean} [enableRetinaIcons=false] Whether to allow the use of cluster icons that
15258 * have sizes that are some multiple (typically double) of their actual display size. Icons such
15259 * as these look better when viewed on high-resolution monitors such as Apple's Retina displays.
15260 * Note: if this property is <code>true</code>, sprites cannot be used as cluster icons.
15261 * @property {number} [batchSize=MarkerClusterer.BATCH_SIZE] Set this property to the
15262 *  number of markers to be processed in a single batch when using a browser other than
15263 *  Internet Explorer (for Internet Explorer, use the batchSizeIE property instead).
15264 * @property {number} [batchSizeIE=MarkerClusterer.BATCH_SIZE_IE] When Internet Explorer is
15265 *  being used, markers are processed in several batches with a small delay inserted between
15266 *  each batch in an attempt to avoid Javascript timeout errors. Set this property to the
15267 *  number of markers to be processed in a single batch; select as high a number as you can
15268 *  without causing a timeout error in the browser. This number might need to be as low as 100
15269 *  if 15,000 markers are being managed, for example.
15270 * @property {string} [imagePath=MarkerClusterer.IMAGE_PATH]
15271 *  The full URL of the root name of the group of image files to use for cluster icons.
15272 *  The complete file name is of the form <code>imagePath</code>n.<code>imageExtension</code>
15273 *  where n is the image file number (1, 2, etc.).
15274 * @property {string} [imageExtension=MarkerClusterer.IMAGE_EXTENSION]
15275 *  The extension name for the cluster icon image files (e.g., <code>"png"</code> or
15276 *  <code>"jpg"</code>).
15277 * @property {Array} [imageSizes=MarkerClusterer.IMAGE_SIZES]
15278 *  An array of numbers containing the widths of the group of
15279 *  <code>imagePath</code>n.<code>imageExtension</code> image files.
15280 *  (The images are assumed to be square.)
15281 */
15282/**
15283 * Creates a MarkerClusterer object with the options specified in {@link MarkerClustererOptions}.
15284 * @constructor
15285 * @extends google.maps.OverlayView
15286 * @param {google.maps.Map} map The Google map to attach to.
15287 * @param {Array.<google.maps.Marker>} [opt_markers] The markers to be added to the cluster.
15288 * @param {MarkerClustererOptions} [opt_options] The optional parameters.
15289 */
15290function MarkerClusterer(map, opt_markers, opt_options) {
15291    // MarkerClusterer implements google.maps.OverlayView interface. We use the
15292    // extend function to extend MarkerClusterer with google.maps.OverlayView
15293    // because it might not always be available when the code is defined so we
15294    // look for it at the last possible moment. If it doesn't exist now then
15295    // there is no point going ahead :)
15296    this.extend(MarkerClusterer, google.maps.OverlayView);
15297
15298    opt_markers = opt_markers || [];
15299    opt_options = opt_options || {};
15300
15301    this.markers_ = [];
15302    this.clusters_ = [];
15303    this.listeners_ = [];
15304    this.activeMap_ = null;
15305    this.ready_ = false;
15306
15307    this.gridSize_ = opt_options.gridSize || 60;
15308    this.minClusterSize_ = opt_options.minimumClusterSize || 2;
15309    this.maxZoom_ = opt_options.maxZoom || null;
15310    this.styles_ = opt_options.styles || [];
15311    this.title_ = opt_options.title || "";
15312    this.zoomOnClick_ = true;
15313    if (opt_options.zoomOnClick !== undefined) {
15314        this.zoomOnClick_ = opt_options.zoomOnClick;
15315    }
15316    this.averageCenter_ = false;
15317    if (opt_options.averageCenter !== undefined) {
15318        this.averageCenter_ = opt_options.averageCenter;
15319    }
15320    this.ignoreHidden_ = false;
15321    if (opt_options.ignoreHidden !== undefined) {
15322        this.ignoreHidden_ = opt_options.ignoreHidden;
15323    }
15324    this.enableRetinaIcons_ = false;
15325    if (opt_options.enableRetinaIcons !== undefined) {
15326        this.enableRetinaIcons_ = opt_options.enableRetinaIcons;
15327    }
15328    this.imagePath_ = opt_options.imagePath || MarkerClusterer.IMAGE_PATH;
15329    this.imageExtension_ = opt_options.imageExtension || MarkerClusterer.IMAGE_EXTENSION;
15330    this.imageSizes_ = opt_options.imageSizes || MarkerClusterer.IMAGE_SIZES;
15331    this.calculator_ = opt_options.calculator || MarkerClusterer.CALCULATOR;
15332    this.batchSize_ = opt_options.batchSize || MarkerClusterer.BATCH_SIZE;
15333    this.batchSizeIE_ = opt_options.batchSizeIE || MarkerClusterer.BATCH_SIZE_IE;
15334    this.clusterClass_ = opt_options.clusterClass || "cluster";
15335
15336    if (navigator.userAgent.toLowerCase().indexOf("msie") !== -1) {
15337        // Try to avoid IE timeout when processing a huge number of markers:
15338        this.batchSize_ = this.batchSizeIE_;
15339    }
15340
15341    this.setupStyles_();
15342
15343    this.addMarkers(opt_markers, true);
15344    this.setMap(map); // Note: this causes onAdd to be called
15345}
15346
15347
15348/**
15349 * Implementation of the onAdd interface method.
15350 * @ignore
15351 */
15352MarkerClusterer.prototype.onAdd = function() {
15353    var cMarkerClusterer = this;
15354
15355    this.activeMap_ = this.getMap();
15356    this.ready_ = true;
15357
15358    this.repaint();
15359
15360    // Add the map event listeners
15361    this.listeners_ = [
15362        google.maps.event.addListener(this.getMap(), "zoom_changed", function() {
15363            cMarkerClusterer.resetViewport_(false);
15364            // Workaround for this Google bug: when map is at level 0 and "-" of
15365            // zoom slider is clicked, a "zoom_changed" event is fired even though
15366            // the map doesn't zoom out any further. In this situation, no "idle"
15367            // event is triggered so the cluster markers that have been removed
15368            // do not get redrawn. Same goes for a zoom in at maxZoom.
15369            if (this.getZoom() === (this.get("minZoom") || 0) || this.getZoom() === this.get("maxZoom")) {
15370                google.maps.event.trigger(this, "idle");
15371            }
15372        }),
15373        google.maps.event.addListener(this.getMap(), "idle", function() {
15374            cMarkerClusterer.redraw_();
15375        })
15376    ];
15377};
15378
15379
15380/**
15381 * Implementation of the onRemove interface method.
15382 * Removes map event listeners and all cluster icons from the DOM.
15383 * All managed markers are also put back on the map.
15384 * @ignore
15385 */
15386MarkerClusterer.prototype.onRemove = function() {
15387    var i;
15388
15389    // Put all the managed markers back on the map:
15390    for (i = 0; i < this.markers_.length; i++) {
15391        if (this.markers_[i].getMap() !== this.activeMap_) {
15392            this.markers_[i].setMap(this.activeMap_);
15393        }
15394    }
15395
15396    // Remove all clusters:
15397    for (i = 0; i < this.clusters_.length; i++) {
15398        this.clusters_[i].remove();
15399    }
15400    this.clusters_ = [];
15401
15402    // Remove map event listeners:
15403    for (i = 0; i < this.listeners_.length; i++) {
15404        google.maps.event.removeListener(this.listeners_[i]);
15405    }
15406    this.listeners_ = [];
15407
15408    this.activeMap_ = null;
15409    this.ready_ = false;
15410};
15411
15412
15413/**
15414 * Implementation of the draw interface method.
15415 * @ignore
15416 */
15417MarkerClusterer.prototype.draw = function() {};
15418
15419
15420/**
15421 * Sets up the styles object.
15422 */
15423MarkerClusterer.prototype.setupStyles_ = function() {
15424    var i, size;
15425    if (this.styles_.length > 0) {
15426        return;
15427    }
15428
15429    for (i = 0; i < this.imageSizes_.length; i++) {
15430        size = this.imageSizes_[i];
15431        this.styles_.push({
15432            url: this.imagePath_ + (i + 1) + "." + this.imageExtension_,
15433            height: size,
15434            width: size
15435        });
15436    }
15437};
15438
15439
15440/**
15441 *  Fits the map to the bounds of the markers managed by the clusterer.
15442 */
15443MarkerClusterer.prototype.fitMapToMarkers = function() {
15444    var i;
15445    var markers = this.getMarkers();
15446    var bounds = new google.maps.LatLngBounds();
15447    for (i = 0; i < markers.length; i++) {
15448        bounds.extend(markers[i].getPosition());
15449    }
15450
15451    this.getMap().fitBounds(bounds);
15452};
15453
15454
15455/**
15456 * Returns the value of the <code>gridSize</code> property.
15457 *
15458 * @return {number} The grid size.
15459 */
15460MarkerClusterer.prototype.getGridSize = function() {
15461    return this.gridSize_;
15462};
15463
15464
15465/**
15466 * Sets the value of the <code>gridSize</code> property.
15467 *
15468 * @param {number} gridSize The grid size.
15469 */
15470MarkerClusterer.prototype.setGridSize = function(gridSize) {
15471    this.gridSize_ = gridSize;
15472};
15473
15474
15475/**
15476 * Returns the value of the <code>minimumClusterSize</code> property.
15477 *
15478 * @return {number} The minimum cluster size.
15479 */
15480MarkerClusterer.prototype.getMinimumClusterSize = function() {
15481    return this.minClusterSize_;
15482};
15483
15484/**
15485 * Sets the value of the <code>minimumClusterSize</code> property.
15486 *
15487 * @param {number} minimumClusterSize The minimum cluster size.
15488 */
15489MarkerClusterer.prototype.setMinimumClusterSize = function(minimumClusterSize) {
15490    this.minClusterSize_ = minimumClusterSize;
15491};
15492
15493
15494/**
15495 *  Returns the value of the <code>maxZoom</code> property.
15496 *
15497 *  @return {number} The maximum zoom level.
15498 */
15499MarkerClusterer.prototype.getMaxZoom = function() {
15500    return this.maxZoom_;
15501};
15502
15503
15504/**
15505 *  Sets the value of the <code>maxZoom</code> property.
15506 *
15507 *  @param {number} maxZoom The maximum zoom level.
15508 */
15509MarkerClusterer.prototype.setMaxZoom = function(maxZoom) {
15510    this.maxZoom_ = maxZoom;
15511};
15512
15513
15514/**
15515 *  Returns the value of the <code>styles</code> property.
15516 *
15517 *  @return {Array} The array of styles defining the cluster markers to be used.
15518 */
15519MarkerClusterer.prototype.getStyles = function() {
15520    return this.styles_;
15521};
15522
15523
15524/**
15525 *  Sets the value of the <code>styles</code> property.
15526 *
15527 *  @param {Array.<ClusterIconStyle>} styles The array of styles to use.
15528 */
15529MarkerClusterer.prototype.setStyles = function(styles) {
15530    this.styles_ = styles;
15531};
15532
15533
15534/**
15535 * Returns the value of the <code>title</code> property.
15536 *
15537 * @return {string} The content of the title text.
15538 */
15539MarkerClusterer.prototype.getTitle = function() {
15540    return this.title_;
15541};
15542
15543
15544/**
15545 *  Sets the value of the <code>title</code> property.
15546 *
15547 *  @param {string} title The value of the title property.
15548 */
15549MarkerClusterer.prototype.setTitle = function(title) {
15550    this.title_ = title;
15551};
15552
15553
15554/**
15555 * Returns the value of the <code>zoomOnClick</code> property.
15556 *
15557 * @return {boolean} True if zoomOnClick property is set.
15558 */
15559MarkerClusterer.prototype.getZoomOnClick = function() {
15560    return this.zoomOnClick_;
15561};
15562
15563
15564/**
15565 *  Sets the value of the <code>zoomOnClick</code> property.
15566 *
15567 *  @param {boolean} zoomOnClick The value of the zoomOnClick property.
15568 */
15569MarkerClusterer.prototype.setZoomOnClick = function(zoomOnClick) {
15570    this.zoomOnClick_ = zoomOnClick;
15571};
15572
15573
15574/**
15575 * Returns the value of the <code>averageCenter</code> property.
15576 *
15577 * @return {boolean} True if averageCenter property is set.
15578 */
15579MarkerClusterer.prototype.getAverageCenter = function() {
15580    return this.averageCenter_;
15581};
15582
15583
15584/**
15585 *  Sets the value of the <code>averageCenter</code> property.
15586 *
15587 *  @param {boolean} averageCenter The value of the averageCenter property.
15588 */
15589MarkerClusterer.prototype.setAverageCenter = function(averageCenter) {
15590    this.averageCenter_ = averageCenter;
15591};
15592
15593
15594/**
15595 * Returns the value of the <code>ignoreHidden</code> property.
15596 *
15597 * @return {boolean} True if ignoreHidden property is set.
15598 */
15599MarkerClusterer.prototype.getIgnoreHidden = function() {
15600    return this.ignoreHidden_;
15601};
15602
15603
15604/**
15605 *  Sets the value of the <code>ignoreHidden</code> property.
15606 *
15607 *  @param {boolean} ignoreHidden The value of the ignoreHidden property.
15608 */
15609MarkerClusterer.prototype.setIgnoreHidden = function(ignoreHidden) {
15610    this.ignoreHidden_ = ignoreHidden;
15611};
15612
15613
15614/**
15615 * Returns the value of the <code>enableRetinaIcons</code> property.
15616 *
15617 * @return {boolean} True if enableRetinaIcons property is set.
15618 */
15619MarkerClusterer.prototype.getEnableRetinaIcons = function() {
15620    return this.enableRetinaIcons_;
15621};
15622
15623
15624/**
15625 *  Sets the value of the <code>enableRetinaIcons</code> property.
15626 *
15627 *  @param {boolean} enableRetinaIcons The value of the enableRetinaIcons property.
15628 */
15629MarkerClusterer.prototype.setEnableRetinaIcons = function(enableRetinaIcons) {
15630    this.enableRetinaIcons_ = enableRetinaIcons;
15631};
15632
15633
15634/**
15635 * Returns the value of the <code>imageExtension</code> property.
15636 *
15637 * @return {string} The value of the imageExtension property.
15638 */
15639MarkerClusterer.prototype.getImageExtension = function() {
15640    return this.imageExtension_;
15641};
15642
15643
15644/**
15645 *  Sets the value of the <code>imageExtension</code> property.
15646 *
15647 *  @param {string} imageExtension The value of the imageExtension property.
15648 */
15649MarkerClusterer.prototype.setImageExtension = function(imageExtension) {
15650    this.imageExtension_ = imageExtension;
15651};
15652
15653
15654/**
15655 * Returns the value of the <code>imagePath</code> property.
15656 *
15657 * @return {string} The value of the imagePath property.
15658 */
15659MarkerClusterer.prototype.getImagePath = function() {
15660    return this.imagePath_;
15661};
15662
15663
15664/**
15665 *  Sets the value of the <code>imagePath</code> property.
15666 *
15667 *  @param {string} imagePath The value of the imagePath property.
15668 */
15669MarkerClusterer.prototype.setImagePath = function(imagePath) {
15670    this.imagePath_ = imagePath;
15671};
15672
15673
15674/**
15675 * Returns the value of the <code>imageSizes</code> property.
15676 *
15677 * @return {Array} The value of the imageSizes property.
15678 */
15679MarkerClusterer.prototype.getImageSizes = function() {
15680    return this.imageSizes_;
15681};
15682
15683
15684/**
15685 *  Sets the value of the <code>imageSizes</code> property.
15686 *
15687 *  @param {Array} imageSizes The value of the imageSizes property.
15688 */
15689MarkerClusterer.prototype.setImageSizes = function(imageSizes) {
15690    this.imageSizes_ = imageSizes;
15691};
15692
15693
15694/**
15695 * Returns the value of the <code>calculator</code> property.
15696 *
15697 * @return {function} the value of the calculator property.
15698 */
15699MarkerClusterer.prototype.getCalculator = function() {
15700    return this.calculator_;
15701};
15702
15703
15704/**
15705 * Sets the value of the <code>calculator</code> property.
15706 *
15707 * @param {function(Array.<google.maps.Marker>, number)} calculator The value
15708 *  of the calculator property.
15709 */
15710MarkerClusterer.prototype.setCalculator = function(calculator) {
15711    this.calculator_ = calculator;
15712};
15713
15714
15715/**
15716 * Returns the value of the <code>batchSizeIE</code> property.
15717 *
15718 * @return {number} the value of the batchSizeIE property.
15719 */
15720MarkerClusterer.prototype.getBatchSizeIE = function() {
15721    return this.batchSizeIE_;
15722};
15723
15724
15725/**
15726 * Sets the value of the <code>batchSizeIE</code> property.
15727 *
15728 *  @param {number} batchSizeIE The value of the batchSizeIE property.
15729 */
15730MarkerClusterer.prototype.setBatchSizeIE = function(batchSizeIE) {
15731    this.batchSizeIE_ = batchSizeIE;
15732};
15733
15734
15735/**
15736 * Returns the value of the <code>clusterClass</code> property.
15737 *
15738 * @return {string} the value of the clusterClass property.
15739 */
15740MarkerClusterer.prototype.getClusterClass = function() {
15741    return this.clusterClass_;
15742};
15743
15744
15745/**
15746 * Sets the value of the <code>clusterClass</code> property.
15747 *
15748 *  @param {string} clusterClass The value of the clusterClass property.
15749 */
15750MarkerClusterer.prototype.setClusterClass = function(clusterClass) {
15751    this.clusterClass_ = clusterClass;
15752};
15753
15754
15755/**
15756 *  Returns the array of markers managed by the clusterer.
15757 *
15758 *  @return {Array} The array of markers managed by the clusterer.
15759 */
15760MarkerClusterer.prototype.getMarkers = function() {
15761    return this.markers_;
15762};
15763
15764
15765/**
15766 *  Returns the number of markers managed by the clusterer.
15767 *
15768 *  @return {number} The number of markers.
15769 */
15770MarkerClusterer.prototype.getTotalMarkers = function() {
15771    return this.markers_.length;
15772};
15773
15774
15775/**
15776 * Returns the current array of clusters formed by the clusterer.
15777 *
15778 * @return {Array} The array of clusters formed by the clusterer.
15779 */
15780MarkerClusterer.prototype.getClusters = function() {
15781    return this.clusters_;
15782};
15783
15784
15785/**
15786 * Returns the number of clusters formed by the clusterer.
15787 *
15788 * @return {number} The number of clusters formed by the clusterer.
15789 */
15790MarkerClusterer.prototype.getTotalClusters = function() {
15791    return this.clusters_.length;
15792};
15793
15794
15795/**
15796 * Adds a marker to the clusterer. The clusters are redrawn unless
15797 *  <code>opt_nodraw</code> is set to <code>true</code>.
15798 *
15799 * @param {google.maps.Marker} marker The marker to add.
15800 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
15801 */
15802MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) {
15803    this.pushMarkerTo_(marker);
15804    if (!opt_nodraw) {
15805        this.redraw_();
15806    }
15807};
15808
15809
15810/**
15811 * Adds an array of markers to the clusterer. The clusters are redrawn unless
15812 *  <code>opt_nodraw</code> is set to <code>true</code>.
15813 *
15814 * @param {Array.<google.maps.Marker>} markers The markers to add.
15815 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
15816 */
15817MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) {
15818    var key;
15819    for (key in markers) {
15820        if (markers.hasOwnProperty(key)) {
15821            this.pushMarkerTo_(markers[key]);
15822        }
15823    }
15824    if (!opt_nodraw) {
15825        this.redraw_();
15826    }
15827};
15828
15829
15830/**
15831 * Pushes a marker to the clusterer.
15832 *
15833 * @param {google.maps.Marker} marker The marker to add.
15834 */
15835MarkerClusterer.prototype.pushMarkerTo_ = function(marker) {
15836    // If the marker is draggable add a listener so we can update the clusters on the dragend:
15837    if (marker.getDraggable()) {
15838        var cMarkerClusterer = this;
15839        google.maps.event.addListener(marker, "dragend", function() {
15840            if (cMarkerClusterer.ready_) {
15841                this.isAdded = false;
15842                cMarkerClusterer.repaint();
15843            }
15844        });
15845    }
15846    marker.isAdded = false;
15847    this.markers_.push(marker);
15848};
15849
15850
15851/**
15852 * Removes a marker from the cluster.  The clusters are redrawn unless
15853 *  <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if the
15854 *  marker was removed from the clusterer.
15855 *
15856 * @param {google.maps.Marker} marker The marker to remove.
15857 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
15858 * @return {boolean} True if the marker was removed from the clusterer.
15859 */
15860MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) {
15861    var removed = this.removeMarker_(marker);
15862
15863    if (!opt_nodraw && removed) {
15864        this.repaint();
15865    }
15866
15867    return removed;
15868};
15869
15870
15871/**
15872 * Removes an array of markers from the cluster. The clusters are redrawn unless
15873 *  <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if markers
15874 *  were removed from the clusterer.
15875 *
15876 * @param {Array.<google.maps.Marker>} markers The markers to remove.
15877 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing.
15878 * @return {boolean} True if markers were removed from the clusterer.
15879 */
15880MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) {
15881    var i, r;
15882    var removed = false;
15883
15884    for (i = 0; i < markers.length; i++) {
15885        r = this.removeMarker_(markers[i]);
15886        removed = removed || r;
15887    }
15888
15889    if (!opt_nodraw && removed) {
15890        this.repaint();
15891    }
15892
15893    return removed;
15894};
15895
15896
15897/**
15898 * Removes a marker and returns true if removed, false if not.
15899 *
15900 * @param {google.maps.Marker} marker The marker to remove
15901 * @return {boolean} Whether the marker was removed or not
15902 */
15903MarkerClusterer.prototype.removeMarker_ = function(marker) {
15904    var i;
15905    var index = -1;
15906    if (this.markers_.indexOf) {
15907        index = this.markers_.indexOf(marker);
15908    } else {
15909        for (i = 0; i < this.markers_.length; i++) {
15910            if (marker === this.markers_[i]) {
15911                index = i;
15912                break;
15913            }
15914        }
15915    }
15916
15917    if (index === -1) {
15918        // Marker is not in our list of markers, so do nothing:
15919        return false;
15920    }
15921
15922    marker.setMap(null);
15923    this.markers_.splice(index, 1); // Remove the marker from the list of managed markers
15924    return true;
15925};
15926
15927
15928/**
15929 * Removes all clusters and markers from the map and also removes all markers
15930 *  managed by the clusterer.
15931 */
15932MarkerClusterer.prototype.clearMarkers = function() {
15933    this.resetViewport_(true);
15934    this.markers_ = [];
15935};
15936
15937
15938/**
15939 * Recalculates and redraws all the marker clusters from scratch.
15940 *  Call this after changing any properties.
15941 */
15942MarkerClusterer.prototype.repaint = function() {
15943    var oldClusters = this.clusters_.slice();
15944    this.clusters_ = [];
15945    this.resetViewport_(false);
15946    this.redraw_();
15947
15948    // Remove the old clusters.
15949    // Do it in a timeout to prevent blinking effect.
15950    setTimeout(function() {
15951        var i;
15952        for (i = 0; i < oldClusters.length; i++) {
15953            oldClusters[i].remove();
15954        }
15955    }, 0);
15956};
15957
15958
15959/**
15960 * Returns the current bounds extended by the grid size.
15961 *
15962 * @param {google.maps.LatLngBounds} bounds The bounds to extend.
15963 * @return {google.maps.LatLngBounds}
15963 The extended bounds.
15964 * @ignore
15965 */
15966MarkerClusterer.prototype.getExtendedBounds = function(bounds) {
15967    var projection = this.getProjection();
15968
15969    // Turn the bounds into latlng.
15970    var tr = new google.maps.LatLng(bounds.getNorthEast().lat(),
15971        bounds.getNorthEast().lng());
15972    var bl = new google.maps.LatLng(bounds.getSouthWest().lat(),
15973        bounds.getSouthWest().lng());
15974
15975    // Convert the points to pixels and the extend out by the grid size.
15976    var trPix = projection.fromLatLngToDivPixel(tr);
15977    trPix.x += this.gridSize_;
15978    trPix.y -= this.gridSize_;
15979
15980    var blPix = projection.fromLatLngToDivPixel(bl);
15981    blPix.x -= this.gridSize_;
15982    blPix.y += this.gridSize_;
15983
15984    // Convert the pixel points back to LatLng
15985    var ne = projection.fromDivPixelToLatLng(trPix);
15986    var sw = projection.fromDivPixelToLatLng(blPix);
15987
15988    // Extend the bounds to contain the new bounds.
15989    bounds.extend(ne);
15990    bounds.extend(sw);
15991
15992    return bounds;
15993};
15994
15995
15996/**
15997 * Redraws all the clusters.
15998 */
15999MarkerClusterer.prototype.redraw_ = function() {
16000    this.createClusters_(0);
16001};
16002
16003
16004/**
16005 * Removes all clusters from the map. The markers are also removed from the map
16006 *  if <code>opt_hide</code> is set to <code>true</code>.
16007 *
16008 * @param {boolean} [opt_hide] Set to <code>true</code> to also remove the markers
16009 *  from the map.
16010 */
16011MarkerClusterer.prototype.resetViewport_ = function(opt_hide) {
16012    var i, marker;
16013    // Remove all the clusters
16014    for (i = 0; i < this.clusters_.length; i++) {
16015        this.clusters_[i].remove();
16016    }
16017    this.clusters_ = [];
16018
16019    // Reset the markers to not be added and to be removed from the map.
16020    for (i = 0; i < this.markers_.length; i++) {
16021        marker = this.markers_[i];
16022        marker.isAdded = false;
16023        if (opt_hide) {
16024            marker.setMap(null);
16025        }
16026    }
16027};
16028
16029
16030/**
16031 * Calculates the distance between two latlng locations in km.
16032 *
16033 * @param {google.maps.LatLng} p1 The first lat lng point.
16034 * @param {google.maps.LatLng} p2 The second lat lng point.
16035 * @return {number} The distance between the two points in km.
16036 * @see http://www.movable-type.co.uk/scripts/latlong.html
16037 */
16038MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) {
16039    var R = 6371; // Radius of the Earth in km
16040    var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
16041    var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
16042    var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
16043        Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) *
16044        Math.sin(dLon / 2) * Math.sin(dLon / 2);
16045    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
16046    var d = R * c;
16047    return d;
16048};
16049
16050
16051/**
16052 * Determines if a marker is contained in a bounds.
16053 *
16054 * @param {google.maps.Marker} marker The marker to check.
16055 * @param {google.maps.LatLngBounds} bounds The bounds to check against.
16056 * @return {boolean} True if the marker is in the bounds.
16057 */
16058MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) {
16059    return bounds.contains(marker.getPosition());
16060};
16061
16062
16063/**
16064 * Adds a marker to a cluster, or creates a new cluster.
16065 *
16066 * @param {google.maps.Marker} marker The marker to add.
16067 */
16068MarkerClusterer.prototype.addToClosestCluster_ = function(marker) {
16069    var i, d, cluster, center;
16070    var distance = 40000; // Some large number
16071    var clusterToAddTo = null;
16072    for (i = 0; i < this.clusters_.length; i++) {
16073        cluster = this.clusters_[i];
16074        center = cluster.getCenter();
16075        if (center) {
16076            d = this.distanceBetweenPoints_(center, marker.getPosition());
16077            if (d < distance) {
16078                distance = d;
16079                clusterToAddTo = cluster;
16080            }
16081        }
16082    }
16083
16084    if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) {
16085        clusterToAddTo.addMarker(marker);
16086    } else {
16087        cluster = new Cluster(this);
16088        cluster.addMarker(marker);
16089        this.clusters_.push(cluster);
16090    }
16091};
16092
16093
16094/**
16095 * Creates the clusters. This is done in batches to avoid timeout errors
16096 *  in some browsers when there is a huge number of markers.
16097 *
16098 * @param {number} iFirst The index of the first marker in the batch of
16099 *  markers to be added to clusters.
16100 */
16101MarkerClusterer.prototype.createClusters_ = function(iFirst) {
16102    var i, marker;
16103    var mapBounds;
16104    var cMarkerClusterer = this;
16105    if (!this.ready_) {
16106        return;
16107    }
16108
16109    // Cancel previous batch processing if we're working on the first batch:
16110    if (iFirst === 0) {
16111        /**
16112         * This event is fired when the <code>MarkerClusterer</code> begins
16113         *  clustering markers.
16114         * @name MarkerClusterer#clusteringbegin
16115         * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered.
16116         * @event
16117         */
16118        google.maps.event.trigger(this, "clusteringbegin", this);
16119
16120        if (typeof this.timerRefStatic !== "undefined") {
16121            clearTimeout(this.timerRefStatic);
16122            delete this.timerRefStatic;
16123        }
16124    }
16125
16126    // Get our current map view bounds.
16127    // Create a new bounds object so we don't affect the map.
16128    //
16129    // See Comments 9 & 11 on Issue 3651 relating to this workaround for a Google Maps bug:
16130    if (this.getMap().getZoom() > 3) {
16131        mapBounds = new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest
16131(),
16132            this.getMap().getBounds().getNorthEast());
16133    } else {
16134        mapBounds = new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472, -178.48388434375), new google.maps.LatLng(-85.08136444384544, 178.00048865625));
16135    }
16136    var bounds = this.getExtendedBounds(mapBounds);
16137
16138    var iLast = Math.min(iFirst + this.batchSize_, this.markers_.length);
16139
16140    for (i = iFirst; i < iLast; i++) {
16141        marker = this.markers_[i];
16142        if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {
16143            if (!this.ignoreHidden_ || (this.ignoreHidden_ && marker.getVisible())) {
16144                this.addToClosestCluster_(marker);
16145            }
16146        }
16147    }
16148
16149    if (iLast < this.markers_.length) {
16150        this.timerRefStatic = setTimeout(function() {
16151            cMarkerClusterer.createClusters_(iLast);
16152        }, 0);
16153    } else {
16154        delete this.timerRefStatic;
16155
16156        /**
16157         * This event is fired when the <code>MarkerClusterer</code> stops
16158         *  clustering markers.
16159         * @name MarkerClusterer#clusteringend
16160         * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered.
16161         * @event
16162         */
16163        google.maps.event.trigger(this, "clusteringend", this);
16164    }
16165};
16166
16167
16168/**
16169 * Extends an object's prototype by another's.
16170 *
16171 * @param {Object} obj1 The object to be extended.
16172 * @param {Object} obj2 The object to extend with.
16173 * @return {Object} The new extended object.
16174 * @ignore
16175 */
16176MarkerClusterer.prototype.extend = function(obj1, obj2) {
16177    return (function(object) {
16178        var property;
16179        for (property in object.prototype) {
16180            this.prototype[property] = object.prototype[property];
16181        }
16182        return this;
16183    }).apply(obj1, [obj2]);
16184};
16185
16186
16187/**
16188 * The default function for determining the label text and style
16189 * for a cluster icon.
16190 *
16191 * @param {Array.<google.maps.Marker>} markers The array of markers represented by the cluster.
16192 * @param {number} numStyles The number of marker styles available.
16193 * @return {ClusterIconInfo} The information resource for the cluster.
16194 * @constant
16195 * @ignore
16196 */
16197MarkerClusterer.CALCULATOR = function(markers, numStyles) {
16198    var index = 0;
16199    var title = "";
16200    var count = markers.length.toString();
16201
16202    var dv = count;
16203    while (dv !== 0) {
16204        dv = parseInt(dv / 10, 10);
16205        index++;
16206    }
16207
16208    index = Math.min(index, numStyles);
16209    return {
16210        text: count,
16211        index: index,
16212        title: title
16213    };
16214};
16215
16216
16217/**
16218 * The number of markers to process in one batch.
16219 *
16220 * @type {number}
16221 * @constant
16222 */
16223MarkerClusterer.BATCH_SIZE = 2000;
16224
16225
16226/**
16227 * The number of markers to process in one batch (IE only).
16228 *
16229 * @type {number}
16230 * @constant
16231 */
16232MarkerClusterer.BATCH_SIZE_IE = 500;
16233
16234
16235/**
16236 * The default root name for the marker cluster images.
16237 *
16238 * @type {string}
16239 * @constant
16240 */
16241MarkerClusterer.IMAGE_PATH = "http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclustererplus/images/m";
16242
16243
16244/**
16245 * The default extension name for the marker cluster images.
16246 *
16247 * @type {string}
16248 * @constant
16249 */
16250MarkerClusterer.IMAGE_EXTENSION = "png";
16251
16252
16253/**
16254 * The default array of sizes for the marker cluster images.
16255 *
16256 * @type {Array.<number>}
16257 * @constant
16258 */
16259MarkerClusterer.IMAGE_SIZES = [53, 56, 66, 78, 90];
16260/**
16261 * Accordion, jQuery Plugin
16262 *
16263 * This plugin provides an accordion with cookie support.
16264 *
16265 * Copyright (c) 2011 John Snyder (snyderplace.com)
16266 * @license http://www.snyderplace.com/accordion/license.txt New BSD
16267 * @version 1.1
16268 */
16269(function($) {
16270    $.fn.accordion = function(options) {
16271
16272        //firewalling
16273        if (!this || this.length < 1) {
16274            return this;
16275        }
16276
16277        initialize(this, options);
16278
16279    };
16280
16281    //create the initial accordion
16282    function initialize(obj, options) {
16283
16284        //build main options before element iteration
16285        var opts = $.extend({}, $.fn.accordion.defaults, options);
16286
16287        //store any opened default values to set cookie later
16288        var opened = '';
16289
16290        //iterate each matched object, bind, and open/close
16291        obj.each(function() {
16292            var $this = $(this);
16293            saveOpts($this, opts);
16294
16295            //bind it to the event
16296            if (opts.bind == 'mouseenter') {
16297                $this.bind('mouseenter', function(e) {
16298                    e.preventDefault();
16299                    toggle($this, opts);
16300                });
16301            }
16302
16303            //bind it to the event
16304            if (opts.bind == 'mouseover') {
16305                $this.bind('mouseover', function(e) {
16306                    e.preventDefault();
16307                    toggle($this, opts);
16308                });
16309            }
16310
16311            //bind it to the event
16312            if (opts.bind == 'click') {
16313                $this.bind('click', function(e) {
16314                    e.preventDefault();
16315                    toggle($this, opts);
16316                });
16317            }
16318
16319            //bind it to the event
16320            if (opts.bind == 'dblclick') {
16321                $this.bind('dblclick', function(e) {
16322                    e.preventDefault();
16323                    toggle($this, opts);
16324                });
16325            }
16326
16327            //initialize the panels
16328            //get the id for this element
16329            var id = $this.attr('id');
16330
16331            //if not using cookies, open defaults
16332            if (!useCookies(opts)) {
16333                //close it if not defaulted to open
16334                if (id != opts.defaultOpen) {
16335                    $this.addClass(opts.cssClose);
16336                    opts.loadClose($this, opts);
16337                } else { //its a default open, open it
16338                    $this.addClass(opts.cssOpen);
16339                    opts.loadOpen($this, opts);
16340                    opened = id;
16341                }
16342            } else { //can use cookies, use them now
16343                //has a cookie been set, this overrides default open
16344                if (issetCookie(opts)) {
16345                    if (inCookie(id, opts) === false) {
16346                        $this.addClass(opts.cssClose);
16347                        opts.loadClose($this, opts);
16348                    } else {
16349                        $this.addClass(opts.cssOpen);
16350                        opts.loadOpen($this, opts);
16351                        opened = id;
16352                    }
16353                } else { //a cookie hasn't been set open defaults
16354                    if (id != opts.defaultOpen) {
16355                        $this.addClass(opts.cssClose);
16356                        opts.loadClose($this, opts);
16357                    } else { //its a default open, open it
16358                        $this.addClass(opts.cssOpen);
16359                        opts.loadOpen($this, opts);
16360                        opened = id;
16361                    }
16362                }
16363            }
16364        });
16365
16366        //now that the loop is done, set the cookie
16367        if (opened.length > 0 && useCookies(opts)) {
16368            setCookie(opened, opts);
16369        } else { //there are none open, set cookie
16370            setCookie('', opts);
16371        }
16372
16373        return obj;
16374    };
16375
16376    //load opts from object
16377    function loadOpts($this) {
16378        return $this.data('accordion-opts');
16379    }
16380
16381    //save opts into object
16382    function saveOpts($this, opts) {
16383        return $this.data('accordion-opts', opts);
16384    }
16385
16386    //hides a accordion panel
16387    function close(opts) {
16388        opened = $(document).find('.' + opts.cssOpen);
16389        $.each(opened, function() {
16390            //give the proper class to the linked element
16391            $(this).addClass(opts.cssClose).removeClass(opts.cssOpen);
16392            opts.animateClose($(this), opts);
16393        });
16394    }
16395
16396    //opens a accordion panel
16397    function open($this, opts) {
16398        close(opts);
16399        //give the proper class to the linked element
16400        $this.removeClass(opts.cssClose).addClass(opts.cssOpen);
16401
16402        //open the element
16403        opts.animateOpen($this, opts);
16404
16405        //do cookies if plugin available
16406        if (useCookies(opts)) {
16407            // split the cookieOpen string by ","
16408            id = $this.attr('id');
16409            setCookie(id, opts);
16410        }
16411    }
16412
16413    //toggle a accordion on an event
16414    function toggle($this, opts) {
16415        // close the only open item
16416        if ($this.hasClass(opts.cssOpen)) {
16417            close(opts);
16418            //do cookies if plugin available
16419            if (useCookies(opts)) {
16420                // split the cookieOpen string by ","
16421                setCookie('', opts);
16422            }
16423            return false;
16424        }
16425        close(opts);
16426        //open a closed element
16427        open($this, opts);
16428        return false;
16429    }
16430
16431    //use cookies?
16432    function useCookies(opts) {
16433        //return false if cookie plugin not present or if a cookie name is not provided
16434        if (!$.cookie || opts.cookieName == '') {
16435            return false;
16436        }
16437
16438        //we can use cookies
16439        return true;
16440    }
16441
16442    //set a cookie
16443    function setCookie(value, opts) {
16444        //can use the cookie plugin
16445        if (!useCookies(opts)) { //no, quit here
16446            return false;
16447        }
16448
16449        //cookie plugin is available, lets set the cookie
16450        $.cookie(opts.cookieName, value, opts.cookieOptions);
16451    }
16452
16453    //check if a accordion is in the cookie
16454    function inCookie(value, opts) {
16455        //can use the cookie plugin
16456        if (!useCookies(opts)) {
16457            return false;
16458        }
16459
16460        //if its not there we don't need to remove from it
16461        if (!issetCookie(opts)) { //quit here, don't have a cookie
16462            return false;
16463        }
16464
16465        //unescape it
16466        cookie = unescape($.cookie(opts.cookieName));
16467
16468        //is this value in the cookie arrray
16469        if (cookie != value) { //no, quit here
16470            return false;
16471        }
16472
16473        return true;
16474    }
16475
16476    //check if a cookie is set
16477    function issetCookie(opts) {
16478        //can we use the cookie plugin
16479        if (!useCookies(opts)) { //no, quit here
16480            return false;
16481        }
16482
16483        //is the cookie set
16484        if ($.cookie(opts.cookieName) == null) { //no, quit here
16485            return false;
16486        }
16487
16488        return true;
16489    }
16490
16491    // settings
16492    $.fn.accordion.defaults = {
16493        cssClose: 'accordion-close', //class you want to assign to a closed accordion header
16494        cssOpen: 'accordion-open', //class you want to assign an opened accordion header
16495        cookieName: 'accordion', //name of the cookie you want to set for this accordion
16496        cookieOptions: { //cookie options, see cookie plugin for details
16497            path: '/',
16498            expires: 7,
16499            domain: '',
16500            secure: ''
16501        },
16502        defaultOpen: '', //id that you want opened by default
16503        speed: 'slow', //speed of the slide effect
16504        bind: 'click', //event to bind to, supports click, dblclick, mouseover and mouseenter
16505        animateOpen: function(elem, opts) { //replace the standard slideDown with custom function
16506            elem.next().stop(true, true).slideDown(opts.speed);
16507        },
16508        animateClose: function(elem, opts) { //replace the standard slideUp with custom function
vendor: 10,614 bytes, lines 16509-16547
16509            elem.next().stop(true, true).slideUp(opts.speed);
16510        },
16511        loadOpen: function(elem, opts) { //replace the default open state with custom function
16512            elem.next().show();
16513        },
16514        loadClose: function(elem, opts) { //replace the default close state with custom function
16515            elem.next().hide();
16516        }
16517    };
16518})(jQuery);
16519/*
16520 * Copyright 2016 Small Batch, Inc.
16521 *
16522 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
16523 * use this file except in compliance with the License. You may obtain a copy of
16524 * the License at
16525 *
16526 * http://www.apache.org/licenses/LICENSE-2.0
16527 *
16528 * Unless required by applicable law or agreed to in writing, software
16529 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16530 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16531 * License for the specific language governing permissions and limitations under
16532 * the License.
16533 */
16534/* Web Font Loader v1.6.26 - (c) Adobe Systems, Google. License: Apache 2.0 */(function(){function aa(a,b,c){return a.call.apply(a.bind,arguments)}function ba(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}}function p(a,b,c){p=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?aa:ba;return p.apply(null,arguments)}var q=Date.now||function(){return+new Date};function ca(a,b){this.a=a;this.m=b||a;this.c=this.m.document}var da=!!window.FontFace;function t(a,b,c,d){b=a.c.createElement(b);if(c)for(var e in c)c.hasOwnProperty(e)&&("style"==e?b.style.cssText=c[e]:b.setAttribute(e,c[e]));d&&b.appendChild(a.c.createTextNode(d));return b}function u(a,b,c){a=a.c.getElementsByTagName(b)[0];a||(a=document.documentElement);a.insertBefore(c,a.lastChild)}function v(a){a.parentNode&&a.parentNode.removeChild(a)}
16535function w(a,b,c){b=b||[];c=c||[];for(var d=a.className.split(/\s+/),e=0;e<b.length;e+=1){for(var f=!1,g=0;g<d.length;g+=1)if(b[e]===d[g]){f=!0;break}f||d.push(b[e])}b=[];for(e=0;e<d.length;e+=1){f=!1;for(g=0;g<c.length;g+=1)if(d[e]===c[g]){f=!0;break}f||b.push(d[e])}a.className=b.join(" ").replace(/\s+/g," ").replace(/^\s+|\s+$/,"")}function y(a,b){for(var c=a.className.split(/\s+/),d=0,e=c.length;d<e;d++)if(c[d]==b)return!0;return!1}
16536function z(a){if("string"===typeof a.f)return a.f;var b=a.m.location.protocol;"about:"==b&&(b=a.a.location.protocol);return"https:"==b?"https:":"http:"}function ea(a){return a.m.location.hostname||a.a.location.hostname}
16537function A(a,b,c){function d(){k&&e&&f&&(k(g),k=null)}b=t(a,"link",{rel:"stylesheet",href:b,media:"all"});var e=!1,f=!0,g=null,k=c||null;da?(b.onload=function(){e=!0;d()},b.onerror=function(){e=!0;g=Error("Stylesheet failed to load");d()}):setTimeout(function(){e=!0;d()},0);u(a,"head",b)}
16538function B(a,b,c,d){var e=a.c.getElementsByTagName("head")[0];if(e){var f=t(a,"script",{src:b}),g=!1;f.onload=f.onreadystatechange=function(){g||this.readyState&&"loaded"!=this.readyState&&"complete"!=this.readyState||(g=!0,c&&c(null),f.onload=f.onreadystatechange=null,"HEAD"==f.parentNode.tagName&&e.removeChild(f))};e.appendChild(f);setTimeout(function(){g||(g=!0,c&&c(Error("Script load timeout")))},d||5E3);return f}return null};function C(){this.a=0;this.c=null}function D(a){a.a++;return function(){a.a--;E(a)}}function F(a,b){a.c=b;E(a)}function E(a){0==a.a&&a.c&&(a.c(),a.c=null)};function G(a){this.a=a||"-"}G.prototype.c=function(a){for(var b=[],c=0;c<arguments.length;c++)b.push(arguments[c].replace(/[\W_]+/g,"").toLowerCase());return b.join(this.a)};function H(a,b){this.c=a;this.f=4;this.a="n";var c=(b||"n4").match(/^([nio])([1-9])$/i);c&&(this.a=c[1],this.f=parseInt(c[2],10))}function fa(a){return I(a)+" "+(a.f+"00")+" 300px "+J(a.c)}function J(a){var b=[];a=a.split(/,\s*/);for(var c=0;c<a.length;c++){var d=a[c].replace(/['"]/g,"");-1!=d.indexOf(" ")||/^\d/.test(d)?b.push("'"+d+"'"):b.push(d)}return b.join(",")}function K(a){return a.a+a.f}function I(a){var b="normal";"o"===a.a?b="oblique":"i"===a.a&&(b="italic");return b}
16539function ga(a){var b=4,c="n",d=null;a&&((d=a.match(/(normal|oblique|italic)/i))&&d[1]&&(c=d[1].substr(0,1).toLowerCase()),(d=a.match(/([1-9]00|normal|bold)/i))&&d[1]&&(/bold/i.test(d[1])?b=7:/[1-9]00/.test(d[1])&&(b=parseInt(d[1].substr(0,1),10))));return c+b};function ha(a,b){this.c=a;this.f=a.m.document.documentElement;this.h=b;this.a=new G("-");this.j=!1!==b.events;this.g=!1!==b.classes}function ia(a){a.g&&w(a.f,[a.a.c("wf","loading")]);L(a,"loading")}function M(a){if(a.g){var b=y(a.f,a.a.c("wf","active")),c=[],d=[a.a.c("wf","loading")];b||c.push(a.a.c("wf","inactive"));w(a.f,c,d)}L(a,"inactive")}function L(a,b,c){if(a.j&&a.h[b])if(c)a.h[b](c.c,K(c));else a.h[b]()};function ja(){this.c={}}function ka(a,b,c){var d=[],e;for(e in b)if(b.hasOwnProperty(e)){var f=a.c[e];f&&d.push(f(b[e],c))}return d};function N(a,b){this.c=a;this.f=b;this.a=t(this.c,"span",{"aria-hidden":"true"},this.f)}function O(a){u(a.c,"body",a.a)}function P(a){return"display:block;position:absolute;top:-9999px;left:-9999px;font-size:300px;width:auto;height:auto;line-height:normal;margin:0;padding:0;font-variant:normal;white-space:nowrap;font-family:"+J(a.c)+";"+("font-style:"+I(a)+";font-weight:"+(a.f+"00")+";")};function Q(a,b,c,d,e,f){this.g=a;this.j=b;this.a=d;this.c=c;this.f=e||3E3;this.h=f||void 0}Q.prototype.start=function(){var a=this.c.m.document,b=this,c=q(),d=new Promise(function(d,e){function k(){q()-c>=b.f?e():a.fonts.load(fa(b.a),b.h).then(function(a){1<=a.length?d():setTimeout(k,25)},function(){e()})}k()}),e=new Promise(function(a,d){setTimeout(d,b.f)});Promise.race([e,d]).then(function(){b.g(b.a)},function(){b.j(b.a)})};function R(a,b,c,d,e,f,g){this.v=a;this.B=b;this.c=c;this.a=d;this.s=g||"BESbswy";this.f={};this.w=e||3E3;this.u=f||null;this.o=this.j=this.h=this.g=null;this.g=new N(this.c,this.s);this.h=new N(this.c,this.s);this.j=new N(this.c,this.s);this.o=new N(this.c,this.s);a=new H(this.a.c+",serif",K(this.a));a=P(a);this.g.a.style.cssText=a;a=new H(this.a.c+",sans-serif",K(this.a));a=P(a);this.h.a.style.cssText=a;a=new H("serif",K(this.a));a=P(a);this.j.a.style.cssText=a;a=new H("sans-serif",K(this.a));a=
16540P(a);this.o.a.style.cssText=a;O(this.g);O(this.h);O(this.j);O(this.o)}var S={D:"serif",C:"sans-serif"},T=null;function U(){if(null===T){var a=/AppleWebKit\/([0-9]+)(?:\.([0-9]+))/.exec(window.navigator.userAgent);T=!!a&&(536>parseInt(a[1],10)||536===parseInt(a[1],10)&&11>=parseInt(a[2],10))}return T}R.prototype.start=function(){this.f.serif=this.j.a.offsetWidth;this.f["sans-serif"]=this.o.a.offsetWidth;this.A=q();la(this)};
16541function ma(a,b,c){for(var d in S)if(S.hasOwnProperty(d)&&b===a.f[S[d]]&&c===a.f[S[d]])return!0;return!1}function la(a){var b=a.g.a.offsetWidth,c=a.h.a.offsetWidth,d;(d=b===a.f.serif&&c===a.f["sans-serif"])||(d=U()&&ma(a,b,c));d?q()-a.A>=a.w?U()&&ma(a,b,c)&&(null===a.u||a.u.hasOwnProperty(a.a.c))?V(a,a.v):V(a,a.B):na(a):V(a,a.v)}function na(a){setTimeout(p(function(){la(this)},a),50)}function V(a,b){setTimeout(p(function(){v(this.g.a);v(this.h.a);v(this.j.a);v(this.o.a);b(this.a)},a),0)};function W(a,b,c){this.c=a;this.a=b;this.f=0;this.o=this.j=!1;this.s=c}var X=null;W.prototype.g=function(a){var b=this.a;b.g&&w(b.f,[b.a.c("wf",a.c,K(a).toString(),"active")],[b.a.c("wf",a.c,K(a).toString(),"loading"),b.a.c("wf",a.c,K(a).toString(),"inactive")]);L(b,"fontactive",a);this.o=!0;oa(this)};
16542W.prototype.h=function(a){var b=this.a;if(b.g){var c=y(b.f,b.a.c("wf",a.c,K(a).toString(),"active")),d=[],e=[b.a.c("wf",a.c,K(a).toString(),"loading")];c||d.push(b.a.c("wf",a.c,K(a).toString(),"inactive"));w(b.f,d,e)}L(b,"fontinactive",a);oa(this)};function oa(a){0==--a.f&&a.j&&(a.o?(a=a.a,a.g&&w(a.f,[a.a.c("wf","active")],[a.a.c("wf","loading"),a.a.c("wf","inactive")]),L(a,"active")):M(a.a))};function pa(a){this.j=a;this.a=new ja;this.h=0;this.f=this.g=!0}pa.prototype.load=function(a){this.c=new ca(this.j,a.context||this.j);this.g=!1!==a.events;this.f=!1!==a.classes;qa(this,new ha(this.c,a),a)};
16543function ra(a,b,c,d,e){var f=0==--a.h;(a.f||a.g)&&setTimeout(function(){var a=e||null,k=d||null||{};if(0===c.length&&f)M(b.a);else{b.f+=c.length;f&&(b.j=f);var h,m=[];for(h=0;h<c.length;h++){var l=c[h],n=k[l.c],r=b.a,x=l;r.g&&w(r.f,[r.a.c("wf",x.c,K(x).toString(),"loading")]);L(r,"fontloading",x);r=null;null===X&&(X=window.FontFace?(x=/Gecko.*Firefox\/(\d+)/.exec(window.navigator.userAgent))?42<parseInt(x[1],10):!0:!1);X?r=new Q(p(b.g,b),p(b.h,b),b.c,l,b.s,n):r=new R(p(b.g,b),p(b.h,b),b.c,l,b.s,a,
16544n);m.push(r)}for(h=0;h<m.length;h++)m[h].start()}},0)}function qa(a,b,c){var d=[],e=c.timeout;ia(b);var d=ka(a.a,c,a.c),f=new W(a.c,b,e);a.h=d.length;b=0;for(c=d.length;b<c;b++)d[b].load(function(b,d,c){ra(a,f,b,d,c)})};function sa(a,b){this.c=a;this.a=b}function ta(a,b,c){var d=z(a.c);a=(a.a.api||"fast.fonts.net/jsapi").replace(/^.*http(s?):(\/\/)?/,"");return d+"//"+a+"/"+b+".js"+(c?"?v="+c:"")}
16545sa.prototype.load=function(a){function b(){if(f["__mti_fntLst"+d]){var c=f["__mti_fntLst"+d](),e=[],h;if(c)for(var m=0;m<c.length;m++){var l=c[m].fontfamily;void 0!=c[m].fontStyle&&void 0!=c[m].fontWeight?(h=c[m].fontStyle+c[m].fontWeight,e.push(new H(l,h))):e.push(new H(l))}a(e)}else setTimeout(function(){b()},50)}var c=this,d=c.a.projectId,e=c.a.version;if(d){var f=c.c.m;B(this.c,ta(c,d,e),function(e){e?a([]):(f["__MonotypeConfiguration__"+d]=function(){return c.a},b())}).id="__MonotypeAPIScript__"+
16546d}else a([])};function ua(a,b){this.c=a;this.a=b}ua.prototype.load=function(a){var b,c,d=this.a.urls||[],e=this.a.families||[],f=this.a.testStrings||{},g=new C;b=0;for(c=d.length;b<c;b++)A(this.c,d[b],D(g));var k=[];b=0;for(c=e.length;b<c;b++)if(d=e[b].split(":"),d[1])for(var h=d[1].split(","),m=0;m<h.length;m+=1)k.push(new H(d[0],h[m]));else k.push(new H(d[0]));F(g,function(){a(k,f)})};function va(a,b,c){a?this.c=a:this.c=b+wa;this.a=[];this.f=[];this.g=c||""}var wa="//fonts.googleapis.com/css";function xa(a,b){for(var c=b.length,d=0;d<c;d++){var e=b[d].split(":");3==e.length&&a.f.push(e.pop());var f="";2==e.length&&""!=e[1]&&(f=":");a.a.push(e.join(f))}}
16547function ya(a){if(0==a.a.length)throw Error("No fonts to load!");if(-1!=a.c.indexOf("kit="))return a.c;for(var b=a.a.length,c=[],d=0;d<b;d++)c.push(a.a[d].replace(/ /g,"+"));b=a.c+"?family="+c.join("%7C");0<a.f.length&&(b+="&subset="+a.f.join(","));0<a.g.length&&(b+="&text="+encodeURIComponent(a.g));return b};function za(a){this.f=a;this.a=[];this.c={}}
16548var Aa={latin:"BESbswy","latin-ext":"\u00e7\u00f6\u00fc\u011f\u015f",cyrillic:"\u0439\u044f\u0416",greek:"\u03b1\u03b2\u03a3",khmer:"\u1780\u1781\u1782",Hanuman:"\u1780\u1781\u1782"},Ba={thin:"1",extralight:"2","extra-light":"2",ultralight:"2","ultra-light":"2",light:"3",regular:"4",book:"4",medium:"5","semi-bold":"6",semibold:"6","demi-bold":"6",demibold:"6",bold:"7","extra-bold":"8",extrabold:"8","ultra-bold":"8",ultrabold:"8",black:"9",heavy:"9",l:"3",r:"4",b:"7"},Ca={i:"i",italic:"i",n:"n",normal:"n"},
16549Da=/^(thin|(?:(?:extra|ultra)-?)?light|regular|book|medium|(?:(?:semi|demi|extra|ultra)-?)?bold|black|heavy|l|r|b|[1-9]00)?(n|i|normal|italic)?$/;
16550function Ea(a){for(var b=a.f.length,c=0;c<b;c++){var d=a.f[c].split(":"),e=d[0].replace(/\+/g," "),f=["n4"];if(2<=d.length){var g;var k=d[1];g=[];if(k)for(var k=k.split(","),h=k.length,m=0;m<h;m++){var l;l=k[m];if(l.match(/^[\w-]+$/)){var n=Da.exec(l.toLowerCase());if(null==n)l="";else{l=n[2];l=null==l||""==l?"n":Ca[l];n=n[1];if(null==n||""==n)n="4";else var r=Ba[n],n=r?r:isNaN(n)?"4":n.substr(0,1);l=[l,n].join("")}}else l="";l&&g.push(l)}0<g.length&&(f=g);3==d.length&&(d=d[2],g=[],d=d?d.split(","):
16551g,0<d.length&&(d=Aa[d[0]])&&(a.c[e]=d))}a.c[e]||(d=Aa[e])&&(a.c[e]=d);for(d=0;d<f.length;d+=1)a.a.push(new H(e,f[d]))}};function Fa(a,b){this.c=a;this.a=b}var Ga={Arimo:!0,Cousine:!0,Tinos:!0};Fa.prototype.load=function(a){var b=new C,c=this.c,d=new va(this.a.api,z(c),this.a.text),e=this.a.families;xa(d,e);var f=new za(e);Ea(f);A(c,ya(d),D(b));F(b,function(){a(f.a,f.c,Ga)})};function Ha(a,b){this.c=a;this.a=b}Ha.prototype.load=function(a){var b=this.a.id,c=this.c.m;b?B(this.c,(this.a.api||"https://use.typekit.net")+"/"+b+".js",function(b){if(b)a([]);else if(c.Typekit&&c.Typekit.config&&c.Typekit.config.fn){b=c.Typekit.config.fn;for(var e=[],f=0;f<b.length;f+=2)for(var g=b[f],k=b[f+1],h=0;h<k.length;h++)e.push(new H(g,k[h]));try{c.Typekit.load({events:!1,classes:!1,async:!0})}catch(m){}a(e)}},2E3):a([])};function Ia(a,b){this.c=a;this.f=b;this.a=[]}Ia.prototype.load=function(a){var b=this.f.id,c=this.c.m,d=this;b?(c.__webfontfontdeckmodule__||(c.__webfontfontdeckmodule__={}),c.__webfontfontdeckmodule__[b]=function(b,c){for(var g=0,k=c.fonts.length;g<k;++g){var h=c.fonts[g];d.a.push(new H(h.name,ga("font-weight:"+h.weight+";font-style:"+h.style)))}a(d.a)},B(this.c,z(this.c)+(this.f.api||"//f.fontdeck.com/s/css/js/")+ea(this.c)+"/"+b+".js",function(b){b&&a([])})):a([])};var Y=new pa(window);Y.a.c.custom=function(a,b){return new ua(b,a)};Y.a.c.fontdeck=function(a,b){return new Ia(b,a)};Y.a.c.monotype=function(a,b){return new sa(b,a)};Y.a.c.typekit=function(a,b){return new Ha(b,a)};Y.a.c.google=function(a,b){return new Fa(b,a)};var Z={load:p(Y.load,Y)};"function"===typeof define&&define.amd?define(function(){return Z}):"undefined"!==typeof module&&module.exports?module.exports=Z:(window.WebFont=Z,window.WebFontConfig&&Y.load(window.WebFontConfig));}());
16552
16553(function($) {
16554    "use strict";
16555
16556    $.fn.pagination = function(maxentries, opts) {
16557        opts = jQuery.extend({
16558            items_per_page: 10,
16559            num_display_entries: 10,
16560            current_page: 0,
16561            num_edge_entries: 0,
16562            link_to: "#",
16563            prev_text: "Prev",
16564            next_text: "Next",
16565            ellipse_text: "...",
16566            prev_show_always: false,
16567            next_show_always: false,
16568            data_source: '',
16569            listing_container: '',
16570
16571            callback: function() {
16572                return false;
16573            }
16574        }, opts || {});
16575
16576        return this.each(function() {
16577            /**
16578             * Calculate the maximum number of pages
16579             */
16580            function numPages() {
16581                return Math.ceil(maxentries / opts.items_per_page);
16582            }
16583
16584            /**
16585             * Calculate start and end point of pagination links depending on 
16586             * current_page and num_display_entries.
16587             * @return {Array}
16588             */
16589            function getInterval() {
16590                var ne_half = Math.ceil(opts.num_display_entries / 2);
16591                var np = numPages();
16592                var upper_limit = np - opts.num_display_entries;
16593                var start = current_page > ne_half ? Math.max(Math.min(current_page - ne_half, upper_limit), 0) : 0;
16594                var end = current_page > ne_half ? Math.min(current_page + ne_half, np) : Math.min(opts.num_display_entries, np);
16595                return [start, end];
16596            }
16597
16598            /**
16599             * This is the event handling function for the pagination links. 
16600             * @param {int} page_id The new page number
16601             */
16602            function pageSelected(page_id, evt) {
16603                current_page = page_id;
16604                drawLinks();
16605                var continuePropagation = opts.callback(page_id, panel);
16606                if (!continuePropagation) {
16607                    if (evt.stopPropagation) {
16608                        evt.stopPropagation();
16609                    } else {
16610                        evt.cancelBubble = true;
16611                    }
16612                }
16613                return continuePropagation;
16614            }
16615
16616            /**
16617             * This function inserts the pagination links into the container element
16618             */
16619            function drawLinks() {
16620                panel.empty();
16621                var interval = getInterval();
16622                var np = numPages();
16623                // This helper function returns a handler function that calls pageSelected with the right page_id
16624                var getClickHandler = function(page_id) {
16625                    return function(evt) {
16626                        return pageSelected(page_id, evt);
16627                    }
16628                }
16629                // Helper function for generating a single link (or a span tag if it's the current page)
16630                var appendItem = function(page_id, appendopts) {
16631                    page_id = page_id < 0 ? 0 : (page_id < np ? page_id : np - 1); // Normalize page id to sane value
16632                    appendopts = jQuery.extend({
16633                        text: page_id + 1,
16634                        classes: ""
16635                    }, appendopts || {});
16636                    if (page_id == current_page) {
16637                        var lnk = jQuery("<span class='current'>" + (appendopts.text) + "</span>");
16638                    } else {
16639                        var lnk = jQuery("<a>" + (appendopts.text) + "</a>")
16640                            .bind("click", getClickHandler(page_id))
16641                            .attr('href', opts.link_to.replace(/__id__/, page_id));
16642
16643
16644                    }
16645                    if (appendopts.classes) {
16646                        lnk.addClass(appendopts.classes);
16647                    }
16648                    panel.append(lnk);
16649                }
16650                // Generate "Previous"-Link
16651                if (opts.prev_text && (current_page > 0 || opts.prev_show_always)) {
16652                    appendItem(current_page - 1, {
16653                        text: opts.prev_text,
16654                        classes: "prev"
16655                    });
16656                }
16657                // Generate starting points
16658
16659                if (np > 1) {
16660
16661                    if (interval[0] > 0 && opts.num_edge_entries > 0) {
16662                        var end = Math.min(opts.num_edge_entries, interval[0]);
16663                        for (var i = 0; i < end; i++) {
16664                            appendItem(i);
16665                        }
16666                        if (opts.num_edge_entries < interval[0] && opts.ellipse_text) {
16667                            jQuery("<span>" + opts.ellipse_text + "</span>").appendTo(panel);
16668                        }
16669                    }
16670                    // Generate interval links
16671                    for (var i = interval[0]; i < interval[1]; i++) {
16672                        appendItem(i);
16673                    }
16674                    // Generate ending points
16675                    if (interval[1] < np && opts.num_edge_entries > 0) {
16676                        if (np - opts.num_edge_entries > interval[1] && opts.ellipse_text) {
16677                            jQuery("<span>" + opts.ellipse_text + "</span>").appendTo(panel);
16678                        }
16679                        var begin = Math.max(np - opts.num_edge_entries, interval[1]);
16680                        for (var i = begin; i < np; i++) {
16681                            appendItem(i);
16682                        }
16683
16684                    }
16685
16686                }
16687
16688
16689                // Generate "Next"-Link
16690                if (opts.next_text && (current_page < np - 1 || opts.next_show_always)) {
16691                    appendItem(current_page + 1, {
16692                        text: opts.next_text,
16693                        classes: "next"
16694                    });
16695                }
16696            }
16697
16698            // Extract current_page from options
16699            var current_page = opts.current_page;
16700            // Create a sane value for maxentries and items_per_page
16701            maxentries = (!maxentries || maxentries < 0) ? 1 : maxentries;
16702            opts.items_per_page = (!opts.items_per_page || opts.items_per_page < 0) ? 1 : opts.items_per_page;
16703            // Store DOM element for easy access from all inner functions
16704            var panel = jQuery(this);
16705            // Attach control functions to the DOM element 
16706            this.selectPage = function(page_id,evt) {
16707                pageSelected(page_id,evt);
16708            }
16709            this.prevPage = function() {
16710                if (current_page > 0) {
16711                    pageSelected(current_page - 1);
16712                    return true;
16713                } else {
16714                    return false;
16715                }
16716            }
16717            this.nextPage = function() {
16718                if (current_page < numPages() - 1) {
16719                    pageSelected(current_page + 1);
16720                    return true;
16721                } else {
16722                    return false;
16723                }
16724            }
16725            // When all initialisation is done, draw the links
16726            drawLinks();
16727            // call callback function
16728            opts.callback(current_page, this);
16729        });
16730    }
16731
16732    var masonry;
16733    $(document).ready(function($) {
16734        $('.gm-style-iw').parent().parent().addClass('wpgmp_infowindow_css');
16735        $("div.scroll-pane").jScrollPane();
16736
16737        //Social share popups
16738        $(".wpgmp-social-share").on("click", function(e) {
16739            e.preventDefault();
16740            var url = $(this).attr("href");
16741            var check_url = url.split('&url=');
16742            if (check_url[1] == '') {
16743                url += window.location.href;
16744            }
16745            var width = 500,
16746                height = 300;
16747            var left = (screen.width / 2) - (width / 2),
16748                top = (screen.height / 2) - (height / 2);
16749            window.open(
16750                url,
16751                "",
16752                "menubar=no,toolbar=no,resizable=yes,scrollbars=yes,width=" 
16752+ width + ",height=" + height + ",top=" + top + ",left=" + left
16753            );
16754        });
16755        var masonry_ref = '';
16756        $(document).on('click','.categories_filter span a', function(e) {
16757            var ref = $(this).closest(".wpgmp_map_container");
16758            var elem = $(ref).find('div.wpgmp_categories');
16759
16760            if ($(this).hasClass('wpgmp_grid')) {
16761                $(this).addClass('active');
16762                $(ref).find('span a.wpgmp_list').removeClass('active');
16763                $(ref).find('span a.wpgmp_print').removeClass('active');
16764                elem.fadeOut(500, function() {
16765                    elem.removeClass('wpgmp_listing_list').addClass('wpgmp_listing_grid');
16766                    elem.show();
16767                    try {
16768
16769
16770                        var container = $(ref).find('.wpgmp_listing_grid');
16771                        if (container) {
16772
16773                            var msnry = $(container).data('masonry');
16774                            if (msnry) {
16775                                msnry.destroy();
16776                            }
16777
16778                            var $grid = $(container).imagesLoaded(function() {
16779                                // init Masonry after all images have loaded
16780                                if( ! $.isFunction($.fn.agfgm_grid_view) ) {
16781
16782                                $grid.masonry({
16783                                    itemSelector: '.wpgmp_listing_grid .wpgmp_locations',
16784                                    columnWidth: '.wpgmp_listing_grid .wpgmp_locations',
16785                                });
16786                            }
16787
16788                            });
16789
16790                        }
16791
16792                    } catch (err) {
16793                        console.log(err);
16794                    }
16795
16796                });
16797            } else if ($(this).hasClass('wpgmp_list')) {
16798                if ($(ref).find('.wpgmp_listing_grid').hasClass('masonry')) {
16799                    var msnry = $(ref).find('.wpgmp_listing_grid').data('masonry');
16800                    msnry.destroy();
16801                }
16802
16803                $(this).addClass('active');
16804                $(ref).find('span a.wpgmp_grid').removeClass('active');
16805                $(ref).find('span a.wpgmp_print').removeClass('active');
16806                elem.fadeOut(500, function() {
16807                    elem.removeClass('wpgmp_listing_grid').addClass('wpgmp_listing_list');
16808                    $(ref).find('.wpgmp_locations').equalHeightGrid();
16809                    elem.fadeIn(500);
16810                });
16811            } else if ($(this).hasClass('wpgmp_print')) {
16812                $(this).addClass('active');
16813                $(ref).find('span a.wpgmp_grid').removeClass('active');
16814                $(ref).find('span a.wpgmp_list').removeClass('active');
16815                $(ref).find('span a.wpgmp_print').removeClass('active');
16816            }
16817        });
16818
16819    });
16820
16821    $.fn.equalHeight = function() {
16822        var heights = [];
16823        $.each(this, function(i, element) {
16824            $element = $(element);
16825            var element_height;
16826            // Should we include the elements padding in it's height?
16827            var includePadding = ($element.css('box-sizing') == 'border-box') || ($element.css('-moz-box-sizing') == 'border-box');
16828            if (includePadding) {
16829                element_height = $element.innerHeight();
16830            } else {
16831                element_height = $element.height();
16832            }
16833            heights.push(element_height);
16834        });
16835        this.css('height', Math.max.apply(window, heights) + 'px');
16836        return this;
16837    };
16838
16839    $.fn.equalHeightGrid = function(columns) {
16840        var $tiles = this;
16841        $tiles.css('height', 'auto');
16842        for (var i = 0; i < $tiles.length; i++) {
16843            if (i % columns === 0) {
16844                var row = $($tiles[i]);
16845                for (var n = 1; n < columns; n++) {
16846                    row = row.add($tiles[i + n]);
16847                }
16848                row.equalHeight();
16849            }
16850        }
16851        return this;
16852    };
16853
16854    $.fn.detectGridColumns = function() {
16855        var offset = 0,
16856            cols = 0;
16857        this.each(function(i, elem) {
16858            var elem_offset = $(elem).offset().top;
16859            if (offset === 0 || elem_offset == offset) {
16860                cols++;
16861                offset = elem_offset;
16862            } else {
16863                return false;
16864            }
16865        });
16866        return cols;
16867    };
16868
16869    $.fn.responsiveEqualHeightGrid = function() {
16870        var _this = this;
16871
16872        function syncHeights() {
16873            var cols = _this.detectGridColumns();
16874            _this.equalHeightGrid(cols);
16875        }
16876        $(window).bind('resize load', syncHeights);
16877        syncHeights();
16878        return this;
16879    };
16880
16881    var re = /([^&=]+)=?([^&]*)/g;
16882    var decodeRE = /\+/g; // Regex for replacing addition symbol with a space
16883    var decode = function(str) {
16884        return decodeURIComponent(str.replace(decodeRE, " "));
16885    };
16886    $.parseParams = function(query) {
16887        var params = {},
16888            e;
16889        while (e = re.exec(query)) {
16890            var k = decode(e[1]),
16891                v = decode(e[2]);
16892            if (k.substring(k.length - 2) === '[]') {
16893                k = k.substring(0, k.length - 2);
16894                (params[k] || (params[k] = [])).push(v);
16895            } else params[k] = v;
16896        }
16897        return params;
16898    };
16899
16900
16901})(jQuery);
16902
16903function wpgmp_set_consent_cookies() {
16904    wpgmp_set_cookie('wpgmp_show_map', "yes", wpgmp_flocal.days_to_remember);
16905    window.location.reload();
16906}
16907
16908function wpgmp_set_noconsent_cookies() {
16909    wpgmp_set_cookie('wpgmp_show_map', "no", wpgmp_flocal.days_to_remember);
16910    window.location.reload();
16911}
16912
16913function wpgmp_get_cookie(cname) {
16914    var name = cname + "=";
16915    var ca = document.cookie.split(';');
16916    for (var i = 0; i < ca.length; i++) {
16917        var c = ca[i];
16918        while (c.charAt(0) == ' ') {
16919            c = c.substring(1);
16920        }
16921        if (c.indexOf(name) == 0) {
16922            return c.substring(name.length, c.length);
16923        }
16924    }
16925    return null;
16926}
16927
16928function wpgmp_set_cookie(cname, cvalue, exdays) {
16929    var d = new Date();
16930    d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
16931    var expires = "expires=" + d.toUTCString();
16932    document.cookie = cname + "=" + cvalue + "; " + expires + "; path=/";
16933}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.