2 * 2009-2014 SpryMedia Ltd - datatables.net/license
5 * Author: Joseph Huckaby - MIT licensed
10 * @description Tools and buttons for DataTables
12 * @file dataTables.tableTools.js
13 * @author SpryMedia Ltd (www.sprymedia.co.uk)
14 * @contact www.sprymedia.co.uk/contact
15 * @copyright Copyright 2009-2014 SpryMedia Ltd.
17 * This source file is free software, available under the following license:
18 * MIT license - http://datatables.net/license/mit
20 * This source file is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
24 * For details please refer to: http://www.datatables.net
28 /* Global scope for TableTools for backwards compatibility.
29 * Will be removed in 2.3
33 (function(window, document, undefined) {
36 var factory = function( $, DataTable ) {
40 //include ZeroClipboard.js
41 /* ZeroClipboard 1.0.4
42 * Author: Joseph Huckaby
45 var ZeroClipboard_TableTools = {
47 version: "1.0.4-TableTools2",
48 clients: {}, // registered upload clients on page, indexed by id
49 moviePath: '', // URL to movie
50 nextId: 1, // ID of next movie
53 // simple DOM lookup utility function
54 if (typeof(thingy) == 'string') {
55 thingy = document.getElementById(thingy);
57 if (!thingy.addClass) {
58 // extend element with a few useful methods
59 thingy.hide = function() { this.style.display = 'none'; };
60 thingy.show = function() { this.style.display = ''; };
61 thingy.addClass = function(name) { this.removeClass(name); this.className += ' ' + name; };
62 thingy.removeClass = function(name) {
63 this.className = this.className.replace( new RegExp("\\s*" + name + "\\s*"), " ").replace(/^\s+/, '').replace(/\s+$/, '');
65 thingy.hasClass = function(name) {
66 return !!this.className.match( new RegExp("\\s*" + name + "\\s*") );
72 setMoviePath: function(path) {
73 // set path to ZeroClipboard.swf
74 this.moviePath = path;
77 dispatch: function(id, eventName, args) {
78 // receive event from flash movie, send to client
79 var client = this.clients[id];
81 client.receiveEvent(eventName, args);
85 register: function(id, client) {
86 // register new client to receive events
87 this.clients[id] = client;
90 getDOMObjectPosition: function(obj) {
91 // get absolute coordinates for dom element
95 width: obj.width ? obj.width : obj.offsetWidth,
96 height: obj.height ? obj.height : obj.offsetHeight
99 if ( obj.style.width !== "" ) {
100 info.width = obj.style.width.replace("px","");
103 if ( obj.style.height !== "" ) {
104 info.height = obj.style.height.replace("px","");
108 info.left += obj.offsetLeft;
109 info.top += obj.offsetTop;
110 obj = obj.offsetParent;
116 Client: function(elem) {
117 // constructor for new simple upload client
121 this.id = ZeroClipboard_TableTools.nextId++;
122 this.movieId = 'ZeroClipboard_TableToolsMovie_' + this.id;
124 // register client with singleton to receive flash events
125 ZeroClipboard_TableTools.register(this.id, this);
134 ZeroClipboard_TableTools.Client.prototype = {
136 id: 0, // unique ID for us
137 ready: false, // whether movie is ready to receive events or not
138 movie: null, // reference to movie object
139 clipText: '', // text to copy to clipboard
140 fileName: '', // default file save name
141 action: 'copy', // action to perform
142 handCursorEnabled: true, // whether to show hand cursor, or default pointer cursor
143 cssEffects: true, // enable CSS mouse effects on dom container
144 handlers: null, // user event handlers
147 glue: function(elem, title) {
148 // glue to DOM element
149 // elem can be ID or actual DOM element object
150 this.domElement = ZeroClipboard_TableTools.$(elem);
152 // float just above object, or zIndex 99 if dom element isn't set
154 if (this.domElement.style.zIndex) {
155 zIndex = parseInt(this.domElement.style.zIndex, 10) + 1;
158 // find X/Y position of domElement
159 var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement);
161 // create floating DIV above element
162 this.div = document.createElement('div');
163 var style = this.div.style;
164 style.position = 'absolute';
167 style.width = (box.width) + 'px';
168 style.height = box.height + 'px';
169 style.zIndex = zIndex;
171 if ( typeof title != "undefined" && title !== "" ) {
172 this.div.title = title;
174 if ( box.width !== 0 && box.height !== 0 ) {
178 // style.backgroundColor = '#f00'; // debug
179 if ( this.domElement ) {
180 this.domElement.appendChild(this.div);
181 this.div.innerHTML = this.getHTML( box.width, box.height ).replace(/&/g, '&');
185 positionElement: function() {
186 var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement);
187 var style = this.div.style;
189 style.position = 'absolute';
190 //style.left = (this.domElement.offsetLeft)+'px';
191 //style.top = this.domElement.offsetTop+'px';
192 style.width = box.width + 'px';
193 style.height = box.height + 'px';
195 if ( box.width !== 0 && box.height !== 0 ) {
201 var flash = this.div.childNodes[0];
202 flash.width = box.width;
203 flash.height = box.height;
206 getHTML: function(width, height) {
207 // return HTML for movie
209 var flashvars = 'id=' + this.id +
213 if (navigator.userAgent.match(/MSIE/)) {
214 // IE gets an OBJECT tag
215 var protocol = location.href.match(/^https/i) ? 'https://' : 'http://';
216 html += '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="'+protocol+'download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=10,0,0,0" width="'+width+'" height="'+height+'" id="'+this.movieId+'" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+ZeroClipboard_TableTools.moviePath+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+flashvars+'"/><param name="wmode" value="transparent"/></object>';
219 // all other browsers get an EMBED tag
220 html += '<embed id="'+this.movieId+'" src="'+ZeroClipboard_TableTools.moviePath+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+width+'" height="'+height+'" name="'+this.movieId+'" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+flashvars+'" wmode="transparent" />';
226 // temporarily hide floater offscreen
228 this.div.style.left = '-2000px';
233 // show ourselves after a call to hide()
237 destroy: function() {
238 // destroy control and floater
239 if (this.domElement && this.div) {
241 this.div.innerHTML = '';
243 var body = document.getElementsByTagName('body')[0];
244 try { body.removeChild( this.div ); } catch(e) {}
246 this.domElement = null;
251 reposition: function(elem) {
252 // reposition our floating div, optionally to new container
253 // warning: container CANNOT change size, only position
255 this.domElement = ZeroClipboard_TableTools.$(elem);
256 if (!this.domElement) {
261 if (this.domElement && this.div) {
262 var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement);
263 var style = this.div.style;
264 style.left = '' + box.left + 'px';
265 style.top = '' + box.top + 'px';
269 clearText: function() {
270 // clear the text to be copy / saved
273 this.movie.clearText();
277 appendText: function(newText) {
278 // append text to that which is to be copied / saved
279 this.clipText += newText;
280 if (this.ready) { this.movie.appendText(newText) ;}
283 setText: function(newText) {
284 // set text to be copied to be copied / saved
285 this.clipText = newText;
286 if (this.ready) { this.movie.setText(newText) ;}
289 setCharSet: function(charSet) {
290 // set the character set (UTF16LE or UTF8)
291 this.charSet = charSet;
292 if (this.ready) { this.movie.setCharSet(charSet) ;}
295 setBomInc: function(bomInc) {
296 // set if the BOM should be included or not
297 this.incBom = bomInc;
298 if (this.ready) { this.movie.setBomInc(bomInc) ;}
301 setFileName: function(newText) {
303 this.fileName = newText;
305 this.movie.setFileName(newText);
309 setAction: function(newText) {
310 // set action (save or copy)
311 this.action = newText;
313 this.movie.setAction(newText);
317 addEventListener: function(eventName, func) {
318 // add user event listener for event
319 // event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel
320 eventName = eventName.toString().toLowerCase().replace(/^on/, '');
321 if (!this.handlers[eventName]) {
322 this.handlers[eventName] = [];
324 this.handlers[eventName].push(func);
327 setHandCursor: function(enabled) {
328 // enable hand cursor (true), or default arrow cursor (false)
329 this.handCursorEnabled = enabled;
331 this.movie.setHandCursor(enabled);
335 setCSSEffects: function(enabled) {
336 // enable or disable CSS effects on DOM container
337 this.cssEffects = !!enabled;
340 receiveEvent: function(eventName, args) {
343 // receive event from flash
344 eventName = eventName.toString().toLowerCase().replace(/^on/, '');
346 // special behavior for certain events
349 // movie claims it is ready, but in IE this isn't always the case...
350 // bug fix: Cannot extend EMBED DOM elements in Firefox, must use traditional function
351 this.movie = document.getElementById(this.movieId);
354 setTimeout( function() { self.receiveEvent('load', null); }, 1 );
358 // firefox on pc needs a "kick" in order to set these in certain cases
359 if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) {
361 setTimeout( function() { self.receiveEvent('load', null); }, 100 );
367 this.movie.clearText();
368 this.movie.appendText( this.clipText );
369 this.movie.setFileName( this.fileName );
370 this.movie.setAction( this.action );
371 this.movie.setCharSet( this.charSet );
372 this.movie.setBomInc( this.incBom );
373 this.movie.setHandCursor( this.handCursorEnabled );
377 if (this.domElement && this.cssEffects) {
378 //this.domElement.addClass('hover');
379 if (this.recoverActive) {
380 this.domElement.addClass('active');
386 if (this.domElement && this.cssEffects) {
387 this.recoverActive = false;
388 if (this.domElement.hasClass('active')) {
389 this.domElement.removeClass('active');
390 this.recoverActive = true;
392 //this.domElement.removeClass('hover');
397 if (this.domElement && this.cssEffects) {
398 this.domElement.addClass('active');
403 if (this.domElement && this.cssEffects) {
404 this.domElement.removeClass('active');
405 this.recoverActive = false;
408 } // switch eventName
410 if (this.handlers[eventName]) {
411 for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) {
412 var func = this.handlers[eventName][idx];
414 if (typeof(func) == 'function') {
415 // actual function reference
418 else if ((typeof(func) == 'object') && (func.length == 2)) {
419 // PHP style object + method, i.e. [myObject, 'myMethod']
420 func[0][ func[1] ](this, args);
422 else if (typeof(func) == 'string') {
424 window[func](this, args);
426 } // foreach event handler defined
427 } // user defined handler for event
432 // For the Flash binding to work, ZeroClipboard_TableTools must be on the global
434 window.ZeroClipboard_TableTools = ZeroClipboard_TableTools;
435 //include TableTools.js
437 * 2009-2014 SpryMedia Ltd - datatables.net/license
440 /*globals TableTools,ZeroClipboard_TableTools*/
443 (function($, window, document) {
446 * TableTools provides flexible buttons and other tools for a DataTables enhanced table
449 * @param {Object} oDT DataTables instance. When using DataTables 1.10 this can
450 * also be a jQuery collection, jQuery selector, table node, DataTables API
451 * instance or DataTables settings object.
452 * @param {Object} oOpts TableTools options
453 * @param {String} oOpts.sSwfPath ZeroClipboard SWF path
454 * @param {String} oOpts.sRowSelect Row selection options - 'none', 'single', 'multi' or 'os'
455 * @param {Function} oOpts.fnPreRowSelect Callback function just prior to row selection
456 * @param {Function} oOpts.fnRowSelected Callback function just after row selection
457 * @param {Function} oOpts.fnRowDeselected Callback function when row is deselected
458 * @param {Array} oOpts.aButtons List of buttons to be used
460 TableTools = function( oDT, oOpts )
462 /* Santiy check that we are a new instance */
463 if ( ! this instanceof TableTools )
465 alert( "Warning: TableTools must be initialised with the keyword 'new'" );
468 // In 1.10 we can use the API to get the settings object from a number of
470 var dtSettings = $.fn.dataTable.Api ?
471 new $.fn.dataTable.Api( oDT ).settings()[0] :
475 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
476 * Public class variables
477 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
480 * @namespace Settings object which contains customisable information for TableTools instance
484 * Store 'this' so the instance can be retrieved from the settings object
492 * DataTables settings objects
495 * @default <i>From the oDT init option</i>
500 * @namespace Print specific information
504 * DataTables draw 'start' point before the printing display was shown
505 * @property saveStart
512 * DataTables draw 'length' point before the printing display was shown
513 * @property saveLength
520 * Page scrolling point before the printing display was shown so it can be restored
521 * @property saveScroll
528 * Wrapped function to end the print display (to maintain scope)
531 * @default function () {}
533 "funcEnd": function () {}
537 * A unique ID is assigned to each button in each instance
538 * @property buttonCounter
545 * @namespace Select rows specific information
549 * Select type - can be 'none', 'single' or 'multi'
557 * Array of nodes which are currently selected
565 * Function to run before the selection can take place. Will cancel the select if the
566 * function returns false
567 * @property preRowSelect
571 "preRowSelect": null,
574 * Function to run when a row is selected
575 * @property postSelected
579 "postSelected": null,
582 * Function to run when a row is deselected
583 * @property postDeselected
587 "postDeselected": null,
590 * Indicate if all rows are selected (needed for server-side processing)
598 * Class name to add to selected TR nodes
599 * @property selectedClass
607 * Store of the user input customisation object
624 * @property buttonSet
631 * When there is more than one TableTools instance for a DataTable, there must be a
632 * master which controls events (row selection etc)
640 * Tag names that are used for creating collections and buttons
648 * @namespace Common and useful DOM elements for the class instance
652 * DIV element that is create and all TableTools buttons (and their children) put into
653 * @property container
660 * The table node to which TableTools will be applied
668 * @namespace Nodes used for the print display
672 * Nodes which have been removed from the display by setting them to display none
680 * The information display saying telling the user about the print display
689 * @namespace Nodes used for a collection display. This contains the currently used collection
693 * The div wrapper containing the buttons in the collection (i.e. the menu)
694 * @property collection
701 * Background display to provide focus and capture events
702 * @property background
711 * @namespace Name space for the classes that this TableTools instance will use
712 * @extends TableTools.classes
714 this.classes = $.extend( true, {}, TableTools.classes );
715 if ( this.s.dt.bJUI )
717 $.extend( true, this.classes, TableTools.classes_themeroller );
721 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
722 * Public class methods
723 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
726 * Retreieve the settings object from an instance
728 * @returns {object} TableTools settings object
730 this.fnSettings = function () {
735 /* Constructor logic */
736 if ( typeof oOpts == 'undefined' )
742 TableTools._aInstances.push( this );
743 this._fnConstruct( oOpts );
750 TableTools.prototype = {
751 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
753 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
756 * Retreieve the settings object from an instance
757 * @returns {array} List of TR nodes which are currently selected
758 * @param {boolean} [filtered=false] Get only selected rows which are
759 * available given the filtering applied to the table. By default
760 * this is false - i.e. all rows, regardless of filtering are
763 "fnGetSelected": function ( filtered )
767 data = this.s.dt.aoData,
768 displayed = this.s.dt.aiDisplay,
773 // Only consider filtered rows
774 for ( i=0, iLen=displayed.length ; i<iLen ; i++ )
776 if ( data[ displayed[i] ]._DTTT_selected )
778 out.push( data[ displayed[i] ].nTr );
785 for ( i=0, iLen=data.length ; i<iLen ; i++ )
787 if ( data[i]._DTTT_selected )
789 out.push( data[i].nTr );
799 * Get the data source objects/arrays from DataTables for the selected rows (same as
800 * fnGetSelected followed by fnGetData on each row from the table)
801 * @returns {array} Data from the TR nodes which are currently selected
803 "fnGetSelectedData": function ()
806 var data=this.s.dt.aoData;
809 for ( i=0, iLen=data.length ; i<iLen ; i++ )
811 if ( data[i]._DTTT_selected )
813 out.push( this.s.dt.oInstance.fnGetData(i) );
822 * Get the indexes of the selected rows
823 * @returns {array} List of row indexes
824 * @param {boolean} [filtered=false] Get only selected rows which are
825 * available given the filtering applied to the table. By default
826 * this is false - i.e. all rows, regardless of filtering are
829 "fnGetSelectedIndexes": function ( filtered )
833 data = this.s.dt.aoData,
834 displayed = this.s.dt.aiDisplay,
839 // Only consider filtered rows
840 for ( i=0, iLen=displayed.length ; i<iLen ; i++ )
842 if ( data[ displayed[i] ]._DTTT_selected )
844 out.push( displayed[i] );
851 for ( i=0, iLen=data.length ; i<iLen ; i++ )
853 if ( data[i]._DTTT_selected )
865 * Check to see if a current row is selected or not
866 * @param {Node} n TR node to check if it is currently selected or not
867 * @returns {Boolean} true if select, false otherwise
869 "fnIsSelected": function ( n )
871 var pos = this.s.dt.oInstance.fnGetPosition( n );
872 return (this.s.dt.aoData[pos]._DTTT_selected===true) ? true : false;
877 * Select all rows in the table
878 * @param {boolean} [filtered=false] Select only rows which are available
879 * given the filtering applied to the table. By default this is false -
880 * i.e. all rows, regardless of filtering are selected.
882 "fnSelectAll": function ( filtered )
884 this._fnRowSelect( filtered ?
885 this.s.dt.aiDisplay :
892 * Deselect all rows in the table
893 * @param {boolean} [filtered=false] Deselect only rows which are available
894 * given the filtering applied to the table. By default this is false -
895 * i.e. all rows, regardless of filtering are deselected.
897 "fnSelectNone": function ( filtered )
899 this._fnRowDeselect( this.fnGetSelectedIndexes(filtered) );
905 * @param {node|object|array} n The row(s) to select. Can be a single DOM
906 * TR node, an array of TR nodes or a jQuery object.
908 "fnSelect": function ( n )
910 if ( this.s.select.type == "single" )
913 this._fnRowSelect( n );
917 this._fnRowSelect( n );
924 * @param {node|object|array} n The row(s) to deselect. Can be a single DOM
925 * TR node, an array of TR nodes or a jQuery object.
927 "fnDeselect": function ( n )
929 this._fnRowDeselect( n );
934 * Get the title of the document - useful for file names. The title is retrieved from either
935 * the configuration object's 'title' parameter, or the HTML document title
936 * @param {Object} oConfig Button configuration object
937 * @returns {String} Button title
939 "fnGetTitle": function( oConfig )
942 if ( typeof oConfig.sTitle != 'undefined' && oConfig.sTitle !== "" ) {
943 sTitle = oConfig.sTitle;
945 var anTitle = document.getElementsByTagName('title');
946 if ( anTitle.length > 0 )
948 sTitle = anTitle[0].innerHTML;
952 /* Strip characters which the OS will object to - checking for UTF8 support in the scripting
955 if ( "\u00A1".toString().length < 4 ) {
956 return sTitle.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
958 return sTitle.replace(/[^a-zA-Z0-9_\.,\-_ !\(\)]/g, "");
964 * Calculate a unity array with the column width by proportion for a set of columns to be
965 * included for a button. This is particularly useful for PDF creation, where we can use the
966 * column widths calculated by the browser to size the columns in the PDF.
967 * @param {Object} oConfig Button configuration object
968 * @returns {Array} Unity array of column ratios
970 "fnCalcColRatios": function ( oConfig )
973 aoCols = this.s.dt.aoColumns,
974 aColumnsInc = this._fnColumnTargets( oConfig.mColumns ),
976 iWidth = 0, iTotal = 0, i, iLen;
978 for ( i=0, iLen=aColumnsInc.length ; i<iLen ; i++ )
980 if ( aColumnsInc[i] )
982 iWidth = aoCols[i].nTh.offsetWidth;
984 aColWidths.push( iWidth );
988 for ( i=0, iLen=aColWidths.length ; i<iLen ; i++ )
990 aColWidths[i] = aColWidths[i] / iTotal;
993 return aColWidths.join('\t');
998 * Get the information contained in a table as a string
999 * @param {Object} oConfig Button configuration object
1000 * @returns {String} Table data as a string
1002 "fnGetTableData": function ( oConfig )
1004 /* In future this could be used to get data from a plain HTML source as well as DataTables */
1007 return this._fnGetDataTablesData( oConfig );
1013 * Pass text to a flash button instance, which will be used on the button's click handler
1014 * @param {Object} clip Flash button object
1015 * @param {String} text Text to set
1017 "fnSetText": function ( clip, text )
1019 this._fnFlashSetText( clip, text );
1024 * Resize the flash elements of the buttons attached to this TableTools instance - this is
1025 * useful for when initialising TableTools when it is hidden (display:none) since sizes can't
1026 * be calculated at that time.
1028 "fnResizeButtons": function ()
1030 for ( var cli in ZeroClipboard_TableTools.clients )
1034 var client = ZeroClipboard_TableTools.clients[cli];
1035 if ( typeof client.domElement != 'undefined' &&
1036 client.domElement.parentNode )
1038 client.positionElement();
1046 * Check to see if any of the ZeroClipboard client's attached need to be resized
1048 "fnResizeRequired": function ()
1050 for ( var cli in ZeroClipboard_TableTools.clients )
1054 var client = ZeroClipboard_TableTools.clients[cli];
1055 if ( typeof client.domElement != 'undefined' &&
1056 client.domElement.parentNode == this.dom.container &&
1057 client.sized === false )
1068 * Programmatically enable or disable the print view
1069 * @param {boolean} [bView=true] Show the print view if true or not given. If false, then
1070 * terminate the print view and return to normal.
1071 * @param {object} [oConfig={}] Configuration for the print view
1072 * @param {boolean} [oConfig.bShowAll=false] Show all rows in the table if true
1073 * @param {string} [oConfig.sInfo] Information message, displayed as an overlay to the
1074 * user to let them know what the print view is.
1075 * @param {string} [oConfig.sMessage] HTML string to show at the top of the document - will
1076 * be included in the printed document.
1078 "fnPrint": function ( bView, oConfig )
1080 if ( oConfig === undefined )
1085 if ( bView === undefined || bView )
1087 this._fnPrintStart( oConfig );
1097 * Show a message to the end user which is nicely styled
1098 * @param {string} message The HTML string to show to the user
1099 * @param {int} time The duration the message is to be shown on screen for (mS)
1101 "fnInfo": function ( message, time ) {
1102 var info = $('<div/>')
1103 .addClass( this.classes.print.info )
1105 .appendTo( 'body' );
1107 setTimeout( function() {
1108 info.fadeOut( "normal", function() {
1117 * Get the container element of the instance for attaching to the DOM
1118 * @returns {node} DOM node
1120 "fnContainer": function () {
1121 return this.dom.container;
1126 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1127 * Private methods (they are of course public in JS, but recommended as private)
1128 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1132 * @method _fnConstruct
1133 * @param {Object} oOpts Same as TableTools constructor
1137 "_fnConstruct": function ( oOpts )
1141 this._fnCustomiseSettings( oOpts );
1143 /* Container element */
1144 this.dom.container = document.createElement( this.s.tags.container );
1145 this.dom.container.className = this.classes.container;
1147 /* Row selection config */
1148 if ( this.s.select.type != 'none' )
1150 this._fnRowSelectConfig();
1154 this._fnButtonDefinations( this.s.buttonSet, this.dom.container );
1157 this.s.dt.aoDestroyCallback.push( {
1158 "sName": "TableTools",
1160 $(that.s.dt.nTBody).off( 'click.DTTT_Select', 'tr' );
1161 $(that.dom.container).empty();
1163 // Remove the instance
1164 var idx = $.inArray( that, TableTools._aInstances );
1166 TableTools._aInstances.splice( idx, 1 );
1174 * Take the user defined settings and the default settings and combine them.
1175 * @method _fnCustomiseSettings
1176 * @param {Object} oOpts Same as TableTools constructor
1180 "_fnCustomiseSettings": function ( oOpts )
1182 /* Is this the master control instance or not? */
1183 if ( typeof this.s.dt._TableToolsInit == 'undefined' )
1185 this.s.master = true;
1186 this.s.dt._TableToolsInit = true;
1189 /* We can use the table node from comparisons to group controls */
1190 this.dom.table = this.s.dt.nTable;
1192 /* Clone the defaults and then the user options */
1193 this.s.custom = $.extend( {}, TableTools.DEFAULTS, oOpts );
1195 /* Flash file location */
1196 this.s.swfPath = this.s.custom.sSwfPath;
1197 if ( typeof ZeroClipboard_TableTools != 'undefined' )
1199 ZeroClipboard_TableTools.moviePath = this.s.swfPath;
1202 /* Table row selecting */
1203 this.s.select.type = this.s.custom.sRowSelect;
1204 this.s.select.preRowSelect = this.s.custom.fnPreRowSelect;
1205 this.s.select.postSelected = this.s.custom.fnRowSelected;
1206 this.s.select.postDeselected = this.s.custom.fnRowDeselected;
1208 // Backwards compatibility - allow the user to specify a custom class in the initialiser
1209 if ( this.s.custom.sSelectedClass )
1211 this.classes.select.row = this.s.custom.sSelectedClass;
1214 this.s.tags = this.s.custom.oTags;
1217 this.s.buttonSet = this.s.custom.aButtons;
1222 * Take the user input arrays and expand them to be fully defined, and then add them to a given
1224 * @method _fnButtonDefinations
1225 * @param {array} buttonSet Set of user defined buttons
1226 * @param {node} wrapper Node to add the created buttons to
1230 "_fnButtonDefinations": function ( buttonSet, wrapper )
1234 for ( var i=0, iLen=buttonSet.length ; i<iLen ; i++ )
1236 if ( typeof buttonSet[i] == "string" )
1238 if ( typeof TableTools.BUTTONS[ buttonSet[i] ] == 'undefined' )
1240 alert( "TableTools: Warning - unknown button type: "+buttonSet[i] );
1243 buttonDef = $.extend( {}, TableTools.BUTTONS[ buttonSet[i] ], true );
1247 if ( typeof TableTools.BUTTONS[ buttonSet[i].sExtends ] == 'undefined' )
1249 alert( "TableTools: Warning - unknown button type: "+buttonSet[i].sExtends );
1252 var o = $.extend( {}, TableTools.BUTTONS[ buttonSet[i].sExtends ], true );
1253 buttonDef = $.extend( o, buttonSet[i], true );
1256 var button = this._fnCreateButton(
1258 $(wrapper).hasClass(this.classes.collection.container)
1262 wrapper.appendChild( button );
1269 * Create and configure a TableTools button
1270 * @method _fnCreateButton
1271 * @param {Object} oConfig Button configuration object
1272 * @returns {Node} Button element
1275 "_fnCreateButton": function ( oConfig, bCollectionButton )
1277 var nButton = this._fnButtonBase( oConfig, bCollectionButton );
1279 if ( oConfig.sAction.match(/flash/) )
1281 if ( ! this._fnHasFlash() ) {
1285 this._fnFlashConfig( nButton, oConfig );
1287 else if ( oConfig.sAction == "text" )
1289 this._fnTextConfig( nButton, oConfig );
1291 else if ( oConfig.sAction == "div" )
1293 this._fnTextConfig( nButton, oConfig );
1295 else if ( oConfig.sAction == "collection" )
1297 this._fnTextConfig( nButton, oConfig );
1298 this._fnCollectionConfig( nButton, oConfig );
1301 if ( this.s.dt.iTabIndex !== -1 ) {
1303 .attr( 'tabindex', this.s.dt.iTabIndex )
1304 .attr( 'aria-controls', this.s.dt.sTableId )
1305 .on( 'keyup.DTTT', function (e) {
1306 // Trigger the click event on return key when focused.
1307 // Note that for Flash buttons this has no effect since we
1308 // can't programmatically trigger the Flash export
1309 if ( e.keyCode === 13 ) {
1310 e.stopPropagation();
1312 $(this).trigger( 'click' );
1315 .on( 'mousedown.DTTT', function (e) {
1316 // On mousedown we want to stop the focus occurring on the
1317 // button, focus is used only for the keyboard navigation.
1318 // But using preventDefault for the flash buttons stops the
1319 // flash action. However, it is not the button that gets
1320 // focused but the flash element for flash buttons, so this
1322 if ( ! oConfig.sAction.match(/flash/) ) {
1333 * Create the DOM needed for the button and apply some base properties. All buttons start here
1334 * @method _fnButtonBase
1335 * @param {o} oConfig Button configuration object
1336 * @returns {Node} DIV element for the button
1339 "_fnButtonBase": function ( o, bCollectionButton )
1341 var sTag, sLiner, sClass;
1343 if ( bCollectionButton )
1345 sTag = o.sTag && o.sTag !== "default" ? o.sTag : this.s.tags.collection.button;
1346 sLiner = o.sLinerTag && o.sLinerTag !== "default" ? o.sLiner : this.s.tags.collection.liner;
1347 sClass = this.classes.collection.buttons.normal;
1351 sTag = o.sTag && o.sTag !== "default" ? o.sTag : this.s.tags.button;
1352 sLiner = o.sLinerTag && o.sLinerTag !== "default" ? o.sLiner : this.s.tags.liner;
1353 sClass = this.classes.buttons.normal;
1357 nButton = document.createElement( sTag ),
1358 nSpan = document.createElement( sLiner ),
1359 masterS = this._fnGetMasterSettings();
1361 nButton.className = sClass+" "+o.sButtonClass;
1362 nButton.setAttribute('id', "ToolTables_"+this.s.dt.sInstance+"_"+masterS.buttonCounter );
1363 nButton.appendChild( nSpan );
1364 nSpan.innerHTML = o.sButtonText;
1366 masterS.buttonCounter++;
1373 * Get the settings object for the master instance. When more than one TableTools instance is
1374 * assigned to a DataTable, only one of them can be the 'master' (for the select rows). As such,
1375 * we will typically want to interact with that master for global properties.
1376 * @method _fnGetMasterSettings
1377 * @returns {Object} TableTools settings object
1380 "_fnGetMasterSettings": function ()
1382 if ( this.s.master )
1388 /* Look for the master which has the same DT as this one */
1389 var instances = TableTools._aInstances;
1390 for ( var i=0, iLen=instances.length ; i<iLen ; i++ )
1392 if ( this.dom.table == instances[i].s.dt.nTable )
1394 return instances[i].s;
1402 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1403 * Button collection functions
1407 * Create a collection button, when activated will present a drop down list of other buttons
1408 * @param {Node} nButton Button to use for the collection activation
1409 * @param {Object} oConfig Button configuration object
1413 "_fnCollectionConfig": function ( nButton, oConfig )
1415 var nHidden = document.createElement( this.s.tags.collection.container );
1416 nHidden.style.display = "none";
1417 nHidden.className = this.classes.collection.container;
1418 oConfig._collection = nHidden;
1419 document.body.appendChild( nHidden );
1421 this._fnButtonDefinations( oConfig.aButtons, nHidden );
1426 * Show a button collection
1427 * @param {Node} nButton Button to use for the collection
1428 * @param {Object} oConfig Button configuration object
1432 "_fnCollectionShow": function ( nButton, oConfig )
1436 oPos = $(nButton).offset(),
1437 nHidden = oConfig._collection,
1439 iDivY = oPos.top + $(nButton).outerHeight(),
1440 iWinHeight = $(window).height(), iDocHeight = $(document).height(),
1441 iWinWidth = $(window).width(), iDocWidth = $(document).width();
1443 nHidden.style.position = "absolute";
1444 nHidden.style.left = iDivX+"px";
1445 nHidden.style.top = iDivY+"px";
1446 nHidden.style.display = "block";
1447 $(nHidden).css('opacity',0);
1449 var nBackground = document.createElement('div');
1450 nBackground.style.position = "absolute";
1451 nBackground.style.left = "0px";
1452 nBackground.style.top = "0px";
1453 nBackground.style.height = ((iWinHeight>iDocHeight)? iWinHeight : iDocHeight) +"px";
1454 nBackground.style.width = ((iWinWidth>iDocWidth)? iWinWidth : iDocWidth) +"px";
1455 nBackground.className = this.classes.collection.background;
1456 $(nBackground).css('opacity',0);
1458 document.body.appendChild( nBackground );
1459 document.body.appendChild( nHidden );
1461 /* Visual corrections to try and keep the collection visible */
1462 var iDivWidth = $(nHidden).outerWidth();
1463 var iDivHeight = $(nHidden).outerHeight();
1465 if ( iDivX + iDivWidth > iDocWidth )
1467 nHidden.style.left = (iDocWidth-iDivWidth)+"px";
1470 if ( iDivY + iDivHeight > iDocHeight )
1472 nHidden.style.top = (iDivY-iDivHeight-$(nButton).outerHeight())+"px";
1475 this.dom.collection.collection = nHidden;
1476 this.dom.collection.background = nBackground;
1478 /* This results in a very small delay for the end user but it allows the animation to be
1479 * much smoother. If you don't want the animation, then the setTimeout can be removed
1481 setTimeout( function () {
1482 $(nHidden).animate({"opacity": 1}, 500);
1483 $(nBackground).animate({"opacity": 0.25}, 500);
1486 /* Resize the buttons to the Flash contents fit */
1487 this.fnResizeButtons();
1489 /* Event handler to remove the collection display */
1490 $(nBackground).click( function () {
1491 that._fnCollectionHide.call( that, null, null );
1497 * Hide a button collection
1498 * @param {Node} nButton Button to use for the collection
1499 * @param {Object} oConfig Button configuration object
1503 "_fnCollectionHide": function ( nButton, oConfig )
1505 if ( oConfig !== null && oConfig.sExtends == 'collection' )
1510 if ( this.dom.collection.collection !== null )
1512 $(this.dom.collection.collection).animate({"opacity": 0}, 500, function (e) {
1513 this.style.display = "none";
1516 $(this.dom.collection.background).animate({"opacity": 0}, 500, function (e) {
1517 this.parentNode.removeChild( this );
1520 this.dom.collection.collection = null;
1521 this.dom.collection.background = null;
1527 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1528 * Row selection functions
1532 * Add event handlers to a table to allow for row selection
1533 * @method _fnRowSelectConfig
1537 "_fnRowSelectConfig": function ()
1539 if ( this.s.master )
1545 aoOpenRows = this.s.dt.aoOpenRows;
1547 $(dt.nTable).addClass( this.classes.select.table );
1549 // When using OS style selection, we want to cancel the shift text
1550 // selection, but only when the shift key is used (so you can
1551 // actually still select text in the table)
1552 if ( this.s.select.type === 'os' ) {
1553 $(dt.nTBody).on( 'mousedown.DTTT_Select', 'tr', function(e) {
1557 .css( '-moz-user-select', 'none' )
1558 .one('selectstart.DTTT_Select', 'tr', function () {
1564 $(dt.nTBody).on( 'mouseup.DTTT_Select', 'tr', function(e) {
1565 $(dt.nTBody).css( '-moz-user-select', '' );
1570 $(dt.nTBody).on( 'click.DTTT_Select', this.s.custom.sRowSelector, function(e) {
1571 var row = this.nodeName.toLowerCase() === 'tr' ?
1573 $(this).parents('tr')[0];
1575 var select = that.s.select;
1576 var pos = that.s.dt.oInstance.fnGetPosition( row );
1578 /* Sub-table must be ignored (odd that the selector won't do this with >) */
1579 if ( row.parentNode != dt.nTBody ) {
1583 /* Check that we are actually working with a DataTables controlled row */
1584 if ( dt.oInstance.fnGetData(row) === null ) {
1588 // Shift click, ctrl click and simple click handling to make
1589 // row selection a lot like a file system in desktop OSs
1590 if ( select.type == 'os' ) {
1591 if ( e.ctrlKey || e.metaKey ) {
1592 // Add or remove from the selection
1593 if ( that.fnIsSelected( row ) ) {
1594 that._fnRowDeselect( row, e );
1597 that._fnRowSelect( row, e );
1600 else if ( e.shiftKey ) {
1601 // Add a range of rows, from the last selected row to
1603 var rowIdxs = that.s.dt.aiDisplay.slice(); // visible rows
1604 var idx1 = $.inArray( select.lastRow, rowIdxs );
1605 var idx2 = $.inArray( pos, rowIdxs );
1607 if ( that.fnGetSelected().length === 0 || idx1 === -1 ) {
1608 // select from top to here - slightly odd, but both
1609 // Windows and Mac OS do this
1610 rowIdxs.splice( $.inArray( pos, rowIdxs )+1, rowIdxs.length );
1613 // reverse so we can shift click 'up' as well as down
1614 if ( idx1 > idx2 ) {
1620 rowIdxs.splice( idx2+1, rowIdxs.length );
1621 rowIdxs.splice( 0, idx1 );
1624 if ( ! that.fnIsSelected( row ) ) {
1626 that._fnRowSelect( rowIdxs, e );
1629 // Deselect range - need to keep the clicked on row selected
1630 rowIdxs.splice( $.inArray( pos, rowIdxs ), 1 );
1631 that._fnRowDeselect( rowIdxs, e );
1635 // No cmd or shift click. Deselect current if selected,
1636 // or select this row only
1637 if ( that.fnIsSelected( row ) && that.fnGetSelected().length === 1 ) {
1638 that._fnRowDeselect( row, e );
1641 that.fnSelectNone();
1642 that._fnRowSelect( row, e );
1646 else if ( that.fnIsSelected( row ) ) {
1647 that._fnRowDeselect( row, e );
1649 else if ( select.type == "single" ) {
1650 that.fnSelectNone();
1651 that._fnRowSelect( row, e );
1653 else if ( select.type == "multi" ) {
1654 that._fnRowSelect( row, e );
1657 select.lastRow = pos;
1658 } );//.on('selectstart', function () { return false; } );
1660 // Bind a listener to the DataTable for when new rows are created.
1661 // This allows rows to be visually selected when they should be and
1662 // deferred rendering is used.
1663 dt.oApi._fnCallbackReg( dt, 'aoRowCreatedCallback', function (tr, data, index) {
1664 if ( dt.aoData[index]._DTTT_selected ) {
1665 $(tr).addClass( that.classes.select.row );
1667 }, 'TableTools-SelectAll' );
1673 * @param {*} src Rows to select - see _fnSelectData for a description of valid inputs
1676 "_fnRowSelect": function ( src, e )
1680 data = this._fnSelectData( src ),
1681 firstTr = data.length===0 ? null : data[0].nTr,
1685 // Get all the rows that will be selected
1686 for ( i=0, len=data.length ; i<len ; i++ )
1690 anSelected.push( data[i].nTr );
1694 // User defined pre-selection function
1695 if ( this.s.select.preRowSelect !== null && !this.s.select.preRowSelect.call(this, e, anSelected, true) )
1700 // Mark them as selected
1701 for ( i=0, len=data.length ; i<len ; i++ )
1703 data[i]._DTTT_selected = true;
1707 $(data[i].nTr).addClass( that.classes.select.row );
1711 // Post-selection function
1712 if ( this.s.select.postSelected !== null )
1714 this.s.select.postSelected.call( this, anSelected );
1717 TableTools._fnEventDispatch( this, 'select', anSelected, true );
1722 * @param {*} src Rows to deselect - see _fnSelectData for a description of valid inputs
1725 "_fnRowDeselect": function ( src, e )
1729 data = this._fnSelectData( src ),
1730 firstTr = data.length===0 ? null : data[0].nTr,
1731 anDeselectedTrs = [],
1734 // Get all the rows that will be deselected
1735 for ( i=0, len=data.length ; i<len ; i++ )
1739 anDeselectedTrs.push( data[i].nTr );
1743 // User defined pre-selection function
1744 if ( this.s.select.preRowSelect !== null && !this.s.select.preRowSelect.call(this, e, anDeselectedTrs, false) )
1749 // Mark them as deselected
1750 for ( i=0, len=data.length ; i<len ; i++ )
1752 data[i]._DTTT_selected = false;
1756 $(data[i].nTr).removeClass( that.classes.select.row );
1760 // Post-deselection function
1761 if ( this.s.select.postDeselected !== null )
1763 this.s.select.postDeselected.call( this, anDeselectedTrs );
1766 TableTools._fnEventDispatch( this, 'select', anDeselectedTrs, false );
1770 * Take a data source for row selection and convert it into aoData points for the DT
1771 * @param {*} src Can be a single DOM TR node, an array of TR nodes (including a
1772 * a jQuery object), a single aoData point from DataTables, an array of aoData
1773 * points or an array of aoData indexes
1774 * @returns {array} An array of aoData points
1776 "_fnSelectData": function ( src )
1778 var out = [], pos, i, iLen;
1783 pos = this.s.dt.oInstance.fnGetPosition( src );
1784 out.push( this.s.dt.aoData[pos] );
1786 else if ( typeof src.length !== 'undefined' )
1788 // jQuery object or an array of nodes, or aoData points
1789 for ( i=0, iLen=src.length ; i<iLen ; i++ )
1791 if ( src[i].nodeName )
1793 pos = this.s.dt.oInstance.fnGetPosition( src[i] );
1794 out.push( this.s.dt.aoData[pos] );
1796 else if ( typeof src[i] === 'number' )
1798 out.push( this.s.dt.aoData[ src[i] ] );
1810 // A single aoData point
1818 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1819 * Text button functions
1823 * Configure a text based button for interaction events
1824 * @method _fnTextConfig
1825 * @param {Node} nButton Button element which is being considered
1826 * @param {Object} oConfig Button configuration object
1830 "_fnTextConfig": function ( nButton, oConfig )
1834 if ( oConfig.fnInit !== null )
1836 oConfig.fnInit.call( this, nButton, oConfig );
1839 if ( oConfig.sToolTip !== "" )
1841 nButton.title = oConfig.sToolTip;
1844 $(nButton).hover( function () {
1845 if ( oConfig.fnMouseover !== null )
1847 oConfig.fnMouseover.call( this, nButton, oConfig, null );
1850 if ( oConfig.fnMouseout !== null )
1852 oConfig.fnMouseout.call( this, nButton, oConfig, null );
1856 if ( oConfig.fnSelect !== null )
1858 TableTools._fnEventListen( this, 'select', function (n) {
1859 oConfig.fnSelect.call( that, nButton, oConfig, n );
1863 $(nButton).click( function (e) {
1864 //e.preventDefault();
1866 if ( oConfig.fnClick !== null )
1868 oConfig.fnClick.call( that, nButton, oConfig, null, e );
1871 /* Provide a complete function to match the behaviour of the flash elements */
1872 if ( oConfig.fnComplete !== null )
1874 oConfig.fnComplete.call( that, nButton, oConfig, null, null );
1877 that._fnCollectionHide( nButton, oConfig );
1883 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1884 * Flash button functions
1888 * Check if the Flash plug-in is available
1889 * @method _fnHasFlash
1890 * @returns {boolean} `true` if Flash available, `false` otherwise
1893 "_fnHasFlash": function ()
1896 var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
1903 navigator.mimeTypes &&
1904 navigator.mimeTypes['application/x-shockwave-flash'] !== undefined &&
1905 navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin
1916 * Configure a flash based button for interaction events
1917 * @method _fnFlashConfig
1918 * @param {Node} nButton Button element which is being considered
1919 * @param {o} oConfig Button configuration object
1923 "_fnFlashConfig": function ( nButton, oConfig )
1926 var flash = new ZeroClipboard_TableTools.Client();
1928 if ( oConfig.fnInit !== null )
1930 oConfig.fnInit.call( this, nButton, oConfig );
1933 flash.setHandCursor( true );
1935 if ( oConfig.sAction == "flash_save" )
1937 flash.setAction( 'save' );
1938 flash.setCharSet( (oConfig.sCharSet=="utf16le") ? 'UTF16LE' : 'UTF8' );
1939 flash.setBomInc( oConfig.bBomInc );
1940 flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1942 else if ( oConfig.sAction == "flash_pdf" )
1944 flash.setAction( 'pdf' );
1945 flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1949 flash.setAction( 'copy' );
1952 flash.addEventListener('mouseOver', function(client) {
1953 if ( oConfig.fnMouseover !== null )
1955 oConfig.fnMouseover.call( that, nButton, oConfig, flash );
1959 flash.addEventListener('mouseOut', function(client) {
1960 if ( oConfig.fnMouseout !== null )
1962 oConfig.fnMouseout.call( that, nButton, oConfig, flash );
1966 flash.addEventListener('mouseDown', function(client) {
1967 if ( oConfig.fnClick !== null )
1969 oConfig.fnClick.call( that, nButton, oConfig, flash );
1973 flash.addEventListener('complete', function (client, text) {
1974 if ( oConfig.fnComplete !== null )
1976 oConfig.fnComplete.call( that, nButton, oConfig, flash, text );
1978 that._fnCollectionHide( nButton, oConfig );
1981 this._fnFlashGlue( flash, nButton, oConfig.sToolTip );
1986 * Wait until the id is in the DOM before we "glue" the swf. Note that this function will call
1987 * itself (using setTimeout) until it completes successfully
1988 * @method _fnFlashGlue
1989 * @param {Object} clip Zero clipboard object
1990 * @param {Node} node node to glue swf to
1991 * @param {String} text title of the flash movie
1995 "_fnFlashGlue": function ( flash, node, text )
1998 var id = node.getAttribute('id');
2000 if ( document.getElementById(id) )
2002 flash.glue( node, text );
2006 setTimeout( function () {
2007 that._fnFlashGlue( flash, node, text );
2014 * Set the text for the flash clip to deal with
2016 * This function is required for large information sets. There is a limit on the
2017 * amount of data that can be transferred between Javascript and Flash in a single call, so
2018 * we use this method to build up the text in Flash by sending over chunks. It is estimated
2019 * that the data limit is around 64k, although it is undocumented, and appears to be different
2020 * between different flash versions. We chunk at 8KiB.
2021 * @method _fnFlashSetText
2022 * @param {Object} clip the ZeroClipboard object
2023 * @param {String} sData the data to be set
2027 "_fnFlashSetText": function ( clip, sData )
2029 var asData = this._fnChunkData( sData, 8192 );
2032 for ( var i=0, iLen=asData.length ; i<iLen ; i++ )
2034 clip.appendText( asData[i] );
2040 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2041 * Data retrieval functions
2045 * Convert the mixed columns variable into a boolean array the same size as the columns, which
2046 * indicates which columns we want to include
2047 * @method _fnColumnTargets
2048 * @param {String|Array} mColumns The columns to be included in data retrieval. If a string
2049 * then it can take the value of "visible" or "hidden" (to include all visible or
2050 * hidden columns respectively). Or an array of column indexes
2051 * @returns {Array} A boolean array the length of the columns of the table, which each value
2052 * indicating if the column is to be included or not
2055 "_fnColumnTargets": function ( mColumns )
2060 var columns = dt.aoColumns;
2061 var columnCount = columns.length;
2063 if ( typeof mColumns == "function" )
2065 var a = mColumns.call( this, dt );
2067 for ( i=0, iLen=columnCount ; i<iLen ; i++ )
2069 aColumns.push( $.inArray( i, a ) !== -1 ? true : false );
2072 else if ( typeof mColumns == "object" )
2074 for ( i=0, iLen=columnCount ; i<iLen ; i++ )
2076 aColumns.push( false );
2079 for ( i=0, iLen=mColumns.length ; i<iLen ; i++ )
2081 aColumns[ mColumns[i] ] = true;
2084 else if ( mColumns == "visible" )
2086 for ( i=0, iLen=columnCount ; i<iLen ; i++ )
2088 aColumns.push( columns[i].bVisible ? true : false );
2091 else if ( mColumns == "hidden" )
2093 for ( i=0, iLen=columnCount ; i<iLen ; i++ )
2095 aColumns.push( columns[i].bVisible ? false : true );
2098 else if ( mColumns == "sortable" )
2100 for ( i=0, iLen=columnCount ; i<iLen ; i++ )
2102 aColumns.push( columns[i].bSortable ? true : false );
2107 for ( i=0, iLen=columnCount ; i<iLen ; i++ )
2109 aColumns.push( true );
2118 * New line character(s) depend on the platforms
2120 * @param {Object} oConfig Button configuration object - only interested in oConfig.sNewLine
2121 * @returns {String} Newline character
2123 "_fnNewline": function ( oConfig )
2125 if ( oConfig.sNewLine == "auto" )
2127 return navigator.userAgent.match(/Windows/) ? "\r\n" : "\n";
2131 return oConfig.sNewLine;
2137 * Get data from DataTables' internals and format it for output
2138 * @method _fnGetDataTablesData
2139 * @param {Object} oConfig Button configuration object
2140 * @param {String} oConfig.sFieldBoundary Field boundary for the data cells in the string
2141 * @param {String} oConfig.sFieldSeperator Field separator for the data cells
2142 * @param {String} oConfig.sNewline New line options
2143 * @param {Mixed} oConfig.mColumns Which columns should be included in the output
2144 * @param {Boolean} oConfig.bHeader Include the header
2145 * @param {Boolean} oConfig.bFooter Include the footer
2146 * @param {Boolean} oConfig.bSelectedOnly Include only the selected rows in the output
2147 * @returns {String} Concatenated string of data
2150 "_fnGetDataTablesData": function ( oConfig )
2152 var i, iLen, j, jLen;
2153 var aRow, aData=[], sLoopData='', arr;
2154 var dt = this.s.dt, tr, child;
2155 var regex = new RegExp(oConfig.sFieldBoundary, "g"); /* Do it here for speed */
2156 var aColumnsInc = this._fnColumnTargets( oConfig.mColumns );
2157 var bSelectedOnly = (typeof oConfig.bSelectedOnly != 'undefined') ? oConfig.bSelectedOnly : false;
2162 if ( oConfig.bHeader )
2166 for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
2168 if ( aColumnsInc[i] )
2170 sLoopData = dt.aoColumns[i].sTitle.replace(/\n/g," ").replace( /<.*?>/g, "" ).replace(/^\s+|\s+$/g,"");
2171 sLoopData = this._fnHtmlDecode( sLoopData );
2173 aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
2177 aData.push( aRow.join(oConfig.sFieldSeperator) );
2180 bSelectedOnly = true;
2186 var aSelected = this.fnGetSelectedIndexes();
2187 bSelectedOnly = this.s.select.type !== "none" && bSelectedOnly && aSelected.length !== 0;
2189 if ( bSelectedOnly ) {
2190 // Use the selected indexes
2191 aDataIndex = aSelected;
2193 else if ( DataTable.Api ) {
2195 aDataIndex = new DataTable.Api( dt )
2196 .rows( oConfig.oSelectorOpts )
2203 aDataIndex = dt.oInstance
2204 .$('tr', oConfig.oSelectorOpts)
2205 .map( function (id, row) {
2206 return dt.oInstance.fnGetPosition( row );
2211 for ( j=0, jLen=aDataIndex.length ; j<jLen ; j++ )
2213 tr = dt.aoData[ aDataIndex[j] ].nTr;
2217 for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
2219 if ( aColumnsInc[i] )
2221 /* Convert to strings (with small optimisation) */
2222 var mTypeData = dt.oApi._fnGetCellData( dt, aDataIndex[j], i, 'display' );
2223 if ( oConfig.fnCellRender )
2225 sLoopData = oConfig.fnCellRender( mTypeData, i, tr, aDataIndex[j] )+"";
2227 else if ( typeof mTypeData == "string" )
2229 /* Strip newlines, replace img tags with alt attr. and finally strip html... */
2230 sLoopData = mTypeData.replace(/\n/g," ");
2232 sLoopData.replace(/<img.*?\s+alt\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+)).*?>/gi,
2234 sLoopData = sLoopData.replace( /<.*?>/g, "" );
2238 sLoopData = mTypeData+"";
2241 /* Trim and clean the data */
2242 sLoopData = sLoopData.replace(/^\s+/, '').replace(/\s+$/, '');
2243 sLoopData = this._fnHtmlDecode( sLoopData );
2245 /* Bound it and add it to the total data */
2246 aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
2250 aData.push( aRow.join(oConfig.sFieldSeperator) );
2252 /* Details rows from fnOpen */
2253 if ( oConfig.bOpenRows )
2255 arr = $.grep(dt.aoOpenRows, function(o) { return o.nParent === tr; });
2257 if ( arr.length === 1 )
2259 sLoopData = this._fnBoundData( $('td', arr[0].nTr).html(), oConfig.sFieldBoundary, regex );
2260 aData.push( sLoopData );
2268 if ( oConfig.bFooter && dt.nTFoot !== null )
2272 for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
2274 if ( aColumnsInc[i] && dt.aoColumns[i].nTf !== null )
2276 sLoopData = dt.aoColumns[i].nTf.innerHTML.replace(/\n/g," ").replace( /<.*?>/g, "" );
2277 sLoopData = this._fnHtmlDecode( sLoopData );
2279 aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
2283 aData.push( aRow.join(oConfig.sFieldSeperator) );
2286 var _sLastData = aData.join( this._fnNewline(oConfig) );
2292 * Wrap data up with a boundary string
2293 * @method _fnBoundData
2294 * @param {String} sData data to bound
2295 * @param {String} sBoundary bounding char(s)
2296 * @param {RegExp} regex search for the bounding chars - constructed outside for efficiency
2298 * @returns {String} bound data
2301 "_fnBoundData": function ( sData, sBoundary, regex )
2303 if ( sBoundary === "" )
2309 return sBoundary + sData.replace(regex, sBoundary+sBoundary) + sBoundary;
2315 * Break a string up into an array of smaller strings
2316 * @method _fnChunkData
2317 * @param {String} sData data to be broken up
2318 * @param {Int} iSize chunk size
2319 * @returns {Array} String array of broken up text
2322 "_fnChunkData": function ( sData, iSize )
2325 var iStrlen = sData.length;
2327 for ( var i=0 ; i<iStrlen ; i+=iSize )
2329 if ( i+iSize < iStrlen )
2331 asReturn.push( sData.substring( i, i+iSize ) );
2335 asReturn.push( sData.substring( i, iStrlen ) );
2344 * Decode HTML entities
2345 * @method _fnHtmlDecode
2346 * @param {String} sData encoded string
2347 * @returns {String} decoded string
2350 "_fnHtmlDecode": function ( sData )
2352 if ( sData.indexOf('&') === -1 )
2357 var n = document.createElement('div');
2359 return sData.replace( /&([^\s]*?);/g, function( match, match2 ) {
2360 if ( match.substr(1, 1) === '#' )
2362 return String.fromCharCode( Number(match2.substr(1)) );
2366 n.innerHTML = match;
2367 return n.childNodes[0].nodeValue;
2374 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2375 * Printing functions
2379 * Show print display
2380 * @method _fnPrintStart
2381 * @param {Event} e Event object
2382 * @param {Object} oConfig Button configuration object
2386 "_fnPrintStart": function ( oConfig )
2389 var oSetDT = this.s.dt;
2391 /* Parse through the DOM hiding everything that isn't needed for the table */
2392 this._fnPrintHideNodes( oSetDT.nTable );
2394 /* Show the whole table */
2395 this.s.print.saveStart = oSetDT._iDisplayStart;
2396 this.s.print.saveLength = oSetDT._iDisplayLength;
2398 if ( oConfig.bShowAll )
2400 oSetDT._iDisplayStart = 0;
2401 oSetDT._iDisplayLength = -1;
2402 if ( oSetDT.oApi._fnCalculateEnd ) {
2403 oSetDT.oApi._fnCalculateEnd( oSetDT );
2405 oSetDT.oApi._fnDraw( oSetDT );
2408 /* Adjust the display for scrolling which might be done by DataTables */
2409 if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
2411 this._fnPrintScrollStart( oSetDT );
2413 // If the table redraws while in print view, the DataTables scrolling
2414 // setup would hide the header, so we need to readd it on draw
2415 $(this.s.dt.nTable).bind('draw.DTTT_Print', function () {
2416 that._fnPrintScrollStart( oSetDT );
2420 /* Remove the other DataTables feature nodes - but leave the table! and info div */
2421 var anFeature = oSetDT.aanFeatures;
2422 for ( var cFeature in anFeature )
2424 if ( cFeature != 'i' && cFeature != 't' && cFeature.length == 1 )
2426 for ( var i=0, iLen=anFeature[cFeature].length ; i<iLen ; i++ )
2428 this.dom.print.hidden.push( {
2429 "node": anFeature[cFeature][i],
2432 anFeature[cFeature][i].style.display = "none";
2437 /* Print class can be used for styling */
2438 $(document.body).addClass( this.classes.print.body );
2440 /* Show information message to let the user know what is happening */
2441 if ( oConfig.sInfo !== "" )
2443 this.fnInfo( oConfig.sInfo, 3000 );
2446 /* Add a message at the top of the page */
2447 if ( oConfig.sMessage )
2450 .addClass( this.classes.print.message )
2451 .html( oConfig.sMessage )
2452 .prependTo( 'body' );
2455 /* Cache the scrolling and the jump to the top of the page */
2456 this.s.print.saveScroll = $(window).scrollTop();
2457 window.scrollTo( 0, 0 );
2459 /* Bind a key event listener to the document for the escape key -
2460 * it is removed in the callback
2462 $(document).bind( "keydown.DTTT", function(e) {
2463 /* Only interested in the escape key */
2464 if ( e.keyCode == 27 )
2467 that._fnPrintEnd.call( that, e );
2474 * Printing is finished, resume normal display
2475 * @method _fnPrintEnd
2476 * @param {Event} e Event object
2480 "_fnPrintEnd": function ( e )
2483 var oSetDT = this.s.dt;
2484 var oSetPrint = this.s.print;
2485 var oDomPrint = this.dom.print;
2487 /* Show all hidden nodes */
2488 this._fnPrintShowNodes();
2490 /* Restore DataTables' scrolling */
2491 if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
2493 $(this.s.dt.nTable).unbind('draw.DTTT_Print');
2495 this._fnPrintScrollEnd();
2498 /* Restore the scroll */
2499 window.scrollTo( 0, oSetPrint.saveScroll );
2501 /* Drop the print message */
2502 $('div.'+this.classes.print.message).remove();
2505 $(document.body).removeClass( 'DTTT_Print' );
2507 /* Restore the table length */
2508 oSetDT._iDisplayStart = oSetPrint.saveStart;
2509 oSetDT._iDisplayLength = oSetPrint.saveLength;
2510 if ( oSetDT.oApi._fnCalculateEnd ) {
2511 oSetDT.oApi._fnCalculateEnd( oSetDT );
2513 oSetDT.oApi._fnDraw( oSetDT );
2515 $(document).unbind( "keydown.DTTT" );
2520 * Take account of scrolling in DataTables by showing the full table
2524 "_fnPrintScrollStart": function ()
2528 nScrollHeadInner = oSetDT.nScrollHead.getElementsByTagName('div')[0],
2529 nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
2530 nScrollBody = oSetDT.nTable.parentNode,
2531 nTheadSize, nTfootSize;
2533 /* Copy the header in the thead in the body table, this way we show one single table when
2534 * in print view. Note that this section of code is more or less verbatim from DT 1.7.0
2536 nTheadSize = oSetDT.nTable.getElementsByTagName('thead');
2537 if ( nTheadSize.length > 0 )
2539 oSetDT.nTable.removeChild( nTheadSize[0] );
2542 if ( oSetDT.nTFoot !== null )
2544 nTfootSize = oSetDT.nTable.getElementsByTagName('tfoot');
2545 if ( nTfootSize.length > 0 )
2547 oSetDT.nTable.removeChild( nTfootSize[0] );
2551 nTheadSize = oSetDT.nTHead.cloneNode(true);
2552 oSetDT.nTable.insertBefore( nTheadSize, oSetDT.nTable.childNodes[0] );
2554 if ( oSetDT.nTFoot !== null )
2556 nTfootSize = oSetDT.nTFoot.cloneNode(true);
2557 oSetDT.nTable.insertBefore( nTfootSize, oSetDT.nTable.childNodes[1] );
2560 /* Now adjust the table's viewport so we can actually see it */
2561 if ( oSetDT.oScroll.sX !== "" )
2563 oSetDT.nTable.style.width = $(oSetDT.nTable).outerWidth()+"px";
2564 nScrollBody.style.width = $(oSetDT.nTable).outerWidth()+"px";
2565 nScrollBody.style.overflow = "visible";
2568 if ( oSetDT.oScroll.sY !== "" )
2570 nScrollBody.style.height = $(oSetDT.nTable).outerHeight()+"px";
2571 nScrollBody.style.overflow = "visible";
2577 * Take account of scrolling in DataTables by showing the full table. Note that the redraw of
2578 * the DataTable that we do will actually deal with the majority of the hard work here
2582 "_fnPrintScrollEnd": function ()
2586 nScrollBody = oSetDT.nTable.parentNode;
2588 if ( oSetDT.oScroll.sX !== "" )
2590 nScrollBody.style.width = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sX );
2591 nScrollBody.style.overflow = "auto";
2594 if ( oSetDT.oScroll.sY !== "" )
2596 nScrollBody.style.height = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sY );
2597 nScrollBody.style.overflow = "auto";
2603 * Resume the display of all TableTools hidden nodes
2604 * @method _fnPrintShowNodes
2608 "_fnPrintShowNodes": function ( )
2610 var anHidden = this.dom.print.hidden;
2612 for ( var i=0, iLen=anHidden.length ; i<iLen ; i++ )
2614 anHidden[i].node.style.display = anHidden[i].display;
2616 anHidden.splice( 0, anHidden.length );
2621 * Hide nodes which are not needed in order to display the table. Note that this function is
2623 * @method _fnPrintHideNodes
2624 * @param {Node} nNode Element which should be showing in a 'print' display
2628 "_fnPrintHideNodes": function ( nNode )
2630 var anHidden = this.dom.print.hidden;
2632 var nParent = nNode.parentNode;
2633 var nChildren = nParent.childNodes;
2634 for ( var i=0, iLen=nChildren.length ; i<iLen ; i++ )
2636 if ( nChildren[i] != nNode && nChildren[i].nodeType == 1 )
2638 /* If our node is shown (don't want to show nodes which were previously hidden) */
2639 var sDisplay = $(nChildren[i]).css("display");
2640 if ( sDisplay != "none" )
2642 /* Cache the node and it's previous state so we can restore it */
2644 "node": nChildren[i],
2647 nChildren[i].style.display = "none";
2652 if ( nParent.nodeName.toUpperCase() != "BODY" )
2654 this._fnPrintHideNodes( nParent );
2661 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2663 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2666 * Store of all instances that have been created of TableTools, so one can look up other (when
2667 * there is need of a master)
2668 * @property _aInstances
2673 TableTools._aInstances = [];
2677 * Store of all listeners and their callback functions
2678 * @property _aListeners
2682 TableTools._aListeners = [];
2686 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2688 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2691 * Get an array of all the master instances
2692 * @method fnGetMasters
2693 * @returns {Array} List of master TableTools instances
2696 TableTools.fnGetMasters = function ()
2699 for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2701 if ( TableTools._aInstances[i].s.master )
2703 a.push( TableTools._aInstances[i] );
2710 * Get the master instance for a table node (or id if a string is given)
2711 * @method fnGetInstance
2712 * @returns {Object} ID of table OR table node, for which we want the TableTools instance
2715 TableTools.fnGetInstance = function ( node )
2717 if ( typeof node != 'object' )
2719 node = document.getElementById(node);
2722 for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2724 if ( TableTools._aInstances[i].s.master && TableTools._aInstances[i].dom.table == node )
2726 return TableTools._aInstances[i];
2734 * Add a listener for a specific event
2735 * @method _fnEventListen
2736 * @param {Object} that Scope of the listening function (i.e. 'this' in the caller)
2737 * @param {String} type Event type
2738 * @param {Function} fn Function
2743 TableTools._fnEventListen = function ( that, type, fn )
2745 TableTools._aListeners.push( {
2754 * An event has occurred - look up every listener and fire it off. We check that the event we are
2755 * going to fire is attached to the same table (using the table node as reference) before firing
2756 * @method _fnEventDispatch
2757 * @param {Object} that Scope of the listening function (i.e. 'this' in the caller)
2758 * @param {String} type Event type
2759 * @param {Node} node Element that the event occurred on (may be null)
2760 * @param {boolean} [selected] Indicate if the node was selected (true) or deselected (false)
2765 TableTools._fnEventDispatch = function ( that, type, node, selected )
2767 var listeners = TableTools._aListeners;
2768 for ( var i=0, iLen=listeners.length ; i<iLen ; i++ )
2770 if ( that.dom.table == listeners[i].that.dom.table && listeners[i].type == type )
2772 listeners[i].fn( node, selected );
2782 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2784 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2788 TableTools.buttonBase = {
2792 "sLinerTag": "default",
2793 "sButtonClass": "DTTT_button_text",
2794 "sButtonText": "Button text",
2798 // Common button specific options
2801 "sFileName": "*.csv",
2802 "sFieldBoundary": "",
2803 "sFieldSeperator": "\t",
2805 "mColumns": "all", /* "all", "visible", "hidden" or array of column integers */
2809 "bSelectedOnly": false,
2810 "oSelectorOpts": undefined, // See http://datatables.net/docs/DataTables/1.9.4/#$ for full options
2813 "fnMouseover": null,
2819 "fnCellRender": null
2824 * @namespace Default button configurations
2826 TableTools.BUTTONS = {
2827 "csv": $.extend( {}, TableTools.buttonBase, {
2828 "sAction": "flash_save",
2829 "sButtonClass": "DTTT_button_csv",
2830 "sButtonText": "CSV",
2831 "sFieldBoundary": '"',
2832 "sFieldSeperator": ",",
2833 "fnClick": function( nButton, oConfig, flash ) {
2834 this.fnSetText( flash, this.fnGetTableData(oConfig) );
2838 "xls": $.extend( {}, TableTools.buttonBase, {
2839 "sAction": "flash_save",
2840 "sCharSet": "utf16le",
2842 "sButtonClass": "DTTT_button_xls",
2843 "sButtonText": "Excel",
2844 "fnClick": function( nButton, oConfig, flash ) {
2845 this.fnSetText( flash, this.fnGetTableData(oConfig) );
2849 "copy": $.extend( {}, TableTools.buttonBase, {
2850 "sAction": "flash_copy",
2851 "sButtonClass": "DTTT_button_copy",
2852 "sButtonText": "Copy",
2853 "fnClick": function( nButton, oConfig, flash ) {
2854 this.fnSetText( flash, this.fnGetTableData(oConfig) );
2856 "fnComplete": function(nButton, oConfig, flash, text) {
2857 var lines = text.split('\n').length;
2858 if (oConfig.bHeader) lines--;
2859 if (this.s.dt.nTFoot !== null && oConfig.bFooter) lines--;
2860 var plural = (lines==1) ? "" : "s";
2861 this.fnInfo( '<h6>Table copied</h6>'+
2862 '<p>Copied '+lines+' row'+plural+' to the clipboard.</p>',
2868 "pdf": $.extend( {}, TableTools.buttonBase, {
2869 "sAction": "flash_pdf",
2871 "sFileName": "*.pdf",
2872 "sButtonClass": "DTTT_button_pdf",
2873 "sButtonText": "PDF",
2874 "sPdfOrientation": "portrait",
2877 "fnClick": function( nButton, oConfig, flash ) {
2878 this.fnSetText( flash,
2879 "title:"+ this.fnGetTitle(oConfig) +"\n"+
2880 "message:"+ oConfig.sPdfMessage +"\n"+
2881 "colWidth:"+ this.fnCalcColRatios(oConfig) +"\n"+
2882 "orientation:"+ oConfig.sPdfOrientation +"\n"+
2883 "size:"+ oConfig.sPdfSize +"\n"+
2884 "--/TableToolsOpts--\n" +
2885 this.fnGetTableData(oConfig)
2890 "print": $.extend( {}, TableTools.buttonBase, {
2891 "sInfo": "<h6>Print view</h6><p>Please use your browser's print function to "+
2892 "print this table. Press escape when finished.</p>",
2895 "sToolTip": "View print view",
2896 "sButtonClass": "DTTT_button_print",
2897 "sButtonText": "Print",
2898 "fnClick": function ( nButton, oConfig ) {
2899 this.fnPrint( true, oConfig );
2903 "text": $.extend( {}, TableTools.buttonBase ),
2905 "select": $.extend( {}, TableTools.buttonBase, {
2906 "sButtonText": "Select button",
2907 "fnSelect": function( nButton, oConfig ) {
2908 if ( this.fnGetSelected().length !== 0 ) {
2909 $(nButton).removeClass( this.classes.buttons.disabled );
2911 $(nButton).addClass( this.classes.buttons.disabled );
2914 "fnInit": function( nButton, oConfig ) {
2915 $(nButton).addClass( this.classes.buttons.disabled );
2919 "select_single": $.extend( {}, TableTools.buttonBase, {
2920 "sButtonText": "Select button",
2921 "fnSelect": function( nButton, oConfig ) {
2922 var iSelected = this.fnGetSelected().length;
2923 if ( iSelected == 1 ) {
2924 $(nButton).removeClass( this.classes.buttons.disabled );
2926 $(nButton).addClass( this.classes.buttons.disabled );
2929 "fnInit": function( nButton, oConfig ) {
2930 $(nButton).addClass( this.classes.buttons.disabled );
2934 "select_all": $.extend( {}, TableTools.buttonBase, {
2935 "sButtonText": "Select all",
2936 "fnClick": function( nButton, oConfig ) {
2939 "fnSelect": function( nButton, oConfig ) {
2940 if ( this.fnGetSelected().length == this.s.dt.fnRecordsDisplay() ) {
2941 $(nButton).addClass( this.classes.buttons.disabled );
2943 $(nButton).removeClass( this.classes.buttons.disabled );
2948 "select_none": $.extend( {}, TableTools.buttonBase, {
2949 "sButtonText": "Deselect all",
2950 "fnClick": function( nButton, oConfig ) {
2951 this.fnSelectNone();
2953 "fnSelect": function( nButton, oConfig ) {
2954 if ( this.fnGetSelected().length !== 0 ) {
2955 $(nButton).removeClass( this.classes.buttons.disabled );
2957 $(nButton).addClass( this.classes.buttons.disabled );
2960 "fnInit": function( nButton, oConfig ) {
2961 $(nButton).addClass( this.classes.buttons.disabled );
2965 "ajax": $.extend( {}, TableTools.buttonBase, {
2966 "sAjaxUrl": "/xhr.php",
2967 "sButtonText": "Ajax button",
2968 "fnClick": function( nButton, oConfig ) {
2969 var sData = this.fnGetTableData(oConfig);
2971 "url": oConfig.sAjaxUrl,
2973 { "name": "tableData", "value": sData }
2975 "success": oConfig.fnAjaxComplete,
2979 "error": function () {
2980 alert( "Error detected when sending table data to server" );
2984 "fnAjaxComplete": function( json ) {
2985 alert( 'Ajax complete' );
2989 "div": $.extend( {}, TableTools.buttonBase, {
2992 "sButtonClass": "DTTT_nonbutton",
2993 "sButtonText": "Text button"
2996 "collection": $.extend( {}, TableTools.buttonBase, {
2997 "sAction": "collection",
2998 "sButtonClass": "DTTT_button_collection",
2999 "sButtonText": "Collection",
3000 "fnClick": function( nButton, oConfig ) {
3001 this._fnCollectionShow(nButton, oConfig);
3006 * on* callback parameters:
3007 * 1. node - button element
3008 * 2. object - configuration object for this button
3009 * 3. object - ZeroClipboard reference (flash button only)
3010 * 4. string - Returned string from Flash (flash button only - and only on 'complete')
3013 // Alias to match the other plug-ins styling
3014 TableTools.buttons = TableTools.BUTTONS;
3018 * @namespace Classes used by TableTools - allows the styles to be override easily.
3019 * Note that when TableTools initialises it will take a copy of the classes object
3020 * and will use its internal copy for the remainder of its run time.
3022 TableTools.classes = {
3023 "container": "DTTT_container",
3025 "normal": "DTTT_button",
3026 "disabled": "DTTT_disabled"
3029 "container": "DTTT_collection",
3030 "background": "DTTT_collection_background",
3032 "normal": "DTTT_button",
3033 "disabled": "DTTT_disabled"
3037 "table": "DTTT_selectable",
3038 "row": "DTTT_selected selected"
3041 "body": "DTTT_Print",
3042 "info": "DTTT_print_info",
3043 "message": "DTTT_PrintMessage"
3049 * @namespace ThemeRoller classes - built in for compatibility with DataTables'
3052 TableTools.classes_themeroller = {
3053 "container": "DTTT_container ui-buttonset ui-buttonset-multi",
3055 "normal": "DTTT_button ui-button ui-state-default"
3058 "container": "DTTT_collection ui-buttonset ui-buttonset-multi"
3064 * @namespace TableTools default settings for initialisation
3066 TableTools.DEFAULTS = {
3067 "sSwfPath": "../swf/copy_csv_xls_pdf.swf",
3068 "sRowSelect": "none",
3069 "sRowSelector": "tr",
3070 "sSelectedClass": null,
3071 "fnPreRowSelect": null,
3072 "fnRowSelected": null,
3073 "fnRowDeselected": null,
3074 "aButtons": [ "copy", "csv", "xls", "pdf", "print" ],
3077 "button": "a", // We really want to use buttons here, but Firefox and IE ignore the
3078 // click on the Flash element in the button (but not mouse[in|out]).
3088 // Alias to match the other plug-ins
3089 TableTools.defaults = TableTools.DEFAULTS;
3093 * Name of this class
3096 * @default TableTools
3098 TableTools.prototype.CLASS = "TableTools";
3102 * TableTools version
3107 TableTools.version = "2.2.3";
3111 // DataTables 1.10 API
3113 // This will be extended in a big way in in TableTools 3 to provide API methods
3114 // such as rows().select() and rows.selected() etc, but for the moment the
3115 // tabletools() method simply returns the instance.
3117 if ( $.fn.dataTable.Api ) {
3118 $.fn.dataTable.Api.register( 'tabletools()', function () {
3121 if ( this.context.length > 0 ) {
3122 tt = TableTools.fnGetInstance( this.context[0].nTable );
3132 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3134 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3137 * Register a new feature with DataTables
3139 if ( typeof $.fn.dataTable == "function" &&
3140 typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
3141 $.fn.dataTableExt.fnVersionCheck('1.9.0') )
3143 $.fn.dataTableExt.aoFeatures.push( {
3144 "fnInit": function( oDTSettings ) {
3145 var init = oDTSettings.oInit;
3147 init.tableTools || init.oTableTools || {} :
3150 return new TableTools( oDTSettings.oInstance, opts ).dom.container;
3153 "sFeature": "TableTools"
3158 alert( "Warning: TableTools requires DataTables 1.9.0 or newer - www.datatables.net/download");
3161 $.fn.DataTable.TableTools = TableTools;
3163 })(jQuery, window, document);
3166 * Register a new feature with DataTables
3168 if ( typeof $.fn.dataTable == "function" &&
3169 typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
3170 $.fn.dataTableExt.fnVersionCheck('1.9.0') )
3172 $.fn.dataTableExt.aoFeatures.push( {
3173 "fnInit": function( oDTSettings ) {
3174 var oOpts = typeof oDTSettings.oInit.oTableTools != 'undefined' ?
3175 oDTSettings.oInit.oTableTools : {};
3177 var oTT = new TableTools( oDTSettings.oInstance, oOpts );
3178 TableTools._aInstances.push( oTT );
3180 return oTT.dom.container;
3183 "sFeature": "TableTools"
3188 alert( "Warning: TableTools 2 requires DataTables 1.9.0 or newer - www.datatables.net/download");
3192 $.fn.dataTable.TableTools = TableTools;
3193 $.fn.DataTable.TableTools = TableTools;
3200 // Define as an AMD module if possible
3201 if ( typeof define === 'function' && define.amd ) {
3202 define( ['jquery', 'datatables'], factory );
3204 else if ( typeof exports === 'object' ) {
3206 factory( require('jquery'), require('datatables') );
3208 else if ( jQuery && !jQuery.fn.dataTable.TableTools ) {
3209 // Otherwise simply initialise as normal, stopping multiple evaluation
3210 factory( jQuery, jQuery.fn.dataTable );
3214 })(window, document);