js/datatables/tools/js/dataTables.tableTools.js   F
last analyzed

Complexity

Total Complexity 417
Complexity/F 3.11

Size

Lines of Code 3053
Function Count 134

Duplication

Duplicated Lines 3053
Ratio 100 %

Importance

Changes 0
Metric Value
cc 0
eloc 1340
nc 0
dl 3053
loc 3053
rs 0.8
c 0
b 0
f 0
wmc 417
mnd 6
bc 374
fnc 134
bpm 2.791
cpm 3.1119
noi 47

2 Functions

Rating   Name   Duplication   Size   Complexity  
C 3051 3051 5
C dataTables.tableTools.js ➔ factory 2594 2594 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like js/datatables/tools/js/dataTables.tableTools.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*! TableTools 2.2.1
2
 * 2009-2014 SpryMedia Ltd - datatables.net/license
3
 *
4
 * ZeroClipboard 1.0.4
5
 * Author: Joseph Huckaby - MIT licensed
6
 */
7
8
/**
9
 * @summary     TableTools
10
 * @description Tools and buttons for DataTables
11
 * @version     2.2.1
12
 * @file        dataTables.tableTools.js
13
 * @author      SpryMedia Ltd (www.sprymedia.co.uk)
14
 * @contact     www.sprymedia.co.uk/contact
15
 * @copyright   Copyright 2009-2014 SpryMedia Ltd.
16
 *
17
 * This source file is free software, available under the following license:
18
 *   MIT license - http://datatables.net/license/mit
19
 *
20
 * This source file is distributed in the hope that it will be useful, but
21
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22
 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
23
 *
24
 * For details please refer to: http://www.datatables.net
25
 */
26
27
28
/* Global scope for TableTools for backwards compatibility.
29
 * Will be removed in 2.3
30
 */
31 View Code Duplication
var TableTools;
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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) {}
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
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.recoverAktif) {
380
						this.domElement.addClass('Aktif');
381
					}
382
				}
383
				break;
384
385
			case 'mouseout':
386
				if (this.domElement && this.cssEffects) {
387
					this.recoverAktif = false;
388
					if (this.domElement.hasClass('Aktif')) {
389
						this.domElement.removeClass('Aktif');
390
						this.recoverAktif = true;
391
					}
392
					//this.domElement.removeClass('hover');
393
				}
394
				break;
395
396
			case 'mousedown':
397
				if (this.domElement && this.cssEffects) {
398
					this.domElement.addClass('Aktif');
399
				}
400
				break;
401
402
			case 'mouseup':
403
				if (this.domElement && this.cssEffects) {
404
					this.domElement.removeClass('Aktif');
405
					this.recoverAktif = 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'" );
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
466
	}
467
468
	// In 1.10 we can use the API to get the settings object from a number of
469
	// sources
470
	var dtSettings = $.fn.dataTable.Api ?
471
		new $.fn.dataTable.Api( oDT ).settings()[0] :
472
		oDT.fnSettings();
473
474
475
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
476
	 * Public class variables
477
	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
478
479
	/**
480
	 * @namespace Settings object which contains customisable information for TableTools instance
481
	 */
482
	this.s = {
483
		/**
484
		 * Store 'this' so the instance can be retrieved from the settings object
485
		 * @property that
486
		 * @type	 object
487
		 * @default  this
488
		 */
489
		"that": this,
490
491
		/** 
492
		 * DataTables settings objects
493
		 * @property dt
494
		 * @type	 object
495
		 * @default  <i>From the oDT init option</i>
496
		 */
497
		"dt": dtSettings,
498
499
		/**
500
		 * @namespace Print specific information
501
		 */
502
		"print": {
503
			/** 
504
			 * DataTables draw 'start' point before the printing display was shown
505
			 *  @property saveStart
506
			 *  @type	 int
507
			 *  @default  -1
508
			 */
509
			"saveStart": -1,
510
511
			/** 
512
			 * DataTables draw 'length' point before the printing display was shown
513
			 *  @property saveLength
514
			 *  @type	 int
515
			 *  @default  -1
516
			 */
517
			"saveLength": -1,
518
519
			/** 
520
			 * Page scrolling point before the printing display was shown so it can be restored
521
			 *  @property saveScroll
522
			 *  @type	 int
523
			 *  @default  -1
524
			 */
525
			"saveScroll": -1,
526
527
			/** 
528
			 * Wrapped function to end the print display (to maintain scope)
529
			 *  @property funcEnd
530
			 *  @type	 Function
531
			 *  @default  function () {}
532
			 */
533
			"funcEnd": function () {}
534
		},
535
536
		/**
537
		 * A unique ID is assigned to each button in each instance
538
		 * @property buttonCounter
539
		 *  @type	 int
540
		 * @default  0
541
		 */
542
		"buttonCounter": 0,
543
544
		/**
545
		 * @namespace Select rows specific information
546
		 */
547
		"select": {
548
			/**
549
			 * Select type - can be 'none', 'single' or 'multi'
550
			 * @property type
551
			 *  @type	 string
552
			 * @default  ""
553
			 */
554
			"type": "",
555
556
			/**
557
			 * Array of nodes which are currently selected
558
			 *  @property selected
559
			 *  @type	 array
560
			 *  @default  []
561
			 */
562
			"selected": [],
563
564
			/**
565
			 * Function to run before the selection can take place. Will cancel the select if the
566
			 * function returns false
567
			 *  @property preRowSelect
568
			 *  @type	 Function
569
			 *  @default  null
570
			 */
571
			"preRowSelect": null,
572
573
			/**
574
			 * Function to run when a row is selected
575
			 *  @property postSelected
576
			 *  @type	 Function
577
			 *  @default  null
578
			 */
579
			"postSelected": null,
580
581
			/**
582
			 * Function to run when a row is deselected
583
			 *  @property postDeselected
584
			 *  @type	 Function
585
			 *  @default  null
586
			 */
587
			"postDeselected": null,
588
589
			/**
590
			 * Indicate if all rows are selected (needed for server-side processing)
591
			 *  @property all
592
			 *  @type	 boolean
593
			 *  @default  false
594
			 */
595
			"all": false,
596
597
			/**
598
			 * Class name to add to selected TR nodes
599
			 *  @property selectedClass
600
			 *  @type	 String
601
			 *  @default  ""
602
			 */
603
			"selectedClass": ""
604
		},
605
606
		/**
607
		 * Store of the user input customisation object
608
		 *  @property custom
609
		 *  @type	 object
610
		 *  @default  {}
611
		 */
612
		"custom": {},
613
614
		/**
615
		 * SWF movie path
616
		 *  @property swfPath
617
		 *  @type	 string
618
		 *  @default  ""
619
		 */
620
		"swfPath": "",
621
622
		/**
623
		 * Default button set
624
		 *  @property buttonSet
625
		 *  @type	 array
626
		 *  @default  []
627
		 */
628
		"buttonSet": [],
629
630
		/**
631
		 * When there is more than one TableTools instance for a DataTable, there must be a 
632
		 * master which controls events (row selection etc)
633
		 *  @property master
634
		 *  @type	 boolean
635
		 *  @default  false
636
		 */
637
		"master": false,
638
639
		/**
640
		 * Tag names that are used for creating collections and buttons
641
		 *  @namesapce
642
		 */
643
		"tags": {}
644
	};
645
646
647
	/**
648
	 * @namespace Common and useful DOM elements for the class instance
649
	 */
650
	this.dom = {
651
		/**
652
		 * DIV element that is create and all TableTools buttons (and their children) put into
653
		 *  @property container
654
		 *  @type	 node
655
		 *  @default  null
656
		 */
657
		"container": null,
658
659
		/**
660
		 * The table node to which TableTools will be applied
661
		 *  @property table
662
		 *  @type	 node
663
		 *  @default  null
664
		 */
665
		"table": null,
666
667
		/**
668
		 * @namespace Nodes used for the print display
669
		 */
670
		"print": {
671
			/**
672
			 * Nodes which have been removed from the display by setting them to display none
673
			 *  @property hidden
674
			 *  @type	 array
675
			 *  @default  []
676
			 */
677
			"hidden": [],
678
679
			/**
680
			 * The information display saying telling the user about the print display
681
			 *  @property message
682
			 *  @type	 node
683
			 *  @default  null
684
			 */
685
			"message": null
686
	  },
687
688
		/**
689
		 * @namespace Nodes used for a collection display. This contains the currently used collection
690
		 */
691
		"collection": {
692
			/**
693
			 * The div wrapper containing the buttons in the collection (i.e. the menu)
694
			 *  @property collection
695
			 *  @type	 node
696
			 *  @default  null
697
			 */
698
			"collection": null,
699
700
			/**
701
			 * Background display to provide focus and capture events
702
			 *  @property background
703
			 *  @type	 node
704
			 *  @default  null
705
			 */
706
			"background": null
707
		}
708
	};
709
710
	/**
711
	 * @namespace Name space for the classes that this TableTools instance will use
712
	 * @extends TableTools.classes
713
	 */
714
	this.classes = $.extend( true, {}, TableTools.classes );
715
	if ( this.s.dt.bJUI )
716
	{
717
		$.extend( true, this.classes, TableTools.classes_themeroller );
718
	}
719
720
721
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
722
	 * Public class methods
723
	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
724
725
	/**
726
	 * Retreieve the settings object from an instance
727
	 *  @method fnSettings
728
	 *  @returns {object} TableTools settings object
729
	 */
730
	this.fnSettings = function () {
731
		return this.s;
732
	};
733
734
735
	/* Constructor logic */
736
	if ( typeof oOpts == 'undefined' )
737
	{
738
		oOpts = {};
739
	}
740
741
	this._fnConstruct( oOpts );
742
743
	return this;
