4 * Description: Tools and buttons for DataTables
5 * Author: Allan Jardine (www.sprymedia.co.uk)
7 * License: GPL v2 or BSD 3 point style
10 * Copyright 2009-2012 Allan Jardine, all rights reserved.
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
18 /* Global scope for TableTools */
21 (function($, window, document) {
24 * TableTools provides flexible buttons and other tools for a DataTables enhanced table
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
36 TableTools = function( oDT, oOpts )
38 /* Santiy check that we are a new instance */
39 if ( !this.CLASS || this.CLASS != "TableTools" )
41 alert( "Warning: TableTools must be initialised with the keyword 'new'" );
45 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
46 * Public class variables
47 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
50 * @namespace Settings object which contains customisable information for TableTools instance
54 * Store 'this' so the instance can be retreieved from the settings object
62 * DataTables settings objects
70 * @namespace Print specific information
74 * DataTables draw 'start' point before the printing display was shown
82 * DataTables draw 'length' point before the printing display was shown
83 * @property saveLength
90 * Page scrolling point before the printing display was shown so it can be restored
91 * @property saveScroll
98 * Wrapped function to end the print display (to maintain scope)
101 * @default function () {}
103 "funcEnd": function () {}
107 * A unique ID is assigned to each button in each instance
108 * @property buttonCounter
115 * @namespace Select rows specific information
119 * Select type - can be 'none', 'single' or 'multi'
127 * Array of nodes which are currently selected
135 * Function to run before the selection can take place. Will cancel the select if the
136 * function returns false
137 * @property preRowSelect
141 "preRowSelect": null,
144 * Function to run when a row is selected
145 * @property postSelected
149 "postSelected": null,
152 * Function to run when a row is deselected
153 * @property postDeselected
157 "postDeselected": null,
160 * Indicate if all rows are selected (needed for server-side processing)
168 * Class name to add to selected TR nodes
169 * @property selectedClass
177 * Store of the user input customisation object
194 * @property buttonSet
201 * When there is more than one TableTools instance for a DataTable, there must be a
202 * master which controls events (row selection etc)
212 * @namespace Common and useful DOM elements for the class instance
216 * DIV element that is create and all TableTools buttons (and their children) put into
217 * @property container
224 * The table node to which TableTools will be applied
232 * @namespace Nodes used for the print display
236 * Nodes which have been removed from the display by setting them to display none
244 * The information display saying tellng the user about the print display
253 * @namespace Nodes used for a collection display. This contains the currently used collection
257 * The div wrapper containing the buttons in the collection (i.e. the menu)
258 * @property collection
265 * Background display to provide focus and capture events
266 * @property background
276 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
277 * Public class methods
278 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
281 * Retreieve the settings object from an instance
283 * @returns {object} TableTools settings object
285 this.fnSettings = function () {
290 /* Constructor logic */
291 if ( typeof oOpts == 'undefined' )
296 this.s.dt = oDT.fnSettings();
297 this._fnConstruct( oOpts );
304 TableTools.prototype = {
305 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
307 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
310 * Retreieve the settings object from an instance
311 * @method fnGetSelected
312 * @returns {array} List of TR nodes which are currently selected
314 "fnGetSelected": function ()
316 var masterS = this._fnGetMasterSettings();
317 return masterS.select.selected;
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
327 "fnGetSelectedData": function ()
329 var masterS = this._fnGetMasterSettings();
330 var selected = masterS.select.selected;
333 for ( var i=0, iLen=selected.length ; i<iLen ; i++ )
335 out.push( this.s.dt.oInstance.fnGetData( selected[i] ) );
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
348 "fnIsSelected": function ( n )
350 var selected = this.fnGetSelected();
351 for ( var i=0, iLen=selected.length ; i<iLen ; i++ )
353 if ( n == selected[i] )
363 * Select all rows in the table
364 * @method fnSelectAll
367 "fnSelectAll": function ()
369 var masterS = this._fnGetMasterSettings();
370 masterS.that._fnRowSelectAll();
375 * Deselect all rows in the table
376 * @method fnSelectNone
379 "fnSelectNone": function ()
381 var masterS = this._fnGetMasterSettings();
382 masterS.that._fnRowDeselectAll();
387 * Select an individual row
391 "fnSelect": function ( n )
393 /* Check if the row is already selected */
394 if ( !this.fnIsSelected( n ) )
396 if ( this.s.select.type == "single" )
398 this._fnRowSelectSingle( n );
400 else if ( this.s.select.type == "multi" )
402 this._fnRowSelectMulti( n );
409 * Deselect an individual row
413 "fnDeselect": function ( n )
415 /* Check if the row is already deselected */
416 if ( this.fnIsSelected( n ) )
418 if ( this.s.select.type == "single" )
420 this._fnRowSelectSingle( n );
422 else if ( this.s.select.type == "multi" )
424 this._fnRowSelectMulti( n );
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
434 * @param {Object} oConfig Button configuration object
435 * @returns {String} Button title
437 "fnGetTitle": function( oConfig )
440 if ( typeof oConfig.sTitle != 'undefined' && oConfig.sTitle !== "" ) {
441 sTitle = oConfig.sTitle;
443 var anTitle = document.getElementsByTagName('title');
444 if ( anTitle.length > 0 )
446 sTitle = anTitle[0].innerHTML;
450 /* Strip characters which the OS will object to - checking for UTF8 support in the scripting
453 if ( "\u00A1".toString().length < 4 ) {
454 return sTitle.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
456 return sTitle.replace(/[^a-zA-Z0-9_\.,\-_ !\(\)]/g, "");
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
469 "fnCalcColRatios": function ( oConfig )
472 aoCols = this.s.dt.aoColumns,
473 aColumnsInc = this._fnColumnTargets( oConfig.mColumns ),
475 iWidth = 0, iTotal = 0, i, iLen;
477 for ( i=0, iLen=aColumnsInc.length ; i<iLen ; i++ )
479 if ( aColumnsInc[i] )
481 iWidth = aoCols[i].nTh.offsetWidth;
483 aColWidths.push( iWidth );
487 for ( i=0, iLen=aColWidths.length ; i<iLen ; i++ )
489 aColWidths[i] = aColWidths[i] / iTotal;
492 return aColWidths.join('\t');
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
502 "fnGetTableData": function ( oConfig )
504 /* In future this could be used to get data from a plain HTML source as well as DataTables */
507 return this._fnGetDataTablesData( oConfig );
513 * Pass text to a flash button instance, which will be used on the button's click handler
515 * @param {Object} clip Flash button object
516 * @param {String} text Text to set
519 "fnSetText": function ( clip, text )
521 this._fnFlashSetText( clip, text );
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
532 "fnResizeButtons": function ()
534 for ( var cli in ZeroClipboard.clients )
538 var client = ZeroClipboard.clients[cli];
539 if ( typeof client.domElement != 'undefined' &&
540 client.domElement.parentNode == this.dom.container )
542 client.positionElement();
550 * Check to see if any of the ZeroClipboard client's attached need to be resized
551 * @method fnResizeRequired
554 "fnResizeRequired": function ()
556 for ( var cli in ZeroClipboard.clients )
560 var client = ZeroClipboard.clients[cli];
561 if ( typeof client.domElement != 'undefined' &&
562 client.domElement.parentNode == this.dom.container &&
563 client.sized === false )
574 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
575 * Private methods (they are of course public in JS, but recommended as private)
576 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
580 * @method _fnConstruct
581 * @param {Object} oOpts Same as TableTools constructor
585 "_fnConstruct": function ( oOpts )
589 this._fnCustomiseSettings( oOpts );
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";
596 /* Row selection config */
597 if ( this.s.select.type != 'none' )
599 this._fnRowSelectConfig();
603 this._fnButtonDefinations( this.s.buttonSet, this.dom.container );
605 /* Destructor - need to wipe the DOM for IE's garbage collector */
606 this.s.dt.aoDestroyCallback.push( {
607 "sName": "TableTools",
609 that.dom.container.innerHTML = "";
616 * Take the user defined settings and the default settings and combine them.
617 * @method _fnCustomiseSettings
618 * @param {Object} oOpts Same as TableTools constructor
622 "_fnCustomiseSettings": function ( oOpts )
624 /* Is this the master control instance or not? */
625 if ( typeof this.s.dt._TableToolsInit == 'undefined' )
627 this.s.master = true;
628 this.s.dt._TableToolsInit = true;
631 /* We can use the table node from comparisons to group controls */
632 this.dom.table = this.s.dt.nTable;
634 /* Clone the defaults and then the user options */
635 this.s.custom = $.extend( {}, TableTools.DEFAULTS, oOpts );
637 /* Flash file location */
638 this.s.swfPath = this.s.custom.sSwfPath;
639 if ( typeof ZeroClipboard != 'undefined' )
641 ZeroClipboard.moviePath = this.s.swfPath;
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;
652 this.s.buttonSet = this.s.custom.aButtons;
657 * Take the user input arrays and expand them to be fully defined, and then add them to a given
659 * @method _fnButtonDefinations
660 * @param {array} buttonSet Set of user defined buttons
661 * @param {node} wrapper Node to add the created buttons to
665 "_fnButtonDefinations": function ( buttonSet, wrapper )
669 for ( var i=0, iLen=buttonSet.length ; i<iLen ; i++ )
671 if ( typeof buttonSet[i] == "string" )
673 if ( typeof TableTools.BUTTONS[ buttonSet[i] ] == 'undefined' )
675 alert( "TableTools: Warning - unknown button type: "+buttonSet[i] );
678 buttonDef = $.extend( {}, TableTools.BUTTONS[ buttonSet[i] ], true );
682 if ( typeof TableTools.BUTTONS[ buttonSet[i].sExtends ] == 'undefined' )
684 alert( "TableTools: Warning - unknown button type: "+buttonSet[i].sExtends );
687 var o = $.extend( {}, TableTools.BUTTONS[ buttonSet[i].sExtends ], true );
688 buttonDef = $.extend( o, buttonSet[i], true );
691 if ( this.s.dt.bJUI )
693 buttonDef.sButtonClass += " ui-button ui-state-default";
694 buttonDef.sButtonClassHover += " ui-state-hover";
697 wrapper.appendChild( this._fnCreateButton( buttonDef ) );
703 * Create and configure a TableTools button
704 * @method _fnCreateButton
705 * @param {Object} oConfig Button configuration object
706 * @returns {Node} Button element
709 "_fnCreateButton": function ( oConfig )
711 var nButton = (oConfig.sAction == 'div') ?
712 this._fnDivBase( oConfig ) : this._fnButtonBase( oConfig );
714 if ( oConfig.sAction == "print" )
716 this._fnPrintConfig( nButton, oConfig );
718 else if ( oConfig.sAction.match(/flash/) )
720 this._fnFlashConfig( nButton, oConfig );
722 else if ( oConfig.sAction == "text" )
724 this._fnTextConfig( nButton, oConfig );
726 else if ( oConfig.sAction == "div" )
728 this._fnTextConfig( nButton, oConfig );
730 else if ( oConfig.sAction == "collection" )
732 this._fnTextConfig( nButton, oConfig );
733 this._fnCollectionConfig( nButton, oConfig );
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
747 "_fnButtonBase": function ( o )
750 nButton = document.createElement('button'),
751 nSpan = document.createElement('span'),
752 masterS = this._fnGetMasterSettings();
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;
759 masterS.buttonCounter++;
766 * Create a DIV element to use for a non-button
768 * @param {o} oConfig Button configuration object
769 * @returns {Node} DIV element for the button
772 "_fnDivBase": function ( o )
775 nDiv = document.createElement('div'),
776 masterS = this._fnGetMasterSettings();
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 )
784 nDiv.appendChild( o.nContent );
787 masterS.buttonCounter++;
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
801 "_fnGetMasterSettings": function ()
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++ )
813 if ( this.dom.table == instances[i].s.dt.nTable )
815 return instances[i].s;
823 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
824 * Button collection functions
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
834 "_fnCollectionConfig": function ( nButton, oConfig )
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;
842 this._fnButtonDefinations( oConfig.aButtons, nHidden );
847 * Show a button collection
848 * @param {Node} nButton Button to use for the collection
849 * @param {Object} oConfig Button configuration object
853 "_fnCollectionShow": function ( nButton, oConfig )
857 oPos = $(nButton).offset(),
858 nHidden = oConfig._collection,
860 iDivY = oPos.top + $(nButton).outerHeight(),
861 iWinHeight = $(window).height(), iDocHeight = $(document).height(),
862 iWinWidth = $(window).width(), iDocWidth = $(document).width();
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);
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);
879 document.body.appendChild( nBackground );
880 document.body.appendChild( nHidden );
882 /* Visual corrections to try and keep the collection visible */
883 var iDivWidth = $(nHidden).outerWidth();
884 var iDivHeight = $(nHidden).outerHeight();
886 if ( iDivX + iDivWidth > iDocWidth )
888 nHidden.style.left = (iDocWidth-iDivWidth)+"px";
891 if ( iDivY + iDivHeight > iDocHeight )
893 nHidden.style.top = (iDivY-iDivHeight-$(nButton).outerHeight())+"px";
896 this.dom.collection.collection = nHidden;
897 this.dom.collection.background = nBackground;
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
902 setTimeout( function () {
903 $(nHidden).animate({"opacity": 1}, 500);
904 $(nBackground).animate({"opacity": 0.25}, 500);
907 /* Event handler to remove the collection display */
908 $(nBackground).click( function () {
909 that._fnCollectionHide.call( that, null, null );
915 * Hide a button collection
916 * @param {Node} nButton Button to use for the collection
917 * @param {Object} oConfig Button configuration object
921 "_fnCollectionHide": function ( nButton, oConfig )
923 if ( oConfig !== null && oConfig.sExtends == 'collection' )
928 if ( this.dom.collection.collection !== null )
930 $(this.dom.collection.collection).animate({"opacity": 0}, 500, function (e) {
931 this.style.display = "none";
934 $(this.dom.collection.background).animate({"opacity": 0}, 500, function (e) {
935 this.parentNode.removeChild( this );
938 this.dom.collection.collection = null;
939 this.dom.collection.background = null;
945 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
946 * Row selection functions
950 * Add event handlers to a table to allow for row selection
951 * @method _fnRowSelectConfig
955 "_fnRowSelectConfig": function ()
962 aoOpenRows = this.s.dt.aoOpenRows;
964 $(that.s.dt.nTable).addClass( 'DTTT_selectable' );
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 )
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 ) {
979 /* User defined selection function */
980 if ( that.s.select.preRowSelect !== null && !that.s.select.preRowSelect.call(that, e) )
986 if ( that.s.select.type == "single" )
988 that._fnRowSelectSingle.call( that, this );
992 that._fnRowSelectMulti.call( that, this );
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
999 that.s.dt.aoDrawCallback.push( {
1001 if ( that.s.select.all && that.s.dt.oFeatures.bServerSide )
1006 "sName": "TableTools_select"
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
1021 "_fnRowSelectSingle": function ( nNode )
1023 if ( this.s.master )
1025 /* Do nothing on the DataTables 'empty' result set row */
1026 if ( $('td', nNode).hasClass(this.s.dt.oClasses.sRowEmpty) )
1031 if ( $(nNode).hasClass(this.s.select.selectedClass) )
1033 this._fnRowDeselect( nNode );
1037 if ( this.s.select.selected.length !== 0 )
1039 this._fnRowDeselectAll();
1042 this.s.select.selected.push( nNode );
1043 $(nNode).addClass( this.s.select.selectedClass );
1045 if ( this.s.select.postSelected !== null )
1047 this.s.select.postSelected.call( this, nNode );
1051 TableTools._fnEventDispatch( this, 'select', nNode );
1057 * Select or deselect a row based on its current state when multiple rows are allowed to be
1059 * @method _fnRowSelectMulti
1060 * @param {Node} nNode TR element which is being 'activated' in some way
1064 "_fnRowSelectMulti": function ( nNode )
1066 if ( this.s.master )
1068 /* Do nothing on the DataTables 'empty' result set row */
1069 if ( $('td', nNode).hasClass(this.s.dt.oClasses.sRowEmpty) )
1074 if ( $(nNode).hasClass(this.s.select.selectedClass) )
1076 this._fnRowDeselect( nNode );
1080 this.s.select.selected.push( nNode );
1081 $(nNode).addClass( this.s.select.selectedClass );
1083 if ( this.s.select.postSelected !== null )
1085 this.s.select.postSelected.call( this, nNode );
1089 TableTools._fnEventDispatch( this, 'select', nNode );
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
1101 "_fnRowSelectAll": function ( )
1103 if ( this.s.master )
1106 for ( var i=0, iLen=this.s.dt.aiDisplayMaster.length ; i<iLen ; i++ )
1108 n = this.s.dt.aoData[ this.s.dt.aiDisplayMaster[i] ].nTr;
1110 if ( !$(n).hasClass(this.s.select.selectedClass) )
1112 this.s.select.selected.push( n );
1113 $(n).addClass( this.s.select.selectedClass );
1117 if ( this.s.select.postSelected !== null )
1119 this.s.select.postSelected.call( this, null );
1122 this.s.select.all = true;
1123 TableTools._fnEventDispatch( this, 'select', null );
1129 * Deselect all TR elements in the table. If nothing is currently selected, then no action is
1131 * @method _fnRowDeselectAll
1135 "_fnRowDeselectAll": function ( )
1137 if ( this.s.master )
1139 for ( var i=this.s.select.selected.length-1 ; i>=0 ; i-- )
1141 this._fnRowDeselect( i, false );
1144 if ( this.s.select.postDeselected !== null )
1146 this.s.select.postDeselected.call( this, null );
1149 this.s.select.all = false;
1150 TableTools._fnEventDispatch( this, 'select', null );
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
1164 "_fnRowDeselect": function ( i, action )
1166 if ( typeof i.nodeName != 'undefined' )
1168 i = $.inArray( i, this.s.select.selected );
1171 var nNode = this.s.select.selected[i];
1172 $(nNode).removeClass(this.s.select.selectedClass);
1173 this.s.select.selected.splice( i, 1 );
1175 if ( (typeof action == 'undefined' || action) && this.s.select.postDeselected !== null )
1177 this.s.select.postDeselected.call( this, nNode );
1180 this.s.select.all = false;
1185 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1186 * Text button functions
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
1197 "_fnTextConfig": function ( nButton, oConfig )
1201 if ( oConfig.fnInit !== null )
1203 oConfig.fnInit.call( this, nButton, oConfig );
1206 if ( oConfig.sToolTip !== "" )
1208 nButton.title = oConfig.sToolTip;
1211 $(nButton).hover( function () {
1212 $(nButton).addClass(oConfig.sButtonClassHover );
1213 if ( oConfig.fnMouseover !== null )
1215 oConfig.fnMouseover.call( this, nButton, oConfig, null );
1218 $(nButton).removeClass( oConfig.sButtonClassHover );
1219 if ( oConfig.fnMouseout !== null )
1221 oConfig.fnMouseout.call( this, nButton, oConfig, null );
1225 if ( oConfig.fnSelect !== null )
1227 TableTools._fnEventListen( this, 'select', function (n) {
1228 oConfig.fnSelect.call( that, nButton, oConfig, n );
1232 $(nButton).click( function (e) {
1235 if ( oConfig.fnClick !== null )
1237 oConfig.fnClick.call( that, nButton, oConfig, null );
1240 /* Provide a complete function to match the behaviour of the flash elements */
1241 if ( oConfig.fnComplete !== null )
1243 oConfig.fnComplete.call( that, nButton, oConfig, null, null );
1246 that._fnCollectionHide( nButton, oConfig );
1252 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1253 * Flash button functions
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
1264 "_fnFlashConfig": function ( nButton, oConfig )
1267 var flash = new ZeroClipboard.Client();
1269 if ( oConfig.fnInit !== null )
1271 oConfig.fnInit.call( this, nButton, oConfig );
1274 flash.setHandCursor( true );
1276 if ( oConfig.sAction == "flash_save" )
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)) );
1283 else if ( oConfig.sAction == "flash_pdf" )
1285 flash.setAction( 'pdf' );
1286 flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1290 flash.setAction( 'copy' );
1293 flash.addEventListener('mouseOver', function(client) {
1294 $(nButton).addClass(oConfig.sButtonClassHover );
1296 if ( oConfig.fnMouseover !== null )
1298 oConfig.fnMouseover.call( that, nButton, oConfig, flash );
1302 flash.addEventListener('mouseOut', function(client) {
1303 $(nButton).removeClass( oConfig.sButtonClassHover );
1305 if ( oConfig.fnMouseout !== null )
1307 oConfig.fnMouseout.call( that, nButton, oConfig, flash );
1311 flash.addEventListener('mouseDown', function(client) {
1312 if ( oConfig.fnClick !== null )
1314 oConfig.fnClick.call( that, nButton, oConfig, flash );
1318 flash.addEventListener('complete', function (client, text) {
1319 if ( oConfig.fnComplete !== null )
1321 oConfig.fnComplete.call( that, nButton, oConfig, flash, text );
1323 that._fnCollectionHide( nButton, oConfig );
1326 this._fnFlashGlue( flash, nButton, oConfig.sToolTip );
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
1340 "_fnFlashGlue": function ( flash, node, text )
1343 var id = node.getAttribute('id');
1345 if ( document.getElementById(id) )
1347 flash.glue( node, text );
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' )
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;
1361 setTimeout( function () {
1362 that._fnFlashGlue( flash, node, text );
1369 * Set the text for the flash clip to deal with
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
1382 "_fnFlashSetText": function ( clip, sData )
1384 var asData = this._fnChunkData( sData, 8192 );
1387 for ( var i=0, iLen=asData.length ; i<iLen ; i++ )
1389 clip.appendText( asData[i] );
1395 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1396 * Data retrieval functions
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
1410 "_fnColumnTargets": function ( mColumns )
1415 if ( typeof mColumns == "object" )
1417 for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1419 aColumns.push( false );
1422 for ( i=0, iLen=mColumns.length ; i<iLen ; i++ )
1424 aColumns[ mColumns[i] ] = true;
1427 else if ( mColumns == "visible" )
1429 for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1431 aColumns.push( dt.aoColumns[i].bVisible ? true : false );
1434 else if ( mColumns == "hidden" )
1436 for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1438 aColumns.push( dt.aoColumns[i].bVisible ? false : true );
1441 else if ( mColumns == "sortable" )
1443 for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1445 aColumns.push( dt.aoColumns[i].bSortable ? true : false );
1450 for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1452 aColumns.push( true );
1461 * New line character(s) depend on the platforms
1463 * @param {Object} oConfig Button configuration object - only interested in oConfig.sNewLine
1464 * @returns {String} Newline character
1466 "_fnNewline": function ( oConfig )
1468 if ( oConfig.sNewLine == "auto" )
1470 return navigator.userAgent.match(/Windows/) ? "\r\n" : "\n";
1474 return oConfig.sNewLine;
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
1493 "_fnGetDataTablesData": function ( oConfig )
1495 var i, iLen, j, jLen;
1496 var sData = '', sLoopData = '';
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;
1506 if ( oConfig.bHeader )
1508 for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1510 if ( aColumnsInc[i] )
1512 sLoopData = dt.aoColumns[i].sTitle.replace(/\n/g," ").replace( /<.*?>/g, "" ).replace(/^\s+|\s+$/g,"");
1513 sLoopData = this._fnHtmlDecode( sLoopData );
1515 sData += this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) +
1516 oConfig.sFieldSeperator;
1519 sData = sData.slice( 0, oConfig.sFieldSeperator.length*-1 );
1526 for ( j=0, jLen=dt.aiDisplay.length ; j<jLen ; j++ )
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) )
1533 for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1535 if ( aColumnsInc[i] )
1537 /* Convert to strings (with small optimisation) */
1538 var mTypeData = dt.oApi._fnGetCellData( dt, dt.aiDisplay[j], i, 'display' );
1539 if ( oConfig.fnCellRender )
1541 sLoopData = oConfig.fnCellRender( mTypeData, i )+"";
1543 else if ( typeof mTypeData == "string" )
1545 /* Strip newlines, replace img tags with alt attr. and finally strip html... */
1546 sLoopData = mTypeData.replace(/\n/g," ");
1548 sLoopData.replace(/<img.*?\s+alt\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+)).*?>/gi,
1550 sLoopData = sLoopData.replace( /<.*?>/g, "" );
1554 sLoopData = mTypeData+"";
1557 /* Trim and clean the data */
1558 sLoopData = sLoopData.replace(/^\s+/, '').replace(/\s+$/, '');
1559 sLoopData = this._fnHtmlDecode( sLoopData );
1561 /* Bound it and add it to the total data */
1562 sData += this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) +
1563 oConfig.sFieldSeperator;
1566 sData = sData.slice( 0, oConfig.sFieldSeperator.length*-1 );
1571 /* Remove the last new line */
1572 sData.slice( 0, -1 );
1577 if ( oConfig.bFooter )
1579 for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1581 if ( aColumnsInc[i] && dt.aoColumns[i].nTf !== null )
1583 sLoopData = dt.aoColumns[i].nTf.innerHTML.replace(/\n/g," ").replace( /<.*?>/g, "" );
1584 sLoopData = this._fnHtmlDecode( sLoopData );
1586 sData += this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) +
1587 oConfig.sFieldSeperator;
1590 sData = sData.slice( 0, oConfig.sFieldSeperator.length*-1 );
1593 /* No pointers here - this is a string copy :-) */
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
1606 * @returns {String} bound data
1609 "_fnBoundData": function ( sData, sBoundary, regex )
1611 if ( sBoundary === "" )
1617 return sBoundary + sData.replace(regex, sBoundary+sBoundary) + sBoundary;
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
1630 "_fnChunkData": function ( sData, iSize )
1633 var iStrlen = sData.length;
1635 for ( var i=0 ; i<iStrlen ; i+=iSize )
1637 if ( i+iSize < iStrlen )
1639 asReturn.push( sData.substring( i, i+iSize ) );
1643 asReturn.push( sData.substring( i, iStrlen ) );
1652 * Decode HTML entities
1653 * @method _fnHtmlDecode
1654 * @param {String} sData encoded string
1655 * @returns {String} decoded string
1658 "_fnHtmlDecode": function ( sData )
1660 if ( sData.indexOf('&') == -1 )
1666 aData = this._fnChunkData( sData, 2048 ),
1667 n = document.createElement('div'),
1669 sReturn = "", sInner;
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
1675 for ( i=0, iLen=aData.length ; i<iLen ; i++ )
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 )
1681 sInner = aData[i].substr( iIndex );
1682 aData[i] = aData[i].substr( 0, iIndex );
1685 n.innerHTML = aData[i];
1686 sReturn += n.childNodes[0].nodeValue;
1694 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1695 * Printing functions
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
1706 "_fnPrintConfig": function ( nButton, oConfig )
1710 if ( oConfig.fnInit !== null )
1712 oConfig.fnInit.call( this, nButton, oConfig );
1715 if ( oConfig.sToolTip !== "" )
1717 nButton.title = oConfig.sToolTip;
1720 $(nButton).hover( function () {
1721 $(nButton).addClass(oConfig.sButtonClassHover );
1723 $(nButton).removeClass( oConfig.sButtonClassHover );
1726 if ( oConfig.fnSelect !== null )
1728 TableTools._fnEventListen( this, 'select', function (n) {
1729 oConfig.fnSelect.call( that, nButton, oConfig, n );
1733 $(nButton).click( function (e) {
1736 that._fnPrintStart.call( that, e, oConfig);
1738 if ( oConfig.fnClick !== null )
1740 oConfig.fnClick.call( that, nButton, oConfig, null );
1743 /* Provide a complete function to match the behaviour of the flash elements */
1744 if ( oConfig.fnComplete !== null )
1746 oConfig.fnComplete.call( that, nButton, oConfig, null, null );
1749 that._fnCollectionHide( nButton, oConfig );
1754 * Show print display
1755 * @method _fnPrintStart
1756 * @param {Event} e Event object
1757 * @param {Object} oConfig Button configuration object
1761 "_fnPrintStart": function ( e, oConfig )
1764 var oSetDT = this.s.dt;
1766 /* Parse through the DOM hiding everything that isn't needed for the table */
1767 this._fnPrintHideNodes( oSetDT.nTable );
1769 /* Show the whole table */
1770 this.s.print.saveStart = oSetDT._iDisplayStart;
1771 this.s.print.saveLength = oSetDT._iDisplayLength;
1773 if ( oConfig.bShowAll )
1775 oSetDT._iDisplayStart = 0;
1776 oSetDT._iDisplayLength = -1;
1777 oSetDT.oApi._fnCalculateEnd( oSetDT );
1778 oSetDT.oApi._fnDraw( oSetDT );
1781 /* Adjust the display for scrolling which might be done by DataTables */
1782 if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
1784 this._fnPrintScrollStart( oSetDT );
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 )
1791 if ( cFeature != 'i' && cFeature != 't' && cFeature.length == 1 )
1793 for ( var i=0, iLen=anFeature[cFeature].length ; i<iLen ; i++ )
1795 this.dom.print.hidden.push( {
1796 "node": anFeature[cFeature][i],
1799 anFeature[cFeature][i].style.display = "none";
1804 /* Print class can be used for styling */
1805 $(document.body).addClass( 'DTTT_Print' );
1807 /* Add a node telling the user what is going on */
1808 if ( oConfig.sInfo !== "" )
1810 var nInfo = document.createElement( "div" );
1811 nInfo.className = "DTTT_print_info";
1812 nInfo.innerHTML = oConfig.sInfo;
1813 document.body.appendChild( nInfo );
1815 setTimeout( function() {
1816 $(nInfo).fadeOut( "normal", function() {
1817 document.body.removeChild( nInfo );
1822 /* Add a message at the top of the page */
1823 if ( oConfig.sMessage !== "" )
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] );
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 );
1835 this.s.print.funcEnd = function(e) {
1836 that._fnPrintEnd.call( that, e );
1838 $(document).bind( "keydown", null, this.s.print.funcEnd );
1843 * Printing is finished, resume normal display
1844 * @method _fnPrintEnd
1845 * @param {Event} e Event object
1849 "_fnPrintEnd": function ( e )
1851 /* Only interested in the escape key */
1852 if ( e.keyCode == 27 )
1857 var oSetDT = this.s.dt;
1858 var oSetPrint = this.s.print;
1859 var oDomPrint = this.dom.print;
1861 /* Show all hidden nodes */
1862 this._fnPrintShowNodes();
1864 /* Restore DataTables' scrolling */
1865 if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
1867 this._fnPrintScrollEnd();
1870 /* Restore the scroll */
1871 window.scrollTo( 0, oSetPrint.saveScroll );
1873 /* Drop the print message */
1874 if ( oDomPrint.message !== null )
1876 document.body.removeChild( oDomPrint.message );
1877 oDomPrint.message = null;
1881 $(document.body).removeClass( 'DTTT_Print' );
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 );
1889 $(document).unbind( "keydown", this.s.print.funcEnd );
1890 this.s.print.funcEnd = null;
1896 * Take account of scrolling in DataTables by showing the full table
1900 "_fnPrintScrollStart": function ()
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
1911 var nTheadSize = oSetDT.nTable.getElementsByTagName('thead');
1912 if ( nTheadSize.length > 0 )
1914 oSetDT.nTable.removeChild( nTheadSize[0] );
1917 if ( oSetDT.nTFoot !== null )
1919 var nTfootSize = oSetDT.nTable.getElementsByTagName('tfoot');
1920 if ( nTfootSize.length > 0 )
1922 oSetDT.nTable.removeChild( nTfootSize[0] );
1926 nTheadSize = oSetDT.nTHead.cloneNode(true);
1927 oSetDT.nTable.insertBefore( nTheadSize, oSetDT.nTable.childNodes[0] );
1929 if ( oSetDT.nTFoot !== null )
1931 nTfootSize = oSetDT.nTFoot.cloneNode(true);
1932 oSetDT.nTable.insertBefore( nTfootSize, oSetDT.nTable.childNodes[1] );
1935 /* Now adjust the table's viewport so we can actually see it */
1936 if ( oSetDT.oScroll.sX !== "" )
1938 oSetDT.nTable.style.width = $(oSetDT.nTable).outerWidth()+"px";
1939 nScrollBody.style.width = $(oSetDT.nTable).outerWidth()+"px";
1940 nScrollBody.style.overflow = "visible";
1943 if ( oSetDT.oScroll.sY !== "" )
1945 nScrollBody.style.height = $(oSetDT.nTable).outerHeight()+"px";
1946 nScrollBody.style.overflow = "visible";
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
1957 "_fnPrintScrollEnd": function ()
1961 nScrollBody = oSetDT.nTable.parentNode;
1963 if ( oSetDT.oScroll.sX !== "" )
1965 nScrollBody.style.width = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sX );
1966 nScrollBody.style.overflow = "auto";
1969 if ( oSetDT.oScroll.sY !== "" )
1971 nScrollBody.style.height = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sY );
1972 nScrollBody.style.overflow = "auto";
1978 * Resume the display of all TableTools hidden nodes
1979 * @method _fnPrintShowNodes
1983 "_fnPrintShowNodes": function ( )
1985 var anHidden = this.dom.print.hidden;
1987 for ( var i=0, iLen=anHidden.length ; i<iLen ; i++ )
1989 anHidden[i].node.style.display = anHidden[i].display;
1991 anHidden.splice( 0, anHidden.length );
1996 * Hide nodes which are not needed in order to display the table. Note that this function is
1998 * @method _fnPrintHideNodes
1999 * @param {Node} nNode Element which should be showing in a 'print' display
2003 "_fnPrintHideNodes": function ( nNode )
2005 var anHidden = this.dom.print.hidden;
2007 var nParent = nNode.parentNode;
2008 var nChildren = nParent.childNodes;
2009 for ( var i=0, iLen=nChildren.length ; i<iLen ; i++ )
2011 if ( nChildren[i] != nNode && nChildren[i].nodeType == 1 )
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" )
2017 /* Cache the node and it's previous state so we can restore it */
2019 "node": nChildren[i],
2022 nChildren[i].style.display = "none";
2027 if ( nParent.nodeName != "BODY" )
2029 this._fnPrintHideNodes( nParent );
2036 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
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
2048 TableTools._aInstances = [];
2052 * Store of all listeners and their callback functions
2053 * @property _aListeners
2057 TableTools._aListeners = [];
2061 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2063 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2066 * Get an array of all the master instances
2067 * @method fnGetMasters
2068 * @returns {Array} List of master TableTools instances
2071 TableTools.fnGetMasters = function ()
2074 for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2076 if ( TableTools._aInstances[i].s.master )
2078 a.push( TableTools._aInstances[i] );
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
2090 TableTools.fnGetInstance = function ( node )
2092 if ( typeof node != 'object' )
2094 node = document.getElementById(node);
2097 for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2099 if ( TableTools._aInstances[i].s.master && TableTools._aInstances[i].dom.table == node )
2101 return TableTools._aInstances[i];
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
2118 TableTools._fnEventListen = function ( that, type, fn )
2120 TableTools._aListeners.push( {
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)
2139 TableTools._fnEventDispatch = function ( that, type, node )
2141 var listeners = TableTools._aListeners;
2142 for ( var i=0, iLen=listeners.length ; i<iLen ; i++ )
2144 if ( that.dom.table == listeners[i].that.dom.table && listeners[i].type == type )
2146 listeners[i].fn( node );
2156 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2158 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2162 * @namespace Default button configurations
2164 TableTools.BUTTONS = {
2166 "sAction": "flash_save",
2169 "sFileName": "*.csv",
2170 "sFieldBoundary": '"',
2171 "sFieldSeperator": ",",
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 */
2181 "bSelectedOnly": false,
2182 "fnMouseover": null,
2184 "fnClick": function( nButton, oConfig, flash ) {
2185 this.fnSetText( flash, this.fnGetTableData(oConfig) );
2190 "fnCellRender": null
2193 "sAction": "flash_save",
2194 "sCharSet": "utf16le",
2196 "sFileName": "*.csv",
2197 "sFieldBoundary": "",
2198 "sFieldSeperator": "\t",
2202 "sButtonClass": "DTTT_button_xls",
2203 "sButtonClassHover": "DTTT_button_xls_hover",
2204 "sButtonText": "Excel",
2208 "bSelectedOnly": false,
2209 "fnMouseover": null,
2211 "fnClick": function( nButton, oConfig, flash ) {
2212 this.fnSetText( flash, this.fnGetTableData(oConfig) );
2217 "fnCellRender": null
2220 "sAction": "flash_copy",
2221 "sFieldBoundary": "",
2222 "sFieldSeperator": "\t",
2225 "sButtonClass": "DTTT_button_copy",
2226 "sButtonClassHover": "DTTT_button_copy_hover",
2227 "sButtonText": "Copy",
2231 "bSelectedOnly": false,
2232 "fnMouseover": null,
2234 "fnClick": function( nButton, oConfig, flash ) {
2235 this.fnSetText( flash, this.fnGetTableData(oConfig) );
2238 "fnComplete": function(nButton, oConfig, flash, text) {
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' );
2246 "fnCellRender": null
2249 "sAction": "flash_pdf",
2250 "sFieldBoundary": "",
2251 "sFieldSeperator": "\t",
2253 "sFileName": "*.pdf",
2256 "sButtonClass": "DTTT_button_pdf",
2257 "sButtonClassHover": "DTTT_button_pdf_hover",
2258 "sButtonText": "PDF",
2262 "bSelectedOnly": false,
2263 "fnMouseover": null,
2265 "sPdfOrientation": "portrait",
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)
2282 "fnCellRender": null
2286 "sInfo": "<h6>Print view</h6><p>Please use your browser's print function to "+
2287 "print this table. Press escape when finished.",
2290 "sToolTip": "View print view",
2291 "sButtonClass": "DTTT_button_print",
2292 "sButtonClassHover": "DTTT_button_print_hover",
2293 "sButtonText": "Print",
2294 "fnMouseover": null,
2300 "fnCellRender": null
2305 "sButtonClass": "DTTT_button_text",
2306 "sButtonClassHover": "DTTT_button_text_hover",
2307 "sButtonText": "Text button",
2311 "bSelectedOnly": false,
2312 "fnMouseover": null,
2318 "fnCellRender": null
2323 "sButtonClass": "DTTT_button_text",
2324 "sButtonClassHover": "DTTT_button_text_hover",
2325 "sButtonText": "Select button",
2329 "fnMouseover": null,
2332 "fnSelect": function( nButton, oConfig ) {
2333 if ( this.fnGetSelected().length !== 0 ) {
2334 $(nButton).removeClass('DTTT_disabled');
2336 $(nButton).addClass('DTTT_disabled');
2340 "fnInit": function( nButton, oConfig ) {
2341 $(nButton).addClass('DTTT_disabled');
2343 "fnCellRender": null
2348 "sButtonClass": "DTTT_button_text",
2349 "sButtonClassHover": "DTTT_button_text_hover",
2350 "sButtonText": "Select button",
2354 "fnMouseover": null,
2357 "fnSelect": function( nButton, oConfig ) {
2358 var iSelected = this.fnGetSelected().length;
2359 if ( iSelected == 1 ) {
2360 $(nButton).removeClass('DTTT_disabled');
2362 $(nButton).addClass('DTTT_disabled');
2366 "fnInit": function( nButton, oConfig ) {
2367 $(nButton).addClass('DTTT_disabled');
2369 "fnCellRender": null
2374 "sButtonClass": "DTTT_button_text",
2375 "sButtonClassHover": "DTTT_button_text_hover",
2376 "sButtonText": "Select all",
2380 "fnMouseover": null,
2382 "fnClick": function( nButton, oConfig ) {
2385 "fnSelect": function( nButton, oConfig ) {
2386 if ( this.fnGetSelected().length == this.s.dt.fnRecordsDisplay() ) {
2387 $(nButton).addClass('DTTT_disabled');
2389 $(nButton).removeClass('DTTT_disabled');
2394 "fnCellRender": null
2399 "sButtonClass": "DTTT_button_text",
2400 "sButtonClassHover": "DTTT_button_text_hover",
2401 "sButtonText": "Deselect all",
2405 "fnMouseover": null,
2407 "fnClick": function( nButton, oConfig ) {
2408 this.fnSelectNone();
2410 "fnSelect": function( nButton, oConfig ) {
2411 if ( this.fnGetSelected().length !== 0 ) {
2412 $(nButton).removeClass('DTTT_disabled');
2414 $(nButton).addClass('DTTT_disabled');
2418 "fnInit": function( nButton, oConfig ) {
2419 $(nButton).addClass('DTTT_disabled');
2421 "fnCellRender": null
2425 "sFieldBoundary": "",
2426 "sFieldSeperator": "\t",
2428 "sAjaxUrl": "/xhr.php",
2430 "sButtonClass": "DTTT_button_text",
2431 "sButtonClassHover": "DTTT_button_text_hover",
2432 "sButtonText": "Ajax button",
2436 "bSelectedOnly": false,
2437 "fnMouseover": null,
2439 "fnClick": function( nButton, oConfig ) {
2440 var sData = this.fnGetTableData(oConfig);
2442 "url": oConfig.sAjaxUrl,
2444 { "name": "tableData", "value": sData }
2446 "success": oConfig.fnAjaxComplete,
2450 "error": function () {
2451 alert( "Error detected when sending table data to server" );
2458 "fnAjaxComplete": function( json ) {
2459 alert( 'Ajax complete' );
2461 "fnCellRender": null
2466 "sButtonClass": "DTTT_nonbutton",
2467 "sButtonClassHover": "",
2468 "sButtonText": "Text button",
2469 "fnMouseover": null,
2476 "fnCellRender": null
2479 "sAction": "collection",
2481 "sButtonClass": "DTTT_button_collection",
2482 "sButtonClassHover": "DTTT_button_collection_hover",
2483 "sButtonText": "Collection",
2484 "fnMouseover": null,
2486 "fnClick": function( nButton, oConfig ) {
2487 this._fnCollectionShow(nButton, oConfig);
2492 "fnCellRender": null
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')
2505 * @namespace TableTools default settings for initialisation
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
2522 * @default TableTools
2524 TableTools.prototype.CLASS = "TableTools";
2528 * TableTools version
2533 TableTools.VERSION = "2.0.2";
2534 TableTools.prototype.VERSION = TableTools.VERSION;
2539 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2541 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2544 * Register a new feature with DataTables
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 : {};
2555 var oTT = new TableTools( oDTSettings.oInstance, oOpts );
2556 TableTools._aInstances.push( oTT );
2558 return oTT.dom.container;
2561 "sFeature": "TableTools"
2566 alert( "Warning: TableTools 2 requires DataTables 1.8.2 or newer - www.datatables.net/download");
2569 })(jQuery, window, document);