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