744
};
745
746
747
748
TableTools.prototype = {
749
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
750
	 * Public methods
751
	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
752
753
	/**
754
	 * Retreieve the settings object from an instance
755
	 *  @returns {array} List of TR nodes which are currently selected
756
	 *  @param {boolean} [filtered=false] Get only selected rows which are  
757
	 *    available given the filtering applied to the table. By default
758
	 *    this is false -  i.e. all rows, regardless of filtering are 
759
	      selected.
760
	 */
761
	"fnGetSelected": function ( filtered )
762
	{
763
		var
764
			out = [],
765
			data = this.s.dt.aoData,
766
			displayed = this.s.dt.aiDisplay,
767
			i, iLen;
768
769
		if ( filtered )
770
		{
771
			// Only consider filtered rows
772
			for ( i=0, iLen=displayed.length ; i<iLen ; i++ )
773
			{
774
				if ( data[ displayed[i] ]._DTTT_selected )
775
				{
776
					out.push( data[ displayed[i] ].nTr );
777
				}
778
			}
779
		}
780
		else
781
		{
782
			// Use all rows
783
			for ( i=0, iLen=data.length ; i<iLen ; i++ )
784
			{
785
				if ( data[i]._DTTT_selected )
786
				{
787
					out.push( data[i].nTr );
788
				}
789
			}
790
		}
791
792
		return out;
793
	},
794
795
796
	/**
797
	 * Get the data source objects/arrays from DataTables for the selected rows (same as
798
	 * fnGetSelected followed by fnGetData on each row from the table)
799
	 *  @returns {array} Data from the TR nodes which are currently selected
800
	 */
801
	"fnGetSelectedData": function ()
802
	{
803
		var out = [];
804
		var data=this.s.dt.aoData;
805
		var i, iLen;
806
807
		for ( i=0, iLen=data.length ; i<iLen ; i++ )
808
		{
809
			if ( data[i]._DTTT_selected )
810
			{
811
				out.push( this.s.dt.oInstance.fnGetData(i) );
812
			}
813
		}
814
815
		return out;
816
	},
817
818
819
	/**
820
	 * Check to see if a current row is selected or not
821
	 *  @param {Node} n TR node to check if it is currently selected or not
822
	 *  @returns {Boolean} true if select, false otherwise
823
	 */
824
	"fnIsSelected": function ( n )
825
	{
826
		var pos = this.s.dt.oInstance.fnGetPosition( n );
827
		return (this.s.dt.aoData[pos]._DTTT_selected===true) ? true : false;
828
	},
829
830
831
	/**
832
	 * Select all rows in the table
833
	 *  @param {boolean} [filtered=false] Select only rows which are available 
834
	 *    given the filtering applied to the table. By default this is false - 
835
	 *    i.e. all rows, regardless of filtering are selected.
836
	 */
837
	"fnSelectAll": function ( filtered )
838
	{
839
		var s = this._fnGetMasterSettings();
840
841
		this._fnRowSelect( (filtered === true) ?
842
			s.dt.aiDisplay :
843
			s.dt.aoData
844
		);
845
	},
846
847
848
	/**
849
	 * Deselect all rows in the table
850
	 *  @param {boolean} [filtered=false] Deselect only rows which are available 
851
	 *    given the filtering applied to the table. By default this is false - 
852
	 *    i.e. all rows, regardless of filtering are deselected.
853
	 */
854
	"fnSelectNone": function ( filtered )
855
	{
856
		var s = this._fnGetMasterSettings();
0 ignored issues
show
Unused Code introduced by
The variable s seems to be never used. Consider removing it.
Loading history...
857
858
		this._fnRowDeselect( this.fnGetSelected(filtered) );
859
	},
860
861
862
	/**
863
	 * Select row(s)
864
	 *  @param {node|object|array} n The row(s) to select. Can be a single DOM
865
	 *    TR node, an array of TR nodes or a jQuery object.
866
	 */
867
	"fnSelect": function ( n )
868
	{
869
		if ( this.s.select.type == "single" )
870
		{
871
			this.fnSelectNone();
872
			this._fnRowSelect( n );
873
		}
874
		else
875
		{
876
			this._fnRowSelect( n );
877
		}
878
	},
879
880
881
	/**
882
	 * Deselect row(s)
883
	 *  @param {node|object|array} n The row(s) to deselect. Can be a single DOM
884
	 *    TR node, an array of TR nodes or a jQuery object.
885
	 */
886
	"fnDeselect": function ( n )
887
	{
888
		this._fnRowDeselect( n );
889
	},
890
891
892
	/**
893
	 * Get the title of the document - useful for file names. The title is retrieved from either
894
	 * the configuration object's 'title' parameter, or the HTML document title
895
	 *  @param   {Object} oConfig Button configuration object
896
	 *  @returns {String} Button title
897
	 */
898
	"fnGetTitle": function( oConfig )
899
	{
900
		var sTitle = "";
901
		if ( typeof oConfig.sTitle != 'undefined' && oConfig.sTitle !== "" ) {
902
			sTitle = oConfig.sTitle;
903
		} else {
904
			var anTitle = document.getElementsByTagName('title');
905
			if ( anTitle.length > 0 )
906
			{
907
				sTitle = anTitle[0].innerHTML;
908
			}
909
		}
910
911
		/* Strip characters which the OS will object to - checking for UTF8 support in the scripting
912
		 * engine
913
		 */
914
		if ( "\u00A1".toString().length < 4 ) {
915
			return sTitle.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
916
		} else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
917
			return sTitle.replace(/[^a-zA-Z0-9_\.,\-_ !\(\)]/g, "");
918
		}
919
	},
920
921
922
	/**
923
	 * Calculate a unity array with the column width by proportion for a set of columns to be
924
	 * included for a button. This is particularly useful for PDF creation, where we can use the
925
	 * column widths calculated by the browser to size the columns in the PDF.
926
	 *  @param   {Object} oConfig Button configuration object
927
	 *  @returns {Array} Unity array of column ratios
928
	 */
929
	"fnCalcColRatios": function ( oConfig )
930
	{
931
		var
932
			aoCols = this.s.dt.aoColumns,
933
			aColumnsInc = this._fnColumnTargets( oConfig.mColumns ),
934
			aColWidths = [],
935
			iWidth = 0, iTotal = 0, i, iLen;
936
937
		for ( i=0, iLen=aColumnsInc.length ; i<iLen ; i++ )
938
		{
939
			if ( aColumnsInc[i] )
940
			{
941
				iWidth = aoCols[i].nTh.offsetWidth;
942
				iTotal += iWidth;
943
				aColWidths.push( iWidth );
944
			}
945
		}
946
947
		for ( i=0, iLen=aColWidths.length ; i<iLen ; i++ )
948
		{
949
			aColWidths[i] = aColWidths[i] / iTotal;
950
		}
951
952
		return aColWidths.join('\t');
953
	},
954
955
956
	/**
957
	 * Get the information contained in a table as a string
958
	 *  @param   {Object} oConfig Button configuration object
959
	 *  @returns {String} Table data as a string
960
	 */
961
	"fnGetTableData": function ( oConfig )
962
	{
963
		/* In future this could be used to get data from a plain HTML source as well as DataTables */
964
		if ( this.s.dt )
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if this.s.dt is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
965
		{
966
			return this._fnGetDataTablesData( oConfig );
967
		}
968
	},
969
970
971
	/**
972
	 * Pass text to a flash button instance, which will be used on the button's click handler
973
	 *  @param   {Object} clip Flash button object
974
	 *  @param   {String} text Text to set
975
	 */
976
	"fnSetText": function ( clip, text )
977
	{
978
		this._fnFlashSetText( clip, text );
979
	},
980
981
982
	/**
983
	 * Resize the flash elements of the buttons attached to this TableTools instance - this is
984
	 * useful for when initialising TableTools when it is hidden (display:none) since sizes can't
985
	 * be calculated at that time.
986
	 */
987
	"fnResizeButtons": function ()
988
	{
989
		for ( var cli in ZeroClipboard_TableTools.clients )
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
990
		{
991
			if ( cli )
992
			{
993
				var client = ZeroClipboard_TableTools.clients[cli];
994
				if ( typeof client.domElement != 'undefined' &&
995
					 client.domElement.parentNode )
996
				{
997
					client.positionElement();
998
				}
999
			}
1000
		}
1001
	},
1002
1003
1004
	/**
1005
	 * Check to see if any of the ZeroClipboard client's attached need to be resized
1006
	 */
1007
	"fnResizeRequired": function ()
1008
	{
1009
		for ( var cli in ZeroClipboard_TableTools.clients )
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
1010
		{
1011
			if ( cli )
1012
			{
1013
				var client = ZeroClipboard_TableTools.clients[cli];
1014
				if ( typeof client.domElement != 'undefined' &&
1015
					 client.domElement.parentNode == this.dom.container &&
1016
					 client.sized === false )
1017
				{
1018
					return true;
1019
				}
1020
			}
1021
		}
1022
		return false;
1023
	},
1024
1025
1026
	/**
1027
	 * Programmatically enable or disable the print view
1028
	 *  @param {boolean} [bView=true] Show the print view if true or not given. If false, then
1029
	 *    terminate the print view and return to normal.
1030
	 *  @param {object} [oConfig={}] Configuration for the print view
1031
	 *  @param {boolean} [oConfig.bShowAll=false] Show all rows in the table if true
1032
	 *  @param {string} [oConfig.sInfo] Information message, displayed as an overlay to the
1033
	 *    user to let them know what the print view is.
1034
	 *  @param {string} [oConfig.sMessage] HTML string to show at the top of the document - will
1035
	 *    be included in the printed document.
1036
	 */
1037
	"fnPrint": function ( bView, oConfig )
1038
	{
1039
		if ( oConfig === undefined )
1040
		{
1041
			oConfig = {};
1042
		}
1043
1044
		if ( bView === undefined || bView )
1045
		{
1046
			this._fnPrintStart( oConfig );
1047
		}
1048
		else
1049
		{
1050
			this._fnPrintEnd();
1051
		}
1052
	},
1053
1054
1055
	/**
1056
	 * Show a message to the end user which is nicely styled
1057
	 *  @param {string} message The HTML string to show to the user
1058
	 *  @param {int} time The duration the message is to be shown on screen for (mS)
1059
	 */
1060
	"fnInfo": function ( message, time ) {
1061
		var info = $('<div/>')
1062
			.addClass( this.classes.print.info )
1063
			.html( message )
1064
			.appendTo( 'body' );
1065
1066
		setTimeout( function() {
1067
			info.fadeOut( "normal", function() {
1068
				info.remove();
1069
			} );
1070
		}, time );
1071
	},
1072
1073
1074
1075
	/**
1076
	 * Get the container element of the instance for attaching to the DOM
1077
	 *   @returns {node} DOM node
1078
	 */
1079
	"fnContainer": function () {
1080
		return this.dom.container;
1081
	},
1082
1083
1084
1085
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1086
	 * Private methods (they are of course public in JS, but recommended as private)
1087
	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1088
1089
	/**
1090
	 * Constructor logic
1091
	 *  @method  _fnConstruct
1092
	 *  @param   {Object} oOpts Same as TableTools constructor
1093
	 *  @returns void
1094
	 *  @private 
1095
	 */
1096
	"_fnConstruct": function ( oOpts )
1097
	{
1098
		var that = this;
1099
1100
		this._fnCustomiseSettings( oOpts );
1101
1102
		/* Container element */
1103
		this.dom.container = document.createElement( this.s.tags.container );
1104
		this.dom.container.className = this.classes.container;
1105
1106
		/* Row selection config */
1107
		if ( this.s.select.type != 'none' )
1108
		{
1109
			this._fnRowSelectConfig();
1110
		}
1111
1112
		/* Buttons */
1113
		this._fnButtonDefinations( this.s.buttonSet, this.dom.container );
1114
1115
		/* Destructor */
1116
		this.s.dt.aoDestroyCallback.push( {
1117
			"sName": "TableTools",
1118
			"fn": function () {
1119
				$(that.s.dt.nTBody).off( 'click.DTTT_Select', 'tr' );
1120
				$(that.dom.container).empty();
1121
1122
				// Remove the instance
1123
				var idx = $.inArray( that, TableTools._aInstances );
1124
				if ( idx !== -1 ) {
1125
					TableTools._aInstances.splice( idx, 1 );
1126
				}
1127
			}
1128
		} );
1129
	},
1130
1131
1132
	/**
1133
	 * Take the user defined settings and the default settings and combine them.
1134
	 *  @method  _fnCustomiseSettings
1135
	 *  @param   {Object} oOpts Same as TableTools constructor
1136
	 *  @returns void
1137
	 *  @private 
1138
	 */
1139
	"_fnCustomiseSettings": function ( oOpts )
1140
	{
1141
		/* Is this the master control instance or not? */
1142
		if ( typeof this.s.dt._TableToolsInit == 'undefined' )
1143
		{
1144
			this.s.master = true;
1145
			this.s.dt._TableToolsInit = true;
1146
		}
1147
1148
		/* We can use the table node from comparisons to group controls */
1149
		this.dom.table = this.s.dt.nTable;
1150
1151
		/* Clone the defaults and then the user options */
1152
		this.s.custom = $.extend( {}, TableTools.DEFAULTS, oOpts );
1153
1154
		/* Flash file location */
1155
		this.s.swfPath = this.s.custom.sSwfPath;
1156
		if ( typeof ZeroClipboard_TableTools != 'undefined' )
1157
		{
1158
			ZeroClipboard_TableTools.moviePath = this.s.swfPath;
1159
		}
1160
1161
		/* Table row selecting */
1162
		this.s.select.type = this.s.custom.sRowSelect;
1163
		this.s.select.preRowSelect = this.s.custom.fnPreRowSelect;
1164
		this.s.select.postSelected = this.s.custom.fnRowSelected;
1165
		this.s.select.postDeselected = this.s.custom.fnRowDeselected;
1166
1167
		// Backwards compatibility - allow the user to specify a custom class in the initialiser
1168
		if ( this.s.custom.sSelectedClass )
1169
		{
1170
			this.classes.select.row = this.s.custom.sSelectedClass;
1171
		}
1172
1173
		this.s.tags = this.s.custom.oTags;
1174
1175
		/* Button set */
1176
		this.s.buttonSet = this.s.custom.aButtons;
1177
	},
