PageSourceSearch

https://ca.radicenter.eu/assets/templates/radicenter3/js/dataTables.tableTools.js?ver=1759400188

js radicenter.eu collected 2026-10-08 17:06:30 UTC 81,267 bytes, 3,084 lines download raw bytes

1/*! TableTools 2.2.1
2 * 2009-2014 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.1
12 * @file        dataTables.tableTools.js
13 * @author      SpryMedia Ltd (www.sprymedia.co.uk)
14 * @contact     www.sprymedia.co.uk/contact
15 * @copyright   Copyright 2009-2014 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-2014 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	this._fnConstruct( oOpts );
742
743	return this;
744};
745
746
747
748TableTools.prototype = {
749	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
750	 * Public methods
751	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
752
753	/**
754	 * Retreieve the settings object from an instance
755	 *  @returns {array} List of TR nodes which are currently selected
756	 *  @param {boolean} [filtered=false] Get only selected rows which are  
757	 *    available given the filtering applied to the table. By default
758	 *    this is false -  i.e. all rows, regardless of filtering are 
759	      selected.
760	 */
761	"fnGetSelected": function ( filtered )
762	{
763		var
764			out = [],
765			data = this.s.dt.aoData,
766			displayed = this.s.dt.aiDisplay,
767			i, iLen;
768
769		if ( filtered )
770		{
771			// Only consider filtered rows
772			for ( i=0, iLen=displayed.length ; i<iLen ; i++ )
773			{
774				if ( data[ displayed[i] ]._DTTT_selected )
775				{
776					out.push( data[ displayed[i] ].nTr );
777				}
778			}
779		}
780		else
781		{
782			// Use all rows
783			for ( i=0, iLen=data.length ; i<iLen ; i++ )
784			{
785				if ( data[i]._DTTT_selected )
786				{
787					out.push( data[i].nTr );
788				}
789			}
790		}
791
792		return out;
793	},
794
795
796	/**
797	 * Get the data source objects/arrays from DataTables for the selected rows (same as
798	 * fnGetSelected followed by fnGetData on each row from the table)
799	 *  @returns {array} Data from the TR nodes which are currently selected
800	 */
801	"fnGetSelectedData": function ()
802	{
803		var out = [];
804		var data=this.s.dt.aoData;
805		var i, iLen;
806
807		for ( i=0, iLen=data.length ; i<iLen ; i++ )
808		{
809			if ( data[i]._DTTT_selected )
810			{
811				out.push( this.s.dt.oInstance.fnGetData(i) );
812			}
813		}
814
815		return out;
816	},
817
818
819	/**
820	 * Check to see if a current row is selected or not
821	 *  @param {Node} n TR node to check if it is currently selected or not
822	 *  @returns {Boolean} true if select, false otherwise
823	 */
824	"fnIsSelected": function ( n )
825	{
826		var pos = this.s.dt.oInstance.fnGetPosition( n );
827		return (this.s.dt.aoData[pos]._DTTT_selected===true) ? true : false;
828	},
829
830
831	/**
832	 * Select all rows in the table
833	 *  @param {boolean} [filtered=false] Select only rows which are available 
834	 *    given the filtering applied to the table. By default this is false - 
835	 *    i.e. all rows, regardless of filtering are selected.
836	 */
837	"fnSelectAll": function ( filtered )
838	{
839		var s = this._fnGetMasterSettings();
840
841		this._fnRowSelect( (filtered === true) ?
842			s.dt.aiDisplay :
843			s.dt.aoData
844		);
845	},
846
847
848	/**
849	 * Deselect all rows in the table
850	 *  @param {boolean} [filtered=false] Deselect only rows which are available 
851	 *    given the filtering applied to the table. By default this is false - 
852	 *    i.e. all rows, regardless of filtering are deselected.
853	 */
854	"fnSelectNone": function ( filtered )
855	{
856		var s = this._fnGetMasterSettings();
857
858		this._fnRowDeselect( this.fnGetSelected(filtered) );
859	},
860
861
862	/**
863	 * Select row(s)
864	 *  @param {node|object|array} n The row(s) to select. Can be a single DOM
865	 *    TR node, an array of TR nodes or a jQuery object.
866	 */
867	"fnSelect": function ( n )
868	{
869		if ( this.s.select.type == "single" )
870		{
871			this.fnSelectNone();
872			this._fnRowSelect( n );
873		}
874		else
875		{
876			this._fnRowSelect( n );
877		}
878	},
879
880
881	/**
882	 * Deselect row(s)
883	 *  @param {node|object|array} n The row(s) to deselect. Can be a single DOM
884	 *    TR node, an array of TR nodes or a jQuery object.
885	 */
886	"fnDeselect": function ( n )
887	{
888		this._fnRowDeselect( n );
889	},
890
891
892	/**
893	 * Get the title of the document - useful for file names. The title is retrieved from either
894	 * the configuration object's 'title' parameter, or the HTML document title
895	 *  @param   {Object} oConfig Button configuration object
896	 *  @returns {String} Button title
897	 */
898	"fnGetTitle": function( oConfig )
899	{
900		var sTitle = "";
901		if ( typeof oConfig.sTitle != 'undefined' && oConfig.sTitle !== "" ) {
902			sTitle = oConfig.sTitle;
903		} else {
904			var anTitle = document.getElementsByTagName('title');
905			if ( anTitle.length > 0 )
906			{
907				sTitle = anTitle[0].innerHTML;
908			}
909		}
910
911		/* Strip characters which the OS will object to - checking for UTF8 support in the scripting
912		 * engine
913		 */
914		if ( "\u00A1".toString().length < 4 ) {
915			return sTitle.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
916		} else {
917			return sTitle.replace(/[^a-zA-Z0-9_\.,\-_ !\(\)]/g, "");
918		}
919	},
920
921
922	/**
923	 * Calculate a unity array with the column width by proportion for a set of columns to be
924	 * included for a button. This is particularly useful for PDF creation, where we can use the
925	 * column widths calculated by the browser to size the columns in the PDF.
926	 *  @param   {Object} oConfig Button configuration object
927	 *  @returns {Array} Unity array of column ratios
928	 */
929	"fnCalcColRatios": function ( oConfig )
930	{
931		var
932			aoCols = this.s.dt.aoColumns,
933			aColumnsInc = this._fnColumnTargets( oConfig.mColumns ),
934			aColWidths = [],
935			iWidth = 0, iTotal = 0, i, iLen;
936
937		for ( i=0, iLen=aColumnsInc.length ; i<iLen ; i++ )
938		{
939			if ( aColumnsInc[i] )
940			{
941				iWidth = aoCols[i].nTh.offsetWidth;
942				iTotal += iWidth;
943				aColWidths.push( iWidth );
944			}
945		}
946
947		for ( i=0, iLen=aColWidths.length ; i<iLen ; i++ )
948		{
949			aColWidths[i] = aColWidths[i] / iTotal;
950		}
951
952		return aColWidths.join('\t');
953	},
954
955
956	/**
957	 * Get the information contained in a table as a string
958	 *  @param   {Object} oConfig Button configuration object
959	 *  @returns {String} Table data as a string
960	 */
961	"fnGetTableData": function ( oConfig )
962	{
963		/* In future this could be used to get data from a plain HTML source as well as DataTables */
964		if ( this.s.dt )
965		{
966			return this._fnGetDataTablesData( oConfig );
967		}
968	},
969
970
971	/**
972	 * Pass text to a flash button instance, which will be used on the button's click handler
973	 *  @param   {Object} clip Flash button object
974	 *  @param   {String} text Text to set
975	 */
976	"fnSetText": function ( clip, text )
977	{
978		this._fnFlashSetText( clip, text );
979	},
980
981
982	/**
983	 * Resize the flash elements of the buttons attached to this TableTools instance - this is
984	 * useful for when initialising TableTools when it is hidden (display:none) since sizes can't
985	 * be calculated at that time.
986	 */
987	"fnResizeButtons": function ()
988	{
989		for ( var cli in ZeroClipboard_TableTools.clients )
990		{
991			if ( cli )
992			{
993				var client = ZeroClipboard_TableTools.clients[cli];
994				if ( typeof client.domElement != 'undefined' &&
995					 client.domElement.parentNode )
996				{
997					client.positionElement();
998				}
999			}
1000		}
1001	},
1002
1003
1004	/**
1005	 * Check to see if any of the ZeroClipboard client's attached need to be resized
1006	 */
1007	"fnResizeRequired": function ()
1008	{
1009		for ( var cli in ZeroClipboard_TableTools.clients )
1010		{
1011			if ( cli )
1012			{
1013				var client = ZeroClipboard_TableTools.clients[cli];
1014				if ( typeof client.domElement != 'undefined' &&
1015					 client.domElement.parentNode == this.dom.container &&
1016					 client.sized === false )
1017				{
1018					return true;
1019				}
1020			}
1021		}
1022		return false;
1023	},
1024
1025
1026	/**
1027	 * Programmatically enable or disable the print view
1028	 *  @param {boolean} [bView=true] Show the print view if true or not given. If false, then
1029	 *    terminate the print view and return to normal.
1030	 *  @param {object} [oConfig={}] Configuration for the print view
1031	 *  @param {boolean} [oConfig.bShowAll=false] Show all rows in the table if true
1032	 *  @param {string} [oConfig.sInfo] Information message, displayed as an overlay to the
1033	 *    user to let them know what the print view is.
1034	 *  @param {string} [oConfig.sMessage] HTML string to show at the top of the document - will
1035	 *    be included in the printed document.
1036	 */
1037	"fnPrint": function ( bView, oConfig )
1038	{
1039		if ( oConfig === undefined )
1040		{
1041			oConfig = {};
1042		}
1043
1044		if ( bView === undefined || bView )
1045		{
1046			this._fnPrintStart( oConfig );
1047		}
1048		else
1049		{
1050			this._fnPrintEnd();
1051		}
1052	},
1053
1054
1055	/**
1056	 * Show a message to the end user which is nicely styled
1057	 *  @param {string} message The HTML string to show to the user
1058	 *  @param {int} time The duration the message is to be shown on screen for (mS)
1059	 */
1060	"fnInfo": function ( message, time ) {
1061		var info = $('<div/>')
1062			.addClass( this.classes.print.info )
1063			.html( message )
1064			.appendTo( 'body' );
1065
1066		setTimeout( function() {
1067			info.fadeOut( "normal", function() {
1068				info.remove();
1069			} );
1070		}, time );
1071	},
1072
1073
1074
1075	/**
1076	 * Get the container element of the instance for attaching to the DOM
1077	 *   @returns {node} DOM node
1078	 */
1079	"fnContainer": function () {
1080		return this.dom.container;
1081	},
1082
1083
1084
1085	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1086	 * Private methods (they are of course public in JS, but recommended as private)
1087	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1088
1089	/**
1090	 * Constructor logic
1091	 *  @method  _fnConstruct
1092	 *  @param   {Object} oOpts Same as TableTools constructor
1093	 *  @returns void
1094	 *  @private 
1095	 */
1096	"_fnConstruct": function ( oOpts )
1097	{
1098		var that = this;
1099
1100		this._fnCustomiseSettings( oOpts );
1101
1102		/* Container element */
1103		this.dom.container = document.createElement( this.s.tags.container );
1104		this.dom.container.className = this.classes.container;
1105
1106		/* Row selection config */
1107		if ( this.s.select.type != 'none' )
1108		{
1109			this._fnRowSelectConfig();
1110		}
1111
1112		/* Buttons */
1113		this._fnButtonDefinations( this.s.buttonSet, this.dom.container );
1114
1115		/* Destructor */
1116		this.s.dt.aoDestroyCallback.push( {
1117			"sName": "TableTools",
1118			"fn": function () {
1119				$(that.s.dt.nTBody).off( 'click.DTTT_Select', 'tr' );
1120				$(that.dom.container).empty();
1121
1122				// Remove the instance
1123				var idx = $.inArray( that, TableTools._aInstances );
1124				if ( idx !== -1 ) {
1125					TableTools._aInstances.splice( idx, 1 );
1126				}
1127			}
1128		} );
1129	},
1130
1131
1132	/**
1133	 * Take the user defined settings and the default settings and combine them.
1134	 *  @method  _fnCustomiseSettings
1135	 *  @param   {Object} oOpts Same as TableTools constructor
1136	 *  @returns void
1137	 *  @private 
1138	 */
1139	"_fnCustomiseSettings": function ( oOpts )
1140	{
1141		/* Is this the master control instance or not? */
1142		if ( typeof this.s.dt._TableToolsInit == 'undefined' )
1143		{
1144			this.s.master = true;
1145			this.s.dt._TableToolsInit = true;
1146		}
1147
1148		/* We can use the table node from comparisons to group controls */
1149		this.dom.table = this.s.dt.nTable;
1150
1151		/* Clone the defaults and then the user options */
1152		this.s.custom = $.extend( {}, TableTools.DEFAULTS, oOpts );
1153
1154		/* Flash file location */
1155		this.s.swfPath = this.s.custom.sSwfPath;
1156		if ( typeof ZeroClipboard_TableTools != 'undefined' )
1157		{
1158			ZeroClipboard_TableTools.moviePath = this.s.swfPath;
1159		}
1160
1161		/* Table row selecting */
1162		this.s.select.type = this.s.custom.sRowSelect;
1163		this.s.select.preRowSelect = this.s.custom.fnPreRowSelect;
1164		this.s.select.postSelected = this.s.custom.fnRowSelected;
1165		this.s.select.postDeselected = this.s.custom.fnRowDeselected;
1166
1167		// Backwards compatibility - allow the user to specify a custom class in the initialiser
1168		if ( this.s.custom.sSelectedClass )
1169		{
1170			this.classes.select.row = this.s.custom.sSelectedClass;
1171		}
1172
1173		this.s.tags = this.s.custom.oTags;
1174
1175		/* Button set */
1176		this.s.buttonSet = this.s.custom.aButtons;
1177	},
1178
1179
1180	/**
1181	 * Take the user input arrays and expand them to be fully defined, and then add them to a given
1182	 * DOM element
1183	 *  @method  _fnButtonDefinations
1184	 *  @param {array} buttonSet Set of user defined buttons
1185	 *  @param {node} wrapper Node to add the created buttons to
1186	 *  @returns void
1187	 *  @private 
1188	 */
1189	"_fnButtonDefinations": function ( buttonSet, wrapper )
1190	{
1191		var buttonDef;
1192
1193		for ( var i=0, iLen=buttonSet.length ; i<iLen ; i++ )
1194		{
1195			if ( typeof buttonSet[i] == "string" )
1196			{
1197				if ( typeof TableTools.BUTTONS[ buttonSet[i] ] == 'undefined' )
1198				{
1199					alert( "TableTools: Warning - unknown button type: "+buttonSet[i] );
1200					continue;
1201				}
1202				buttonDef = $.extend( {}, TableTools.BUTTONS[ buttonSet[i] ], true );
1203			}
1204			else
1205			{
1206				if ( typeof TableTools.BUTTONS[ buttonSet[i].sExtends ] == 'undefined' )
1207				{
1208					alert( "TableTools: Warning - unknown button type: "+buttonSet[i].sExtends );
1209					continue;
1210				}
1211				var o = $.extend( {}, TableTools.BUTTONS[ buttonSet[i].sExtends ], true );
1212				buttonDef = $.extend( o, buttonSet[i], true );
1213			}
1214
1215			wrapper.appendChild( this._fnCreateButton(
1216				buttonDef,
1217				$(wrapper).hasClass(this.classes.collection.container)
1218			) );
1219		}
1220	},
1221
1222
1223	/**
1224	 * Create and configure a TableTools button
1225	 *  @method  _fnCreateButton
1226	 *  @param   {Object} oConfig Button configuration object
1227	 *  @returns {Node} Button element
1228	 *  @private 
1229	 */
1230	"_fnCreateButton": function ( oConfig, bCollectionButton )
1231	{
1232	  var nButton = this._fnButtonBase( oConfig, bCollectionButton );
1233
1234		if ( oConfig.sAction.match(/flash/) )
1235		{
1236			this._fnFlashConfig( nButton, oConfig );
1237		}
1238		else if ( oConfig.sAction == "text" )
1239		{
1240			this._fnTextConfig( nButton, oConfig );
1241		}
1242		else if ( oConfig.sAction == "div" )
1243		{
1244			this._fnTextConfig( nButton, oConfig );
1245		}
1246		else if ( oConfig.sAction == "collection" )
1247		{
1248			this._fnTextConfig( nButton, oConfig );
1249			this._fnCollectionConfig( nButton, oConfig );
1250		}
1251
1252		return nButton;
1253	},
1254
1255
1256	/**
1257	 * Create the DOM needed for the button and apply some base properties. All buttons start here
1258	 *  @method  _fnButtonBase
1259	 *  @param   {o} oConfig Button configuration object
1260	 *  @returns {Node} DIV element for the button
1261	 *  @private
1262	 */
1263	"_fnButtonBase": function ( o, bCollectionButton )
1264	{
1265		var sTag, sLiner, sClass;
1266
1267		if ( bCollectionButton )
1268		{
1269			sTag = o.sTag && o.sTag !== "default" ? o.sTag : this.s.tags.collection.button;
1270			sLiner = o.sLinerTag && o.sLinerTag !== "default" ? o.sLiner : this.s.tags.collection.liner;
1271			sClass = this.classes.collection.buttons.normal;
1272		}
1273		else
1274		{
1275			sTag = o.sTag && o.sTag !== "default" ? o.sTag : this.s.tags.button;
1276			sLiner = o.sLinerTag && o.sLinerTag !== "default" ? o.sLiner : this.s.tags.liner;
1277			sClass = this.classes.buttons.normal;
1278		}
1279
1280		var
1281		  nButton = document.createElement( sTag ),
1282		  nSpan = document.createElement( sLiner ),
1283		  masterS = this._fnGetMasterSettings();
1284
1285		nButton.className = sClass+" "+o.sButtonClass;
1286		nButton.setAttribute('id', "ToolTables_"+this.s.dt.sInstance+"_"+masterS.buttonCounter );
1287		nButton.appendChild( nSpan );
1288		nSpan.innerHTML = o.sButtonText;
1289
1290		masterS.buttonCounter++;
1291
1292		return nButton;
1293	},
1294
1295
1296	/**
1297	 * Get the settings object for the master instance. When more than one TableTools instance is
1298	 * assigned to a DataTable, only one of them can be the 'master' (for the select rows). As such,
1299	 * we will typically want to interact with that master for global properties.
1300	 *  @method  _fnGetMasterSettings
1301	 *  @returns {Object} TableTools settings object
1302	 *  @private 
1303	 */
1304	"_fnGetMasterSettings": function ()
1305	{
1306		if ( this.s.master )
1307		{
1308			return this.s;
1309		}
1310		else
1311		{
1312			/* Look for the master which has the same DT as this one */
1313			var instances = TableTools._aInstances;
1314			for ( var i=0, iLen=instances.length ; i<iLen ; i++ )
1315			{
1316				if ( this.dom.table == instances[i].s.dt.nTable )
1317				{
1318					return instances[i].s;
1319				}
1320			}
1321		}
1322	},
1323
1324
1325
1326	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1327	 * Button collection functions
1328	 */
1329
1330	/**
1331	 * Create a collection button, when activated will present a drop down list of other buttons
1332	 *  @param   {Node} nButton Button to use for the collection activation
1333	 *  @param   {Object} oConfig Button configuration object
1334	 *  @returns void
1335	 *  @private
1336	 */
1337	"_fnCollectionConfig": function ( nButton, oConfig )
1338	{
1339		var nHidden = document.createElement( this.s.tags.collection.container );
1340		nHidden.style.display = "none";
1341		nHidden.className = this.classes.collection.container;
1342		oConfig._collection = nHidden;
1343		document.body.appendChild( nHidden );
1344
1345		this._fnButtonDefinations( oConfig.aButtons, nHidden );
1346	},
1347
1348
1349	/**
1350	 * Show a button collection
1351	 *  @param   {Node} nButton Button to use for the collection
1352	 *  @param   {Object} oConfig Button configuration object
1353	 *  @returns void
1354	 *  @private
1355	 */
1356	"_fnCollectionShow": function ( nButton, oConfig )
1357	{
1358		var
1359			that = this,
1360			oPos = $(nButton).offset(),
1361			nHidden = oConfig._collection,
1362			iDivX = oPos.left,
1363			iDivY = oPos.top + $(nButton).outerHeight(),
1364			iWinHeight = $(window).height(), iDocHeight = $(document).height(),
1365			iWinWidth = $(window).width(), iDocWidth = $(document).width();
1366
1367		nHidden.style.position = "absolute";
1368		nHidden.style.left = iDivX+"px";
1369		nHidden.style.top = iDivY+"px";
1370		nHidden.style.display = "block";
1371		$(nHidden).css('opacity',0);
1372
1373		var nBackground = document.createElement('div');
1374		nBackground.style.position = "absolute";
1375		nBackground.style.left = "0px";
1376		nBackground.style.top = "0px";
1377		nBackground.style.height = ((iWinHeight>iDocHeight)? iWinHeight : iDocHeight) +"px";
1378		nBackground.style.width = ((iWinWidth>iDocWidth)? iWinWidth : iDocWidth) +"px";
1379		nBackground.className = this.classes.collection.background;
1380		$(nBackground).css('opacity',0);
1381
1382		document.body.appendChild( nBackground );
1383		document.body.appendChild( nHidden );
1384
1385		/* Visual corrections to try and keep the collection visible */
1386		var iDivWidth = $(nHidden).outerWidth();
1387		var iDivHeight = $(nHidden).outerHeight();
1388
1389		if ( iDivX + iDivWidth > iDocWidth )
1390		{
1391			nHidden.style.left = (iDocWidth-iDivWidth)+"px";
1392		}
1393
1394		if ( iDivY + iDivHeight > iDocHeight )
1395		{
1396			nHidden.style.top = (iDivY-iDivHeight-$(nButton).outerHeight())+"px";
1397		}
1398
1399		this.dom.collection.collection = nHidden;
1400		this.dom.collection.background = nBackground;
1401
1402		/* This results in a very small delay for the end user but it allows the animation to be
1403		 * much smoother. If you don't want the animation, then the setTimeout can be removed
1404		 */
1405		setTimeout( function () {
1406			$(nHidden).animate({"opacity": 1}, 500);
1407			$(nBackground).animate({"opacity": 0.25}, 500);
1408		}, 10 );
1409
1410		/* Resize the buttons to the Flash contents fit */
1411		this.fnResizeButtons();
1412
1413		/* Event handler to remove the collection display */
1414		$(nBackground).click( function () {
1415			that._fnCollectionHide.call( that, null, null );
1416		} );
1417	},
1418
1419
1420	/**
1421	 * Hide a button collection
1422	 *  @param   {Node} nButton Button to use for the collection
1423	 *  @param   {Object} oConfig Button configuration object
1424	 *  @returns void
1425	 *  @private
1426	 */
1427	"_fnCollectionHide": function ( nButton, oConfig )
1428	{
1429		if ( oConfig !== null && oConfig.sExtends == 'collection' )
1430		{
1431			return;
1432		}
1433
1434		if ( this.dom.collection.collection !== null )
1435		{
1436			$(this.dom.collection.collection).animate({"opacity": 0}, 500, function (e) {
1437				this.style.display = "none";
1438			} );
1439
1440			$(this.dom.collection.background).animate({"opacity": 0}, 500, function (e) {
1441				this.parentNode.removeChild( this );
1442			} );
1443
1444			this.dom.collection.collection = null;
1445			this.dom.collection.background = null;
1446		}
1447	},
1448
1449
1450
1451	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1452	 * Row selection functions
1453	 */
1454
1455	/**
1456	 * Add event handlers to a table to allow for row selection
1457	 *  @method  _fnRowSelectConfig
1458	 *  @returns void
1459	 *  @private 
1460	 */
1461	"_fnRowSelectConfig": function ()
1462	{
1463		if ( this.s.master )
1464		{
1465			var
1466				that = this,
1467				i, iLen,
1468				dt = this.s.dt,
1469				aoOpenRows = this.s.dt.aoOpenRows;
1470
1471			$(dt.nTable).addClass( this.classes.select.table );
1472
1473			// When using OS style selection, we want to cancel the shift text
1474			// selection, but only when the shift key is used (so you can
1475			// actually still select text in the table)
1476			if ( this.s.select.type === 'os' ) {
1477				$(dt.nTBody).on( 'mousedown.DTTT_Select', 'tr', function(e) {
1478					if ( e.shiftKey ) {
1479
1480						$(dt.nTBody)
1481							.css( '-moz-user-select', 'none' )
1482							.one('selectstart.DTTT_Select', 'tr', function () {
1483								return false;
1484							} );
1485					}
1486				} );
1487
1488				$(dt.nTBody).on( 'mouseup.DTTT_Select', 'tr', function(e) {
1489					$(dt.nTBody).css( '-moz-user-select', '' );
1490				} );
1491			}
1492
1493			// Row selection
1494			$(dt.nTBody).on( 'click.DTTT_Select', this.s.custom.sRowSelector, function(e) {
1495				var row = this.nodeName.toLowerCase() === 'tr' ?
1496					this :
1497					$(this).parents('tr')[0];
1498
1499				var select = that.s.select;
1500				var pos = that.s.dt.oInstance.fnGetPosition( row );
1501
1502				/* Sub-table must be ignored (odd that the selector won't do this with >) */
1503				if ( row.parentNode != dt.nTBody ) {
1504					return;
1505				}
1506
1507				/* Check that we are actually working with a DataTables controlled row */
1508				if ( dt.oInstance.fnGetData(row) === null ) {
1509				    return;
1510				}
1511
1512				// Shift click, ctrl click and simple click handling to make
1513				// row selection a lot like a file system in desktop OSs
1514				if ( select.type == 'os' ) {
1515					if ( e.ctrlKey || e.metaKey ) {
1516						// Add or remove from the selection
1517						if ( that.fnIsSelected( row ) ) {
1518							that._fnRowDeselect( row, e );
1519						}
1520						else {
1521							that._fnRowSelect( row, e );
1522						}
1523					}
1524					else if ( e.shiftKey ) {
1525						// Add a range of rows, from the last selected row to
1526						// this one
1527						var rowIdxs = that.s.dt.aiDisplay.slice(); // visible rows
1528						var idx1 = $.inArray( select.lastRow, rowIdxs );
1529						var idx2 = $.inArray( pos, rowIdxs );
1530
1531						if ( that.fnGetSelected().length === 0 || idx1 === -1 ) {
1532							// select from top to here - slightly odd, but both
1533							// Windows and Mac OS do this
1534							rowIdxs.splice( $.inArray( pos, rowIdxs )+1, rowIdxs.length );
1535						}
1536						else {
1537							// reverse so we can shift click 'up' as well as down
1538							if ( idx1 > idx2 ) {
1539								var tmp = idx2;
1540								idx2 = idx1;
1541								idx1 = tmp;
1542							}
1543
1544							rowIdxs.splice( idx2+1, rowIdxs.length );
1545							rowIdxs.splice( 0, idx1 );
1546						}
1547
1548						if ( ! that.fnIsSelected( row ) ) {
1549							// Select range
1550							that._fnRowSelect( rowIdxs, e );
1551						}
1552						else {
1553							// Deselect range - need to keep the clicked on row selected
1554							rowIdxs.splice( $.inArray( pos, rowIdxs ), 1 );
1555							that._fnRowDeselect( rowIdxs, e );
1556						}
1557					}
1558					else {
1559						// No cmd or shift click. Deselect current if selected,
1560						// or select this row only
1561						if ( that.fnIsSelected( row ) && that.fnGetSelected().length === 1 ) {
1562							that._fnRowDeselect( row, e );
1563						}
1564						else {
1565							that.fnSelectNone();
1566							that._fnRowSelect( row, e );
1567						}
1568					}
1569				}
1570				else if ( that.fnIsSelected( row ) ) {
1571					that._fnRowDeselect( row, e );
1572				}
1573				else if ( select.type == "single" ) {
1574					that.fnSelectNone();
1575					that._fnRowSelect( row, e );
1576				}
1577				else if ( select.type == "multi" ) {
1578					that._fnRowSelect( row, e );
1579				}
1580
1581				select.lastRow = pos;
1582			} );//.on('selectstart', function () { return false; } );
1583
1584			// Bind a listener to the DataTable for when new rows are created.
1585			// This allows rows to be visually selected when they should be and
1586			// deferred rendering is used.
1587			dt.oApi._fnCallbackReg( dt, 'aoRowCreatedCallback', function (tr, data, index) {
1588				if ( dt.aoData[index]._DTTT_selected ) {
1589					$(tr).addClass( that.classes.select.row );
1590				}
1591			}, 'TableTools-SelectAll' );
1592		}
1593	},
1594
1595	/**
1596	 * Select rows
1597	 *  @param   {*} src Rows to select - see _fnSelectData for a description of valid inputs
1598	 *  @private 
1599	 */
1600	"_fnRowSelect": function ( src, e )
1601	{
1602		var
1603			that = this,
1604			data = this._fnSelectData( src ),
1605			firstTr = data.length===0 ? null : data[0].nTr,
1606			anSelected = [],
1607			i, len;
1608
1609		// Get all the rows that will be selected
1610		for ( i=0, len=data.length ; i<len ; i++ )
1611		{
1612			if ( data[i].nTr )
1613			{
1614				anSelected.push( data[i].nTr );
1615			}
1616		}
1617
1618		// User defined pre-selection function
1619		if ( this.s.select.preRowSelect !== null && !this.s.select.preRowSelect.call(this, e, anSelected, true) )
1620		{
1621			return;
1622		}
1623
1624		// Mark them as selected
1625		for ( i=0, len=data.length ; i<len ; i++ )
1626		{
1627			data[i]._DTTT_selected = true;
1628
1629			if ( data[i].nTr )
1630			{
1631				$(data[i].nTr).addClass( that.classes.select.row );
1632			}
1633		}
1634
1635		// Post-selection function
1636		if ( this.s.select.postSelected !== null )
1637		{
1638			this.s.select.postSelected.call( this, anSelected );
1639		}
1640
1641		TableTools._fnEventDispatch( this, 'select', anSelected, true );
1642	},
1643
1644	/**
1645	 * Deselect rows
1646	 *  @param   {*} src Rows to deselect - see _fnSelectData for a description of valid inputs
1647	 *  @private 
1648	 */
1649	"_fnRowDeselect": function ( src, e )
1650	{
1651		var
1652			that = this,
1653			data = this._fnSelectData( src ),
1654			firstTr = data.length===0 ? null : data[0].nTr,
1655			anDeselectedTrs = [],
1656			i, len;
1657
1658		// Get all the rows that will be deselected
1659		for ( i=0, len=data.length ; i<len ; i++ )
1660		{
1661			if ( data[i].nTr )
1662			{
1663				anDeselectedTrs.push( data[i].nTr );
1664			}
1665		}
1666
1667		// User defined pre-selection function
1668		if ( this.s.select.preRowSelect !== null && !this.s.select.preRowSelect.call(this, e, anDeselectedTrs, false) )
1669		{
1670			return;
1671		}
1672
1673		// Mark them as deselected
1674		for ( i=0, len=data.length ; i<len ; i++ )
1675		{
1676			data[i]._DTTT_selected = false;
1677
1678			if ( data[i].nTr )
1679			{
1680				$(data[i].nTr).removeClass( that.classes.select.row );
1681			}
1682		}
1683
1684		// Post-deselection function
1685		if ( this.s.select.postDeselected !== null )
1686		{
1687			this.s.select.postDeselected.call( this, anDeselectedTrs );
1688		}
1689
1690		TableTools._fnEventDispatch( this, 'select', anDeselectedTrs, false );
1691	},
1692
1693	/**
1694	 * Take a data source for row selection and convert it into aoData points for the DT
1695	 *   @param {*} src Can be a single DOM TR node, an array of TR nodes (including a
1696	 *     a jQuery object), a single aoData point from DataTables, an array of aoData
1697	 *     points or an array of aoData indexes
1698	 *   @returns {array} An array of aoData points
1699	 */
1700	"_fnSelectData": function ( src )
1701	{
1702		var out = [], pos, i, iLen;
1703
1704		if ( src.nodeName )
1705		{
1706			// Single node
1707			pos = this.s.dt.oInstance.fnGetPosition( src );
1708			out.push( this.s.dt.aoData[pos] );
1709		}
1710		else if ( typeof src.length !== 'undefined' )
1711		{
1712			// jQuery object or an array of nodes, or aoData points
1713			for ( i=0, iLen=src.length ; i<iLen ; i++ )
1714			{
1715				if ( src[i].nodeName )
1716				{
1717					pos = this.s.dt.oInstance.fnGetPosition( src[i] );
1718					out.push( this.s.dt.aoData[pos] );
1719				}
1720				else if ( typeof src[i] === 'number' )
1721				{
1722					out.push( this.s.dt.aoData[ src[i] ] );
1723				}
1724				else
1725				{
1726					out.push( src[i] );
1727				}
1728			}
1729
1730			return out;
1731		}
1732		else
1733		{
1734			// A single aoData point
1735			out.push( src );
1736		}
1737
1738		return out;
1739	},
1740
1741
1742	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1743	 * Text button functions
1744	 */
1745
1746	/**
1747	 * Configure a text based button for interaction events
1748	 *  @method  _fnTextConfig
1749	 *  @param   {Node} nButton Button element which is being considered
1750	 *  @param   {Object} oConfig Button configuration object
1751	 *  @returns void
1752	 *  @private 
1753	 */
1754	"_fnTextConfig": function ( nButton, oConfig )
1755	{
1756		var that = this;
1757
1758		if ( oConfig.fnInit !== null )
1759		{
1760			oConfig.fnInit.call( this, nButton, oConfig );
1761		}
1762
1763		if ( oConfig.sToolTip !== "" )
1764		{
1765			nButton.title = oConfig.sToolTip;
1766		}
1767
1768		$(nButton).hover( function () {
1769			if ( oConfig.fnMouseover !== null )
1770			{
1771				oConfig.fnMouseover.call( this, nButton, oConfig, null );
1772			}
1773		}, function () {
1774			if ( oConfig.fnMouseout !== null )
1775			{
1776				oConfig.fnMouseout.call( this, nButton, oConfig, null );
1777			}
1778		} );
1779
1780		if ( oConfig.fnSelect !== null )
1781		{
1782			TableTools._fnEventListen( this, 'select', function (n) {
1783				oConfig.fnSelect.call( that, nButton, oConfig, n );
1784			} );
1785		}
1786
1787		$(nButton).click( function (e) {
1788			//e.preventDefault();
1789
1790			if ( oConfig.fnClick !== null )
1791			{
1792				oConfig.fnClick.call( that, nButton, oConfig, null, e );
1793			}
1794
1795			/* Provide a complete function to match the behaviour of the flash elements */
1796			if ( oConfig.fnComplete !== null )
1797			{
1798				oConfig.fnComplete.call( that, nButton, oConfig, null, null );
1799			}
1800
1801			that._fnCollectionHide( nButton, oConfig );
1802		} );
1803	},
1804
1805
1806
1807	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1808	 * Flash button functions
1809	 */
1810
1811	/**
1812	 * Configure a flash based button for interaction events
1813	 *  @method  _fnFlashConfig
1814	 *  @param   {Node} nButton Button element which is being considered
1815	 *  @param   {o} oConfig Button configuration object
1816	 *  @returns void
1817	 *  @private 
1818	 */
1819	"_fnFlashConfig": function ( nButton, oConfig )
1820	{
1821		var that = this;
1822		var flash = new ZeroClipboard_TableTools.Client();
1823
1824		if ( oConfig.fnInit !== null )
1825		{
1826			oConfig.fnInit.call( this, nButton, oConfig );
1827		}
1828
1829		flash.setHandCursor( true );
1830
1831		if ( oConfig.sAction == "flash_save" )
1832		{
1833			flash.setAction( 'save' );
1834			flash.setCharSet( (oConfig.sCharSet=="utf16le") ? 'UTF16LE' : 'UTF8' );
1835			flash.setBomInc( oConfig.bBomInc );
1836			flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1837		}
1838		else if ( oConfig.sAction == "flash_pdf" )
1839		{
1840			flash.setAction( 'pdf' );
1841			flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1842		}
1843		else
1844		{
1845			flash.setAction( 'copy' );
1846		}
1847
1848		flash.addEventListener('mouseOver', function(client) {
1849			if ( oConfig.fnMouseover !== null )
1850			{
1851				oConfig.fnMouseover.call( that, nButton, oConfig, flash );
1852			}
1853		} );
1854
1855		flash.addEventListener('mouseOut', function(client) {
1856			if ( oConfig.fnMouseout !== null )
1857			{
1858				oConfig.fnMouseout.call( that, nButton, oConfig, flash );
1859			}
1860		} );
1861
1862		flash.addEventListener('mouseDown', function(client) {
1863			if ( oConfig.fnClick !== null )
1864			{
1865				oConfig.fnClick.call( that, nButton, oConfig, flash );
1866			}
1867		} );
1868
1869		flash.addEventListener('complete', function (client, text) {
1870			if ( oConfig.fnComplete !== null )
1871			{
1872				oConfig.fnComplete.call( that, nButton, oConfig, flash, text );
1873			}
1874			that._fnCollectionHide( nButton, oConfig );
1875		} );
1876
1877		this._fnFlashGlue( flash, nButton, oConfig.sToolTip );
1878	},
1879
1880
1881	/**
1882	 * Wait until the id is in the DOM before we "glue" the swf. Note that this function will call
1883	 * itself (using setTimeout) until it completes successfully
1884	 *  @method  _fnFlashGlue
1885	 *  @param   {Object} clip Zero clipboard object
1886	 *  @param   {Node} node node to glue swf to
1887	 *  @param   {String} text title of the flash movie
1888	 *  @returns void
1889	 *  @private 
1890	 */
1891	"_fnFlashGlue": function ( flash, node, text )
1892	{
1893		var that = this;
1894		var id = node.getAttribute('id');
1895
1896		if ( document.getElementById(id) )
1897		{
1898			flash.glue( node, text );
1899		}
1900		else
1901		{
1902			setTimeout( function () {
1903				that._fnFlashGlue( flash, node, text );
1904			}, 100 );
1905		}
1906	},
1907
1908
1909	/**
1910	 * Set the text for the flash clip to deal with
1911	 * 
1912	 * This function is required for large information sets. There is a limit on the 
1913	 * amount of data that can be transferred between Javascript and Flash in a single call, so
1914	 * we use this method to build up the text in Flash by sending over chunks. It is estimated
1915	 * that the data limit is around 64k, although it is undocumented, and appears to be different
1916	 * between different flash versions. We chunk at 8KiB.
1917	 *  @method  _fnFlashSetText
1918	 *  @param   {Object} clip the ZeroClipboard object
1919	 *  @param   {String} sData the data to be set
1920	 *  @returns void
1921	 *  @private 
1922	 */
1923	"_fnFlashSetText": function ( clip, sData )
1924	{
1925		var asData = this._fnChunkData( sData, 8192 );
1926
1927		clip.clearText();
1928		for ( var i=0, iLen=asData.length ; i<iLen ; i++ )
1929		{
1930			clip.appendText( asData[i] );
1931		}
1932	},
1933
1934
1935
1936	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1937	 * Data retrieval functions
1938	 */
1939
1940	/**
1941	 * Convert the mixed columns variable into a boolean array the same size as the columns, which
1942	 * indicates which columns we want to include
1943	 *  @method  _fnColumnTargets
1944	 *  @param   {String|Array} mColumns The columns to be included in data retrieval. If a string
1945	 *			 then it can take the value of "visible" or "hidden" (to include all visible or
1946	 *			 hidden columns respectively). Or an array of column indexes
1947	 *  @returns {Array} A boolean array the length of the columns of the table, which each value
1948	 *			 indicating if the column is to be included or not
1949	 *  @private 
1950	 */
1951	"_fnColumnTargets": function ( mColumns )
1952	{
1953		var aColumns = [];
1954		var dt = this.s.dt;
1955		var i, iLen;
1956
1957		if ( typeof mColumns == "object" )
1958		{
1959			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1960			{
1961				aColumns.push( false );
1962			}
1963
1964			for ( i=0, iLen=mColumns.length ; i<iLen ; i++ )
1965			{
1966				aColumns[ mColumns[i] ] = true;
1967			}
1968		}
1969		else if ( mColumns == "visible" )
1970		{
1971			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1972			{
1973				aColumns.push( dt.aoColumns[i].bVisible ? true : false );
1974			}
1975		}
1976		else if ( mColumns == "hidden" )
1977		{
1978			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1979			{
1980				aColumns.push( dt.aoColumns[i].bVisible ? false : true );
1981			}
1982		}
1983		else if ( mColumns == "sortable" )
1984		{
1985			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1986			{
1987				aColumns.push( dt.aoColumns[i].bSortable ? true : false );
1988			}
1989		}
1990		else /* all */
1991		{
1992			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1993			{
1994				aColumns.push( true );
1995			}
1996		}
1997
1998		return aColumns;
1999	},
2000
2001
2002	/**
2003	 * New line character(s) depend on the platforms
2004	 *  @method  method
2005	 *  @param   {Object} oConfig Button configuration object - only interested in oConfig.sNewLine
2006	 *  @returns {String} Newline character
2007	 */
2008	"_fnNewline": function ( oConfig )
2009	{
2010		if ( oConfig.sNewLine == "auto" )
2011		{
2012			return navigator.userAgent.match(/Windows/) ? "\r\n" : "\n";
2013		}
2014		else
2015		{
2016			return oConfig.sNewLine;
2017		}
2018	},
2019
2020
2021	/**
2022	 * Get data from DataTables' internals and format it for output
2023	 *  @method  _fnGetDataTablesData
2024	 *  @param   {Object} oConfig Button configuration object
2025	 *  @param   {String} oConfig.sFieldBoundary Field boundary for the data cells in the string
2026	 *  @param   {String} oConfig.sFieldSeperator Field separator for the data cells
2027	 *  @param   {String} oConfig.sNewline New line options
2028	 *  @param   {Mixed} oConfig.mColumns Which columns should be included in the output
2029	 *  @param   {Boolean} oConfig.bHeader Include the header
2030	 *  @param   {Boolean} oConfig.bFooter Include the footer
2031	 *  @param   {Boolean} oConfig.bSelectedOnly Include only the selected rows in the output
2032	 *  @returns {String} Concatenated string of data
2033	 *  @private 
2034	 */
2035	"_fnGetDataTablesData": function ( oConfig )
2036	{
2037		var i, iLen, j, jLen;
2038		var aRow, aData=[], sLoopData='', arr;
2039		var dt = this.s.dt, tr, child;
2040		var regex = new RegExp(oConfig.sFieldBoundary, "g"); /* Do it here for speed */
2041		var aColumnsInc = this._fnColumnTargets( oConfig.mColumns );
2042		var bSelectedOnly = (typeof oConfig.bSelectedOnly != 'undefined') ? oConfig.bSelectedOnly : false;
2043
2044		/*
2045		 * Header
2046		 */
2047		if ( oConfig.bHeader )
2048		{
2049			aRow = [];
2050
2051			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
2052			{
2053				if ( aColumnsInc[i] )
2054				{
2055					sLoopData = dt.aoColumns[i].sTitle.replace(/\n/g," ").replace( /<.*?>/g, "" ).replace(/^\s+|\s+$/g,"");
2056					sLoopData = this._fnHtmlDecode( sLoopData );
2057
2058					aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
2059				}
2060			}
2061
2062			aData.push( aRow.join(oConfig.sFieldSeperator) );
2063		}
2064
2065		/*
2066		 * Body
2067		 */
2068		var aSelected = this.fnGetSelected();
2069		bSelectedOnly = this.s.select.type !== "none" && bSelectedOnly && aSelected.length !== 0;
2070
2071		var aDataIndex = dt.oInstance
2072			.$('tr', oConfig.oSelectorOpts)
2073			.map( function (id, row) {
2074				// If "selected only", then ensure that the row is in the selected list
2075				return bSelectedOnly && $.inArray( row, aSelected ) === -1 ?
2076					null :
2077					dt.oInstance.fnGetPosition( row );
2078			} )
2079			.get();
2080
2081		for ( j=0, jLen=aDataIndex.length ; j<jLen ; j++ )
2082		{
2083			tr = dt.aoData[ aDataIndex[j] ].nTr;
2084			aRow = [];
2085
2086			/* Columns */
2087			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
2088			{
2089				if ( aColumnsInc[i] )
2090				{
2091					/* Convert to strings (with small optimisation) */
2092					var mTypeData = dt.oApi._fnGetCellData( dt, aDataIndex[j], i, 'display' );
2093					if ( oConfig.fnCellRender )
2094					{
2095						sLoopData = oConfig.fnCellRender( mTypeData, i, tr, aDataIndex[j] )+"";
2096					}
2097					else if ( typeof mTypeData == "string" )
2098					{
2099						/* Strip newlines, replace img tags with alt attr. and finally strip html... */
2100						sLoopData = mTypeData.replace(/\n/g," ");
2101						sLoopData =
2102						    sLoopData.replace(/<img.*?\s+alt\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+)).*?>/gi,
2103						        '$1$2$3');
2104						sLoopData = sLoopData.replace( /<.*?>/g, "" );
2105					}
2106					else
2107					{
2108						sLoopData = mTypeData+"";
2109					}
2110
2111					/* Trim and clean the data */
2112					sLoopData = sLoopData.replace(/^\s+/, '').replace(/\s+$/, '');
2113					sLoopData = this._fnHtmlDecode( sLoopData );
2114
2115					/* Bound it and add it to the total data */
2116					aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
2117				}
2118			}
2119
2120			aData.push( aRow.join(oConfig.sFieldSeperator) );
2121
2122			/* Details rows from fnOpen */
2123			if ( oConfig.bOpenRows )
2124			{
2125				arr = $.grep(dt.aoOpenRows, function(o) { return o.nParent === tr; });
2126
2127				if ( arr.length === 1 )
2128				{
2129					sLoopData = this._fnBoundData( $('td', arr[0].nTr).html(), oConfig.sFieldBoundary, regex );
2130					aData.push( sLoopData );
2131				}
2132			}
2133		}
2134
2135		/*
2136		 * Footer
2137		 */
2138		if ( oConfig.bFooter && dt.nTFoot !== null )
2139		{
2140			aRow = [];
2141
2142			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
2143			{
2144				if ( aColumnsInc[i] && dt.aoColumns[i].nTf !== null )
2145				{
2146					sLoopData = dt.aoColumns[i].nTf.innerHTML.replace(/\n/g," ").replace( /<.*?>/g, "" );
2147					sLoopData = this._fnHtmlDecode( sLoopData );
2148
2149					aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
2150				}
2151			}
2152
2153			aData.push( aRow.join(oConfig.sFieldSeperator) );
2154		}
2155
2156		var _sLastData = aData.join( this._fnNewline(oConfig) );
2157		return _sLastData;
2158	},
2159
2160
2161	/**
2162	 * Wrap data up with a boundary string
2163	 *  @method  _fnBoundData
2164	 *  @param   {String} sData data to bound
2165	 *  @param   {String} sBoundary bounding char(s)
2166	 *  @param   {RegExp} regex search for the bounding chars - constructed outside for efficiency
2167	 *			 in the loop
2168	 *  @returns {String} bound data
2169	 *  @private 
2170	 */
2171	"_fnBoundData": function ( sData, sBoundary, regex )
2172	{
2173		if ( sBoundary === "" )
2174		{
2175			return sData;
2176		}
2177		else
2178		{
2179			return sBoundary + sData.replace(regex, sBoundary+sBoundary) + sBoundary;
2180		}
2181	},
2182
2183
2184	/**
2185	 * Break a string up into an array of smaller strings
2186	 *  @method  _fnChunkData
2187	 *  @param   {String} sData data to be broken up
2188	 *  @param   {Int} iSize chunk size
2189	 *  @returns {Array} String array of broken up text
2190	 *  @private 
2191	 */
2192	"_fnChunkData": function ( sData, iSize )
2193	{
2194		var asReturn = [];
2195		var iStrlen = sData.length;
2196
2197		for ( var i=0 ; i<iStrlen ; i+=iSize )
2198		{
2199			if ( i+iSize < iStrlen )
2200			{
2201				asReturn.push( sData.substring( i, i+iSize ) );
2202			}
2203			else
2204			{
2205				asReturn.push( sData.substring( i, iStrlen ) );
2206			}
2207		}
2208
2209		return asReturn;
2210	},
2211
2212
2213	/**
2214	 * Decode HTML entities
2215	 *  @method  _fnHtmlDecode
2216	 *  @param   {String} sData encoded string
2217	 *  @returns {String} decoded string
2218	 *  @private 
2219	 */
2220	"_fnHtmlDecode": function ( sData )
2221	{
2222		if ( sData.indexOf('&') === -1 )
2223		{
2224			return sData;
2225		}
2226
2227		var n = document.createElement('div');
2228
2229		return sData.replace( /&([^\s]*);/g, function( match, match2 ) {
2230			if ( match.substr(1, 1) === '#' )
2231			{
2232				return String.fromCharCode( Number(match2.substr(1)) );
2233			}
2234			else
2235			{
2236				n.innerHTML = match;
2237				return n.childNodes[0].nodeValue;
2238			}
2239		} );
2240	},
2241
2242
2243
2244	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2245	 * Printing functions
2246	 */
2247
2248	/**
2249	 * Show print display
2250	 *  @method  _fnPrintStart
2251	 *  @param   {Event} e Event object
2252	 *  @param   {Object} oConfig Button configuration object
2253	 *  @returns void
2254	 *  @private 
2255	 */
2256	"_fnPrintStart": function ( oConfig )
2257	{
2258	  var that = this;
2259	  var oSetDT = this.s.dt;
2260
2261		/* Parse through the DOM hiding everything that isn't needed for the table */
2262		this._fnPrintHideNodes( oSetDT.nTable );
2263
2264		/* Show the whole table */
2265		this.s.print.saveStart = oSetDT._iDisplayStart;
2266		this.s.print.saveLength = oSetDT._iDisplayLength;
2267
2268		if ( oConfig.bShowAll )
2269		{
2270			oSetDT._iDisplayStart = 0;
2271			oSetDT._iDisplayLength = -1;
2272			if ( oSetDT.oApi._fnCalculateEnd ) {
2273				oSetDT.oApi._fnCalculateEnd( oSetDT );
2274			}
2275			oSetDT.oApi._fnDraw( oSetDT );
2276		}
2277
2278		/* Adjust the display for scrolling which might be done by DataTables */
2279		if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
2280		{
2281			this._fnPrintScrollStart( oSetDT );
2282
2283			// If the table redraws while in print view, the DataTables scrolling
2284			// setup would hide the header, so we need to readd it on draw
2285			$(this.s.dt.nTable).bind('draw.DTTT_Print', function () {
2286				that._fnPrintScrollStart( oSetDT );
2287			} );
2288		}
2289
2290		/* Remove the other DataTables feature nodes - but leave the table! and info div */
2291		var anFeature = oSetDT.aanFeatures;
2292		for ( var cFeature in anFeature )
2293		{
2294			if ( cFeature != 'i' && cFeature != 't' && cFeature.length == 1 )
2295			{
2296				for ( var i=0, iLen=anFeature[cFeature].length ; i<iLen ; i++ )
2297				{
2298					this.dom.print.hidden.push( {
2299						"node": anFeature[cFeature][i],
2300						"display": "block"
2301					} );
2302					anFeature[cFeature][i].style.display = "none";
2303				}
2304			}
2305		}
2306
2307		/* Print class can be used for styling */
2308		$(document.body).addClass( this.classes.print.body );
2309
2310		/* Show information message to let the user know what is happening */
2311		if ( oConfig.sInfo !== "" )
2312		{
2313			this.fnInfo( oConfig.sInfo, 3000 );
2314		}
2315
2316		/* Add a message at the top of the page */
2317		if ( oConfig.sMessage )
2318		{
2319			$('<div/>')
2320				.addClass( this.classes.print.message )
2321				.html( oConfig.sMessage )
2322				.prependTo( 'body' );
2323		}
2324
2325		/* Cache the scrolling and the jump to the top of the page */
2326		this.s.print.saveScroll = $(window).scrollTop();
2327		window.scrollTo( 0, 0 );
2328
2329		/* Bind a key event listener to the document for the escape key -
2330		 * it is removed in the callback
2331		 */
2332		$(document).bind( "keydown.DTTT", function(e) {
2333			/* Only interested in the escape key */
2334			if ( e.keyCode == 27 )
2335			{
2336				e.preventDefault();
2337				that._fnPrintEnd.call( that, e );
2338			}
2339		} );
2340	},
2341
2342
2343	/**
2344	 * Printing is finished, resume normal display
2345	 *  @method  _fnPrintEnd
2346	 *  @param   {Event} e Event object
2347	 *  @returns void
2348	 *  @private 
2349	 */
2350	"_fnPrintEnd": function ( e )
2351	{
2352		var that = this;
2353		var oSetDT = this.s.dt;
2354		var oSetPrint = this.s.print;
2355		var oDomPrint = this.dom.print;
2356
2357		/* Show all hidden nodes */
2358		this._fnPrintShowNodes();
2359
2360		/* Restore DataTables' scrolling */
2361		if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
2362		{
2363			$(this.s.dt.nTable).unbind('draw.DTTT_Print');
2364
2365			this._fnPrintScrollEnd();
2366		}
2367
2368		/* Restore the scroll */
2369		window.scrollTo( 0, oSetPrint.saveScroll );
2370
2371		/* Drop the print message */
2372		$('div.'+this.classes.print.message).remove();
2373
2374		/* Styling class */
2375		$(document.body).removeClass( 'DTTT_Print' );
2376
2377		/* Restore the table length */
2378		oSetDT._iDisplayStart = oSetPrint.saveStart;
2379		oSetDT._iDisplayLength = oSetPrint.saveLength;
2380		if ( oSetDT.oApi._fnCalculateEnd ) {
2381			oSetDT.oApi._fnCalculateEnd( oSetDT );
2382		}
2383		oSetDT.oApi._fnDraw( oSetDT );
2384
2385		$(document).unbind( "keydown.DTTT" );
2386	},
2387
2388
2389	/**
2390	 * Take account of scrolling in DataTables by showing the full table
2391	 *  @returns void
2392	 *  @private 
2393	 */
2394	"_fnPrintScrollStart": function ()
2395	{
2396		var
2397			oSetDT = this.s.dt,
2398			nScrollHeadInner = oSetDT.nScrollHead.getElementsByTagName('div')[0],
2399			nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
2400			nScrollBody = oSetDT.nTable.parentNode,
2401			nTheadSize, nTfootSize;
2402
2403		/* Copy the header in the thead in the body table, this way we show one single table when
2404		 * in print view. Note that this section of code is more or less verbatim from DT 1.7.0
2405		 */
2406		nTheadSize = oSetDT.nTable.getElementsByTagName('thead');
2407		if ( nTheadSize.length > 0 )
2408		{
2409			oSetDT.nTable.removeChild( nTheadSize[0] );
2410		}
2411
2412		if ( oSetDT.nTFoot !== null )
2413		{
2414			nTfootSize = oSetDT.nTable.getElementsByTagName('tfoot');
2415			if ( nTfootSize.length > 0 )
2416			{
2417				oSetDT.nTable.removeChild( nTfootSize[0] );
2418			}
2419		}
2420
2421		nTheadSize = oSetDT.nTHead.cloneNode(true);
2422		oSetDT.nTable.insertBefore( nTheadSize, oSetDT.nTable.childNodes[0] );
2423
2424		if ( oSetDT.nTFoot !== null )
2425		{
2426			nTfootSize = oSetDT.nTFoot.cloneNode(true);
2427			oSetDT.nTable.insertBefore( nTfootSize, oSetDT.nTable.childNodes[1] );
2428		}
2429
2430		/* Now adjust the table's viewport so we can actually see it */
2431		if ( oSetDT.oScroll.sX !== "" )
2432		{
2433			oSetDT.nTable.style.width = $(oSetDT.nTable).outerWidth()+"px";
2434			nScrollBody.style.width = $(oSetDT.nTable).outerWidth()+"px";
2435			nScrollBody.style.overflow = "visible";
2436		}
2437
2438		if ( oSetDT.oScroll.sY !== "" )
2439		{
2440			nScrollBody.style.height = $(oSetDT.nTable).outerHeight()+"px";
2441			nScrollBody.style.overflow = "visible";
2442		}
2443	},
2444
2445
2446	/**
2447	 * Take account of scrolling in DataTables by showing the full table. Note that the redraw of
2448	 * the DataTable that we do will actually deal with the majority of the hard 
2448work here
2449	 *  @returns void
2450	 *  @private 
2451	 */
2452	"_fnPrintScrollEnd": function ()
2453	{
2454		var
2455			oSetDT = this.s.dt,
2456			nScrollBody = oSetDT.nTable.parentNode;
2457
2458		if ( oSetDT.oScroll.sX !== "" )
2459		{
2460			nScrollBody.style.width = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sX );
2461			nScrollBody.style.overflow = "auto";
2462		}
2463
2464		if ( oSetDT.oScroll.sY !== "" )
2465		{
2466			nScrollBody.style.height = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sY );
2467			nScrollBody.style.overflow = "auto";
2468		}
2469	},
2470
2471
2472	/**
2473	 * Resume the display of all TableTools hidden nodes
2474	 *  @method  _fnPrintShowNodes
2475	 *  @returns void
2476	 *  @private 
2477	 */
2478	"_fnPrintShowNodes": function ( )
2479	{
2480	  var anHidden = this.dom.print.hidden;
2481
2482		for ( var i=0, iLen=anHidden.length ; i<iLen ; i++ )
2483		{
2484			anHidden[i].node.style.display = anHidden[i].display;
2485		}
2486		anHidden.splice( 0, anHidden.length );
2487	},
2488
2489
2490	/**
2491	 * Hide nodes which are not needed in order to display the table. Note that this function is
2492	 * recursive
2493	 *  @method  _fnPrintHideNodes
2494	 *  @param   {Node} nNode Element which should be showing in a 'print' display
2495	 *  @returns void
2496	 *  @private 
2497	 */
2498	"_fnPrintHideNodes": function ( nNode )
2499	{
2500		var anHidden = this.dom.print.hidden;
2501
2502		var nParent = nNode.parentNode;
2503		var nChildren = nParent.childNodes;
2504		for ( var i=0, iLen=nChildren.length ; i<iLen ; i++ )
2505		{
2506			if ( nChildren[i] != nNode && nChildren[i].nodeType == 1 )
2507			{
2508				/* If our node is shown (don't want to show nodes which were previously hidden) */
2509				var sDisplay = $(nChildren[i]).css("display");
2510				if ( sDisplay != "none" )
2511				{
2512					/* Cache the node and it's previous state so we can restore it */
2513					anHidden.push( {
2514						"node": nChildren[i],
2515						"display": sDisplay
2516					} );
2517					nChildren[i].style.display = "none";
2518				}
2519			}
2520		}
2521
2522		if ( nParent.nodeName.toUpperCase() != "BODY" )
2523		{
2524			this._fnPrintHideNodes( nParent );
2525		}
2526	}
2527};
2528
2529
2530
2531/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2532 * Static variables
2533 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2534
2535/**
2536 * Store of all instances that have been created of TableTools, so one can look up other (when
2537 * there is need of a master)
2538 *  @property _aInstances
2539 *  @type	 Array
2540 *  @default  []
2541 *  @private
2542 */
2543TableTools._aInstances = [];
2544
2545
2546/**
2547 * Store of all listeners and their callback functions
2548 *  @property _aListeners
2549 *  @type	 Array
2550 *  @default  []
2551 */
2552TableTools._aListeners = [];
2553
2554
2555
2556/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2557 * Static methods
2558 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2559
2560/**
2561 * Get an array of all the master instances
2562 *  @method  fnGetMasters
2563 *  @returns {Array} List of master TableTools instances
2564 *  @static
2565 */
2566TableTools.fnGetMasters = function ()
2567{
2568	var a = [];
2569	for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2570	{
2571		if ( TableTools._aInstances[i].s.master )
2572		{
2573			a.push( TableTools._aInstances[i] );
2574		}
2575	}
2576	return a;
2577};
2578
2579/**
2580 * Get the master instance for a table node (or id if a string is given)
2581 *  @method  fnGetInstance
2582 *  @returns {Object} ID of table OR table node, for which we want the TableTools instance
2583 *  @static
2584 */
2585TableTools.fnGetInstance = function ( node )
2586{
2587	if ( typeof node != 'object' )
2588	{
2589		node = document.getElementById(node);
2590	}
2591
2592	for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2593	{
2594		if ( TableTools._aInstances[i].s.master && TableTools._aInstances[i].dom.table == node )
2595		{
2596			return TableTools._aInstances[i];
2597		}
2598	}
2599	return null;
2600};
2601
2602
2603/**
2604 * Add a listener for a specific event
2605 *  @method  _fnEventListen
2606 *  @param   {Object} that Scope of the listening function (i.e. 'this' in the caller)
2607 *  @param   {String} type Event type
2608 *  @param   {Function} fn Function
2609 *  @returns void
2610 *  @private
2611 *  @static
2612 */
2613TableTools._fnEventListen = function ( that, type, fn )
2614{
2615	TableTools._aListeners.push( {
2616		"that": that,
2617		"type": type,
2618		"fn": fn
2619	} );
2620};
2621
2622
2623/**
2624 * An event has occurred - look up every listener and fire it off. We check that the event we are
2625 * going to fire is attached to the same table (using the table node as reference) before firing
2626 *  @method  _fnEventDispatch
2627 *  @param   {Object} that Scope of the listening function (i.e. 'this' in the caller)
2628 *  @param   {String} type Event type
2629 *  @param   {Node} node Element that the event occurred on (may be null)
2630 *  @param   {boolean} [selected] Indicate if the node was selected (true) or deselected (false)
2631 *  @returns void
2632 *  @private
2633 *  @static
2634 */
2635TableTools._fnEventDispatch = function ( that, type, node, selected )
2636{
2637	var listeners = TableTools._aListeners;
2638	for ( var i=0, iLen=listeners.length ; i<iLen ; i++ )
2639	{
2640		if ( that.dom.table == listeners[i].that.dom.table && listeners[i].type == type )
2641		{
2642			listeners[i].fn( node, selected );
2643		}
2644	}
2645};
2646
2647
2648
2649
2650
2651
2652/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2653 * Constants
2654 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2655
2656
2657
2658TableTools.buttonBase = {
2659	// Button base
2660	"sAction": "text",
2661	"sTag": "default",
2662	"sLinerTag": "default",
2663	"sButtonClass": "DTTT_button_text",
2664	"sButtonText": "Button text",
2665	"sTitle": "",
2666	"sToolTip": "",
2667
2668	// Common button specific options
2669	"sCharSet": "utf8",
2670	"bBomInc": false,
2671	"sFileName": "*.csv",
2672	"sFieldBoundary": "",
2673	"sFieldSeperator": "\t",
2674	"sNewLine": "auto",
2675	"mColumns": "all", /* "all", "visible", "hidden" or array of column integers */
2676	"bHeader": true,
2677	"bFooter": true,
2678	"bOpenRows": false,
2679	"bSelectedOnly": false,
2680	"oSelectorOpts": undefined, // See http://datatables.net/docs/DataTables/1.9.4/#$ for full options
2681
2682	// Callbacks
2683	"fnMouseover": null,
2684	"fnMouseout": null,
2685	"fnClick": null,
2686	"fnSelect": null,
2687	"fnComplete": null,
2688	"fnInit": null,
2689	"fnCellRender": null
2690};
2691
2692
2693/**
2694 * @namespace Default button configurations
2695 */
2696TableTools.BUTTONS = {
2697	"csv": $.extend( {}, TableTools.buttonBase, {
2698		"sAction": "flash_save",
2699		"sButtonClass": "DTTT_button_csv",
2700		"sButtonText": "CSV",
2701		"sFieldBoundary": '"',
2702		"sFieldSeperator": ",",
2703		"fnClick": function( nButton, oConfig, flash ) {
2704			this.fnSetText( flash, this.fnGetTableData(oConfig) );
2705		}
2706	} ),
2707
2708	"xls": $.extend( {}, TableTools.buttonBase, {
2709		"sAction": "flash_save",
2710		"sCharSet": "utf16le",
2711		"bBomInc": true,
2712		"sButtonClass": "DTTT_button_xls",
2713		"sButtonText": "Excel",
2714		"fnClick": function( nButton, oConfig, flash ) {
2715			this.fnSetText( flash, this.fnGetTableData(oConfig) );
2716		}
2717	} ),
2718
2719	"copy": $.extend( {}, TableTools.buttonBase, {
2720		"sAction": "flash_copy",
2721		"sButtonClass": "DTTT_button_copy",
2722		"sButtonText": "Copy",
2723		"fnClick": function( nButton, oConfig, flash ) {
2724			this.fnSetText( flash, this.fnGetTableData(oConfig) );
2725		},
2726		"fnComplete": function(nButton, oConfig, flash, text) {
2727			var
2728				lines = text.split('\n').length,
2729				len = this.s.dt.nTFoot === null ? lines-1 : lines-2,
2730				plural = (len==1) ? "" : "s";
2731			this.fnInfo( '<h6>Table copied</h6>'+
2732				'<p>Copied '+len+' row'+plural+' to the clipboard.</p>',
2733				1500
2734			);
2735		}
2736	} ),
2737
2738	"pdf": $.extend( {}, TableTools.buttonBase, {
2739		"sAction": "flash_pdf",
2740		"sNewLine": "\n",
2741		"sFileName": "*.pdf",
2742		"sButtonClass": "DTTT_button_pdf",
2743		"sButtonText": "PDF",
2744		"sPdfOrientation": "portrait",
2745		"sPdfSize": "A4",
2746		"sPdfMessage": "",
2747		"fnClick": function( nButton, oConfig, flash ) {
2748			this.fnSetText( flash,
2749				"title:"+ this.fnGetTitle(oConfig) +"\n"+
2750				"message:"+ oConfig.sPdfMessage +"\n"+
2751				"colWidth:"+ this.fnCalcColRatios(oConfig) +"\n"+
2752				"orientation:"+ oConfig.sPdfOrientation +"\n"+
2753				"size:"+ oConfig.sPdfSize +"\n"+
2754				"--/TableToolsOpts--\n" +
2755				this.fnGetTableData(oConfig)
2756			);
2757		}
2758	} ),
2759
2760	"print": $.extend( {}, TableTools.buttonBase, {
2761		"sInfo": "<h6>Print view</h6><p>Please use your browser's print function to "+
2762		  "print this table. Press escape when finished.</p>",
2763		"sMessage": null,
2764		"bShowAll": true,
2765		"sToolTip": "View print view",
2766		"sButtonClass": "DTTT_button_print",
2767		"sButtonText": "Print",
2768		"fnClick": function ( nButton, oConfig ) {
2769			this.fnPrint( true, oConfig );
2770		}
2771	} ),
2772
2773	"text": $.extend( {}, TableTools.buttonBase ),
2774
2775	"select": $.extend( {}, TableTools.buttonBase, {
2776		"sButtonText": "Select button",
2777		"fnSelect": function( nButton, oConfig ) {
2778			if ( this.fnGetSelected().length !== 0 ) {
2779				$(nButton).removeClass( this.classes.buttons.disabled );
2780			} else {
2781				$(nButton).addClass( this.classes.buttons.disabled );
2782			}
2783		},
2784		"fnInit": function( nButton, oConfig ) {
2785			$(nButton).addClass( this.classes.buttons.disabled );
2786		}
2787	} ),
2788
2789	"select_single": $.extend( {}, TableTools.buttonBase, {
2790		"sButtonText": "Select button",
2791		"fnSelect": function( nButton, oConfig ) {
2792			var iSelected = this.fnGetSelected().length;
2793			if ( iSelected == 1 ) {
2794				$(nButton).removeClass( this.classes.buttons.disabled );
2795			} else {
2796				$(nButton).addClass( this.classes.buttons.disabled );
2797			}
2798		},
2799		"fnInit": function( nButton, oConfig ) {
2800			$(nButton).addClass( this.classes.buttons.disabled );
2801		}
2802	} ),
2803
2804	"select_all": $.extend( {}, TableTools.buttonBase, {
2805		"sButtonText": "Select all",
2806		"fnClick": function( nButton, oConfig ) {
2807			this.fnSelectAll();
2808		},
2809		"fnSelect": function( nButton, oConfig ) {
2810			if ( this.fnGetSelected().length == this.s.dt.fnRecordsDisplay() ) {
2811				$(nButton).addClass( this.classes.buttons.disabled );
2812			} else {
2813				$(nButton).removeClass( this.classes.buttons.disabled );
2814			}
2815		}
2816	} ),
2817
2818	"select_none": $.extend( {}, TableTools.buttonBase, {
2819		"sButtonText": "Deselect all",
2820		"fnClick": function( nButton, oConfig ) {
2821			this.fnSelectNone();
2822		},
2823		"fnSelect": function( nButton, oConfig ) {
2824			if ( this.fnGetSelected().length !== 0 ) {
2825				$(nButton).removeClass( this.classes.buttons.disabled );
2826			} else {
2827				$(nButton).addClass( this.classes.buttons.disabled );
2828			}
2829		},
2830		"fnInit": function( nButton, oConfig ) {
2831			$(nButton).addClass( this.classes.buttons.disabled );
2832		}
2833	} ),
2834
2835	"ajax": $.extend( {}, TableTools.buttonBase, {
2836		"sAjaxUrl": "/xhr.php",
2837		"sButtonText": "Ajax button",
2838		"fnClick": function( nButton, oConfig ) {
2839			var sData = this.fnGetTableData(oConfig);
2840			$.ajax( {
2841				"url": oConfig.sAjaxUrl,
2842				"data": [
2843					{ "name": "tableData", "value": sData }
2844				],
2845				"success": oConfig.fnAjaxComplete,
2846				"dataType": "json",
2847				"type": "POST",
2848				"cache": false,
2849				"error": function () {
2850					alert( "Error detected when sending table data to server" );
2851				}
2852			} );
2853		},
2854		"fnAjaxComplete": function( json ) {
2855			alert( 'Ajax complete' );
2856		}
2857	} ),
2858
2859	"div": $.extend( {}, TableTools.buttonBase, {
2860		"sAction": "div",
2861		"sTag": "div",
2862		"sButtonClass": "DTTT_nonbutton",
2863		"sButtonText": "Text button"
2864	} ),
2865
2866	"collection": $.extend( {}, TableTools.buttonBase, {
2867		"sAction": "collection",
2868		"sButtonClass": "DTTT_button_collection",
2869		"sButtonText": "Collection",
2870		"fnClick": function( nButton, oConfig ) {
2871			this._fnCollectionShow(nButton, oConfig);
2872		}
2873	} )
2874};
2875/*
2876 *  on* callback parameters:
2877 *     1. node - button element
2878 *     2. object - configuration object for this button
2879 *     3. object - ZeroClipboard reference (flash button only)
2880 *     4. string - Returned string from Flash (flash button only - and only on 'complete')
2881 */
2882
2883// Alias to match the other plug-ins styling
2884TableTools.buttons = TableTools.BUTTONS;
2885
2886
2887/**
2888 * @namespace Classes used by TableTools - allows the styles to be override easily.
2889 *   Note that when TableTools initialises it will take a copy of the classes object
2890 *   and will use its internal copy for the remainder of its run time.
2891 */
2892TableTools.classes = {
2893	"container": "DTTT_container",
2894	"buttons": {
2895		"normal": "DTTT_button",
2896		"disabled": "DTTT_disabled"
2897	},
2898	"collection": {
2899		"container": "DTTT_collection",
2900		"background": "DTTT_collection_background",
2901		"buttons": {
2902			"normal": "DTTT_button",
2903			"disabled": "DTTT_disabled"
2904		}
2905	},
2906	"select": {
2907		"table": "DTTT_selectable",
2908		"row": "DTTT_selected selected"
2909	},
2910	"print": {
2911		"body": "DTTT_Print",
2912		"info": "DTTT_print_info",
2913		"message": "DTTT_PrintMessage"
2914	}
2915};
2916
2917
2918/**
2919 * @namespace ThemeRoller classes - built in for compatibility with DataTables' 
2920 *   bJQueryUI option.
2921 */
2922TableTools.classes_themeroller = {
2923	"container": "DTTT_container ui-buttonset ui-buttonset-multi",
2924	"buttons": {
2925		"normal": "DTTT_button ui-button ui-state-default"
2926	},
2927	"collection": {
2928		"container": "DTTT_collection ui-buttonset ui-buttonset-multi"
2929	}
2930};
2931
2932
2933/**
2934 * @namespace TableTools default settings for initialisation
2935 */
2936TableTools.DEFAULTS = {
2937	"sSwfPath":        "../swf/copy_csv_xls_pdf.swf",
2938	"sRowSelect":      "none",
2939	"sRowSelector":    "tr",
2940	"sSelectedClass":  null,
2941	"fnPreRowSelect":  null,
2942	"fnRowSelected":   null,
2943	"fnRowDeselected": null,
2944	"aButtons":        [ "copy", "csv", "xls", "pdf", "print" ],
2945	"oTags": {
2946		"container": "div",
2947		"button": "a", // We really want to use buttons here, but Firefox and IE ignore the
2948		                 // click on the Flash element in the button (but not mouse[in|out]).
2949		"liner": "span",
2950		"collection": {
2951			"container": "div",
2952			"button": "a",
2953			"liner": "span"
2954		}
2955	}
2956};
2957
2958// Alias to match the other plug-ins
2959TableTools.defaults = TableTools.DEFAULTS;
2960
2961
2962/**
2963 * Name of this class
2964 *  @constant CLASS
2965 *  @type	 String
2966 *  @default  TableTools
2967 */
2968TableTools.prototype.CLASS = "TableTools";
2969
2970
2971/**
2972 * TableTools version
2973 *  @constant  VERSION
2974 *  @type	  String
2975 *  @default   See code
2976 */
2977TableTools.version = "2.2.1";
2978
2979
2980
2981// DataTables 1.10 API
2982// 
2983// This will be extended in a big way in in TableTools 3 to provide API methods
2984// such as rows().select() and rows.selected() etc, but for the moment the
2985// tabletools() method simply returns the instance.
2986
2987if ( $.fn.dataTable.Api ) {
2988	$.fn.dataTable.Api.register( 'tabletools()', function () {
2989		var tt = null;
2990
2991		if ( this.context.length > 0 ) {
2992			tt = TableTools.fnGetInstance( this.context[0].nTable );
2993		}
2994
2995		return tt;
2996	} );
2997}
2998
2999
3000
3001
3002/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3003 * Initialisation
3004 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3005
3006/*
3007 * Register a new feature with DataTables
3008 */
3009if ( typeof $.fn.dataTable == "function" &&
3010	 typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
3011	 $.fn.dataTableExt.fnVersionCheck('1.9.0') )
3012{
3013	$.fn.dataTableExt.aoFeatures.push( {
3014		"fnInit": function( oDTSettings ) {
3015			var init = oDTSettings.oInit;
3016			var opts = init ?
3017				init.tableTools || init.oTableTools || {} :
3018				{};
3019
3020			var oTT = new TableTools( oDTSettings.oInstance, opts );
3021			TableTools._aInstances.push( oTT );
3022
3023			return oTT.dom.container;
3024		},
3025		"cFeature": "T",
3026		"sFeature": "TableTools"
3027	} );
3028}
3029else
3030{
3031	alert( "Warning: TableTools requires DataTables 1.9.0 or newer - www.datatables.net/download");
3032}
3033
3034$.fn.DataTable.TableTools = TableTools;
3035
3036})(jQuery, window, document);
3037
3038/*
3039 * Register a new feature with DataTables
3040 */
3041if ( typeof $.fn.dataTable == "function" &&
3042	 typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
3043	 $.fn.dataTableExt.fnVersionCheck('1.9.0') )
3044{
3045	$.fn.dataTableExt.aoFeatures.push( {
3046		"fnInit": function( oDTSettings ) {
3047			var oOpts = typeof oDTSettings.oInit.oTableTools != 'undefined' ?
3048				oDTSettings.oInit.oTableTools : {};
3049
3050			var oTT = new TableTools( oDTSettings.oInstance, oOpts );
3051			TableTools._aInstances.push( oTT );
3052
3053			return oTT.dom.container;
3054		},
3055		"cFeature": "T",
3056		"sFeature": "TableTools"
3057	} );
3058}
3059else
3060{
3061	alert( "Warning: TableTools 2 requires DataTables 1.9.0 or newer - www.datatables.net/download");
3062}
3063
3064
3065$.fn.dataTable.TableTools = TableTools;
3066$.fn.DataTable.TableTools = TableTools;
3067
3068
3069return TableTools;
3070}; // /factory
3071
3072
3073// Define as an AMD module if possible
3074if ( typeof define === 'function' && define.amd ) {
3075	define( 'datatables-tabletools', ['jquery', 'datatables'], factory );
3076}
3077else if ( jQuery && !jQuery.fn.dataTable.TableTools ) {
3078	// Otherwise simply initialise as normal, stopping multiple evaluation
3079	factory( jQuery, jQuery.fn.dataTable );
3080}
3081
3082
3083})(window, document);
3084

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.