js/datatables/tools/TableTools-master/src/TableTools.js   F
last analyzed

Complexity

Total Complexity 315
Complexity/F 3.15

Size

Lines of Code 2594
Function Count 100

Duplication

Duplicated Lines 2594
Ratio 100 %

Importance

Changes 0
Metric Value
cc 0
eloc 1097
nc 0
dl 2594
loc 2594
rs 1.212
c 0
b 0
f 0
wmc 315
mnd 6
bc 292
fnc 100
bpm 2.92
cpm 3.15
noi 45

57 Functions

Rating   Name   Duplication   Size   Complexity  
A TableTools._fnCustomiseSettings 39 39 4
A TableTools.fnPrint 16 16 4
A TableTools.fnGetSelectedData 16 16 3
A TableTools._fnPrintEnd 37 37 4
A TableTools._fnEventListen 8 8 1
A TableTools._fnChunkData 19 19 3
B TableTools._fnSelectData 40 40 6
A $.extend.fnAjaxComplete 3 3 1
A $.extend.fnInit 3 3 1
A TableTools._fnFlashGlue 16 16 2
A TableTools.fnGetTitle 22 22 5
A TableTools._fnNewline 11 11 3
A TableTools.fnIsSelected 5 5 2
A TableTools.fnGetTableData 8 8 2
C TableTools._fnRowDeselect 43 43 9
A TableTools.fnDeselect 4 4 1
A $.fn.dataTable.Api.register(ꞌtabletools()ꞌ) 9 9 2
A TableTools._fnEventDispatch 11 11 4
B TableTools.js ➔ TableTools 285 285 5
B TableTools._fnRowSelectConfig 133 133 3
A $.extend.fnClick 3 3 1
A TableTools.fnSelect 12 12 2
B TableTools._fnButtonDefinations 32 32 5
A $.extend.fnComplete 10 10 3
A TableTools._fnTextConfig 50 50 4
A $.extend.fnSelect 7 7 2
B TableTools._fnPrintScrollStart 50 50 7
F TableTools._fnColumnTargets 49 49 14
B TableTools._fnPrintHideNodes 29 29 6
C TableTools._fnButtonBase 31 31 10
A TableTools.fnInfo 12 12 1
A TableTools.fnSelectAll 9 9 2
A TableTools._fnConstruct 34 34 2
A TableTools._fnHtmlDecode 21 21 2
A TableTools._fnGetMasterSettings 19 19 4
A TableTools.fnResizeButtons 15 15 5
A TableTools.fnGetInstance 16 16 5
A TableTools._fnCollectionConfig 10 10 1
B TableTools.fnResizeRequired 17 17 6
D TableTools._fnPrintStart 85 85 12
F TableTools._fnGetDataTablesData 124 124 17
A TableTools._fnBoundData 11 11 2
C TableTools._fnRowSelect 43 43 9
A TableTools._fnFlashSetText 10 10 2
A TableTools.fnCalcColRatios 25 25 4
B TableTools._fnCollectionShow 62 62 5
A $.fn.dataTableExt.aoFeatures.push.fnInit 11 11 4
A TableTools.fnContainer 3 3 1
A TableTools._fnPrintScrollEnd 18 18 3
B TableTools._fnFlashConfig 60 60 5
A TableTools._fnCreateButton 24 24 5
A TableTools.fnGetMasters 12 12 3
B TableTools.fnGetSelected 33 33 6
A TableTools._fnPrintShowNodes 10 10 2
A TableTools.fnSelectNone 6 6 1
A TableTools.fnSetText 4 4 1
A TableTools._fnCollectionHide 21 21 4

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