1178
1179
1180
	/**
1181
	 * Take the user input arrays and expand them to be fully defined, and then add them to a given
1182
	 * DOM element
1183
	 *  @method  _fnButtonDefinations
1184
	 *  @param {array} buttonSet Set of user defined buttons
1185
	 *  @param {node} wrapper Node to add the created buttons to
1186
	 *  @returns void
1187
	 *  @private 
1188
	 */
1189
	"_fnButtonDefinations": function ( buttonSet, wrapper )
1190
	{
1191
		var buttonDef;
1192
1193
		for ( var i=0, iLen=buttonSet.length ; i<iLen ; i++ )
1194
		{
1195
			if ( typeof buttonSet[i] == "string" )
1196
			{
1197
				if ( typeof TableTools.BUTTONS[ buttonSet[i] ] == 'undefined' )
1198
				{
1199
					alert( "TableTools: Warning - unknown button type: "+buttonSet[i] );
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
1200
					continue;
1201
				}
1202
				buttonDef = $.extend( {}, TableTools.BUTTONS[ buttonSet[i] ], true );
1203
			}
1204
			else
1205
			{
1206
				if ( typeof TableTools.BUTTONS[ buttonSet[i].sExtends ] == 'undefined' )
1207
				{
1208
					alert( "TableTools: Warning - unknown button type: "+buttonSet[i].sExtends );
1209
					continue;
1210
				}
1211
				var o = $.extend( {}, TableTools.BUTTONS[ buttonSet[i].sExtends ], true );
1212
				buttonDef = $.extend( o, buttonSet[i], true );
1213
			}
1214
1215
			wrapper.appendChild( this._fnCreateButton(
1216
				buttonDef,
1217
				$(wrapper).hasClass(this.classes.collection.container)
1218
			) );
1219
		}
1220
	},
1221
1222
1223
	/**
1224
	 * Create and configure a TableTools button
1225
	 *  @method  _fnCreateButton
1226
	 *  @param   {Object} oConfig Button configuration object
1227
	 *  @returns {Node} Button element
1228
	 *  @private 
1229
	 */
1230
	"_fnCreateButton": function ( oConfig, bCollectionButton )
1231
	{
1232
	  var nButton = this._fnButtonBase( oConfig, bCollectionButton );
1233
1234
		if ( oConfig.sAction.match(/flash/) )
1235
		{
1236
			this._fnFlashConfig( nButton, oConfig );
1237
		}
1238
		else if ( oConfig.sAction == "text" )
1239
		{
1240
			this._fnTextConfig( nButton, oConfig );
1241
		}
1242
		else if ( oConfig.sAction == "div" )
1243
		{
1244
			this._fnTextConfig( nButton, oConfig );
1245
		}
1246
		else if ( oConfig.sAction == "collection" )
1247
		{
1248
			this._fnTextConfig( nButton, oConfig );
1249
			this._fnCollectionConfig( nButton, oConfig );
1250
		}
1251
1252
		return nButton;
1253
	},
1254
1255
1256
	/**
1257
	 * Create the DOM needed for the button and apply some base properties. All buttons start here
1258
	 *  @method  _fnButtonBase
1259
	 *  @param   {o} oConfig Button configuration object
1260
	 *  @returns {Node} DIV element for the button
1261
	 *  @private
1262
	 */
1263
	"_fnButtonBase": function ( o, bCollectionButton )
1264
	{
1265
		var sTag, sLiner, sClass;
1266
1267
		if ( bCollectionButton )
1268
		{
1269
			sTag = o.sTag && o.sTag !== "default" ? o.sTag : this.s.tags.collection.button;
1270
			sLiner = o.sLinerTag && o.sLinerTag !== "default" ? o.sLiner : this.s.tags.collection.liner;
1271
			sClass = this.classes.collection.buttons.normal;
1272
		}
1273
		else
1274
		{
1275
			sTag = o.sTag && o.sTag !== "default" ? o.sTag : this.s.tags.button;
1276
			sLiner = o.sLinerTag && o.sLinerTag !== "default" ? o.sLiner : this.s.tags.liner;
1277
			sClass = this.classes.buttons.normal;
1278
		}
1279
1280
		var
1281
		  nButton = document.createElement( sTag ),
1282
		  nSpan = document.createElement( sLiner ),
1283
		  masterS = this._fnGetMasterSettings();
1284
1285
		nButton.className = sClass+" "+o.sButtonClass;
1286
		nButton.setAttribute('id', "ToolTables_"+this.s.dt.sInstance+"_"+masterS.buttonCounter );
1287
		nButton.appendChild( nSpan );
1288
		nSpan.innerHTML = o.sButtonText;
1289
1290
		masterS.buttonCounter++;
1291
1292
		return nButton;
1293
	},
1294
1295
1296
	/**
1297
	 * Get the settings object for the master instance. When more than one TableTools instance is
1298
	 * assigned to a DataTable, only one of them can be the 'master' (for the select rows). As such,
1299
	 * we will typically want to interact with that master for global properties.
1300
	 *  @method  _fnGetMasterSettings
1301
	 *  @returns {Object} TableTools settings object
1302
	 *  @private 
1303
	 */
1304
	"_fnGetMasterSettings": function ()
1305
	{
1306
		if ( this.s.master )
1307
		{
1308
			return this.s;
1309
		}
1310
		else
1311
		{
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
1312
			/* Look for the master which has the same DT as this one */
1313
			var instances = TableTools._aInstances;
1314
			for ( var i=0, iLen=instances.length ; i<iLen ; i++ )
1315
			{
1316
				if ( this.dom.table == instances[i].s.dt.nTable )
1317
				{
1318
					return instances[i].s;
1319
				}
1320
			}
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1321
		}
1322
	},
1323
1324
1325
1326
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1327
	 * Button collection functions
1328
	 */
1329
1330
	/**
1331
	 * Create a collection button, when activated will present a drop down list of other buttons
1332
	 *  @param   {Node} nButton Button to use for the collection activation
1333
	 *  @param   {Object} oConfig Button configuration object
1334
	 *  @returns void
1335
	 *  @private
1336
	 */
1337
	"_fnCollectionConfig": function ( nButton, oConfig )
1338
	{
1339
		var nHidden = document.createElement( this.s.tags.collection.container );
1340
		nHidden.style.display = "none";
1341
		nHidden.className = this.classes.collection.container;
1342
		oConfig._collection = nHidden;
1343
		document.body.appendChild( nHidden );
1344
1345
		this._fnButtonDefinations( oConfig.aButtons, nHidden );
1346
	},
1347
1348
1349
	/**
1350
	 * Show a button collection
1351
	 *  @param   {Node} nButton Button to use for the collection
1352
	 *  @param   {Object} oConfig Button configuration object
1353
	 *  @returns void
1354
	 *  @private
1355
	 */
1356
	"_fnCollectionShow": function ( nButton, oConfig )
1357
	{
1358
		var
1359
			that = this,
1360
			oPos = $(nButton).offset(),
1361
			nHidden = oConfig._collection,
1362
			iDivX = oPos.left,
1363
			iDivY = oPos.top + $(nButton).outerHeight(),
1364
			iWinHeight = $(window).height(), iDocHeight = $(document).height(),
1365
			iWinWidth = $(window).width(), iDocWidth = $(document).width();
1366
1367
		nHidden.style.position = "absolute";
1368
		nHidden.style.left = iDivX+"px";
1369
		nHidden.style.top = iDivY+"px";
1370
		nHidden.style.display = "block";
1371
		$(nHidden).css('opacity',0);
1372
1373
		var nBackground = document.createElement('div');
1374
		nBackground.style.position = "absolute";
1375
		nBackground.style.left = "0px";
1376
		nBackground.style.top = "0px";
1377
		nBackground.style.height = ((iWinHeight>iDocHeight)? iWinHeight : iDocHeight) +"px";
1378
		nBackground.style.width = ((iWinWidth>iDocWidth)? iWinWidth : iDocWidth) +"px";
1379
		nBackground.className = this.classes.collection.background;
1380
		$(nBackground).css('opacity',0);
1381
1382
		document.body.appendChild( nBackground );
1383
		document.body.appendChild( nHidden );
1384
1385
		/* Visual corrections to try and keep the collection visible */
1386
		var iDivWidth = $(nHidden).outerWidth();
1387
		var iDivHeight = $(nHidden).outerHeight();
1388
1389
		if ( iDivX + iDivWidth > iDocWidth )
1390
		{
1391
			nHidden.style.left = (iDocWidth-iDivWidth)+"px";
1392
		}
1393
1394
		if ( iDivY + iDivHeight > iDocHeight )
1395
		{
1396
			nHidden.style.top = (iDivY-iDivHeight-$(nButton).outerHeight())+"px";
1397
		}
1398
1399
		this.dom.collection.collection = nHidden;
1400
		this.dom.collection.background = nBackground;
1401
1402
		/* This results in a very small delay for the end user but it allows the animation to be
1403
		 * much smoother. If you don't want the animation, then the setTimeout can be removed
1404
		 */
1405
		setTimeout( function () {
1406
			$(nHidden).animate({"opacity": 1}, 500);
1407
			$(nBackground).animate({"opacity": 0.25}, 500);
1408
		}, 10 );
1409
1410
		/* Resize the buttons to the Flash contents fit */
1411
		this.fnResizeButtons();
1412
1413
		/* Event handler to remove the collection display */
1414
		$(nBackground).click( function () {
1415
			that._fnCollectionHide.call( that, null, null );
1416
		} );
1417
	},
1418
1419
1420
	/**
1421
	 * Hide a button collection
1422
	 *  @param   {Node} nButton Button to use for the collection
1423
	 *  @param   {Object} oConfig Button configuration object
1424
	 *  @returns void
1425
	 *  @private
1426
	 */
1427
	"_fnCollectionHide": function ( nButton, oConfig )
1428
	{
1429
		if ( oConfig !== null && oConfig.sExtends == 'collection' )
1430
		{
1431
			return;
1432
		}
1433
1434
		if ( this.dom.collection.collection !== null )
1435
		{
1436
			$(this.dom.collection.collection).animate({"opacity": 0}, 500, function (e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1437
				this.style.display = "none";
1438
			} );
1439
1440
			$(this.dom.collection.background).animate({"opacity": 0}, 500, function (e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1441
				this.parentNode.removeChild( this );
1442
			} );
1443
1444
			this.dom.collection.collection = null;
1445
			this.dom.collection.background = null;
1446
		}
1447
	},
1448
1449
1450
1451
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1452
	 * Row selection functions
1453
	 */
1454
1455
	/**
1456
	 * Add event handlers to a table to allow for row selection
1457
	 *  @method  _fnRowSelectConfig
1458
	 *  @returns void
1459
	 *  @private 
1460
	 */
1461
	"_fnRowSelectConfig": function ()
