PageSourceSearch

https://caribepay.com/Areas/Transaction/Content/plugins/datatables…/TableTools/js/dataTables.tableTools.js

js caribepay.com collected 2026-10-02 16:00:50 UTC 84,578 bytes, 3,230 lines download raw bytes

1/*! TableTools 2.2.4
2 * 2009-2015 SpryMedia Ltd - datatables.net/license
3 *
4 * ZeroClipboard 1.0.4
5 * Author: Joseph Huckaby - MIT licensed
6 */
7
8/**
9 * @summary     TableTools
10 * @description Tools and buttons for DataTables
11 * @version     2.2.4
12 * @file        dataTables.tableTools.js
13 * @author      SpryMedia Ltd (www.sprymedia.co.uk)
14 * @contact     www.sprymedia.co.uk/contact
15 * @copyright   Copyright 2009-2015 SpryMedia Ltd.
16 *
17 * This source file is free software, available under the following license:
18 *   MIT license - http://datatables.net/license/mit
19 *
20 * This source file is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
23 *
24 * For details please refer to: http://www.datatables.net
25 */
26
27
28/* Global scope for TableTools for backwards compatibility.
29 * Will be removed in 2.3
30 */
31var TableTools;
32
33(function(window, document, undefined) {
34
35
36var factory = function( $, DataTable ) {
37"use strict";
38
39
40//include ZeroClipboard.js
41/* ZeroClipboard 1.0.4
42 * Author: Joseph Huckaby
43 */
44
45var ZeroClipboard_TableTools = {
46
47	version: "1.0.4-TableTools2",
48	clients: {}, // registered upload clients on page, indexed by id
49	moviePath: '', // URL to movie
50	nextId: 1, // ID of next movie
51
52	$: function(thingy) {
53		// simple DOM lookup utility function
54		if (typeof(thingy) == 'string') {
55			thingy = document.getElementById(thingy);
56		}
57		if (!thingy.addClass) {
58			// extend element with a few useful methods
59			thingy.hide = function() { this.style.display = 'none'; };
60			thingy.show = function() { this.style.display = ''; };
61			thingy.addClass = function(name) { this.removeClass(name); this.className += ' ' + name; };
62			thingy.removeClass = function(name) {
63				this.className = this.className.replace( new RegExp("\\s*" + name + "\\s*"), " ").replace(/^\s+/, '').replace(/\s+$/, '');
64			};
65			thingy.hasClass = function(name) {
66				return !!this.className.match( new RegExp("\\s*" + name + "\\s*") );
67			};
68		}
69		return thingy;
70	},
71
72	setMoviePath: function(path) {
73		// set path to ZeroClipboard.swf
74		this.moviePath = path;
75	},
76
77	dispatch: function(id, eventName, args) {
78		// receive event from flash movie, send to client
79		var client = this.clients[id];
80		if (client) {
81			client.receiveEvent(eventName, args);
82		}
83	},
84
85	register: function(id, client) {
86		// register new client to receive events
87		this.clients[id] = client;
88	},
89
90	getDOMObjectPosition: function(obj) {
91		// get absolute coordinates for dom element
92		var info = {
93			left: 0,
94			top: 0,
95			width: obj.width ? obj.width : obj.offsetWidth,
96			height: obj.height ? obj.height : obj.offsetHeight
97		};
98
99		if ( obj.style.width !== "" ) {
100			info.width = obj.style.width.replace("px","");
101		}
102
103		if ( obj.style.height !== "" ) {
104			info.height = obj.style.height.replace("px","");
105		}
106
107		while (obj) {
108			info.left += obj.offsetLeft;
109			info.top += obj.offsetTop;
110			obj = obj.offsetParent;
111		}
112
113		return info;
114	},
115
116	Client: function(elem) {
117		// constructor for new simple upload client
118		this.handlers = {};
119
120		// unique ID
121		this.id = ZeroClipboard_TableTools.nextId++;
122		this.movieId = 'ZeroClipboard_TableToolsMovie_' + this.id;
123
124		// register client with singleton to receive flash events
125		ZeroClipboard_TableTools.register(this.id, this);
126
127		// create movie
128		if (elem) {
129			this.glue(elem);
130		}
131	}
132};
133
134ZeroClipboard_TableTools.Client.prototype = {
135
136	id: 0, // unique ID for us
137	ready: false, // whether movie is ready to receive events or not
138	movie: null, // reference to movie object
139	clipText: '', // text to copy to clipboard
140	fileName: '', // default file save name
141	action: 'copy', // action to perform
142	handCursorEnabled: true, // whether to show hand cursor, or default pointer cursor
143	cssEffects: true, // enable CSS mouse effects on dom container
144	handlers: null, // user event handlers
145	sized: false,
146
147	glue: function(elem, title) {
148		// glue to DOM element
149		// elem can be ID or actual DOM element object
150		this.domElement = ZeroClipboard_TableTools.$(elem);
151
152		// float just above object, or zIndex 99 if dom element isn't set
153		var zIndex = 99;
154		if (this.domElement.style.zIndex) {
155			zIndex = parseInt(this.domElement.style.zIndex, 10) + 1;
156		}
157
158		// find X/Y position of domElement
159		var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement);
160
161		// create floating DIV above element
162		this.div = document.createElement('div');
163		var style = this.div.style;
164		style.position = 'absolute';
165		style.left = '0px';
166		style.top = '0px';
167		style.width = (box.width) + 'px';
168		style.height = box.height + 'px';
169		style.zIndex = zIndex;
170
171		if ( typeof title != "undefined" && title !== "" ) {
172			this.div.title = title;
173		}
174		if ( box.width !== 0 && box.height !== 0 ) {
175			this.sized = true;
176		}
177
178		// style.backgroundColor = '#f00'; // debug
179		if ( this.domElement ) {
180			this.domElement.appendChild(this.div);
181			this.div.innerHTML = this.getHTML( box.width, box.height ).replace(/&/g, '&');
182		}
183	},
184
185	positionElement: function() {
186		var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement);
187		var style = this.div.style;
188
189		style.position = 'absolute';
190		//style.left = (this.domElement.offsetLeft)+'px';
191		//style.top = this.domElement.offsetTop+'px';
192		style.width = box.width + 'px';
193		style.height = box.height + 'px';
194
195		if ( box.width !== 0 && box.height !== 0 ) {
196			this.sized = true;
197		} else {
198			return;
199		}
200
201		var flash = this.div.childNodes[0];
202		flash.width = box.width;
203		flash.height = box.height;
204	},
205
206	getHTML: function(width, height) {
207		// return HTML for movie
208		var html = '';
209		var flashvars = 'id=' + this.id +
210			'&width=' + width +
211			'&height=' + height;
212
213		if (navigator.userAgent.match(/MSIE/)) {
214			// IE gets an OBJECT tag
215			var protocol = location.href.match(/^https/i) ? 'https://' : 'http://';
216			html += '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="'+protocol+'download.macromedia.com/pub/s
216hockwave/cabs/flash/swflash.cab#version=10,0,0,0" width="'+width+'" height="'+height+'" id="'+this.movieId+'" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+ZeroClipboard_TableTools.moviePath+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+flashvars+'"/><param name="wmode" value="transparent"/></object>';
217		}
218		else {
219			// all other browsers get an EMBED tag
220			html += '<embed id="'+this.movieId+'" src="'+ZeroClipboard_TableTools.moviePath+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+width+'" height="'+height+'" name="'+this.movieId+'" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+flashvars+'" wmode="transparent" />';
221		}
222		return html;
223	},
224
225	hide: function() {
226		// temporarily hide floater offscreen
227		if (this.div) {
228			this.div.style.left = '-2000px';
229		}
230	},
231
232	show: function() {
233		// show ourselves after a call to hide()
234		this.reposition();
235	},
236
237	destroy: function() {
238		// destroy control and floater
239		if (this.domElement && this.div) {
240			this.hide();
241			this.div.innerHTML = '';
242
243			var body = document.getElementsByTagName('body')[0];
244			try { body.removeChild( this.div ); } catch(e) {}
245
246			this.domElement = null;
247			this.div = null;
248		}
249	},
250
251	reposition: function(elem) {
252		// reposition our floating div, optionally to new container
253		// warning: container CANNOT change size, only position
254		if (elem) {
255			this.domElement = ZeroClipboard_TableTools.$(elem);
256			if (!this.domElement) {
257				this.hide();
258			}
259		}
260
261		if (this.domElement && this.div) {
262			var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement);
263			var style = this.div.style;
264			style.left = '' + box.left + 'px';
265			style.top = '' + box.top + 'px';
266		}
267	},
268
269	clearText: function() {
270		// clear the text to be copy / saved
271		this.clipText = '';
272		if (this.ready) {
273			this.movie.clearText();
274		}
275	},
276
277	appendText: function(newText) {
278		// append text to that which is to be copied / saved
279		this.clipText += newText;
280		if (this.ready) { this.movie.appendText(newText) ;}
281	},
282
283	setText: function(newText) {
284		// set text to be copied to be copied / saved
285		this.clipText = newText;
286		if (this.ready) { this.movie.setText(newText) ;}
287	},
288
289	setCharSet: function(charSet) {
290		// set the character set (UTF16LE or UTF8)
291		this.charSet = charSet;
292		if (this.ready) { this.movie.setCharSet(charSet) ;}
293	},
294
295	setBomInc: function(bomInc) {
296		// set if the BOM should be included or not
297		this.incBom = bomInc;
298		if (this.ready) { this.movie.setBomInc(bomInc) ;}
299	},
300
301	setFileName: function(newText) {
302		// set the file name
303		this.fileName = newText;
304		if (this.ready) {
305			this.movie.setFileName(newText);
306		}
307	},
308
309	setAction: function(newText) {
310		// set action (save or copy)
311		this.action = newText;
312		if (this.ready) {
313			this.movie.setAction(newText);
314		}
315	},
316
317	addEventListener: function(eventName, func) {
318		// add user event listener for event
319		// event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel
320		eventName = eventName.toString().toLowerCase().replace(/^on/, '');
321		if (!this.handlers[eventName]) {
322			this.handlers[eventName] = [];
323		}
324		this.handlers[eventName].push(func);
325	},
326
327	setHandCursor: function(enabled) {
328		// enable hand cursor (true), or default arrow cursor (false)
329		this.handCursorEnabled = enabled;
330		if (this.ready) {
331			this.movie.setHandCursor(enabled);
332		}
333	},
334
335	setCSSEffects: function(enabled) {
336		// enable or disable CSS effects on DOM container
337		this.cssEffects = !!enabled;
338	},
339
340	receiveEvent: function(eventName, args) {
341		var self;
342
343		// receive event from flash
344		eventName = eventName.toString().toLowerCase().replace(/^on/, '');
345
346		// special behavior for certain events
347		switch (eventName) {
348			case 'load':
349				// movie claims it is ready, but in IE this isn't always the case...
350				// bug fix: Cannot extend EMBED DOM elements in Firefox, must use traditional function
351				this.movie = document.getElementById(this.movieId);
352				if (!this.movie) {
353					self = this;
354					setTimeout( function() { self.receiveEvent('load', null); }, 1 );
355					return;
356				}
357
358				// firefox on pc needs a "kick" in order to set these in certain cases
359				if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) {
360					self = this;
361					setTimeout( function() { self.receiveEvent('load', null); }, 100 );
362					this.ready = true;
363					return;
364				}
365
366				this.ready = true;
367				this.movie.clearText();
368				this.movie.appendText( this.clipText );
369				this.movie.setFileName( this.fileName );
370				this.movie.setAction( this.action );
371				this.movie.setCharSet( this.charSet );
372				this.movie.setBomInc( this.incBom );
373				this.movie.setHandCursor( this.handCursorEnabled );
374				break;
375
376			case 'mouseover':
377				if (this.domElement && this.cssEffects) {
378					//this.domElement.addClass('hover');
379					if (this.recoverActive) {
380						this.domElement.addClass('active');
381					}
382				}
383				break;
384
385			case 'mouseout':
386				if (this.domElement && this.cssEffects) {
387					this.recoverActive = false;
388					if (this.domElement.hasClass('active')) {
389						this.domElement.removeClass('active');
390						this.recoverActive = true;
391					}
392					//this.domElement.removeClass('hover');
393				}
394				break;
395
396			case 'mousedown':
397				if (this.domElement && this.cssEffects) {
398					this.domElement.addClass('active');
399				}
400				break;
401
402			case 'mouseup':
403				if (this.domElement && this.cssEffects) {
404					this.domElement.removeClass('active');
405					this.recoverActive = false;
406				}
407				break;
408		} // switch eventName
409
410		if (this.handlers[eventName]) {
411			for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) {
412				var func = this.handlers[eventName][idx];
413
414				if (typeof(func) == 'function') {
415					// actual function reference
416					func(this, args);
417				}
418				else if ((typeof(func) == 'object') && (func.length == 2)) {
419					// PHP style object + method, i.e. [myObject, 'myMethod']
420					func[0][ func[1] ](this, args);
421				}
422				else if (typeof(func) == 'string') {
423					// name of function
424					window[func](this, args);
425				}
426			} // foreach event handler defined
427		} // user defined handler for event
428	}
429
430};
431
432// For the Flash binding to work, ZeroClipboard_TableTools must be on the global
433// object list
434window.ZeroClipboard_TableTools = ZeroClipboard_TableTools;
435//include TableTools.js
436/* TableTools
437 * 2009-2015 SpryMedia Ltd - datatables.net/license
438 */
439
440/*globals TableTools,ZeroClipboard_TableTools*/
441
442
443(function($, window, document) {
444
445/** 
446 * TableTools provides flexible buttons and other tools for a DataTables enhanced table
447 * @class TableTools
448 * @constructor
449 * @param {Object} oDT DataTables instance. When using DataTables 1.10 this can
450 *   also be a jQuery collection, jQuery selector, table node, DataTables API
451 *   instance or DataTables settings object.
452 * @param {Object} oOpts TableTools options
453 * @param {String} oOpts.sSwfPath ZeroClipboard SWF path
454 * @param {String} oOpts.sRowSelect Row selection options - 'none', 'single', 'multi' or 'os'
455 * @param {Function} oOpts.fnPreRowSelect Callback function just prior to row selection
456 * @param {Function} oOpts.fnRowSelected Callback function just after row selection
457 * @param {Function} oOpts.fnRowDeselected Callback function when row is deselected
458 * @param {Array} oOpts.aButtons List of buttons to be used
459 */
460TableTools = function( oDT, oOpts )
461{
462	/* Santiy check that we are a new instance */
463	if ( ! this instanceof TableTools )
464	{
465		alert( "Warning: TableTools must be initialised with the keyword 'new'" );
466	}
467
468	// In 1.10 we can use the API to get the settings object from a number of
469	// sources
470	var dtSettings = $.fn.dataTable.Api ?
471		new $.fn.dataTable.Api( oDT ).settings()[0] :
472		oDT.fnSettings();
473
474
475	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
476	 * Public class variables
477	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
478
479	/**
480	 * @namespace Settings object which contains customisable information for TableTools instance
481	 */
482	this.s = {
483		/**
484		 * Store 'this' so the instance can be retrieved from the settings object
485		 * @property that
486		 * @type	 object
487		 * @default  this
488		 */
489		"that": this,
490
491		/** 
492		 * DataTables settings objects
493		 * @property dt
494		 * @type	 object
495		 * @default  <i>From the oDT init option</i>
496		 */
497		"dt": dtSettings,
498
499		/**
500		 * @namespace Print specific information
501		 */
502		"print": {
503			/** 
504			 * DataTables draw 'start' point before the printing display was shown
505			 *  @property saveStart
506			 *  @type	 int
507			 *  @default  -1
508			 */
509			"saveStart": -1,
510
511			/** 
512			 * DataTables draw 'length' point before the printing display was shown
513			 *  @property saveLength
514			 *  @type	 int
515			 *  @default  -1
516			 */
517			"saveLength": -1,
518
519			/** 
520			 * Page scrolling point before the printing display was shown so it can be restored
521			 *  @property saveScroll
522			 *  @type	 int
523			 *  @default  -1
524			 */
525			"saveScroll": -1,
526
527			/** 
528			 * Wrapped function to end the print display (to maintain scope)
529			 *  @property funcEnd
530			 *  @type	 Function
531			 *  @default  function () {}
532			 */
533			"funcEnd": function () {}
534		},
535
536		/**
537		 * A unique ID is assigned to each button in each instance
538		 * @property buttonCounter
539		 *  @type	 int
540		 * @default  0
541		 */
542		"buttonCounter": 0,
543
544		/**
545		 * @namespace Select rows specific information
546		 */
547		"select": {
548			/**
549			 * Select type - can be 'none', 'single' or 'multi'
550			 * @property type
551			 *  @type	 string
552			 * @default  ""
553			 */
554			"type": "",
555
556			/**
557			 * Array of nodes which are currently selected
558			 *  @property selected
559			 *  @type	 array
560			 *  @default  []
561			 */
562			"selected": [],
563
564			/**
565			 * Function to run before the selection can take place. Will cancel the select if the
566			 * function returns false
567			 *  @property preRowSelect
568			 *  @type	 Function
569			 *  @default  null
570			 */
571			"preRowSelect": null,
572
573			/**
574			 * Function to run when a row is selected
575			 *  @property postSelected
576			 *  @type	 Function
577			 *  @default  null
578			 */
579			"postSelected": null,
580
581			/**
582			 * Function to run when a row is deselected
583			 *  @property postDeselected
584			 *  @type	 Function
585			 *  @default  null
586			 */
587			"postDeselected": null,
588
589			/**
590			 * Indicate if all rows are selected (needed for server-side processing)
591			 *  @property all
592			 *  @type	 boolean
593			 *  @default  false
594			 */
595			"all": false,
596
597			/**
598			 * Class name to add to selected TR nodes
599			 *  @property selectedClass
600			 *  @type	 String
601			 *  @default  ""
602			 */
603			"selectedClass": ""
604		},
605
606		/**
607		 * Store of the user input customisation object
608		 *  @property custom
609		 *  @type	 object
610		 *  @default  {}
611		 */
612		"custom": {},
613
614		/**
615		 * SWF movie path
616		 *  @property swfPath
617		 *  @type	 string
618		 *  @default  ""
619		 */
620		"swfPath": "",
621
622		/**
623		 * Default button set
624		 *  @property buttonSet
625		 *  @type	 array
626		 *  @default  []
627		 */
628		"buttonSet": [],
629
630		/**
631		 * When there is more than one TableTools instance for a DataTable, there must be a 
632		 * master which controls events (row selection etc)
633		 *  @property master
634		 *  @type	 boolean
635		 *  @default  false
636		 */
637		"master": false,
638
639		/**
640		 * Tag names that are used for creating collections and buttons
641		 *  @namesapce
642		 */
643		"tags": {}
644	};
645
646
647	/**
648	 * @namespace Common and useful DOM elements for the class instance
649	 */
650	this.dom = {
651		/**
652		 * DIV element that is create and all TableTools buttons (and their children) put into
653		 *  @property container
654		 *  @type	 node
655		 *  @default  null
656		 */
657		"container": null,
658
659		/**
660		 * The table node to which TableTools will be applied
661		 *  @property table
662		 *  @type	 node
663		 *  @default  null
664		 */
665		"table": null,
666
667		/**
668		 * @namespace Nodes used for the print display
669		 */
670		"print": {
671			/**
672			 * Nodes which have been removed from the display by setting them to display none
673			 *  @property hidden
674			 *  @type	 array
675			 *  @default  []
676			 */
677			"hidden": [],
678
679			/**
680			 * The information display saying telling the user about the print display
681			 *  @property message
682			 *  @type	 node
683			 *  @default  null
684			 */
685			"message": null
686	  },
687
688		/**
689		 * @namespace Nodes used for a collection display. This contains the currently used collection
690		 */
691		"collection": {
692			/**
693			 * The div wrapper containing the buttons in the collection (i.e. the menu)
694			 *  @property collection
695			 *  @type	 node
696			 *  @default  null
697			 */
698			"collection": null,
699
700			/**
701			 * Background display to provide focus and capture events
702			 *  @property background
703			 *  @type	 node
704			 *  @default  null
705			 */
706			"background": null
707		}
708	};
709
710	/**
711	 * @namespace Name space for the classes that this TableTools instance will use
712	 * @extends TableTools.classes
713	 */
714	this.classes = $.extend( true, {}, TableTools.classes );
715	if ( this.s.dt.bJUI )
716	{
717		$.extend( true, this.classes, TableTools.classes_themeroller );
718	}
719
720
721	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
722	 * Public class methods
723	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
724
725	/**
726	 * Retreieve the settings object from an instance
727	 *  @method fnSettings
728	 *  @returns {object} TableTools settings object
729	 */
730	this.fnSettings = function () {
731		return this.s;
732	};
733
734
735	/* Constructor logic */
736	if ( typeof oOpts == 'undefined' )
737	{
738		oOpts = {};
739	}
740
741
742	TableTools._aInstances.push( this );
743	this._fnConstruct( oOpts );
744
745	return this;
746};
747
748
749
750TableTools.prototype = {
751	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
752	 * Public methods
753	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
754
755	/**
756	 * Retreieve the settings object from an instance
757	 *  @returns {array} List of TR nodes which are currently selected
758	 *  @param {boolean} [filtered=false] Get only selected rows which are  
759	 *    available given the filtering applied to the table. By default
760	 *    this is false -  i.e. all rows, regardless of filtering are 
761	      selected.
762	 */
763	"fnGetSelected": function ( filtered )
764	{
765		var
766			out = [],
767			data = this.s.dt.aoData,
768			displayed = this.s.dt.aiDisplay,
769			i, iLen;
770
771		if ( filtered )
772		{
773			// Only consider filtered rows
774			for ( i=0, iLen=displayed.length ; i<iLen ; i++ )
775			{
776				if ( data[ displayed[i] ]._DTTT_selected )
777				{
778					out.push( data[ displayed[i] ].nTr );
779				}
780			}
781		}
782		else
783		{
784			// Use all rows
785			for ( i=0, iLen=data.length ; i<iLen ; i++ )
786			{
787				if ( data[i]._DTTT_selected )
788				{
789					out.push( data[i].nTr );
790				}
791			}
792		}
793
794		return out;
795	},
796
797
798	/**
799	 * Get the data source objects/arrays from DataTables for the selected rows (same as
800	 * fnGetSelected followed by fnGetData on each row from the table)
801	 *  @returns {array} Data from the TR nodes which are currently selected
802	 */
803	"fnGetSelectedData": function ()
804	{
805		var out = [];
806		var data=this.s.dt.aoData;
807		var i, iLen;
808
809		for ( i=0, iLen=data.length ; i<iLen ; i++ )
810		{
811			if ( data[i]._DTTT_selected )
812			{
813				out.push( this.s.dt.oInstance.fnGetData(i) );
814			}
815		}
816
817		return out;
818	},
819
820
821	/**
822	 * Get the indexes of the selected rows
823	 *  @returns {array} List of row indexes
824	 *  @param {boolean} [filtered=false] Get only selected rows which are  
825	 *    available given the filtering applied to the table. By default
826	 *    this is false -  i.e. all rows, regardless of filtering are 
827	      selected.
828	 */
829	"fnGetSelectedIndexes": function ( filtered )
830	{
831		var
832			out = [],
833			data = this.s.dt.aoData,
834			displayed = this.s.dt.aiDisplay,
835			i, iLen;
836
837		if ( filtered )
838		{
839			// Only consider filtered rows
840			for ( i=0, iLen=displayed.length ; i<iLen ; i++ )
841			{
842				if ( data[ displayed[i] ]._DTTT_selected )
843				{
844					out.push( displayed[i] );
845				}
846			}
847		}
848		else
849		{
850			// Use all rows
851			for ( i=0, iLen=data.length ; i<iLen ; i++ )
852			{
853				if ( data[i]._DTTT_selected )
854				{
855					out.push( i );
856				}
857			}
858		}
859
860		return out;
861	},
862
863
864	/**
865	 * Check to see if a current row is selected or not
866	 *  @param {Node} n TR node to check if it is currently selected or not
867	 *  @returns {Boolean} true if select, false otherwise
868	 */
869	"fnIsSelected": function ( n )
870	{
871		var pos = this.s.dt.oInstance.fnGetPosition( n );
872		return (this.s.dt.aoData[pos]._DTTT_selected===true) ? true : false;
873	},
874
875
876	/**
877	 * Select all rows in the table
878	 *  @param {boolean} [filtered=false] Select only rows which are available 
879	 *    given the filtering applied to the table. By default this is false - 
880	 *    i.e. all rows, regardless of filtering are selected.
881	 */
882	"fnSelectAll": function ( filtered )
883	{
884		this._fnRowSelect( filtered ?
885			this.s.dt.aiDisplay :
886			this.s.dt.aoData
887		);
888	},
889
890
891	/**
892	 * Deselect all rows in the table
893	 *  @param {boolean} [filtered=false] Deselect only rows which are available 
894	 *    given the filtering applied to the table. By default this is false - 
895	 *    i.e. all rows, regardless of filtering are deselected.
896	 */
897	"fnSelectNone": function ( filtered )
898	{
899		this._fnRowDeselect( this.fnGetSelectedIndexes(filtered) );
900	},
901
902
903	/**
904	 * Select row(s)
905	 *  @param {node|object|array} n The row(s) to select. Can be a single DOM
906	 *    TR node, an array of TR nodes or a jQuery object.
907	 */
908	"fnSelect": function ( n )
909	{
910		if ( this.s.select.type == "single" )
911		{
912			this.fnSelectNone();
913			this._fnRowSelect( n );
914		}
915		else
916		{
917			this._fnRowSelect( n );
918		}
919	},
920
921
922	/**
923	 * Deselect row(s)
924	 *  @param {node|object|array} n The row(s) to deselect. Can be a single DOM
925	 *    TR node, an array of TR nodes or a jQuery object.
926	 */
927	"fnDeselect": function ( n )
928	{
929		this._fnRowDeselect( n );
930	},
931
932
933	/**
934	 * Get the title of the document - useful for file names. The title is retrieved from either
935	 * the configuration object's 'title' parameter, or the HTML document title
936	 *  @param   {Object} oConfig Button configuration object
937	 *  @returns {String} Button title
938	 */
939	"fnGetTitle": function( oConfig )
940	{
941		var sTitle = "";
942		if ( typeof oConfig.sTitle != 'undefined' && oConfig.sTitle !== "" ) {
943			sTitle = oConfig.sTitle;
944		} else {
945			var anTitle = document.getElementsByTagName('title');
946			if ( anTitle.length > 0 )
947			{
948				sTitle = anTitle[0].innerHTML;
949			}
950		}
951
952		/* Strip characters which the OS will object to - checking for UTF8 support in the scripting
953		 * engine
954		 */
955		if ( "\u00A1".toString().length < 4 ) {
956			return sTitle.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
957		} else {
958			return sTitle.replace(/[^a-zA-Z0-9_\.,\-_ !\(\)]/g, "");
959		}
960	},
961
962
963	/**
964	 * Calculate a unity array with the column width by proportion for a set of columns to be
965	 * included for a button. This is particularly useful for PDF creation, where we can use the
966	 * column widths calculated by the browser to size the columns in the PDF.
967	 *  @param   {Object} oConfig Button configuration object
968	 *  @returns {Array} Unity array of column ratios
969	 */
970	"fnCalcColRatios": function ( oConfig )
971	{
972		var
973			aoCols = this.s.dt.aoColumns,
974			aColumnsInc = this._fnColumnTargets( oConfig.mColumns ),
975			aColWidths = [],
976			iWidth = 0, iTotal = 0, i, iLen;
977
978		for ( i=0, iLen=aColumnsInc.length ; i<iLen ; i++ )
979		{
980			if ( aColumnsInc[i] )
981			{
982				iWidth = aoCols[i].nTh.offsetWidth;
983				iTotal += iWidth;
984				aColWidths.push( iWidth );
985			}
986		}
987
988		for ( i=0, iLen=aColWidths.length ; i<iLen ; i++ )
989		{
990			aColWidths[i] = aColWidths[i] / iTotal;
991		}
992
993		return aColWidths.join('\t');
994	},
995
996
997	/**
998	 * Get the information contained in a table as a string
999	 *  @param   {Object} oConfig Button configuration object
1000	 *  @returns {String} Table data as a string
1001	 */
1002	"fnGetTableData": function ( oConfig )
1003	{
1004		/* In future this could be used to get data from a plain HTML source as well as DataTables */
1005		if ( this.s.dt )
1006		{
1007			return this._fnGetDataTablesData( oConfig );
1008		}
1009	},
1010
1011
1012	/**
1013	 * Pass text to a flash button instance, which will be used on the button's click handler
1014	 *  @param   {Object} clip Flash button object
1015	 *  @param   {String} text Text to set
1016	 */
1017	"fnSetText": function ( clip, text )
1018	{
1019		this._fnFlashSetText( clip, text );
1020	},
1021
1022
1023	/**
1024	 * Resize the flash elements of the buttons attached to this TableTools instance - this is
1025	 * useful for when initialising TableTools when it is hidden (display:none) since sizes can't
1026	 * be calculated at that time.
1027	 */
1028	"fnResizeButtons": function ()
1029	{
1030		for ( var cli in ZeroClipboard_TableTools.clients )
1031		{
1032			if ( cli )
1033			{
1034				var client = ZeroClipboard_TableTools.clients[cli];
1035				if ( typeof client.domElement != 'undefined' &&
1036					 client.domElement.parentNode )
1037				{
1038					client.positionElement();
1039				}
1040			}
1041		}
1042	},
1043
1044
1045	/**
1046	 * Check to see if any of the ZeroClipboard client's attached need to be resized
1047	 */
1048	"fnResizeRequired": function ()
1049	{
1050		for ( var cli in ZeroClipboard_TableTools.clients )
1051		{
1052			if ( cli )
1053			{
1054				var client = ZeroClipboard_TableTools.clients[cli];
1055				if ( typeof client.domElement != 'undefined' &&
1056					 client.domElement.parentNode == this.dom.container &&
1057					 client.sized === false )
1058				{
1059					return true;
1060				}
1061			}
1062		}
1063		return false;
1064	},
1065
1066
1067	/**
1068	 * Programmatically enable or disable the print view
1069	 *  @param {boolean} [bView=true] Show the print view if true or not given. If false, then
1070	 *    terminate the print view and return to normal.
1071	 *  @param {object} [oConfig={}] Configuration for the print view
1072	 *  @param {boolean} [oConfig.bShowAll=false] Show all rows in the table if true
1073	 *  @param {string} [oConfig.sInfo] Information message, displayed as an overlay to the
1074	 *    user to let them know what the print view is.
1075	 *  @param {string} [oConfig.sMessage] HTML string to show at the top of the document - will
1076	 *    be included in the printed document.
1077	 */
1078	"fnPrint": function ( bView, oConfig )
1079	{
1080		if ( oConfig === undefined )
1081		{
1082			oConfig = {};
1083		}
1084
1085		if ( bView === undefined || bView )
1086		{
1087			this._fnPrintStart( oConfig );
1088		}
1089		else
1090		{
1091			this._fnPrintEnd();
1092		}
1093	},
1094
1095
1096	/**
1097	 * Show a message to the end user which is nicely styled
1098	 *  @param {string} message The HTML string to show to the user
1099	 *  @param {int} time The duration the message is to be shown on screen for (mS)
1100	 */
1101	"fnInfo": function ( message, time ) {
1102		var info = $('<div/>')
1103			.addClass( this.classes.print.info )
1104			.html( message )
1105			.appendTo( 'body' );
1106
1107		setTimeout( function() {
1108			info.fadeOut( "normal", function() {
1109				info.remove();
1110			} );
1111		}, time );
1112	},
1113
1114
1115
1116	/**
1117	 * Get the container element of the instance for attaching to the DOM
1118	 *   @returns {node} DOM node
1119	 */
1120	"fnContainer": function () {
1121		return this.dom.container;
1122	},
1123
1124
1125
1126	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1127	 * Private methods (they are of course public in JS, but recommended as private)
1128	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1129
1130	/**
1131	 * Constructor logic
1132	 *  @method  _fnConstruct
1133	 *  @param   {Object} oOpts Same as TableTools constructor
1134	 *  @returns void
1135	 *  @private 
1136	 */
1137	"_fnConstruct": function ( oOpts )
1138	{
1139		var that = this;
1140
1141		this._fnCustomiseSettings( oOpts );
1142
1143		/* Container element */
1144		this.dom.container = document.createElement( this.s.tags.container );
1145		this.dom.container.className = this.classes.container;
1146
1147		/* Row selection config */
1148		if ( this.s.select.type != 'none' )
1149		{
1150			this._fnRowSelectConfig();
1151		}
1152
1153		/* Buttons */
1154		this._fnButtonDefinations( this.s.buttonSet, this.dom.container );
1155
1156		/* Destructor */
1157		this.s.dt.aoDestroyCallback.push( {
1158			"sName": "TableTools",
1159			"fn": function () {
1160				$(that.s.dt.nTBody)
1161					.off( 'click.DTTT_Select', that.s.custom.sRowSelector )
1162					.off( 'mousedown.DTTT_Select', 'tr' )
1163					.off( 'mouseup.DTTT_Select', 'tr' );
1164
1165				$(that.dom.container).empty();
1166
1167				// Remove the instance
1168				var idx = $.inArray( that, TableTools._aInstances );
1169				if ( idx !== -1 ) {
1170					TableTools._aInstances.splice( idx, 1 );
1171				}
1172			}
1173		} );
1174	},
1175
1176
1177	/**
1178	 * Take the user defined settings and the default settings and combine them.
1179	 *  @method  _fnCustomiseSettings
1180	 *  @param   {Object} oOpts Same as TableTools constructor
1181	 *  @returns void
1182	 *  @private 
1183	 */
1184	"_fnCustomiseSettings": function ( oOpts )
1185	{
1186		/* Is this the master control instance or not? */
1187		if ( typeof this.s.dt._TableToolsInit == 'undefined' )
1188		{
1189			this.s.master = true;
1190			this.s.dt._TableToolsInit = true;
1191		}
1192
1193		/* We can use the table node from comparisons to group controls */
1194		this.dom.table = this.s.dt.nTable;
1195
1196		/* Clone the defaults and then the user options */
1197		this.s.custom = $.extend( {}, TableTools.DEFAULTS, oOpts );
1198
1199		/* Flash file location */
1200		this.s.swfPath = this.s.custom.sSwfPath;
1201		if ( typeof ZeroClipboard_TableTools != 'undefined' )
1202		{
1203			ZeroClipboard_TableTools.moviePath = this.s.swfPath;
1204		}
1205
1206		/* Table row selecting */
1207		this.s.select.type = this.s.custom.sRowSelect;
1208		this.s.select.preRowSelect = this.s.custom.fnPreRowSelect;
1209		this.s.select.postSelected = this.s.custom.fnRowSelected;
1210		this.s.select.postDeselected = this.s.custom.fnRowDeselected;
1211
1212		// Backwards compatibility - allow the user to specify a custom class in the initialiser
1213		if ( this.s.custom.sSelectedClass )
1214		{
1215			this.classes.select.row = this.s.custom.sSelectedClass;
1216		}
1217
1218		this.s.tags = this.s.custom.oTags;
1219
1220		/* Button set */
1221		this.s.buttonSet = this.s.custom.aButtons;
1222	},
1223
1224
1225	/**
1226	 * Take the user input arrays and expand them to be fully defined, and then add them to a given
1227	 * DOM element
1228	 *  @method  _fnButtonDefinations
1229	 *  @param {array} buttonSet Set of user defined buttons
1230	 *  @param {node} wrapper Node to add the created buttons to
1231	 *  @returns void
1232	 *  @private 
1233	 */
1234	"_fnButtonDefinations": function ( buttonSet, wrapper )
1235	{
1236		var buttonDef;
1237
1238		for ( var i=0, iLen=buttonSet.length ; i<iLen ; i++ )
1239		{
1240			if ( typeof buttonSet[i] == "string" )
1241			{
1242				if ( typeof TableTools.BUTTONS[ buttonSet[i] ] == 'undefined' )
1243				{
1244					alert( "TableTools: Warning - unknown button type: "+buttonSet[i] );
1245					continue;
1246				}
1247				buttonDef = $.extend( {}, TableTools.BUTTONS[ buttonSet[i] ], true );
1248			}
1249			else
1250			{
1251				if ( typeof TableTools.BUTTONS[ buttonSet[i].sExtends ] == 'undefined' )
1252				{
1253					alert( "TableTools: Warning - unknown button type: "+buttonSet[i].sExtends );
1254					continue;
1255				}
1256				var o = $.extend( {}, TableTools.BUTTONS[ buttonSet[i].sExtends ], true );
1257				buttonDef = $.extend( o, buttonSet[i], true );
1258			}
1259
1260			var button = this._fnCreateButton(
1261				buttonDef,
1262				$(wrapper).hasClass(this.classes.collection.container)
1263			);
1264
1265			if ( button ) {
1266				wrapper.appendChild( button );
1267			}
1268		}
1269	},
1270
1271
1272	/**
1273	 * Create and configure a TableTools button
1274	 *  @method  _fnCreateButton
1275	 *  @param   {Object} oConfig Button configuration object
1276	 *  @returns {Node} Button element
1277	 *  @private 
1278	 */
1279	"_fnCreateButton": function ( oConfig, bCollectionButton )
1280	{
1281	  var nButton = this._fnButtonBase( oConfig, bCollectionButton );
1282
1283		if ( oConfig.sAction.match(/flash/) )
1284		{
1285			if ( ! this._fnHasFlash() ) {
1286				return false;
1287			}
1288
1289			this._fnFlashConfig( nButton, oConfig );
1290		}
1291		else if ( oConfig.sAction == "text" )
1292		{
1293			this._fnTextConfig( nButton, oConfig );
1294		}
1295		else if ( oConfig.sAction == "div" )
1296		{
1297			this._fnTextConfig( nButton, oConfig );
1298		}
1299		else if ( oConfig.sAction == "collection" )
1300		{
1301			this._fnTextConfig( nButton, oConfig );
1302			this._fnCollectionConfig( nButton, oConfig );
1303		}
1304
1305		if ( this.s.dt.iTabIndex !== -1 ) {
1306			$(nButton)
1307				.attr( 'tabindex', this.s.dt.iTabIndex )
1308				.attr( 'aria-controls', this.s.dt.sTableId )
1309				.on( 'keyup.DTTT', function (e) {
1310					// Trigger the click event on return key when focused.
1311					// Note that for Flash buttons this has no effect since we
1312					// can't programmatically trigger the Flash export
1313					if ( e.keyCode === 13 ) {
1314						e.stopPropagation();
1315
1316						$(this).trigger( 'click' );
1317					}
1318				} )
1319				.on( 'mousedown.DTTT', function (e) {
1320					// On mousedown we want to stop the focus occurring on the
1321					// button, focus is used only for the keyboard navigation.
1322					// But using preventDefault for the flash buttons stops the
1323					// flash action. However, it is not the button that gets
1324					// focused but the flash element for flash buttons, so this
1325					// works
1326					if ( ! oConfig.sAction.match(/flash/) ) {
1327						e.preventDefault();
1328					}
1329				} );
1330		}
1331
1332		return nButton;
1333	},
1334
1335
1336	/**
1337	 * Create the DOM needed for the button and apply some base properties. All buttons start here
1338	 *  @method  _fnButtonBase
1339	 *  @param   {o} oConfig Button configuration object
1340	 *  @returns {Node} DIV element for the button
1341	 *  @private
1342	 */
1343	"_fnButtonBase": function ( o, bCollectionButton )
1344	{
1345		var sTag, sLiner, sClass;
1346
1347		if ( bCollectionButton )
1348		{
1349			sTag = o.sTag && o.sTag !== "default" ? o.sTag : this.s.tags.collection.button;
1350			sLiner = o.sLinerTag && o.sLinerTag !== "default" ? o.sLiner : this.s.tags.collection.liner;
1351			sClass = this.classes.collection.buttons.normal;
1352		}
1353		else
1354		{
1355			sTag = o.sTag && o.sTag !== "default" ? o.sTag : this.s.tags.button;
1356			sLiner = o.sLinerTag && o.sLinerTag !== "default" ? o.sLiner : this.s.tags.liner;
1357			sClass = this.classes.buttons.normal;
1358		}
1359
1360		var
1361		  nButton = document.createElement( sTag ),
1362		  nSpan = document.createElement( sLiner ),
1363		  masterS = this._fnGetMasterSettings();
1364
1365		nButton.className = sClass+" "+o.sButtonClass;
1366		nButton.setAttribute('id', "ToolTables_"+this.s.dt.sInstance+"_"+masterS.buttonCounter );
1367		nButton.appendChild( nSpan );
1368		nSpan.innerHTML = o.sButtonText;
1369
1370		masterS.buttonCounter++;
1371
1372		return nButton;
1373	},
1374
1375
1376	/**
1377	 * Get the settings object for the master instance. When more than one TableTools instance is
1378	 * assigned to a DataTable, only one of them can be the 'master' (for the select rows). As such,
1379	 * we will typically want to interact with that master for global properties.
1380	 *  @method  _fnGetMasterSettings
1381	 *  @returns {Object} TableTools settings object
1382	 *  @private 
1383	 */
1384	"_fnGetMasterSettings": function ()
1385	{
1386		if ( this.s.master )
1387		{
1388			return this.s;
1389		}
1390		else
1391		{
1392			/* Look for the master which has the same DT as this one */
1393			var instances = TableTools._aInstances;
1394			for ( var i=0, iLen=instances.length ; i<iLen ; i++ )
1395			{
1396				if ( this.dom.table == instances[i].s.dt.nTable )
1397				{
1398					return instances[i].s;
1399				}
1400			}
1401		}
1402	},
1403
1404
1405
1406	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1407	 * Button collection functions
1408	 */
1409
1410	/**
1411	 * Create a collection button, when activated will present a drop down list of other buttons
1412	 *  @param   {Node} nButton Button to use for the collection activation
1413	 *  @param   {Object} oConfig Button configuration object
1414	 *  @returns void
1415	 *  @private
1416	 */
1417	"_fnCollectionConfig": function ( nButton, oConfig )
1418	{
1419		var nHidden = document.createElement( this.s.tags.collection.container );
1420		nHidden.style.display = "none";
1421		nHidden.className = this.classes.collection.container;
1422		oConfig._collection = nHidden;
1423		document.body.appendChild( nHidden );
1424
1425		this._fnButtonDefinations( oConfig.aButtons, nHidden );
1426	},
1427
1428
1429	/**
1430	 * Show a button collection
1431	 *  @param   {Node} nButton Button to use for the collection
1432	 *  @param   {Object} oConfig Button configuration object
1433	 *  @returns void
1434	 *  @private
1435	 */
1436	"_fnCollectionShow": function ( nButton, oConfig )
1437	{
1438		var
1439			that = this,
1440			oPos = $(nButton).offset(),
1441			nHidden = oConfig._collection,
1442			iDivX = oPos.left,
1443			iDivY = oPos.top + $(nButton).outerHeight(),
1444			iWinHeight = $(window).height(), iDocHeight = $(document).height(),
1445			iWinWidth = $(window).width(), iDocWidth = $(document).width();
1446
1447		nHidden.style.position = "absolute";
1448		nHidden.style.left = iDivX+"px";
1449		nHidden.style.top = iDivY+"px";
1450		nHidden.style.display = "block";
1451		$(nHidden).css('opacity',0);
1452
1453		var nBackground = document.createElement('div');
1454		nBackground.style.position = "absolute";
1455		nBackground.style.left = "0px";
1456		nBackground.style.top = "0px";
1457		nBackground.style.height = ((iWinHeight>iDocHeight)? iWinHeight : iDocHeight) +"px";
1458		nBackground.style.width = ((iWinWidth>iDocWidth)? iWinWidth : iDocWidth) +"px";
1459		nBackground.className = this.classes.collection.background;
1460		$(nBackground).css('opacity',0);
1461
1462		document.body.appendChild( nBackground );
1463		document.body.appendChild( nHidden );
1464
1465		/* Visual corrections to try and keep the collection visible */
1466		var iDivWidth = $(nHidden).outerWidth();
1467		var iDivHeight = $(nHidden).outerHeight();
1468
1469		if ( iDivX + iDivWidth > iDocWidth )
1470		{
1471			nHidden.style.left = (iDocWidth-iDivWidth)+"px";
1472		}
1473
1474		if ( iDivY + iDivHeight > iDocHeight )
1475		{
1476			nHidden.style.top = (iDivY-iDivHeight-$(nButton).outerHeight())+"px";
1477		}
1478
1479		this.dom.collection.collection = nHidden;
1480		this.dom.collection.background = nBackground;
1481
1482		/* This results in a very small delay for the end user but it allows the animation to be
1483		 * much smoother. If you don't want the animation, then the setTimeout can be removed
1484		 */
1485		setTimeout( function () {
1486			$(nHidden).animate({"opacity": 1}, 500);
1487			$(nBackground).animate({"opacity": 0.25}, 500);
1488		}, 10 );
1489
1490		/* Resize the buttons to the Flash contents fit */
1491		this.fnResizeButtons();
1492
1493		/* Event handler to remove the collection display */
1494		$(nBackground).click( function () {
1495			that._fnCollectionHide.call( that, null, null );
1496		} );
1497	},
1498
1499
1500	/**
1501	 * Hide a button collection
1502	 *  @param   {Node} nButton Button to use for the collection
1503	 *  @param   {Object} oConfig Button configuration object
1504	 *  @returns void
1505	 *  @private
1506	 */
1507	"_fnCollectionHide": function ( nButton, oConfig )
1508	{
1509		if ( oConfig !== null && oConfig.sExtends == 'collection' )
1510		{
1511			return;
1512		}
1513
1514		if ( this.dom.collection.collection !== null )
1515		{
1516			$(this.dom.collection.collection).animate({"opacity": 0}, 500, function (e) {
1517				this.style.display = "none";
1518			} );
1519
1520			$(this.dom.collection.background).animate({"opacity": 0}, 500, function (e) {
1521				this.parentNode.removeChild( this );
1522			} );
1523
1524			this.dom.collection.collection = null;
1525			this.dom.collection.background = null;
1526		}
1527	},
1528
1529
1530
1531	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1532	 * Row selection functions
1533	 */
1534
1535	/**
1536	 * Add event handlers to a table to allow for row selection
1537	 *  @method  _fnRowSelectConfig
1538	 *  @returns void
1539	 *  @private 
1540	 */
1541	"_fnRowSelectConfig": function ()
1542	{
1543		if ( this.s.master )
1544		{
1545			var
1546				that = this,
1547				i, iLen,
1548				dt = this.s.dt,
1549				aoOpenRows = this.s.dt.aoOpenRows;
1550
1551			$(dt.nTable).addClass( this.classes.select.table );
1552
1553			// When using OS style selection, we want to cancel the shift text
1554			// selection, but only when the shift key is used (so you can
1555			// actually still select text in the table)
1556			if ( this.s.select.type === 'os' ) {
1557				$(dt.nTBody).on( 'mousedown.DTTT_Select', 'tr', function(e) {
1558					if ( e.shiftKey ) {
1559
1560						$(dt.nTBody)
1561							.css( '-moz-user-select', 'none' )
1562							.one('selectstart.DTTT_Select', 'tr', function () {
1563								return false;
1564							} );
1565					}
1566				} );
1567
1568				$(dt.nTBody).on( 'mouseup.DTTT_Select', 'tr', function(e) {
1569					$(dt.nTBody).css( '-moz-user-select', '' );
1570				} );
1571			}
1572
1573			// Row selection
1574			$(dt.nTBody).on( 'click.DTTT_Select', this.s.custom.sRowSelector, function(e) {
1575				var row = this.nodeName.toLowerCase() === 'tr' ?
1576					this :
1577					$(this).parents('tr')[0];
1578
1579				var select = that.s.select;
1580				var pos = that.s.dt.oInstance.fnGetPosition( row );
1581
1582				/* Sub-table must be ignored (odd that the selector won't do this with >) */
1583				if ( row.parentNode != dt.nTBody ) {
1584					return;
1585				}
1586
1587				/* Check that we are actually working with a DataTables controlled row */
1588				if ( dt.oInstance.fnGetData(row) === null ) {
1589				    return;
1590				}
1591
1592				// Shift click, ctrl click and simple click handling to make
1593				// row selection a lot like a file system in desktop OSs
1594				if ( select.type == 'os' ) {
1595					if ( e.ctrlKey || e.metaKey ) {
1596						// Add or remove from the selection
1597						if ( that.fnIsSelected( row ) ) {
1598							that._fnRowDeselect( row, e );
1599						}
1600						else {
1601							that._fnRowSelect( row, e );
1602						}
1603					}
1604					else if ( e.shiftKey ) {
1605						// Add a range of rows, from the last selected row to
1606						// this one
1607						var rowIdxs = that.s.dt.aiDisplay.slice(); // visible rows
1608						var idx1 = $.inArray( select.lastRow, rowIdxs );
1609						var idx2 = $.inArray( pos, rowIdxs );
1610
1611						if ( that.fnGetSelected().length === 0 || idx1 === -1 ) {
1612							// select from top to here - slightly odd, but both
1613							// Windows and Mac OS do this
1614							rowIdxs.splice( $.inArray( pos, rowIdxs )+1, rowIdxs.length );
1615						}
1616						else {
1617							// reverse so we can shift click 'up' as well as down
1618							if ( idx1 > idx2 ) {
1619								var tmp = idx2;
1620								idx2 = idx1;
1621								idx1 = tmp;
1622							}
1623
1624							rowIdxs.splice( idx2+1, rowIdxs.length );
1625							rowIdxs.splice( 0, idx1 );
1626						}
1627
1628						if ( ! that.fnIsSelected( row ) ) {
1629							// Select range
1630							that._fnRowSelect( rowIdxs, e );
1631						}
1632						else {
1633							// Deselect range - need to keep the clicked on row selected
1634							rowIdxs.splice( $.inArray( pos, rowIdxs ), 1 );
1635							that._fnRowDeselect( rowIdxs, e );
1636						}
1637					}
1638					else {
1639						// No cmd or shift click. Deselect current if selected,
1640						// or select this row only
1641						if ( that.fnIsSelected( row ) && that.fnGetSelected().length === 1 ) {
1642							that._fnRowDeselect( row, e );
1643						}
1644						else {
1645							that.fnSelectNone();
1646							that._fnRowSelect( row, e );
1647						}
1648					}
1649				}
1650				else if ( that.fnIsSelected( row ) ) {
1651					that._fnRowDeselect( row, e );
1652				}
1653				else if ( select.type == "single" ) {
1654					that.fnSelectNone();
1655					that._fnRowSelect( row, e );
1656				}
1657				else if ( select.type == "multi" ) {
1658					that._fnRowSelect( row, e );
1659				}
1660
1661				select.lastRow = pos;
1662			} );//.on('selectstart', function () { return false; } );
1663
1664			// Bind a listener to the DataTable for when new rows are created.
1665			// This allows rows to be visually selected when they should be and
1666			// deferred rendering is used.
1667			dt.oApi._fnCallbackReg( dt, 'aoRowCreatedCallback', function (tr, data, index) {
1668				if ( dt.aoData[index]._DTTT_selected ) {
1669					$(tr).addClass( that.classes.select.row );
1670				}
1671			}, 'TableTools-SelectAll' );
1672		}
1673	},
1674
1675	/**
1676	 * Select rows
1677	 *  @param   {*} src Rows to select - see _fnSelectData for a description of valid inputs
1678	 *  @private 
1679	 */
1680	"_fnRowSelect": function ( src, e )
1681	{
1682		var
1683			that = this,
1684			data = this._fnSelectData( src ),
1685			firstTr = data.length===0 ? null : data[0].nTr,
1686			anSelected = [],
1687			i, len;
1688
1689		// Get all the rows that will be selected
1690		for ( i=0, len=data.length ; i<len ; i++ )
1691		{
1692			if ( data[i].nTr )
1693			{
1694				anSelected.push( data[i].nTr );
1695			}
1696		}
1697
1698		// User defined pre-selection function
1699		if ( this.s.select.preRowSelect !== null && !this.s.select.preRowSelect.call(this, e, anSelected, true) )
1700		{
1701			return;
1702		}
1703
1704		// Mark them as selected
1705		for ( i=0, len=data.length ; i<len ; i++ )
1706		{
1707			data[i]._DTTT_selected = true;
1708
1709			if ( data[i].nTr )
1710			{
1711				$(data[i].nTr).addClass( that.classes.select.row );
1712			}
1713		}
1714
1715		// Post-selection function
1716		if ( this.s.select.postSelected !== null )
1717		{
1718			this.s.select.postSelected.call( this, anSelected );
1719		}
1720
1721		TableTools._fnEventDispatch( this, 'select', anSelected, true );
1722	},
1723
1724	/**
1725	 * Deselect rows
1726	 *  @param   {*} src Rows to deselect - see _fnSelectData for a description of valid input
1726s
1727	 *  @private 
1728	 */
1729	"_fnRowDeselect": function ( src, e )
1730	{
1731		var
1732			that = this,
1733			data = this._fnSelectData( src ),
1734			firstTr = data.length===0 ? null : data[0].nTr,
1735			anDeselectedTrs = [],
1736			i, len;
1737
1738		// Get all the rows that will be deselected
1739		for ( i=0, len=data.length ; i<len ; i++ )
1740		{
1741			if ( data[i].nTr )
1742			{
1743				anDeselectedTrs.push( data[i].nTr );
1744			}
1745		}
1746
1747		// User defined pre-selection function
1748		if ( this.s.select.preRowSelect !== null && !this.s.select.preRowSelect.call(this, e, anDeselectedTrs, false) )
1749		{
1750			return;
1751		}
1752
1753		// Mark them as deselected
1754		for ( i=0, len=data.length ; i<len ; i++ )
1755		{
1756			data[i]._DTTT_selected = false;
1757
1758			if ( data[i].nTr )
1759			{
1760				$(data[i].nTr).removeClass( that.classes.select.row );
1761			}
1762		}
1763
1764		// Post-deselection function
1765		if ( this.s.select.postDeselected !== null )
1766		{
1767			this.s.select.postDeselected.call( this, anDeselectedTrs );
1768		}
1769
1770		TableTools._fnEventDispatch( this, 'select', anDeselectedTrs, false );
1771	},
1772
1773	/**
1774	 * Take a data source for row selection and convert it into aoData points for the DT
1775	 *   @param {*} src Can be a single DOM TR node, an array of TR nodes (including a
1776	 *     a jQuery object), a single aoData point from DataTables, an array of aoData
1777	 *     points or an array of aoData indexes
1778	 *   @returns {array} An array of aoData points
1779	 */
1780	"_fnSelectData": function ( src )
1781	{
1782		var out = [], pos, i, iLen;
1783
1784		if ( src.nodeName )
1785		{
1786			// Single node
1787			pos = this.s.dt.oInstance.fnGetPosition( src );
1788			out.push( this.s.dt.aoData[pos] );
1789		}
1790		else if ( typeof src.length !== 'undefined' )
1791		{
1792			// jQuery object or an array of nodes, or aoData points
1793			for ( i=0, iLen=src.length ; i<iLen ; i++ )
1794			{
1795				if ( src[i].nodeName )
1796				{
1797					pos = this.s.dt.oInstance.fnGetPosition( src[i] );
1798					out.push( this.s.dt.aoData[pos] );
1799				}
1800				else if ( typeof src[i] === 'number' )
1801				{
1802					out.push( this.s.dt.aoData[ src[i] ] );
1803				}
1804				else
1805				{
1806					out.push( src[i] );
1807				}
1808			}
1809
1810			return out;
1811		}
1812		else if ( typeof src === 'number' )
1813		{
1814			out.push(this.s.dt.aoData[src]);
1815		}
1816		else
1817		{
1818			// A single aoData point
1819			out.push( src );
1820		}
1821
1822		return out;
1823	},
1824
1825
1826	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1827	 * Text button functions
1828	 */
1829
1830	/**
1831	 * Configure a text based button for interaction events
1832	 *  @method  _fnTextConfig
1833	 *  @param   {Node} nButton Button element which is being considered
1834	 *  @param   {Object} oConfig Button configuration object
1835	 *  @returns void
1836	 *  @private 
1837	 */
1838	"_fnTextConfig": function ( nButton, oConfig )
1839	{
1840		var that = this;
1841
1842		if ( oConfig.fnInit !== null )
1843		{
1844			oConfig.fnInit.call( this, nButton, oConfig );
1845		}
1846
1847		if ( oConfig.sToolTip !== "" )
1848		{
1849			nButton.title = oConfig.sToolTip;
1850		}
1851
1852		$(nButton).hover( function () {
1853			if ( oConfig.fnMouseover !== null )
1854			{
1855				oConfig.fnMouseover.call( this, nButton, oConfig, null );
1856			}
1857		}, function () {
1858			if ( oConfig.fnMouseout !== null )
1859			{
1860				oConfig.fnMouseout.call( this, nButton, oConfig, null );
1861			}
1862		} );
1863
1864		if ( oConfig.fnSelect !== null )
1865		{
1866			TableTools._fnEventListen( this, 'select', function (n) {
1867				oConfig.fnSelect.call( that, nButton, oConfig, n );
1868			} );
1869		}
1870
1871		$(nButton).click( function (e) {
1872			//e.preventDefault();
1873
1874			if ( oConfig.fnClick !== null )
1875			{
1876				oConfig.fnClick.call( that, nButton, oConfig, null, e );
1877			}
1878
1879			/* Provide a complete function to match the behaviour of the flash elements */
1880			if ( oConfig.fnComplete !== null )
1881			{
1882				oConfig.fnComplete.call( that, nButton, oConfig, null, null );
1883			}
1884
1885			that._fnCollectionHide( nButton, oConfig );
1886		} );
1887	},
1888
1889
1890
1891	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1892	 * Flash button functions
1893	 */
1894	
1895	/**
1896	 * Check if the Flash plug-in is available
1897	 *  @method  _fnHasFlash
1898	 *  @returns {boolean} `true` if Flash available, `false` otherwise
1899	 *  @private 
1900	 */
1901	"_fnHasFlash": function ()
1902	{
1903		try {
1904			var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
1905			if (fo) {
1906				return true;
1907			}
1908		}
1909		catch (e) {
1910			if (
1911				navigator.mimeTypes &&
1912				navigator.mimeTypes['application/x-shockwave-flash'] !== undefined &&
1913				navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin
1914			) {
1915				return true;
1916			}
1917		}
1918
1919		return false;
1920	},
1921
1922
1923	/**
1924	 * Configure a flash based button for interaction events
1925	 *  @method  _fnFlashConfig
1926	 *  @param   {Node} nButton Button element which is being considered
1927	 *  @param   {o} oConfig Button configuration object
1928	 *  @returns void
1929	 *  @private 
1930	 */
1931	"_fnFlashConfig": function ( nButton, oConfig )
1932	{
1933		var that = this;
1934		var flash = new ZeroClipboard_TableTools.Client();
1935
1936		if ( oConfig.fnInit !== null )
1937		{
1938			oConfig.fnInit.call( this, nButton, oConfig );
1939		}
1940
1941		flash.setHandCursor( true );
1942
1943		if ( oConfig.sAction == "flash_save" )
1944		{
1945			flash.setAction( 'save' );
1946			flash.setCharSet( (oConfig.sCharSet=="utf16le") ? 'UTF16LE' : 'UTF8' );
1947			flash.setBomInc( oConfig.bBomInc );
1948			flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1949		}
1950		else if ( oConfig.sAction == "flash_pdf" )
1951		{
1952			flash.setAction( 'pdf' );
1953			flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1954		}
1955		else
1956		{
1957			flash.setAction( 'copy' );
1958		}
1959
1960		flash.addEventListener('mouseOver', function(client) {
1961			if ( oConfig.fnMouseover !== null )
1962			{
1963				oConfig.fnMouseover.call( that, nButton, oConfig, flash );
1964			}
1965		} );
1966
1967		flash.addEventListener('mouseOut', function(client) {
1968			if ( oConfig.fnMouseout !== null )
1969			{
1970				oConfig.fnMouseout.call( that, nButton, oConfig, flash );
1971			}
1972		} );
1973
1974		flash.addEventListener('mouseDown', function(client) {
1975			if ( oConfig.fnClick !== null )
1976			{
1977				oConfig.fnClick.call( that, nButton, oConfig, flash );
1978			}
1979		} );
1980
1981		flash.addEventListener('complete', function (client, text) {
1982			if ( oConfig.fnComplete !== null )
1983			{
1984				oConfig.fnComplete.call( that, nButton, oConfig, flash, text );
1985			}
1986			that._fnCollectionHide( nButton, oConfig );
1987		} );
1988
1989		if ( oConfig.fnSelect !== null )
1990		{
1991			TableTools._fnEventListen( this, 'select', function (n) {
1992				oConfig.fnSelect.call( that, nButton, oConfig, n );
1993			} );
1994		}
1995
1996		this._fnFlashGlue( flash, nButton, oConfig.sToolTip );
1997	},
1998
1999
2000	/**
2001	 * Wait until the id is in the DOM before we "glue" the swf. Note that this function will call
2002	 * itself (using setTimeout) until it completes successfully
2003	 *  @method  _fnFlashGlue
2004	 *  @param   {Object} clip Zero clipboard object
2005	 *  @param   {Node} node node to glue swf to
2006	 *  @param   {String} text title of the flash movie
2007	 *  @returns void
2008	 *  @private 
2009	 */
2010	"_fnFlashGlue": function ( flash, node, text )
2011	{
2012		var that = this;
2013		var id = node.getAttribute('id');
2014
2015		if ( document.getElementById(id) )
2016		{
2017			flash.glue( node, text );
2018		}
2019		else
2020		{
2021			setTimeout( function () {
2022				that._fnFlashGlue( flash, node, text );
2023			}, 100 );
2024		}
2025	},
2026
2027
2028	/**
2029	 * Set the text for the flash clip to deal with
2030	 * 
2031	 * This function is required for large information sets. There is a limit on the 
2032	 * amount of data that can be transferred between Javascript and Flash in a single call, so
2033	 * we use this method to build up the text in Flash by sending over chunks. It is estimated
2034	 * that the data limit is around 64k, although it is undocumented, and appears to be different
2035	 * between different flash versions. We chunk at 8KiB.
2036	 *  @method  _fnFlashSetText
2037	 *  @param   {Object} clip the ZeroClipboard object
2038	 *  @param   {String} sData the data to be set
2039	 *  @returns void
2040	 *  @private 
2041	 */
2042	"_fnFlashSetText": function ( clip, sData )
2043	{
2044		var asData = this._fnChunkData( sData, 8192 );
2045
2046		clip.clearText();
2047		for ( var i=0, iLen=asData.length ; i<iLen ; i++ )
2048		{
2049			clip.appendText( asData[i] );
2050		}
2051	},
2052
2053
2054
2055	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2056	 * Data retrieval functions
2057	 */
2058
2059	/**
2060	 * Convert the mixed columns variable into a boolean array the same size as the columns, which
2061	 * indicates which columns we want to include
2062	 *  @method  _fnColumnTargets
2063	 *  @param   {String|Array} mColumns The columns to be included in data retrieval. If a string
2064	 *			 then it can take the value of "visible" or "hidden" (to include all visible or
2065	 *			 hidden columns respectively). Or an array of column indexes
2066	 *  @returns {Array} A boolean array the length of the columns of the table, which each value
2067	 *			 indicating if the column is to be included or not
2068	 *  @private 
2069	 */
2070	"_fnColumnTargets": function ( mColumns )
2071	{
2072		var aColumns = [];
2073		var dt = this.s.dt;
2074		var i, iLen;
2075		var columns = dt.aoColumns;
2076		var columnCount = columns.length;
2077
2078		if ( typeof mColumns == "function" )
2079		{
2080			var a = mColumns.call( this, dt );
2081
2082			for ( i=0, iLen=columnCount ; i<iLen ; i++ )
2083			{
2084				aColumns.push( $.inArray( i, a ) !== -1 ? true : false );
2085			}
2086		}
2087		else if ( typeof mColumns == "object" )
2088		{
2089			for ( i=0, iLen=columnCount ; i<iLen ; i++ )
2090			{
2091				aColumns.push( false );
2092			}
2093
2094			for ( i=0, iLen=mColumns.length ; i<iLen ; i++ )
2095			{
2096				aColumns[ mColumns[i] ] = true;
2097			}
2098		}
2099		else if ( mColumns == "visible" )
2100		{
2101			for ( i=0, iLen=columnCount ; i<iLen ; i++ )
2102			{
2103				aColumns.push( columns[i].bVisible ? true : false );
2104			}
2105		}
2106		else if ( mColumns == "hidden" )
2107		{
2108			for ( i=0, iLen=columnCount ; i<iLen ; i++ )
2109			{
2110				aColumns.push( columns[i].bVisible ? false : true );
2111			}
2112		}
2113		else if ( mColumns == "sortable" )
2114		{
2115			for ( i=0, iLen=columnCount ; i<iLen ; i++ )
2116			{
2117				aColumns.push( columns[i].bSortable ? true : false );
2118			}
2119		}
2120		else /* all */
2121		{
2122			for ( i=0, iLen=columnCount ; i<iLen ; i++ )
2123			{
2124				aColumns.push( true );
2125			}
2126		}
2127
2128		return aColumns;
2129	},
2130
2131
2132	/**
2133	 * New line character(s) depend on the platforms
2134	 *  @method  method
2135	 *  @param   {Object} oConfig Button configuration object - only interested in oConfig.sNewLine
2136	 *  @returns {String} Newline character
2137	 */
2138	"_fnNewline": function ( oConfig )
2139	{
2140		if ( oConfig.sNewLine == "auto" )
2141		{
2142			return navigator.userAgent.match(/Windows/) ? "\r\n" : "\n";
2143		}
2144		else
2145		{
2146			return oConfig.sNewLine;
2147		}
2148	},
2149
2150
2151	/**
2152	 * Get data from DataTables' internals and format it for output
2153	 *  @method  _fnGetDataTablesData
2154	 *  @param   {Object} oConfig Button configuration object
2155	 *  @param   {String} oConfig.sFieldBoundary Field boundary for the data cells in the string
2156	 *  @param   {String} oConfig.sFieldSeperator Field separator for the data cells
2157	 *  @param   {String} oConfig.sNewline New line options
2158	 *  @param   {Mixed} oConfig.mColumns Which columns should be included in the output
2159	 *  @param   {Boolean} oConfig.bHeader Include the header
2160	 *  @param   {Boolean} oConfig.bFooter Include the footer
2161	 *  @param   {Boolean} oConfig.bSelectedOnly Include only the selected rows in the output
2162	 *  @returns {String} Concatenated string of data
2163	 *  @private 
2164	 */
2165	"_fnGetDataTablesData": function ( oConfig )
2166	{
2167		var i, iLen, j, jLen;
2168		var aRow, aData=[], sLoopData='', arr;
2169		var dt = this.s.dt, tr, child;
2170		var regex = new RegExp(oConfig.sFieldBoundary, "g"); /* Do it here for speed */
2171		var aColumnsInc = this._fnColumnTargets( oConfig.mColumns );
2172		var bSelectedOnly = (typeof oConfig.bSelectedOnly != 'undefined') ? oConfig.bSelectedOnly : false;
2173
2174		/*
2175		 * Header
2176		 */
2177		if ( oConfig.bHeader )
2178		{
2179			aRow = [];
2180
2181			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
2182			{
2183				if ( aColumnsInc[i] )
2184				{
2185					sLoopData = dt.aoColumns[i].sTitle.replace(/\n/g," ").replace( /<.*?>/g, "" ).replace(/^\s+|\s+$/g,"");
2186					sLoopData = this._fnHtmlDecode( sLoopData );
2187
2188					aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
2189				}
2190			}
2191
2192			aData.push( aRow.join(oConfig.sFieldSeperator) );
2193		}
2194
2195		bSelectedOnly = true;
2196
2197		/*
2198		 * Body
2199		 */
2200		var aDataIndex;
2201		var aSelected = this.fnGetSelectedIndexes();
2202		bSelectedOnly = this.s.select.type !== "none" && bSelectedOnly && aSelected.length !== 0;
2203
2204		if ( bSelectedOnly ) {
2205			// Use the selected indexes
2206			aDataIndex = aSelected;
2207		}
2208		else if ( DataTable.Api ) {
2209			// 1.10+ style
2210			aDataIndex = new DataTable.Api( dt )
2211				.rows( oConfig.oSelectorOpts )
2212				.indexes()
2213				.flatten()
2214				.toArray();
2215		}
2216		else {
2217			// 1.9- style
2218			aDataIndex = dt.oInstance
2219				.$('tr', oConfig.oSelectorOpts)
2220				.map( function (id, row) {
2221					return dt.oInstance.fnGetPosition( row );
2222				} )
2223				.get();
2224		}
2225
2226		for ( j=0, jLen=aDataIndex.length ; j<jLen ; j++ )
2227		{
2228			tr = dt.aoData[ aDataIndex[j] ].nTr;
2229			aRow = [];
2230
2231			/* Columns */
2232			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
2233			{
2234				if ( aColumnsInc[i] )
2235				{
2236					/* Convert to strings (with small optimisation) */
2237					var mTypeData = dt.oApi._fnGetCellData( dt, aDataIndex[j], i, 'display' );
2238					if ( oConfig.fnCellRender )
2239					{
2240						sLoopData = oConfig.fnCellRender( mTypeData, i, tr, aDataIndex[j] )+"";
2241					}
2242					else if ( typeof mTypeData == "string" )
2243					{
2244						/* Strip newlines, replace img tags with alt attr. and finally strip html... */
2245						sLoopData = mTypeData.replace(/\n/g," ");
2246						sLoopData =
2247						    sLoopData.replace(/<img.*?\s+alt\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+)).*?>/gi,
2248						        '$1$2$3');
2249						sLoopData = sLoopData.replace( /<.*?>/g, "" );
2250					}
2251					else
2252					{
2253						sLoopData = mTypeData+"";
2254					}
2255
2256					/* Trim and clean the data */
2257					sLoopData = sLoopData.replace(/^\s+/, '').replace(/\s+$/, '');
2258					sLoopData = this._fnHtmlDecode( sLoopData );
2259
2260					/* Bound it and add it to the total data */
2261					aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
2262				}
2263			}
2264
2265			aData.push( aRow.join(oConfig.sFieldSeperator) );
2266
2267			/* Details rows from fnOpen */
2268			if ( oConfig.bOpenRows )
2269			{
2270				arr = $.grep(dt.aoOpenRows, function(o) { return o.nParent === tr; });
2271
2272				if ( arr.length === 1 )
2273				{
2274					sLoopData = this._fnBoundData( $('td', arr[0].nTr).html(), oConfig.sFieldBoundary, regex );
2275					aData.push( sLoopData );
2276				}
2277			}
2278		}
2279
2280		/*
2281		 * Footer
2282		 */
2283		if ( oConfig.bFooter && dt.nTFoot !== null )
2284		{
2285			aRow = [];
2286
2287			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
2288			{
2289				if ( aColumnsInc[i] && dt.aoColumns[i].nTf !== null )
2290				{
2291					sLoopData = dt.aoColumns[i].nTf.innerHTML.replace(/\n/g," ").replace( /<.*?>/g, "" );
2292					sLoopData = this._fnHtmlDecode( sLoopData );
2293
2294					aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
2295				}
2296			}
2297
2298			aData.push( aRow.join(oConfig.sFieldSeperator) );
2299		}
2300
2301		var _sLastData = aData.join( this._fnNewline(oConfig) );
2302		return _sLastData;
2303	},
2304
2305
2306	/**
2307	 * Wrap data up with a boundary string
2308	 *  @method  _fnBoundData
2309	 *  @param   {String} sData data to bound
2310	 *  @param   {String} sBoundary bounding char(s)
2311	 *  @param   {RegExp} regex search for the bounding chars - constructed outside for efficiency
2312	 *			 in the loop
2313	 *  @returns {String} bound data
2314	 *  @private 
2315	 */
2316	"_fnBoundData": function ( sData, sBoundary, regex )
2317	{
2318		if ( sBoundary === "" )
2319		{
2320			return sData;
2321		}
2322		else
2323		{
2324			return sBoundary + sData.replace(regex, sBoundary+sBoundary) + sBoundary;
2325		}
2326	},
2327
2328
2329	/**
2330	 * Break a string up into an array of smaller strings
2331	 *  @method  _fnChunkData
2332	 *  @param   {String} sData data to be broken up
2333	 *  @param   {Int} iSize chunk size
2334	 *  @returns {Array} String array of broken up text
2335	 *  @private 
2336	 */
2337	"_fnChunkData": function ( sData, iSize )
2338	{
2339		var asReturn = [];
2340		var iStrlen = sData.length;
2341
2342		for ( var i=0 ; i<iStrlen ; i+=iSize )
2343		{
2344			if ( i+iSize < iStrlen )
2345			{
2346				asReturn.push( sData.substring( i, i+iSize ) );
2347			}
2348			else
2349			{
2350				asReturn.push( sData.substring( i, iStrlen ) );
2351			}
2352		}
2353
2354		return asReturn;
2355	},
2356
2357
2358	/**
2359	 * Decode HTML entities
2360	 *  @method  _fnHtmlDecode
2361	 *  @param   {String} sData encoded string
2362	 *  @returns {String} decoded string
2363	 *  @private 
2364	 */
2365	"_fnHtmlDecode": function ( sData )
2366	{
2367		if ( sData.indexOf('&') === -1 )
2368		{
2369			return sData;
2370		}
2371
2372		var n = document.createElement('div');
2373
2374		return sData.replace( /&([^\s]*?);/g, function( match, match2 ) {
2375			if ( match.substr(1, 1) === '#' )
2376			{
2377				return String.fromCharCode( Number(match2.substr(1)) );
2378			}
2379			else
2380			{
2381				n.innerHTML = match;
2382				return n.childNodes[0].nodeValue;
2383			}
2384		} );
2385	},
2386
2387
2388
2389	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2390	 * Printing functions
2391	 */
2392
2393	/**
2394	 * Show print display
2395	 *  @method  _fnPrintStart
2396	 *  @param   {Event} e Event object
2397	 *  @param   {Object} oConfig Button configuration object
2398	 *  @returns void
2399	 *  @private 
2400	 */
2401	"_fnPrintStart": function ( oConfig )
2402	{
2403	  var that = this;
2404	  var oSetDT = this.s.dt;
2405
2406		/* Parse through the DOM hiding everything that isn't needed for the table */
2407		this._fnPrintHideNodes( oSetDT.nTable );
2408
2409		/* Show the whole table */
2410		this.s.print.saveStart = oSetDT._iDisplayStart;
2411		this.s.print.saveLength = oSetDT._iDisplayLength;
2412
2413		if ( oConfig.bShowAll )
2414		{
2415			oSetDT._iDisplayStart = 0;
2416			oSetDT._iDisplayLength = -1;
2417			if ( oSetDT.oApi._fnCalculateEnd ) {
2418				oSetDT.oApi._fnCalculateEnd( oSetDT );
2419			}
2420			oSetDT.oApi._fnDraw( oSetDT );
2421		}
2422
2423		/* Adjust the display for scrolling which might be done by DataTables */
2424		if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
2425		{
2426			this._fnPrintScrollStart( oSetDT );
2427
2428			// If the table redraws while in print view, the DataTables scrolling
2429			// setup would hide the header, so we need to readd it on draw
2430			$(this.s.dt.nTable).bind('draw.DTTT_Print', function () {
2431				that._fnPrintScrollStart( oSetDT );
2432			} );
2433		}
2434
2435		/* Remove the other DataTables feature nodes - but leave the table! and info div */
2436		var anFeature = oSetDT.aanFeatures;
2437		for ( var cFeature in anFeature )
2438		{
2439			if ( cFeature != 'i' && cFeature != 't' && cFeature.length == 1 )
2440			{
2441				for ( var i=0, iLen=anFeature[cFeature].length ; i<iLen ; i++ )
2442				{
2443					this.dom.print.hidden.push( {
2444						"node": anFeature[cFeature][i],
2445						"display": "block"
2446					} );
2447					anFeature[cFeature][i].style.display = "none";
2448				}
2449			}
2450		}
2451
2452		/* Print class can be used for styling */
2453		$(document.body).addClass( this.classes.print.body );
2454
2455		/* Show information message to let the user know what is happening */
2456		if ( oConfig.sInfo !== "" )
2457		{
2458			this.fnInfo( oConfig.sInfo, 3000 );
2459		}
2460
2461		/* Add a message at the top of the page */
2462		if ( oConfig.sMessage )
2463		{
2464			$('<div/>')
2465				.addClass( this.classes.print.message )
2466				.html( oConfig.sMessage )
2467				.prependTo( 'body' );
2468		}
2469
2470		/* Cache the scrolling and the jump to the top of the page */
2471		this.s.print.saveScroll = $(window).scrollTop();
2472		window.scrollTo( 0, 0 );
2473
2474		/* Bind a key event listener to the document for the escape key -
2475		 * it is removed in the callback
2476		 */
2477		$(document).bind( "keydown.DTTT", function(e) {
2478			/* Only interested in the escape key */
2479			if ( e.keyCode == 27 )
2480			{
2481				e.preventDefault();
2482				that._fnPrintEnd.call( that, e );
2483			}
2484		} );
2485	},
2486
2487
2488	/**
2489	 * Printing is finished, resume normal display
2490	 *  @method  _fnPrintEnd
2491	 *  @param   {Event} e Event object
2492	 *  @returns void
2493	 *  @private 
2494	 */
2495	"_fnPrintEnd": function ( e )
2496	{
2497		var that = this;
2498		var oSetDT = this.s.dt;
2499		var oSetPrint = this.s.print;
2500		var oDomPrint = this.dom.print;
2501
2502		/* Show all hidden nodes */
2503		this._fnPrintShowNodes();
2504
2505		/* Restore DataTables' scrolling */
2506		if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
2507		{
2508			$(this.s.dt.nTable).unbind('draw.DTTT_Print');
2509
2510			this._fnPrintScrollEnd();
2511		}
2512
2513		/* Restore the scroll */
2514		window.scrollTo( 0, oSetPrint.saveScroll );
2515
2516		/* Drop the print message */
2517		$('div.'+this.classes.print.message).remove();
2518
2519		/* Styling class */
2520		$(document.body).removeClass( 'DTTT_Print' );
2521
2522		/* Restore the table length */
2523		oSetDT._iDisplayStart = oSetPrint.saveStart;
2524		oSetDT._iDisplayLength = oSetPrint.saveLength;
2525		if ( oSetDT.oApi._fnCalculateEnd ) {
2526			oSetDT.oApi._fnCalculateEnd( oSetDT );
2527		}
2528		oSetDT.oApi._fnDraw( oSetDT );
2529
2530		$(document).unbind( "keydown.DTTT" );
2531	},
2532
2533
2534	/**
2535	 * Take account of scrolling in DataTables by showing the full table
2536	 *  @returns void
2537	 *  @private 
2538	 */
2539	"_fnPrintScrollStart": function ()
2540	{
2541		var
2542			oSetDT = this.s.dt,
2543			nScrollHeadInner = oSetDT.nScrollHead.getElementsByTagName('div')[0],
2544			nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
2545			nScrollBody = oSetDT.nTable.parentNode,
2546			nTheadSize, nTfootSize;
2547
2548		/* Copy the header in the thead in the body table, this way we show one single table when
2549		 * in print view. Note that this section of code is more or less verbatim from DT 1.7.0
2550		 */
2551		nTheadSize = oSetDT.nTable.getElementsByTagName('thead');
2552		if ( nTheadSize.length > 0 )
2553		{
2554			oSetDT.nTable.removeChild( nTheadSize[0] );
2555		}
2556
2557		if ( oSetDT.nTFoot !== null )
2558		{
2559			nTfootSize = oSetDT.nTable.getElementsByTagName('tfoot');
2560			if ( nTfootSize.length > 0 )
2561			{
2562				oSetDT.nTable.removeChild( nTfootSize[0] );
2563			}
2564		}
2565
2566		nTheadSize = oSetDT.nTHead.cloneNode(true);
2567		oSetDT.nTable.insertBefore( nTheadSize, oSetDT.nTable.childNodes[0] );
2568
2569		if ( oSetDT.nTFoot !== null )
2570		{
2571			nTfootSize = oSetDT.nTFoot.cloneNode(true);
2572			oSetDT.nTable.insertBefore( nTfootSize, oSetDT.nTable.childNodes[1] );
2573		}
2574
2575		/* Now adjust the table's viewport so we can actually see it */
2576		if ( oSetDT.oScroll.sX !== "" )
2577		{
2578			oSetDT.nTable.style.width = $(oSetDT.nTable).outerWidth()+"px";
2579			nScrollBody.style.width = $(oSetDT.nTable).outerWidth()+"px";
2580			nScrollBody.style.overflow = "visible";
2581		}
2582
2583		if ( oSetDT.oScroll.sY !== "" )
2584		{
2585			nScrollBody.style.height = $(oSetDT.nTable).outerHeight()+"px";
2586			nScrollBody.style.overflow = "visible";
2587		}
2588	},
2589
2590
2591	/**
2592	 * Take account of scrolling in DataTables by showing the full table. Note that the redraw of
2593	 * the DataTable that we do will actually deal with the majority of the hard 
2593work here
2594	 *  @returns void
2595	 *  @private 
2596	 */
2597	"_fnPrintScrollEnd": function ()
2598	{
2599		var
2600			oSetDT = this.s.dt,
2601			nScrollBody = oSetDT.nTable.parentNode;
2602
2603		if ( oSetDT.oScroll.sX !== "" )
2604		{
2605			nScrollBody.style.width = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sX );
2606			nScrollBody.style.overflow = "auto";
2607		}
2608
2609		if ( oSetDT.oScroll.sY !== "" )
2610		{
2611			nScrollBody.style.height = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sY );
2612			nScrollBody.style.overflow = "auto";
2613		}
2614	},
2615
2616
2617	/**
2618	 * Resume the display of all TableTools hidden nodes
2619	 *  @method  _fnPrintShowNodes
2620	 *  @returns void
2621	 *  @private 
2622	 */
2623	"_fnPrintShowNodes": function ( )
2624	{
2625	  var anHidden = this.dom.print.hidden;
2626
2627		for ( var i=0, iLen=anHidden.length ; i<iLen ; i++ )
2628		{
2629			anHidden[i].node.style.display = anHidden[i].display;
2630		}
2631		anHidden.splice( 0, anHidden.length );
2632	},
2633
2634
2635	/**
2636	 * Hide nodes which are not needed in order to display the table. Note that this function is
2637	 * recursive
2638	 *  @method  _fnPrintHideNodes
2639	 *  @param   {Node} nNode Element which should be showing in a 'print' display
2640	 *  @returns void
2641	 *  @private 
2642	 */
2643	"_fnPrintHideNodes": function ( nNode )
2644	{
2645		var anHidden = this.dom.print.hidden;
2646
2647		var nParent = nNode.parentNode;
2648		var nChildren = nParent.childNodes;
2649		for ( var i=0, iLen=nChildren.length ; i<iLen ; i++ )
2650		{
2651			if ( nChildren[i] != nNode && nChildren[i].nodeType == 1 )
2652			{
2653				/* If our node is shown (don't want to show nodes which were previously hidden) */
2654				var sDisplay = $(nChildren[i]).css("display");
2655				if ( sDisplay != "none" )
2656				{
2657					/* Cache the node and it's previous state so we can restore it */
2658					anHidden.push( {
2659						"node": nChildren[i],
2660						"display": sDisplay
2661					} );
2662					nChildren[i].style.display = "none";
2663				}
2664			}
2665		}
2666
2667		if ( nParent.nodeName.toUpperCase() != "BODY" )
2668		{
2669			this._fnPrintHideNodes( nParent );
2670		}
2671	}
2672};
2673
2674
2675
2676/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2677 * Static variables
2678 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2679
2680/**
2681 * Store of all instances that have been created of TableTools, so one can look up other (when
2682 * there is need of a master)
2683 *  @property _aInstances
2684 *  @type	 Array
2685 *  @default  []
2686 *  @private
2687 */
2688TableTools._aInstances = [];
2689
2690
2691/**
2692 * Store of all listeners and their callback functions
2693 *  @property _aListeners
2694 *  @type	 Array
2695 *  @default  []
2696 */
2697TableTools._aListeners = [];
2698
2699
2700
2701/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2702 * Static methods
2703 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2704
2705/**
2706 * Get an array of all the master instances
2707 *  @method  fnGetMasters
2708 *  @returns {Array} List of master TableTools instances
2709 *  @static
2710 */
2711TableTools.fnGetMasters = function ()
2712{
2713	var a = [];
2714	for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2715	{
2716		if ( TableTools._aInstances[i].s.master )
2717		{
2718			a.push( TableTools._aInstances[i] );
2719		}
2720	}
2721	return a;
2722};
2723
2724/**
2725 * Get the master instance for a table node (or id if a string is given)
2726 *  @method  fnGetInstance
2727 *  @returns {Object} ID of table OR table node, for which we want the TableTools instance
2728 *  @static
2729 */
2730TableTools.fnGetInstance = function ( node )
2731{
2732	if ( typeof node != 'object' )
2733	{
2734		node = document.getElementById(node);
2735	}
2736
2737	for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2738	{
2739		if ( TableTools._aInstances[i].s.master && TableTools._aInstances[i].dom.table == node )
2740		{
2741			return TableTools._aInstances[i];
2742		}
2743	}
2744	return null;
2745};
2746
2747
2748/**
2749 * Add a listener for a specific event
2750 *  @method  _fnEventListen
2751 *  @param   {Object} that Scope of the listening function (i.e. 'this' in the caller)
2752 *  @param   {String} type Event type
2753 *  @param   {Function} fn Function
2754 *  @returns void
2755 *  @private
2756 *  @static
2757 */
2758TableTools._fnEventListen = function ( that, type, fn )
2759{
2760	TableTools._aListeners.push( {
2761		"that": that,
2762		"type": type,
2763		"fn": fn
2764	} );
2765};
2766
2767
2768/**
2769 * An event has occurred - look up every listener and fire it off. We check that the event we are
2770 * going to fire is attached to the same table (using the table node as reference) before firing
2771 *  @method  _fnEventDispatch
2772 *  @param   {Object} that Scope of the listening function (i.e. 'this' in the caller)
2773 *  @param   {String} type Event type
2774 *  @param   {Node} node Element that the event occurred on (may be null)
2775 *  @param   {boolean} [selected] Indicate if the node was selected (true) or deselected (false)
2776 *  @returns void
2777 *  @private
2778 *  @static
2779 */
2780TableTools._fnEventDispatch = function ( that, type, node, selected )
2781{
2782	var listeners = TableTools._aListeners;
2783	for ( var i=0, iLen=listeners.length ; i<iLen ; i++ )
2784	{
2785		if ( that.dom.table == listeners[i].that.dom.table && listeners[i].type == type )
2786		{
2787			listeners[i].fn( node, selected );
2788		}
2789	}
2790};
2791
2792
2793
2794
2795
2796
2797/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2798 * Constants
2799 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2800
2801
2802
2803TableTools.buttonBase = {
2804	// Button base
2805	"sAction": "text",
2806	"sTag": "default",
2807	"sLinerTag": "default",
2808	"sButtonClass": "DTTT_button_text",
2809	"sButtonText": "Button text",
2810	"sTitle": "",
2811	"sToolTip": "",
2812
2813	// Common button specific options
2814	"sCharSet": "utf8",
2815	"bBomInc": false,
2816	"sFileName": "*.csv",
2817	"sFieldBoundary": "",
2818	"sFieldSeperator": "\t",
2819	"sNewLine": "auto",
2820	"mColumns": "all", /* "all", "visible", "hidden" or array of column integers */
2821	"bHeader": true,
2822	"bFooter": true,
2823	"bOpenRows": false,
2824	"bSelectedOnly": false,
2825	"oSelectorOpts": undefined, // See http://datatables.net/docs/DataTables/1.9.4/#$ for full options
2826
2827	// Callbacks
2828	"fnMouseover": null,
2829	"fnMouseout": null,
2830	"fnClick": null,
2831	"fnSelect": null,
2832	"fnComplete": null,
2833	"fnInit": null,
2834	"fnCellRender": null
2835};
2836
2837
2838/**
2839 * @namespace Default button configurations
2840 */
2841TableTools.BUTTONS = {
2842	"csv": $.extend( {}, TableTools.buttonBase, {
2843		"sAction": "flash_save",
2844		"sButtonClass": "DTTT_button_csv",
2845		"sButtonText": "CSV",
2846		"sFieldBoundary": '"',
2847		"sFieldSeperator": ",",
2848		"fnClick": function( nButton, oConfig, flash ) {
2849			this.fnSetText( flash, this.fnGetTableData(oConfig) );
2850		}
2851	} ),
2852
2853	"xls": $.extend( {}, TableTools.buttonBase, {
2854		"sAction": "flash_save",
2855		"sCharSet": "utf16le",
2856		"bBomInc": true,
2857		"sButtonClass": "DTTT_button_xls",
2858		"sButtonText": "Excel",
2859		"fnClick": function( nButton, oConfig, flash ) {
2860			this.fnSetText( flash, this.fnGetTableData(oConfig) );
2861		}
2862	} ),
2863
2864	"copy": $.extend( {}, TableTools.buttonBase, {
2865		"sAction": "flash_copy",
2866		"sButtonClass": "DTTT_button_copy",
2867		"sButtonText": "Copy",
2868		"fnClick": function( nButton, oConfig, flash ) {
2869			this.fnSetText( flash, this.fnGetTableData(oConfig) );
2870		},
2871		"fnComplete": function(nButton, oConfig, flash, text) {
2872			var lines = text.split('\n').length;
2873            if (oConfig.bHeader) lines--;
2874            if (this.s.dt.nTFoot !== null && oConfig.bFooter) lines--;
2875			var plural = (lines==1) ? "" : "s";
2876			this.fnInfo( '<h6>Table copied</h6>'+
2877				'<p>Copied '+lines+' row'+plural+' to the clipboard.</p>',
2878				1500
2879			);
2880		}
2881	} ),
2882
2883	"pdf": $.extend( {}, TableTools.buttonBase, {
2884		"sAction": "flash_pdf",
2885		"sNewLine": "\n",
2886		"sFileName": "*.pdf",
2887		"sButtonClass": "DTTT_button_pdf",
2888		"sButtonText": "PDF",
2889		"sPdfOrientation": "portrait",
2890		"sPdfSize": "A4",
2891		"sPdfMessage": "",
2892		"fnClick": function( nButton, oConfig, flash ) {
2893			this.fnSetText( flash,
2894				"title:"+ this.fnGetTitle(oConfig) +"\n"+
2895				"message:"+ oConfig.sPdfMessage +"\n"+
2896				"colWidth:"+ this.fnCalcColRatios(oConfig) +"\n"+
2897				"orientation:"+ oConfig.sPdfOrientation +"\n"+
2898				"size:"+ oConfig.sPdfSize +"\n"+
2899				"--/TableToolsOpts--\n" +
2900				this.fnGetTableData(oConfig)
2901			);
2902		}
2903	} ),
2904
2905	"print": $.extend( {}, TableTools.buttonBase, {
2906		"sInfo": "<h6>Print view</h6><p>Please use your browser's print function to "+
2907		  "print this table. Press escape when finished.</p>",
2908		"sMessage": null,
2909		"bShowAll": true,
2910		"sToolTip": "View print view",
2911		"sButtonClass": "DTTT_button_print",
2912		"sButtonText": "Print",
2913		"fnClick": function ( nButton, oConfig ) {
2914			this.fnPrint( true, oConfig );
2915		}
2916	} ),
2917
2918	"text": $.extend( {}, TableTools.buttonBase ),
2919
2920	"select": $.extend( {}, TableTools.buttonBase, {
2921		"sButtonText": "Select button",
2922		"fnSelect": function( nButton, oConfig ) {
2923			if ( this.fnGetSelected().length !== 0 ) {
2924				$(nButton).removeClass( this.classes.buttons.disabled );
2925			} else {
2926				$(nButton).addClass( this.classes.buttons.disabled );
2927			}
2928		},
2929		"fnInit": function( nButton, oConfig ) {
2930			$(nButton).addClass( this.classes.buttons.disabled );
2931		}
2932	} ),
2933
2934	"select_single": $.extend( {}, TableTools.buttonBase, {
2935		"sButtonText": "Select button",
2936		"fnSelect": function( nButton, oConfig ) {
2937			var iSelected = this.fnGetSelected().length;
2938			if ( iSelected == 1 ) {
2939				$(nButton).removeClass( this.classes.buttons.disabled );
2940			} else {
2941				$(nButton).addClass( this.classes.buttons.disabled );
2942			}
2943		},
2944		"fnInit": function( nButton, oConfig ) {
2945			$(nButton).addClass( this.classes.buttons.disabled );
2946		}
2947	} ),
2948
2949	"select_all": $.extend( {}, TableTools.buttonBase, {
2950		"sButtonText": "Select all",
2951		"fnClick": function( nButton, oConfig ) {
2952			this.fnSelectAll();
2953		},
2954		"fnSelect": function( nButton, oConfig ) {
2955			if ( this.fnGetSelected().length == this.s.dt.fnRecordsDisplay() ) {
2956				$(nButton).addClass( this.classes.buttons.disabled );
2957			} else {
2958				$(nButton).removeClass( this.classes.buttons.disabled );
2959			}
2960		}
2961	} ),
2962
2963	"select_none": $.extend( {}, TableTools.buttonBase, {
2964		"sButtonText": "Deselect all",
2965		"fnClick": function( nButton, oConfig ) {
2966			this.fnSelectNone();
2967		},
2968		"fnSelect": function( nButton, oConfig ) {
2969			if ( this.fnGetSelected().length !== 0 ) {
2970				$(nButton).removeClass( this.classes.buttons.disabled );
2971			} else {
2972				$(nButton).addClass( this.classes.buttons.disabled );
2973			}
2974		},
2975		"fnInit": function( nButton, oConfig ) {
2976			$(nButton).addClass( this.classes.buttons.disabled );
2977		}
2978	} ),
2979
2980	"ajax": $.extend( {}, TableTools.buttonBase, {
2981		"sAjaxUrl": "/xhr.php",
2982		"sButtonText": "Ajax button",
2983		"fnClick": function( nButton, oConfig ) {
2984			var sData = this.fnGetTableData(oConfig);
2985			$.ajax( {
2986				"url": oConfig.sAjaxUrl,
2987				"data": [
2988					{ "name": "tableData", "value": sData }
2989				],
2990				"success": oConfig.fnAjaxComplete,
2991				"dataType": "json",
2992				"type": "POST",
2993				"cache": false,
2994				"error": function () {
2995					alert( "Error detected when sending table data to server" );
2996				}
2997			} );
2998		},
2999		"fnAjaxComplete": function( json ) {
3000			alert( 'Ajax complete' );
3001		}
3002	} ),
3003
3004	"div": $.extend( {}, TableTools.buttonBase, {
3005		"sAction": "div",
3006		"sTag": "div",
3007		"sButtonClass": "DTTT_nonbutton",
3008		"sButtonText": "Text button"
3009	} ),
3010
3011	"collection": $.extend( {}, TableTools.buttonBase, {
3012		"sAction": "collection",
3013		"sButtonClass": "DTTT_button_collection",
3014		"sButtonText": "Collection",
3015		"fnClick": function( nButton, oConfig ) {
3016			this._fnCollectionShow(nButton, oConfig);
3017		}
3018	} )
3019};
3020/*
3021 *  on* callback parameters:
3022 *     1. node - button element
3023 *     2. object - configuration object for this button
3024 *     3. object - ZeroClipboard reference (flash button only)
3025 *     4. string - Returned string from Flash (flash button only - and only on 'complete')
3026 */
3027
3028// Alias to match the other plug-ins styling
3029TableTools.buttons = TableTools.BUTTONS;
3030
3031
3032/**
3033 * @namespace Classes used by TableTools - allows the styles to be override easily.
3034 *   Note that when TableTools initialises it will take a copy of the classes object
3035 *   and will use its internal copy for the remainder of its run time.
3036 */
3037TableTools.classes = {
3038	"container": "DTTT_container",
3039	"buttons": {
3040		"normal": "DTTT_button",
3041		"disabled": "DTTT_disabled"
3042	},
3043	"collection": {
3044		"container": "DTTT_collection",
3045		"background": "DTTT_collection_background",
3046		"buttons": {
3047			"normal": "DTTT_button",
3048			"disabled": "DTTT_disabled"
3049		}
3050	},
3051	"select": {
3052		"table": "DTTT_selectable",
3053		"row": "DTTT_selected selected"
3054	},
3055	"print": {
3056		"body": "DTTT_Print",
3057		"info": "DTTT_print_info",
3058		"message": "DTTT_PrintMessage"
3059	}
3060};
3061
3062
3063/**
3064 * @namespace ThemeRoller classes - built in for compatibility with DataTables' 
3065 *   bJQueryUI option.
3066 */
3067TableTools.classes_themeroller = {
3068	"container": "DTTT_container ui-buttonset ui-buttonset-multi",
3069	"buttons": {
3070		"normal": "DTTT_button ui-button ui-state-default"
3071	},
3072	"collection": {
3073		"container": "DTTT_collection ui-buttonset ui-buttonset-multi"
3074	}
3075};
3076
3077
3078/**
3079 * @namespace TableTools default settings for initialisation
3080 */
3081TableTools.DEFAULTS = {
3082	"sSwfPath":        "../swf/copy_csv_xls_pdf.swf",
3083	"sRowSelect":      "none",
3084	"sRowSelector":    "tr",
3085	"sSelectedClass":  null,
3086	"fnPreRowSelect":  null,
3087	"fnRowSelected":   null,
3088	"fnRowDeselected": null,
3089	"aButtons":        [ "copy", "csv", "xls", "pdf", "print" ],
3090	"oTags": {
3091		"container": "div",
3092		"button": "a", // We really want to use buttons here, but Firefox and IE ignore the
3093		                 // click on the Flash element in the button (but not mouse[in|out]).
3094		"liner": "span",
3095		"collection": {
3096			"container": "div",
3097			"button": "a",
3098			"liner": "span"
3099		}
3100	}
3101};
3102
3103// Alias to match the other plug-ins
3104TableTools.defaults = TableTools.DEFAULTS;
3105
3106
3107/**
3108 * Name of this class
3109 *  @constant CLASS
3110 *  @type	 String
3111 *  @default  TableTools
3112 */
3113TableTools.prototype.CLASS = "TableTools";
3114
3115
3116/**
3117 * TableTools version
3118 *  @constant  VERSION
3119 *  @type	  String
3120 *  @default   See code
3121 */
3122TableTools.version = "2.2.4";
3123
3124
3125
3126// DataTables 1.10 API
3127// 
3128// This will be extended in a big way in in TableTools 3 to provide API methods
3129// such as rows().select() and rows.selected() etc, but for the moment the
3130// tabletools() method simply returns the instance.
3131
3132if ( $.fn.dataTable.Api ) {
3133	$.fn.dataTable.Api.register( 'tabletools()', function () {
3134		var tt = null;
3135
3136		if ( this.context.length > 0 ) {
3137			tt = TableTools.fnGetInstance( this.context[0].nTable );
3138		}
3139
3140		return tt;
3141	} );
3142}
3143
3144
3145
3146
3147/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3148 * Initialisation
3149 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3150
3151/*
3152 * Register a new feature with DataTables
3153 */
3154if ( typeof $.fn.dataTable == "function" &&
3155	 typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
3156	 $.fn.dataTableExt.fnVersionCheck('1.9.0') )
3157{
3158	$.fn.dataTableExt.aoFeatures.push( {
3159		"fnInit": function( oDTSettings ) {
3160			var init = oDTSettings.oInit;
3161			var opts = init ?
3162				init.tableTools || init.oTableTools || {} :
3163				{};
3164
3165			return new TableTools( oDTSettings.oInstance, opts ).dom.container;
3166		},
3167		"cFeature": "T",
3168		"sFeature": "TableTools"
3169	} );
3170}
3171else
3172{
3173	alert( "Warning: TableTools requires DataTables 1.9.0 or newer - www.datatables.net/download");
3174}
3175
3176$.fn.DataTable.TableTools = TableTools;
3177
3178})(jQuery, window, document);
3179
3180/*
3181 * Register a new feature with DataTables
3182 */
3183if ( typeof $.fn.dataTable == "function" &&
3184	 typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
3185	 $.fn.dataTableExt.fnVersionCheck('1.9.0') )
3186{
3187	$.fn.dataTableExt.aoFeatures.push( {
3188		"fnInit": function( oDTSettings ) {
3189			var oOpts = typeof oDTSettings.oInit.oTableTools != 'undefined' ?
3190				oDTSettings.oInit.oTableTools : {};
3191
3192			var oTT = new TableTools( oDTSettings.oInstance, oOpts );
3193			TableTools._aInstances.push( oTT );
3194
3195			return oTT.dom.container;
3196		},
3197		"cFeature": "T",
3198		"sFeature": "TableTools"
3199	} );
3200}
3201else
3202{
3203	alert( "Warning: TableTools 2 requires DataTables 1.9.0 or newer - www.datatables.net/download");
3204}
3205
3206
3207$.fn.dataTable.TableTools = TableTools;
3208$.fn.DataTable.TableTools = TableTools;
3209
3210
3211return TableTools;
3212}; // /factory
3213
3214
3215// Define as an AMD module if possible
3216if ( typeof define === 'function' && define.amd ) {
3217	define( ['jquery', 'datatables'], factory );
3218}
3219else if ( typeof exports === 'object' ) {
3220    // Node/CommonJS
3221    factory( require('jquery'), require('datatables') );
3222}
3223else if ( jQuery && !jQuery.fn.dataTable.TableTools ) {
3224	// Otherwise simply initialise as normal, stopping multiple evaluation
3225	factory( jQuery, jQuery.fn.dataTable );
3226}
3227
3228
3229})(window, document);
3230

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.