Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

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