1462
	{
1463
		if ( this.s.master )
1464
		{
1465
			var
1466
				that = this,
1467
				i, iLen,
0 ignored issues
show
Unused Code introduced by
The variable i seems to be never used. Consider removing it.
Loading history...
Unused Code introduced by
The variable iLen seems to be never used. Consider removing it.
Loading history...
1468
				dt = this.s.dt,
1469
				aoOpenRows = this.s.dt.aoOpenRows;
0 ignored issues
show
Unused Code introduced by
The assignment to variable aoOpenRows seems to be never used. Consider removing it.
Loading history...
1470
1471
			$(dt.nTable).addClass( this.classes.select.table );
1472
1473
			// When using OS style selection, we want to cancel the shift text
1474
			// selection, but only when the shift key is used (so you can
1475
			// actually still select text in the table)
1476
			if ( this.s.select.type === 'os' ) {
1477
				$(dt.nTBody).on( 'mousedown.DTTT_Select', 'tr', function(e) {
1478
					if ( e.shiftKey ) {
1479
1480
						$(dt.nTBody)
1481
							.css( '-moz-user-select', 'none' )
1482
							.one('selectstart.DTTT_Select', 'tr', function () {
1483
								return false;
1484
							} );
1485
					}
1486
				} );
1487
1488
				$(dt.nTBody).on( 'mouseup.DTTT_Select', 'tr', function(e) {
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1489
					$(dt.nTBody).css( '-moz-user-select', '' );
1490
				} );
1491
			}
1492
1493
			// Row selection
1494
			$(dt.nTBody).on( 'click.DTTT_Select', this.s.custom.sRowSelector, function(e) {
1495
				var row = this.nodeName.toLowerCase() === 'tr' ?
1496
					this :
1497
					$(this).parents('tr')[0];
1498
1499
				var select = that.s.select;
1500
				var pos = that.s.dt.oInstance.fnGetPosition( row );
1501
1502
				/* Sub-table must be ignored (odd that the selector won't do this with >) */
1503
				if ( row.parentNode != dt.nTBody ) {
1504
					return;
1505
				}
1506
1507
				/* Check that we are actually working with a DataTables controlled row */
1508
				if ( dt.oInstance.fnGetData(row) === null ) {
1509
				    return;
1510
				}
1511
1512
				// Shift click, ctrl click and simple click handling to make
1513
				// row selection a lot like a file system in desktop OSs
1514
				if ( select.type == 'os' ) {
1515
					if ( e.ctrlKey || e.metaKey ) {
1516
						// Add or remove from the selection
1517
						if ( that.fnIsSelected( row ) ) {
1518
							that._fnRowDeselect( row, e );
1519
						}
1520
						else {
1521
							that._fnRowSelect( row, e );
1522
						}
1523
					}
1524
					else if ( e.shiftKey ) {
1525
						// Add a range of rows, from the last selected row to
1526
						// this one
1527
						var rowIdxs = that.s.dt.aiDisplay.slice(); // visible rows
1528
						var idx1 = $.inArray( select.lastRow, rowIdxs );
1529
						var idx2 = $.inArray( pos, rowIdxs );
1530
1531
						if ( that.fnGetSelected().length === 0 || idx1 === -1 ) {
1532
							// select from top to here - slightly odd, but both
1533
							// Windows and Mac OS do this
1534
							rowIdxs.splice( $.inArray( pos, rowIdxs )+1, rowIdxs.length );
1535
						}
1536
						else {
1537
							// reverse so we can shift click 'up' as well as down
1538
							if ( idx1 > idx2 ) {
1539
								var tmp = idx2;
1540
								idx2 = idx1;
1541
								idx1 = tmp;
1542
							}
1543
1544
							rowIdxs.splice( idx2+1, rowIdxs.length );
1545
							rowIdxs.splice( 0, idx1 );
1546
						}
1547
1548
						if ( ! that.fnIsSelected( row ) ) {
1549
							// Select range
1550
							that._fnRowSelect( rowIdxs, e );
1551
						}
1552
						else {
1553
							// Deselect range - need to keep the clicked on row selected
1554
							rowIdxs.splice( $.inArray( pos, rowIdxs ), 1 );
1555
							that._fnRowDeselect( rowIdxs, e );
1556
						}
1557
					}
1558
					else {
1559
						// No cmd or shift click. Deselect current if selected,
1560
						// or select this row only
1561
						if ( that.fnIsSelected( row ) && that.fnGetSelected().length === 1 ) {
1562
							that._fnRowDeselect( row, e );
1563
						}
1564
						else {
1565
							that.fnSelectNone();
1566
							that._fnRowSelect( row, e );
1567
						}
1568
					}
1569
				}
1570
				else if ( that.fnIsSelected( row ) ) {
1571
					that._fnRowDeselect( row, e );
1572
				}
1573
				else if ( select.type == "single" ) {
1574
					that.fnSelectNone();
1575
					that._fnRowSelect( row, e );
1576
				}
1577
				else if ( select.type == "multi" ) {
1578
					that._fnRowSelect( row, e );
1579
				}
1580
1581
				select.lastRow = pos;
1582
			} );//.on('selectstart', function () { return false; } );
1583
1584
			// Bind a listener to the DataTable for when new rows are created.
1585
			// This allows rows to be visually selected when they should be and
1586
			// deferred rendering is used.
1587
			dt.oApi._fnCallbackReg( dt, 'aoRowCreatedCallback', function (tr, data, index) {
1588
				if ( dt.aoData[index]._DTTT_selected ) {
1589
					$(tr).addClass( that.classes.select.row );
1590
				}
1591
			}, 'TableTools-SelectAll' );
1592
		}
1593
	},
1594
1595
	/**
1596
	 * Select rows
1597
	 *  @param   {*} src Rows to select - see _fnSelectData for a description of valid inputs
1598
	 *  @private 
1599
	 */
1600
	"_fnRowSelect": function ( src, e )
1601
	{
1602
		var
1603
			that = this,
1604
			data = this._fnSelectData( src ),
1605
			firstTr = data.length===0 ? null : data[0].nTr,
0 ignored issues
show
Unused Code introduced by
The variable firstTr seems to be never used. Consider removing it.
Loading history...
1606
			anSelected = [],
1607
			i, len;
1608
1609
		// Get all the rows that will be selected
1610
		for ( i=0, len=data.length ; i<len ; i++ )
1611
		{
1612
			if ( data[i].nTr )
1613
			{
1614
				anSelected.push( data[i].nTr );
1615
			}
1616
		}
1617
1618
		// User defined pre-selection function
1619
		if ( this.s.select.preRowSelect !== null && !this.s.select.preRowSelect.call(this, e, anSelected, true) )
1620
		{
1621
			return;
1622
		}
1623
1624
		// Mark them as selected
1625
		for ( i=0, len=data.length ; i<len ; i++ )
1626
		{
1627
			data[i]._DTTT_selected = true;
1628
1629
			if ( data[i].nTr )
1630
			{
1631
				$(data[i].nTr).addClass( that.classes.select.row );
1632
			}
1633
		}
1634
1635
		// Post-selection function
1636
		if ( this.s.select.postSelected !== null )
1637
		{
1638
			this.s.select.postSelected.call( this, anSelected );
1639
		}
1640
1641
		TableTools._fnEventDispatch( this, 'select', anSelected, true );
1642
	},
1643
1644
	/**
1645
	 * Deselect rows
1646
	 *  @param   {*} src Rows to deselect - see _fnSelectData for a description of valid inputs
1647
	 *  @private 
1648
	 */
1649
	"_fnRowDeselect": function ( src, e )
1650
	{
1651
		var
1652
			that = this,
1653
			data = this._fnSelectData( src ),
1654
			firstTr = data.length===0 ? null : data[0].nTr,
0 ignored issues
show
Unused Code introduced by
The variable firstTr seems to be never used. Consider removing it.
Loading history...
1655
			anDeselectedTrs = [],
1656
			i, len;
1657
1658
		// Get all the rows that will be deselected
1659
		for ( i=0, len=data.length ; i<len ; i++ )
1660
		{
1661
			if ( data[i].nTr )
1662
			{
1663
				anDeselectedTrs.push( data[i].nTr );
1664
			}
1665
		}
1666
1667
		// User defined pre-selection function
1668
		if ( this.s.select.preRowSelect !== null && !this.s.select.preRowSelect.call(this, e, anDeselectedTrs, false) )
1669
		{
1670
			return;
1671
		}
1672
1673
		// Mark them as deselected
1674
		for ( i=0, len=data.length ; i<len ; i++ )
1675
		{
1676
			data[i]._DTTT_selected = false;
1677
1678
			if ( data[i].nTr )
1679
			{
1680
				$(data[i].nTr).removeClass( that.classes.select.row );
1681
			}
1682
		}
1683
1684
		// Post-deselection function
1685
		if ( this.s.select.postDeselected !== null )
1686
		{
1687
			this.s.select.postDeselected.call( this, anDeselectedTrs );
1688
		}
1689
1690
		TableTools._fnEventDispatch( this, 'select', anDeselectedTrs, false );
1691
	},
1692
1693
	/**
1694
	 * Take a data source for row selection and convert it into aoData points for the DT
1695
	 *   @param {*} src Can be a single DOM TR node, an array of TR nodes (including a
1696
	 *     a jQuery object), a single aoData point from DataTables, an array of aoData
1697
	 *     points or an array of aoData indexes
1698
	 *   @returns {array} An array of aoData points
1699
	 */
1700
	"_fnSelectData": function ( src )
1701
	{
1702
		var out = [], pos, i, iLen;
1703
1704
		if ( src.nodeName )
1705
		{
1706
			// Single node
1707
			pos = this.s.dt.oInstance.fnGetPosition( src );
1708
			out.push( this.s.dt.aoData[pos] );
1709
		}
1710
		else if ( typeof src.length !== 'undefined' )
1711
		{
1712
			// jQuery object or an array of nodes, or aoData points
1713
			for ( i=0, iLen=src.length ; i<iLen ; i++ )
1714
			{
1715
				if ( src[i].nodeName )
1716
				{
1717
					pos = this.s.dt.oInstance.fnGetPosition( src[i] );
1718
					out.push( this.s.dt.aoData[pos] );
1719
				}
1720
				else if ( typeof src[i] === 'number' )
1721
				{
1722
					out.push( this.s.dt.aoData[ src[i] ] );
1723
				}
1724
				else
1725
				{
1726
					out.push( src[i] );
1727
				}
1728
			}
1729
1730
			return out;
1731
		}
1732
		else
1733
		{
1734
			// A single aoData point
1735
			out.push( src );
1736
		}
1737
1738
		return out;
1739
	},
1740
1741
1742
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1743
	 * Text button functions
1744
	 */
1745
1746
	/**
1747
	 * Configure a text based button for interaction events
1748
	 *  @method  _fnTextConfig
1749
	 *  @param   {Node} nButton Button element which is being considered
1750
	 *  @param   {Object} oConfig Button configuration object
1751
	 *  @returns void
1752
	 *  @private 
1753
	 */
1754
	"_fnTextConfig": function ( nButton, oConfig )
1755
	{
1756
		var that = this;
1757
1758
		if ( oConfig.fnInit !== null )
1759
		{
1760
			oConfig.fnInit.call( this, nButton, oConfig );
1761
		}
1762
1763
		if ( oConfig.sToolTip !== "" )
1764
		{
1765
			nButton.title = oConfig.sToolTip;
1766
		}
1767
1768
		$(nButton).hover( function () {
1769
			if ( oConfig.fnMouseover !== null )
1770
			{
1771
				oConfig.fnMouseover.call( this, nButton, oConfig, null );
1772
			}
1773
		}, function () {
1774
			if ( oConfig.fnMouseout !== null )
1775
			{
1776
				oConfig.fnMouseout.call( this, nButton, oConfig, null );
1777
			}
1778
		} );
1779
1780
		if ( oConfig.fnSelect !== null )
1781
		{
1782
			TableTools._fnEventListen( this, 'select', function (n) {
1783
				oConfig.fnSelect.call( that, nButton, oConfig, n );
1784
			} );
1785
		}
1786
1787
		$(nButton).click( function (e) {
1788
			//e.preventDefault();
1789
1790
			if ( oConfig.fnClick !== null )
1791
			{
1792
				oConfig.fnClick.call( that, nButton, oConfig, null, e );
1793
			}
1794
1795
			/* Provide a complete function to match the behaviour of the flash elements */
1796
			if ( oConfig.fnComplete !== null )
1797
			{
1798
				oConfig.fnComplete.call( that, nButton, oConfig, null, null );
1799
			}
1800
1801
			that._fnCollectionHide( nButton, oConfig );
1802
		} );
1803
	},
