Subversion-Projekte lars-tiefland.webanos.zeldi.de

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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