Project

General

Profile

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
 */
31
var TableTools;
32

    
33
(function(window, document, undefined) {
34

    
35

    
36
var factory = function( $, DataTable ) {
37
"use strict";
38

    
39

    
40
//include ZeroClipboard.js
41
/* ZeroClipboard 1.0.4
42
 * Author: Joseph Huckaby
43
 */
44

    
45
var 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

    
134
ZeroClipboard_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/shockwave/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
434
window.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
 */
460
TableTools = 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

    
748
TableTools.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 work 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
 */
2543
TableTools._aInstances = [];
2544

    
2545

    
2546
/**
2547
 * Store of all listeners and their callback functions
2548
 *  @property _aListeners
2549
 *  @type	 Array
2550
 *  @default  []
2551
 */
2552
TableTools._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
 */
2566
TableTools.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
 */
2585
TableTools.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
 */
2613
TableTools._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
 */
2635
TableTools._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

    
2658
TableTools.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
 */
2696
TableTools.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
2884
TableTools.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
 */
2892
TableTools.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
 */
2922
TableTools.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
 */
2936
TableTools.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
2959
TableTools.defaults = TableTools.DEFAULTS;
2960

    
2961

    
2962
/**
2963
 * Name of this class
2964
 *  @constant CLASS
2965
 *  @type	 String
2966
 *  @default  TableTools
2967
 */
2968
TableTools.prototype.CLASS = "TableTools";
2969

    
2970

    
2971
/**
2972
 * TableTools version
2973
 *  @constant  VERSION
2974
 *  @type	  String
2975
 *  @default   See code
2976
 */
2977
TableTools.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

    
2987
if ( $.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
 */
3009
if ( 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
}
3029
else
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
 */
3041
if ( 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
}
3059
else
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

    
3069
return TableTools;
3070
}; // /factory
3071

    
3072

    
3073
// Define as an AMD module if possible
3074
if ( typeof define === 'function' && define.amd ) {
3075
	define( 'datatables-tabletools', ['jquery', 'datatables'], factory );
3076
}
3077
else 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

    
(1-1/2)