1804
1805
1806
1807
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1808
	 * Flash button functions
1809
	 */
1810
1811
	/**
1812
	 * Configure a flash based button for interaction events
1813
	 *  @method  _fnFlashConfig
1814
	 *  @param   {Node} nButton Button element which is being considered
1815
	 *  @param   {o} oConfig Button configuration object
1816
	 *  @returns void
1817
	 *  @private 
1818
	 */
1819
	"_fnFlashConfig": function ( nButton, oConfig )
1820
	{
1821
		var that = this;
1822
		var flash = new ZeroClipboard_TableTools.Client();
1823
1824
		if ( oConfig.fnInit !== null )
1825
		{
1826
			oConfig.fnInit.call( this, nButton, oConfig );
1827
		}
1828
1829
		flash.setHandCursor( true );
1830
1831
		if ( oConfig.sAction == "flash_save" )
1832
		{
1833
			flash.setAction( 'save' );
1834
			flash.setCharSet( (oConfig.sCharSet=="utf16le") ? 'UTF16LE' : 'UTF8' );
1835
			flash.setBomInc( oConfig.bBomInc );
1836
			flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1837
		}
1838
		else if ( oConfig.sAction == "flash_pdf" )
1839
		{
1840
			flash.setAction( 'pdf' );
1841
			flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1842
		}
1843
		else
1844
		{
1845
			flash.setAction( 'copy' );
1846
		}
1847
1848
		flash.addEventListener('mouseOver', function(client) {
0 ignored issues
show
Unused Code introduced by
The parameter client is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1849
			if ( oConfig.fnMouseover !== null )
1850
			{
1851
				oConfig.fnMouseover.call( that, nButton, oConfig, flash );
1852
			}
1853
		} );
1854
1855
		flash.addEventListener('mouseOut', function(client) {
0 ignored issues
show
Unused Code introduced by
The parameter client is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1856
			if ( oConfig.fnMouseout !== null )
1857
			{
1858
				oConfig.fnMouseout.call( that, nButton, oConfig, flash );
1859
			}
1860
		} );
1861
1862
		flash.addEventListener('mouseDown', function(client) {
0 ignored issues
show
Unused Code introduced by
The parameter client is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1863
			if ( oConfig.fnClick !== null )
1864
			{
1865
				oConfig.fnClick.call( that, nButton, oConfig, flash );
1866
			}
1867
		} );
1868
1869
		flash.addEventListener('complete', function (client, text) {
1870
			if ( oConfig.fnComplete !== null )
1871
			{
1872
				oConfig.fnComplete.call( that, nButton, oConfig, flash, text );
1873
			}
1874
			that._fnCollectionHide( nButton, oConfig );
1875
		} );
1876
1877
		this._fnFlashGlue( flash, nButton, oConfig.sToolTip );
1878
	},
1879
1880
1881
	/**
1882
	 * Wait until the id is in the DOM before we "glue" the swf. Note that this function will call
1883
	 * itself (using setTimeout) until it completes successfully
1884
	 *  @method  _fnFlashGlue
1885
	 *  @param   {Object} clip Zero clipboard object
1886
	 *  @param   {Node} node node to glue swf to
1887
	 *  @param   {String} text title of the flash movie
1888
	 *  @returns void
1889
	 *  @private 
1890
	 */
1891
	"_fnFlashGlue": function ( flash, node, text )
1892
	{
1893
		var that = this;
1894
		var id = node.getAttribute('id');
1895
1896
		if ( document.getElementById(id) )
1897
		{
1898
			flash.glue( node, text );
1899
		}
1900
		else
1901
		{
1902
			setTimeout( function () {
1903
				that._fnFlashGlue( flash, node, text );
1904
			}, 100 );
1905
		}
1906
	},
1907
1908
1909
	/**
1910
	 * Set the text for the flash clip to deal with
1911
	 * 
1912
	 * This function is required for large information sets. There is a limit on the 
1913
	 * amount of data that can be transferred between Javascript and Flash in a single call, so
1914
	 * we use this method to build up the text in Flash by sending over chunks. It is estimated
1915
	 * that the data limit is around 64k, although it is undocumented, and appears to be different
1916
	 * between different flash versions. We chunk at 8KiB.
1917
	 *  @method  _fnFlashSetText
1918
	 *  @param   {Object} clip the ZeroClipboard object
1919
	 *  @param   {String} sData the data to be set
1920
	 *  @returns void
1921
	 *  @private 
1922
	 */
1923
	"_fnFlashSetText": function ( clip, sData )
1924
	{
1925
		var asData = this._fnChunkData( sData, 8192 );
1926
1927
		clip.clearText();
1928
		for ( var i=0, iLen=asData.length ; i<iLen ; i++ )
1929
		{
1930
			clip.appendText( asData[i] );
1931
		}
1932
	},
1933
1934
1935
1936
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1937
	 * Data retrieval functions
1938
	 */
1939
1940
	/**
1941
	 * Convert the mixed columns variable into a boolean array the same size as the columns, which
1942
	 * indicates which columns we want to include
1943
	 *  @method  _fnColumnTargets
1944
	 *  @param   {String|Array} mColumns The columns to be included in data retrieval. If a string
1945
	 *			 then it can take the value of "visible" or "hidden" (to include all visible or
1946
	 *			 hidden columns respectively). Or an array of column indexes
1947
	 *  @returns {Array} A boolean array the length of the columns of the table, which each value
1948
	 *			 indicating if the column is to be included or not
1949
	 *  @private 
1950
	 */
1951
	"_fnColumnTargets": function ( mColumns )
1952
	{
1953
		var aColumns = [];
1954
		var dt = this.s.dt;
1955
		var i, iLen;
1956
1957
		if ( typeof mColumns == "object" )
1958
		{
1959
			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1960
			{
1961
				aColumns.push( false );
1962
			}
1963
1964
			for ( i=0, iLen=mColumns.length ; i<iLen ; i++ )
1965
			{
1966
				aColumns[ mColumns[i] ] = true;
1967
			}
1968
		}
1969
		else if ( mColumns == "visible" )
1970
		{
1971
			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1972
			{
1973
				aColumns.push( dt.aoColumns[i].bVisible ? true : false );
1974
			}
1975
		}
1976
		else if ( mColumns == "hidden" )
1977
		{
1978
			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1979
			{
1980
				aColumns.push( dt.aoColumns[i].bVisible ? false : true );
1981
			}
1982
		}
1983
		else if ( mColumns == "sortable" )
1984
		{
1985
			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1986
			{
1987
				aColumns.push( dt.aoColumns[i].bSortable ? true : false );
1988
			}
1989
		}
1990
		else /* all */
1991
		{
1992
			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1993
			{
1994
				aColumns.push( true );
1995
			}
1996
		}
1997
1998
		return aColumns;
1999
	},
2000
2001
2002
	/**
2003
	 * New line character(s) depend on the platforms
2004
	 *  @method  method
2005
	 *  @param   {Object} oConfig Button configuration object - only interested in oConfig.sNewLine
2006
	 *  @returns {String} Newline character
2007
	 */
2008
	"_fnNewline": function ( oConfig )
2009
	{
2010
		if ( oConfig.sNewLine == "auto" )
2011
		{
2012
			return navigator.userAgent.match(/Windows/) ? "\r\n" : "\n";
2013
		}
2014
		else
2015
		{
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
2016
			return oConfig.sNewLine;
2017
		}
2018
	},
2019
2020
2021
	/**
2022
	 * Get data from DataTables' internals and format it for output
2023
	 *  @method  _fnGetDataTablesData
2024
	 *  @param   {Object} oConfig Button configuration object
2025
	 *  @param   {String} oConfig.sFieldBoundary Field boundary for the data cells in the string
2026
	 *  @param   {String} oConfig.sFieldSeperator Field separator for the data cells
2027
	 *  @param   {String} oConfig.sNewline New line options
2028
	 *  @param   {Mixed} oConfig.mColumns Which columns should be included in the output
2029
	 *  @param   {Boolean} oConfig.bHeader Include the header
2030
	 *  @param   {Boolean} oConfig.bFooter Include the footer
2031
	 *  @param   {Boolean} oConfig.bSelectedOnly Include only the selected rows in the output
2032
	 *  @returns {String} Concatenated string of data
2033
	 *  @private 
2034
	 */
2035
	"_fnGetDataTablesData": function ( oConfig )
2036
	{
2037
		var i, iLen, j, jLen;
2038
		var aRow, aData=[], sLoopData='', arr;
2039
		var dt = this.s.dt, tr, child;
0 ignored issues
show
Unused Code introduced by
The variable child seems to be never used. Consider removing it.
Loading history...
2040
		var regex = new RegExp(oConfig.sFieldBoundary, "g"); /* Do it here for speed */
2041
		var aColumnsInc = this._fnColumnTargets( oConfig.mColumns );
2042
		var bSelectedOnly = (typeof oConfig.bSelectedOnly != 'undefined') ? oConfig.bSelectedOnly : false;
2043
2044
		/*
2045
		 * Header
2046
		 */
2047
		if ( oConfig.bHeader )
2048
		{
2049
			aRow = [];
2050
2051
			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
2052
			{
2053
				if ( aColumnsInc[i] )
2054
				{
2055
					sLoopData = dt.aoColumns[i].sTitle.replace(/\n/g," ").replace( /<.*?>/g, "" ).replace(/^\s+|\s+$/g,"");
2056
					sLoopData = this._fnHtmlDecode( sLoopData );
2057
2058
					aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
2059
				}
2060
			}
2061
2062
			aData.push( aRow.join(oConfig.sFieldSeperator) );
2063
		}
2064
2065
		/*
2066
		 * Body
2067
		 */
2068
		var aSelected = this.fnGetSelected();
2069
		bSelectedOnly = this.s.select.type !== "none" && bSelectedOnly && aSelected.length !== 0;
2070
2071
		var aDataIndex = dt.oInstance
2072
			.$('tr', oConfig.oSelectorOpts)
2073
			.map( function (id, row) {
2074
				// If "selected only", then ensure that the row is in the selected list
2075
				return bSelectedOnly && $.inArray( row, aSelected ) === -1 ?
2076
					null :
2077
					dt.oInstance.fnGetPosition( row );
2078
			} )
2079
			.get();
2080
2081
		for ( j=0, jLen=aDataIndex.length ; j<jLen ; j++ )
2082
		{
2083
			tr = dt.aoData[ aDataIndex[j] ].nTr;
2084
			aRow = [];
2085
2086
			/* Columns */
2087
			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
2088
			{
2089
				if ( aColumnsInc[i] )
2090
				{
2091
					/* Convert to strings (with small optimisation) */
2092
					var mTypeData = dt.oApi._fnGetCellData( dt, aDataIndex[j], i, 'display' );
2093
					if ( oConfig.fnCellRender )
2094
					{
2095
						sLoopData = oConfig.fnCellRender( mTypeData, i, tr, aDataIndex[j] )+"";
2096
					}
2097
					else if ( typeof mTypeData == "string" )
2098
					{
2099
						/* Strip newlines, replace img tags with alt attr. and finally strip html... */
2100
						sLoopData = mTypeData.replace(/\n/g," ");
2101
						sLoopData =
2102
						    sLoopData.replace(/<img.*?\s+alt\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+)).*?>/gi,
2103
						        '$1$2$3');
2104
						sLoopData = sLoopData.replace( /<.*?>/g, "" );
2105
					}
2106
					else
2107
					{
2108
						sLoopData = mTypeData+"";
2109
					}
2110
2111
					/* Trim and clean the data */
2112
					sLoopData = sLoopData.replace(/^\s+/, '').replace(/\s+$/, '');
2113
					sLoopData = this._fnHtmlDecode( sLoopData );
2114
2115
					/* Bound it and add it to the total data */
2116
					aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
2117
				}
2118
			}
2119
2120
			aData.push( aRow.join(oConfig.sFieldSeperator) );
