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