2121
2122
			/* Details rows from fnOpen */
2123
			if ( oConfig.bOpenRows )
2124
			{
2125
				arr = $.grep(dt.aoOpenRows, function(o) { return o.nParent === tr; });
0 ignored issues
show
Bug introduced by
The variable tr is changed as part of the for loop for example by dt.aoData.aDataIndex.j.nTr on line 2083. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
2126
2127
				if ( arr.length === 1 )
2128
				{
2129
					sLoopData = this._fnBoundData( $('td', arr[0].nTr).html(), oConfig.sFieldBoundary, regex );
2130
					aData.push( sLoopData );
2131
				}
2132
			}
2133
		}
2134
2135
		/*
2136
		 * Footer
2137
		 */
2138
		if ( oConfig.bFooter && dt.nTFoot !== null )
2139
		{
2140
			aRow = [];
2141
2142
			for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
2143
			{
2144
				if ( aColumnsInc[i] && dt.aoColumns[i].nTf !== null )
2145
				{
2146
					sLoopData = dt.aoColumns[i].nTf.innerHTML.replace(/\n/g," ").replace( /<.*?>/g, "" );
2147
					sLoopData = this._fnHtmlDecode( sLoopData );
2148
2149
					aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
2150
				}
2151
			}
2152
2153
			aData.push( aRow.join(oConfig.sFieldSeperator) );
2154
		}
2155
2156
		var _sLastData = aData.join( this._fnNewline(oConfig) );
2157
		return _sLastData;
2158
	},
2159
2160
2161
	/**
2162
	 * Wrap data up with a boundary string
2163
	 *  @method  _fnBoundData
2164
	 *  @param   {String} sData data to bound
2165
	 *  @param   {String} sBoundary bounding char(s)
2166
	 *  @param   {RegExp} regex search for the bounding chars - constructed outside for efficiency
2167
	 *			 in the loop
2168
	 *  @returns {String} bound data
2169
	 *  @private 
2170
	 */
2171
	"_fnBoundData": function ( sData, sBoundary, regex )
2172
	{
2173
		if ( sBoundary === "" )
2174
		{
2175
			return sData;
2176
		}
2177
		else
2178
		{
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
2179
			return sBoundary + sData.replace(regex, sBoundary+sBoundary) + sBoundary;
2180
		}
2181
	},
2182
2183
2184
	/**
2185
	 * Break a string up into an array of smaller strings
2186
	 *  @method  _fnChunkData
2187
	 *  @param   {String} sData data to be broken up
2188
	 *  @param   {Int} iSize chunk size
2189
	 *  @returns {Array} String array of broken up text
2190
	 *  @private 
2191
	 */
2192
	"_fnChunkData": function ( sData, iSize )
2193
	{
2194
		var asReturn = [];
2195
		var iStrlen = sData.length;
2196
2197
		for ( var i=0 ; i<iStrlen ; i+=iSize )
2198
		{
2199
			if ( i+iSize < iStrlen )
2200
			{
2201
				asReturn.push( sData.substring( i, i+iSize ) );
2202
			}
2203
			else
2204
			{
2205
				asReturn.push( sData.substring( i, iStrlen ) );
2206
			}
2207
		}
2208
2209
		return asReturn;
2210
	},
2211
2212
2213
	/**
2214
	 * Decode HTML entities
2215
	 *  @method  _fnHtmlDecode
2216
	 *  @param   {String} sData encoded string
2217
	 *  @returns {String} decoded string
2218
	 *  @private 
2219
	 */
2220
	"_fnHtmlDecode": function ( sData )
2221
	{
2222
		if ( sData.indexOf('&') === -1 )
2223
		{
2224
			return sData;
2225
		}
2226
2227
		var n = document.createElement('div');
2228
2229
		return sData.replace( /&([^\s]*);/g, function( match, match2 ) {
2230
			if ( match.substr(1, 1) === '#' )
2231
			{
2232
				return String.fromCharCode( Number(match2.substr(1)) );
2233
			}
2234
			else
2235
			{
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
2236
				n.innerHTML = match;
2237
				return n.childNodes[0].nodeValue;
2238
			}
2239
		} );
2240
	},
2241
2242
2243
2244
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2245
	 * Printing functions
2246
	 */
2247
2248
	/**
2249
	 * Show print display
2250
	 *  @method  _fnPrintStart
2251
	 *  @param   {Event} e Event object
2252
	 *  @param   {Object} oConfig Button configuration object
2253
	 *  @returns void
2254
	 *  @private 
2255
	 */
2256
	"_fnPrintStart": function ( oConfig )
2257
	{
2258
	  var that = this;
2259
	  var oSetDT = this.s.dt;
2260
2261
		/* Parse through the DOM hiding everything that isn't needed for the table */
2262
		this._fnPrintHideNodes( oSetDT.nTable );
2263
2264
		/* Show the whole table */
2265
		this.s.print.saveStart = oSetDT._iDisplayStart;
2266
		this.s.print.saveLength = oSetDT._iDisplayLength;
2267
2268
		if ( oConfig.bShowAll )
2269
		{
2270
			oSetDT._iDisplayStart = 0;
2271
			oSetDT._iDisplayLength = -1;
2272
			if ( oSetDT.oApi._fnCalculateEnd ) {
2273
				oSetDT.oApi._fnCalculateEnd( oSetDT );
2274
			}
2275
			oSetDT.oApi._fnDraw( oSetDT );
2276
		}
2277
2278
		/* Adjust the display for scrolling which might be done by DataTables */
2279
		if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
2280
		{
2281
			this._fnPrintScrollStart( oSetDT );
2282
2283
			// If the table redraws while in print view, the DataTables scrolling
2284
			// setup would hide the header, so we need to readd it on draw
2285
			$(this.s.dt.nTable).bind('draw.DTTT_Print', function () {
2286
				that._fnPrintScrollStart( oSetDT );
2287
			} );
2288
		}
2289
2290
		/* Remove the other DataTables feature nodes - but leave the table! and info div */
2291
		var anFeature = oSetDT.aanFeatures;
2292
		for ( var cFeature in anFeature )
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
2293
		{
2294
			if ( cFeature != 'i' && cFeature != 't' && cFeature.length == 1 )
2295
			{
2296
				for ( var i=0, iLen=anFeature[cFeature].length ; i<iLen ; i++ )
2297
				{
2298
					this.dom.print.hidden.push( {
2299
						"node": anFeature[cFeature][i],
2300
						"display": "block"
2301
					} );
2302
					anFeature[cFeature][i].style.display = "none";
2303
				}
2304
			}
2305
		}
2306
2307
		/* Print class can be used for styling */
2308
		$(document.body).addClass( this.classes.print.body );
2309
2310
		/* Show information message to let the user know what is happening */
2311
		if ( oConfig.sInfo !== "" )
2312
		{
2313
			this.fnInfo( oConfig.sInfo, 3000 );
2314
		}
2315
2316
		/* Add a message at the top of the page */
2317
		if ( oConfig.sMessage )
2318
		{
2319
			$('<div/>')
2320
				.addClass( this.classes.print.message )
2321
				.html( oConfig.sMessage )
2322
				.prependTo( 'body' );
2323
		}
2324
2325
		/* Cache the scrolling and the jump to the top of the page */
2326
		this.s.print.saveScroll = $(window).scrollTop();
2327
		window.scrollTo( 0, 0 );
2328
2329
		/* Bind a key event listener to the document for the escape key -
2330
		 * it is removed in the callback
2331
		 */
2332
		$(document).bind( "keydown.DTTT", function(e) {
2333
			/* Only interested in the escape key */
2334
			if ( e.keyCode == 27 )
2335
			{
2336
				e.preventDefault();
2337
				that._fnPrintEnd.call( that, e );
2338
			}
2339
		} );
2340
	},
2341
2342
2343
	/**
2344
	 * Printing is finished, resume normal display
2345
	 *  @method  _fnPrintEnd
2346
	 *  @param   {Event} e Event object
2347
	 *  @returns void
2348
	 *  @private 
2349
	 */
2350
	"_fnPrintEnd": function ( e )
0 ignored issues
show
Unused Code introduced by
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
2351
	{
2352
		var that = this;
0 ignored issues
show
Unused Code introduced by
The variable that seems to be never used. Consider removing it.
Loading history...
2353
		var oSetDT = this.s.dt;
2354
		var oSetPrint = this.s.print;
2355
		var oDomPrint = this.dom.print;
0 ignored issues
show
Unused Code introduced by
The variable oDomPrint seems to be never used. Consider removing it.
Loading history...
2356
2357
		/* Show all hidden nodes */
2358
		this._fnPrintShowNodes();
2359
2360
		/* Restore DataTables' scrolling */
2361
		if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
2362
		{
2363
			$(this.s.dt.nTable).unbind('draw.DTTT_Print');
2364
2365
			this._fnPrintScrollEnd();
2366
		}
2367
2368
		/* Restore the scroll */
2369
		window.scrollTo( 0, oSetPrint.saveScroll );
2370
2371
		/* Drop the print message */
2372
		$('div.'+this.classes.print.message).remove();
2373
2374
		/* Styling class */
2375
		$(document.body).removeClass( 'DTTT_Print' );
2376
2377
		/* Restore the table length */
2378
		oSetDT._iDisplayStart = oSetPrint.saveStart;
2379
		oSetDT._iDisplayLength = oSetPrint.saveLength;
2380
		if ( oSetDT.oApi._fnCalculateEnd ) {
2381
			oSetDT.oApi._fnCalculateEnd( oSetDT );
2382
		}
2383
		oSetDT.oApi._fnDraw( oSetDT );
2384
2385
		$(document).unbind( "keydown.DTTT" );
2386
	},
2387
2388
2389
	/**
2390
	 * Take account of scrolling in DataTables by showing the full table
2391
	 *  @returns void
2392
	 *  @private 
2393
	 */
2394
	"_fnPrintScrollStart": function ()
2395
	{
2396
		var
2397
			oSetDT = this.s.dt,
2398
			nScrollHeadInner = oSetDT.nScrollHead.getElementsByTagName('div')[0],
2399
			nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
0 ignored issues
show
Unused Code introduced by
The variable nScrollHeadTable seems to be never used. Consider removing it.
Loading history...
2400
			nScrollBody = oSetDT.nTable.parentNode,
2401
			nTheadSize, nTfootSize;
2402
2403
		/* Copy the header in the thead in the body table, this way we show one single table when
2404
		 * in print view. Note that this section of code is more or less verbatim from DT 1.7.0
2405
		 */
2406
		nTheadSize = oSetDT.nTable.getElementsByTagName('thead');
2407
		if ( nTheadSize.length > 0 )
2408
		{
2409
			oSetDT.nTable.removeChild( nTheadSize[0] );
2410
		}
2411
2412
		if ( oSetDT.nTFoot !== null )
2413
		{
2414
			nTfootSize = oSetDT.nTable.getElementsByTagName('tfoot');
2415
			if ( nTfootSize.length > 0 )
2416
			{
2417
				oSetDT.nTable.removeChild( nTfootSize[0] );
2418
			}
2419
		}
2420
2421
		nTheadSize = oSetDT.nTHead.cloneNode(true);
2422
		oSetDT.nTable.insertBefore( nTheadSize, oSetDT.nTable.childNodes[0] );
2423
2424
		if ( oSetDT.nTFoot !== null )
2425
		{
2426
			nTfootSize = oSetDT.nTFoot.cloneNode(true);
2427
			oSetDT.nTable.insertBefore( nTfootSize, oSetDT.nTable.childNodes[1] );
2428
		}
2429
2430
		/* Now adjust the table's viewport so we can actually see it */
2431
		if ( oSetDT.oScroll.sX !== "" )
2432
		{
2433
			oSetDT.nTable.style.width = $(oSetDT.nTable).outerWidth()+"px";
2434
			nScrollBody.style.width = $(oSetDT.nTable).outerWidth()+"px";
2435
			nScrollBody.style.overflow = "visible";
2436
		}
2437
2438
		if ( oSetDT.oScroll.sY !== "" )
2439
		{
2440
			nScrollBody.style.height = $(oSetDT.nTable).outerHeight()+"px";
2441
			nScrollBody.style.overflow = "visible";
2442
		}
2443
	},
2444
2445
2446
	/**
2447
	 * Take account of scrolling in DataTables by showing the full table. Note that the redraw of
2448
	 * the DataTable that we do will actually deal with the majority of the hard work here
2449
	 *  @returns void
2450
	 *  @private 
2451
	 */
2452
	"_fnPrintScrollEnd": function ()
2453
	{
2454
		var
2455
			oSetDT = this.s.dt,
2456
			nScrollBody = oSetDT.nTable.parentNode;
2457
2458
		if ( oSetDT.oScroll.sX !== "" )
2459
		{
2460
			nScrollBody.style.width = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sX );
2461
			nScrollBody.style.overflow = "auto";
2462
		}
2463
2464
		if ( oSetDT.oScroll.sY !== "" )
2465
		{
2466
			nScrollBody.style.height = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sY );
2467
			nScrollBody.style.overflow = "auto";
2468
		}
2469
	},
2470
2471
2472
	/**
2473
	 * Resume the display of all TableTools hidden nodes
2474
	 *  @method  _fnPrintShowNodes
2475
	 *  @returns void
2476
	 *  @private 
2477
	 */
2478
	"_fnPrintShowNodes": function ( )
2479
	{
2480
	  var anHidden = this.dom.print.hidden;
2481
2482
		for ( var i=0, iLen=anHidden.length ; i<iLen ; i++ )
2483
		{
2484
			anHidden[i].node.style.display = anHidden[i].display;
2485
		}
2486
		anHidden.splice( 0, anHidden.length );
2487
	},
2488
2489
2490
	/**
2491
	 * Hide nodes which are not needed in order to display the table. Note that this function is
2492
	 * recursive
2493
	 *  @method  _fnPrintHideNodes
2494
	 *  @param   {Node} nNode Element which should be showing in a 'print' display
2495
	 *  @returns void
2496
	 *  @private 
2497
	 */
2498
	"_fnPrintHideNodes": function ( nNode )
2499
	{
2500
		var anHidden = this.dom.print.hidden;
2501
2502
		var nParent = nNode.parentNode;
2503
		var nChildren = nParent.childNodes;
2504
		for ( var i=0, iLen=nChildren.length ; i<iLen ; i++ )
2505
		{
2506
			if ( nChildren[i] != nNode && nChildren[i].nodeType == 1 )
2507
			{
2508
				/* If our node is shown (don't want to show nodes which were previously hidden) */
2509
				var sDisplay = $(nChildren[i]).css("display");
2510
				if ( sDisplay != "none" )
2511
				{
2512
					/* Cache the node and it's previous state so we can restore it */
2513
					anHidden.push( {
2514
						"node": nChildren[i],
2515
						"display": sDisplay
2516
					} );
2517
					nChildren[i].style.display = "none";
2518
				}
2519
			}
2520
		}
2521
2522
		if ( nParent.nodeName.toUpperCase() != "BODY" )
2523
		{
2524
			this._fnPrintHideNodes( nParent );
2525
		}
2526
	}
2527
};
2528
2529
2530
2531
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2532
 * Static variables
2533
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2534
2535
/**
2536
 * Store of all instances that have been created of TableTools, so one can look up other (when
2537
 * there is need of a master)
2538
 *  @property _aInstances
2539
 *  @type	 Array
2540
 *  @default  []
2541
 *  @private
2542
 */
2543
TableTools._aInstances = [];
2544
2545
2546
/**
2547
 * Store of all listeners and their callback functions
2548
 *  @property _aListeners
2549
 *  @type	 Array
2550
 *  @default  []
2551
 */
2552
TableTools._aListeners = [];
2553
2554
2555
2556
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2557
 * Static methods
2558
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2559
2560
/**
2561
 * Get an array of all the master instances
2562
 *  @method  fnGetMasters
2563
 *  @returns {Array} List of master TableTools instances
2564
 *  @static
2565
 */
2566
TableTools.fnGetMasters = function ()
2567
{
2568
	var a = [];
2569
	for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2570
	{
2571
		if ( TableTools._aInstances[i].s.master )
2572
		{
2573
			a.push( TableTools._aInstances[i] );
2574
		}
2575
	}
2576
	return a;
2577
};
2578
2579
/**
2580
 * Get the master instance for a table node (or id if a string is given)
2581
 *  @method  fnGetInstance
2582
 *  @returns {Object} ID of table OR table node, for which we want the TableTools instance
2583
 *  @static
2584
 */
2585
TableTools.fnGetInstance = function ( node )
2586
{
2587
	if ( typeof node != 'object' )
2588
	{
2589
		node = document.getElementById(node);
2590
	}
2591
2592
	for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2593
	{
2594
		if ( TableTools._aInstances[i].s.master && TableTools._aInstances[i].dom.table == node )
2595
		{
2596
			return TableTools._aInstances[i];
2597
		}
2598
	}
2599
	return null;
2600
};
2601
2602
2603
/**
2604
 * Add a listener for a specific event
2605
 *  @method  _fnEventListen
2606
 *  @param   {Object} that Scope of the listening function (i.e. 'this' in the caller)
2607
 *  @param   {String} type Event type
2608
 *  @param   {Function} fn Function
2609
 *  @returns void
2610
 *  @private
2611
 *  @static
2612
 */
2613
TableTools._fnEventListen = function ( that, type, fn )
2614
{
2615
	TableTools._aListeners.push( {
2616
		"that": that,
2617
		"type": type,
2618
		"fn": fn
2619
	} );
2620
};
2621
2622
2623
/**
2624
 * An event has occurred - look up every listener and fire it off. We check that the event we are
2625
 * going to fire is attached to the same table (using the table node as reference) before firing
2626
 *  @method  _fnEventDispatch
2627
 *  @param   {Object} that Scope of the listening function (i.e. 'this' in the caller)
2628
 *  @param   {String} type Event type
2629
 *  @param   {Node} node Element that the event occurred on (may be null)
2630
 *  @param   {boolean} [selected] Indicate if the node was selected (true) or deselected (false)
2631
 *  @returns void
2632
 *  @private
2633
 *  @static
2634
 */
2635
TableTools._fnEventDispatch = function ( that, type, node, selected )
2636
{
2637
	var listeners = TableTools._aListeners;
2638
	for ( var i=0, iLen=listeners.length ; i<iLen ; i++ )
2639
	{
2640
		if ( that.dom.table == listeners[i].that.dom.table && listeners[i].type == type )
2641
		{
2642
			listeners[i].fn( node, selected );
2643
		}
2644
	}
2645
};
2646
2647
2648
2649
2650
2651
2652
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2653
 * Constants
2654
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2655
2656
2657
2658
TableTools.buttonBase = {
2659
	// Button base
2660
	"sAction": "text",
2661
	"sTag": "default",
2662
	"sLinerTag": "default",
2663
	"sButtonClass": "DTTT_button_text",
2664
	"sButtonText": "Button text",
2665
	"sTitle": "",
2666
	"sToolTip": "",
2667
2668
	// Common button specific options
2669
	"sCharSet": "utf8",
2670
	"bBomInc": false,
2671
	"sFileName": "*.csv",
2672
	"sFieldBoundary": "",
2673
	"sFieldSeperator": "\t",
2674
	"sNewLine": "auto",
2675
	"mColumns": "all", /* "all", "visible", "hidden" or array of column integers */
2676
	"bHeader": true,
2677
	"bFooter": true,
2678
	"bOpenRows": false,
2679
	"bSelectedOnly": false,
2680
	"oSelectorOpts": undefined, // See http://datatables.net/docs/DataTables/1.9.4/#$ for full options
2681
2682
	// Callbacks
2683
	"fnMouseover": null,
2684
	"fnMouseout": null,
2685
	"fnClick": null,
2686
	"fnSelect": null,
2687
	"fnComplete": null,
2688
	"fnInit": null,
2689
	"fnCellRender": null
2690
};
2691
2692
2693
/**
2694
 * @namespace Default button configurations
2695
 */
2696
TableTools.BUTTONS = {
2697
	"csv": $.extend( {}, TableTools.buttonBase, {
2698
		"sAction": "flash_save",
2699
		"sButtonClass": "DTTT_button_csv",
2700
		"sButtonText": "CSV",
2701
		"sFieldBoundary": '"',
2702
		"sFieldSeperator": ",",
2703
		"fnClick": function( nButton, oConfig, flash ) {
2704
			this.fnSetText( flash, this.fnGetTableData(oConfig) );
2705
		}
2706
	} ),
2707
2708
	"xls": $.extend( {}, TableTools.buttonBase, {
2709
		"sAction": "flash_save",
2710
		"sCharSet": "utf16le",
2711
		"bBomInc": true,
2712
		"sButtonClass": "DTTT_button_xls",
2713
		"sButtonText": "Excel",
2714
		"fnClick": function( nButton, oConfig, flash ) {
2715
			this.fnSetText( flash, this.fnGetTableData(oConfig) );
2716
		}
2717
	} ),
2718
2719
	"copy": $.extend( {}, TableTools.buttonBase, {
2720
		"sAction": "flash_copy",
2721
		"sButtonClass": "DTTT_button_copy",
2722
		"sButtonText": "Copy",
2723
		"fnClick": function( nButton, oConfig, flash ) {
2724
			this.fnSetText( flash, this.fnGetTableData(oConfig) );
2725
		},
2726
		"fnComplete": function(nButton, oConfig, flash, text) {
2727
			var
2728
				lines = text.split('\n').length,
2729
				len = this.s.dt.nTFoot === null ? lines-1 : lines-2,
2730
				plural = (len==1) ? "" : "s";
2731
			this.fnInfo( '<h6>Table copied</h6>'+
2732
				'<p>Copied '+len+' row'+plural+' to the clipboard.</p>',
2733
				1500
2734
			);
2735
		}
2736
	} ),
2737
2738
	"pdf": $.extend( {}, TableTools.buttonBase, {
2739
		"sAction": "flash_pdf",
2740
		"sNewLine": "\n",
2741
		"sFileName": "*.pdf",
2742
		"sButtonClass": "DTTT_button_pdf",
2743
		"sButtonText": "PDF",
2744
		"sPdfOrientation": "portrait",
2745
		"sPdfSize": "A4",
2746
		"sPdfMessage": "",
2747
		"fnClick": function( nButton, oConfig, flash ) {
2748
			this.fnSetText( flash,
2749
				"title:"+ this.fnGetTitle(oConfig) +"\n"+
2750
				"message:"+ oConfig.sPdfMessage +"\n"+
2751
				"colWidth:"+ this.fnCalcColRatios(oConfig) +"\n"+
2752
				"orientation:"+ oConfig.sPdfOrientation +"\n"+
2753
				"size:"+ oConfig.sPdfSize +"\n"+
2754
				"--/TableToolsOpts--\n" +
2755
				this.fnGetTableData(oConfig)
2756
			);
2757
		}
2758
	} ),
2759
2760
	"print": $.extend( {}, TableTools.buttonBase, {
2761
		"sInfo": "<h6>Print view</h6><p>Please use your browser's print function to "+
2762
		  "print this table. Press escape when finished.</p>",
2763
		"sMessage": null,
2764
		"bShowAll": true,
2765
		"sToolTip": "View print view",
2766
		"sButtonClass": "DTTT_button_print",
2767
		"sButtonText": "Print",
2768
		"fnClick": function ( nButton, oConfig ) {
2769
			this.fnPrint( true, oConfig );
2770
		}
2771
	} ),
2772
2773
	"text": $.extend( {}, TableTools.buttonBase ),
2774
2775
	"select": $.extend( {}, TableTools.buttonBase, {
2776
		"sButtonText": "Select button",
2777
		"fnSelect": function( nButton, oConfig ) {
0 ignored issues
show
Unused Code introduced by
The parameter oConfig is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
2778
			if ( this.fnGetSelected().length !== 0 ) {
2779
				$(nButton).removeClass( this.classes.buttons.disabled );
2780
			} else {
2781
				$(nButton).addClass( this.classes.buttons.disabled );
2782
			}
2783
		},
2784
		"fnInit": function( nButton, oConfig ) {
0 ignored issues
show
Unused Code introduced by
The parameter oConfig is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
2785
			$(nButton).addClass( this.classes.buttons.disabled );
2786
		}
2787
	} ),
2788
2789
	"select_single": $.extend( {}, TableTools.buttonBase, {
2790
		"sButtonText": "Select button",
2791
		"fnSelect": function( nButton, oConfig ) {
0 ignored issues
show
Unused Code introduced by
The parameter oConfig is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
2792
			var iSelected = this.fnGetSelected().length;
2793
			if ( iSelected == 1 ) {
2794
				$(nButton).removeClass( this.classes.buttons.disabled );
2795
			} else {
2796
				$(nButton).addClass( this.classes.buttons.disabled );
2797
			}
2798
		},
2799
		"fnInit": function( nButton, oConfig ) {
0 ignored issues
show
Unused Code introduced by
The parameter oConfig is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
2800
			$(nButton).addClass( this.classes.buttons.disabled );
2801
		}
2802
	} ),
2803
2804
	"select_all": $.extend( {}, TableTools.buttonBase, {
2805
		"sButtonText": "Select all",
2806
		"fnClick": function( nButton, oConfig ) {
0 ignored issues
show
Unused Code introduced by
The parameter oConfig is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter nButton is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
2807
			this.fnSelectAll();
2808
		},
2809
		"fnSelect": function( nButton, oConfig ) {
0 ignored issues
show
Unused Code introduced by
The parameter oConfig is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
2810
			if ( this.fnGetSelected().length == this.s.dt.fnRecordsDisplay() ) {
2811
				$(nButton).addClass( this.classes.buttons.disabled );
2812
			} else {
2813
				$(nButton).removeClass( this.classes.buttons.disabled );
2814
			}
2815
		}
2816
	} ),
2817
2818
	"select_none": $.extend( {}, TableTools.buttonBase, {
2819
		"sButtonText": "Deselect all",
2820
		"fnClick": function( nButton, oConfig ) {
0 ignored issues
show
Unused Code introduced by
The parameter oConfig is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter nButton is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
2821
			this.fnSelectNone();
2822
		},
2823
		"fnSelect": function( nButton, oConfig ) {
0 ignored issues
show
Unused Code introduced by
The parameter oConfig is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
2824
			if ( this.fnGetSelected().length !== 0 ) {
2825
				$(nButton).removeClass( this.classes.buttons.disabled );
2826
			} else {
2827
				$(nButton).addClass( this.classes.buttons.disabled );
2828
			}
2829
		},
2830
		"fnInit": function( nButton, oConfig ) {
0 ignored issues
show
Unused Code introduced by
The parameter oConfig is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
2831
			$(nButton).addClass( this.classes.buttons.disabled );
2832
		}
2833
	} ),
2834
2835
	"ajax": $.extend( {}, TableTools.buttonBase, {
2836
		"sAjaxUrl": "/xhr.php",
2837
		"sButtonText": "Ajax button",
2838
		"fnClick": function( nButton, oConfig ) {
2839
			var sData = this.fnGetTableData(oConfig);
2840
			$.ajax( {
2841
				"url": oConfig.sAjaxUrl,
2842
				"data": [
2843
					{ "name": "tableData", "value": sData }
2844
				],
2845
				"success": oConfig.fnAjaxComplete,
2846
				"dataType": "json",
2847
				"type": "POST",
2848
				"cache": false,
2849
				"error": function () {
2850
					alert( "Error detected when sending table data to server" );
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
2851
				}
2852
			} );
2853
		},
2854
		"fnAjaxComplete": function( json ) {
0 ignored issues
show
Unused Code introduced by
The parameter json is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
2855
			alert( 'Ajax complete' );
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
2856
		}
2857
	} ),
2858
2859
	"div": $.extend( {}, TableTools.buttonBase, {
2860
		"sAction": "div",
2861
		"sTag": "div",
2862
		"sButtonClass": "DTTT_nonbutton",
2863
		"sButtonText": "Text button"
2864
	} ),
2865
2866
	"collection": $.extend( {}, TableTools.buttonBase, {
2867
		"sAction": "collection",
2868
		"sButtonClass": "DTTT_button_collection",
2869
		"sButtonText": "Collection",
2870
		"fnClick": function( nButton, oConfig ) {
2871
			this._fnCollectionShow(nButton, oConfig);
2872
		}
2873
	} )
2874
};
2875
/*
2876
 *  on* callback parameters:
2877
 *     1. node - button element
2878
 *     2. object - configuration object for this button
2879
 *     3. object - ZeroClipboard reference (flash button only)
2880
 *     4. string - Returned string from Flash (flash button only - and only on 'complete')
2881
 */
2882
2883
// Alias to match the other plug-ins styling
2884
TableTools.buttons = TableTools.BUTTONS;
2885
2886
2887
/**
2888
 * @namespace Classes used by TableTools - allows the styles to be override easily.
2889
 *   Note that when TableTools initialises it will take a copy of the classes object
2890
 *   and will use its internal copy for the remainder of its run time.
2891
 */
2892
TableTools.classes = {
2893
	"container": "DTTT_container",
2894
	"buttons": {
2895
		"normal": "DTTT_button",
2896
		"disabled": "DTTT_disabled"
2897
	},
2898
	"collection": {
2899
		"container": "DTTT_collection",
2900
		"background": "DTTT_collection_background",
2901
		"buttons": {
2902
			"normal": "DTTT_button",
2903
			"disabled": "DTTT_disabled"
2904
		}
2905
	},
2906
	"select": {
2907
		"table": "DTTT_selectable",
2908
		"row": "DTTT_selected selected"
2909
	},
2910
	"print": {
2911
		"body": "DTTT_Print",
2912
		"info": "DTTT_print_info",
2913
		"message": "DTTT_PrintMessage"
2914
	}
2915
};
2916
2917
2918
/**
2919
 * @namespace ThemeRoller classes - built in for compatibility with DataTables' 
2920
 *   bJQueryUI option.
2921
 */
2922
TableTools.classes_themeroller = {
2923
	"container": "DTTT_container ui-buttonset ui-buttonset-multi",
2924
	"buttons": {
2925
		"normal": "DTTT_button ui-button ui-state-default"
2926
	},
2927
	"collection": {
2928
		"container": "DTTT_collection ui-buttonset ui-buttonset-multi"
2929
	}
2930
};
2931
2932
2933
/**
2934
 * @namespace TableTools default settings for initialisation
2935
 */
2936
TableTools.DEFAULTS = {
2937
	"sSwfPath":        "../swf/copy_csv_xls_pdf.swf",
2938
	"sRowSelect":      "none",
2939
	"sRowSelector":    "tr",
2940
	"sSelectedClass":  null,
2941
	"fnPreRowSelect":  null,
2942
	"fnRowSelected":   null,
2943
	"fnRowDeselected": null,
2944
	"aButtons":        [ "copy", "csv", "xls", "pdf", "print" ],
2945
	"oTags": {
2946
		"container": "div",
2947
		"button": "a", // We really want to use buttons here, but Firefox and IE ignore the
2948
		                 // click on the Flash element in the button (but not mouse[in|out]).
2949
		"liner": "span",
2950
		"collection": {
2951
			"container": "div",
2952
			"button": "a",
2953
			"liner": "span"
2954
		}
2955
	}
2956
};
2957
2958
// Alias to match the other plug-ins
2959
TableTools.defaults = TableTools.DEFAULTS;
2960
2961
2962
/**
2963
 * Name of this class
2964
 *  @constant CLASS
2965
 *  @type	 String
2966
 *  @default  TableTools
2967
 */
2968
TableTools.prototype.CLASS = "TableTools";
2969
2970
2971
/**
2972
 * TableTools version
2973
 *  @constant  VERSION
2974
 *  @type	  String
2975
 *  @default   See code
2976
 */
2977
TableTools.version = "2.2.1";
2978
2979
2980
2981
// DataTables 1.10 API
2982
// 
2983
// This will be extended in a big way in in TableTools 3 to provide API methods
2984
// such as rows().select() and rows.selected() etc, but for the moment the
2985
// tabletools() method simply returns the instance.
2986
2987
if ( $.fn.dataTable.Api ) {
2988
	$.fn.dataTable.Api.register( 'tabletools()', function () {
2989
		var tt = null;
2990
2991
		if ( this.context.length > 0 ) {
2992
			tt = TableTools.fnGetInstance( this.context[0].nTable );
2993
		}
2994
2995
		return tt;
2996
	} );
2997
}
2998
2999
3000
3001
3002
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3003
 * Initialisation
3004
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3005
3006
/*
3007
 * Register a new feature with DataTables
3008
 */
3009
if ( typeof $.fn.dataTable == "function" &&
3010
	 typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
3011
	 $.fn.dataTableExt.fnVersionCheck('1.9.0') )
3012
{
3013
	$.fn.dataTableExt.aoFeatures.push( {
3014
		"fnInit": function( oDTSettings ) {
3015
			var init = oDTSettings.oInit;
3016
			var opts = init ?
3017
				init.tableTools || init.oTableTools || {} :
3018
				{};
3019
3020
			var oTT = new TableTools( oDTSettings.oInstance, opts );
3021
			TableTools._aInstances.push( oTT );
3022
3023
			return oTT.dom.container;
3024
		},
3025
		"cFeature": "T",
3026
		"sFeature": "TableTools"
3027
	} );
3028
}
3029
else
3030
{
3031
	alert( "Warning: TableTools requires DataTables 1.9.0 or newer - www.datatables.net/download");
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
3032
}
3033
3034
$.fn.DataTable.TableTools = TableTools;
3035
3036
})(jQuery, window, document);
3037
3038
/*
3039
 * Register a new feature with DataTables
3040
 */
3041
if ( typeof $.fn.dataTable == "function" &&
3042
	 typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
3043
	 $.fn.dataTableExt.fnVersionCheck('1.9.0') )
3044
{
3045
	$.fn.dataTableExt.aoFeatures.push( {
3046
		"fnInit": function( oDTSettings ) {
3047
			var oOpts = typeof oDTSettings.oInit.oTableTools != 'undefined' ?
3048
				oDTSettings.oInit.oTableTools : {};
3049
3050
			var oTT = new TableTools( oDTSettings.oInstance, oOpts );
3051
			TableTools._aInstances.push( oTT );
3052
3053
			return oTT.dom.container;
3054
		},
3055
		"cFeature": "T",
3056
		"sFeature": "TableTools"
3057
	} );
3058
}
3059
else
3060
{
3061
	alert( "Warning: TableTools 2 requires DataTables 1.9.0 or newer - www.datatables.net/download");
0 ignored issues
show
Debugging Code Best Practice introduced by
The alert UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
3062
}
3063
3064
3065
$.fn.dataTable.TableTools = TableTools;
3066
$.fn.DataTable.TableTools = TableTools;
3067
3068
3069
return TableTools;
3070
}; // /factory
3071
3072
3073
// Define as an AMD module if possible
3074
if ( typeof define === 'function' && define.amd ) {
3075
	define( 'datatables-tabletools', ['jquery', 'datatables'], factory );
3076
}
3077
else if ( jQuery && !jQuery.fn.dataTable.TableTools ) {
3078
	// Otherwise simply initialise as normal, stopping multiple evaluation
3079
	factory( jQuery, jQuery.fn.dataTable );
3080
}
3081
3082
3083
})(window, document);
3084
3085