/* * File: TableTools.js * Version: 2.1.5 * Description: Tools and buttons for DataTables * Author: Allan Jardine (www.sprymedia.co.uk) * Language: Javascript * License: GPL v2 or BSD 3 point style * Project: DataTables * * Copyright 2009-2013 Allan Jardine, all rights reserved. * * This source file is free software, under either the GPL v2 license or a * BSD style license, available at: * http://datatables.net/license_gpl2 * http://datatables.net/license_bsd */ /* Global scope for TableTools */ var TableTools; (function($, window, document) { /** * TableTools provides flexible buttons and other tools for a DataTables enhanced table * @class TableTools * @constructor * @param {Object} oDT DataTables instance * @param {Object} oOpts TableTools options * @param {String} oOpts.sSwfPath ZeroClipboard SWF path * @param {String} oOpts.sRowSelect Row selection options - 'none', 'single' or 'multi' * @param {Function} oOpts.fnPreRowSelect Callback function just prior to row selection * @param {Function} oOpts.fnRowSelected Callback function just after row selection * @param {Function} oOpts.fnRowDeselected Callback function when row is deselected * @param {Array} oOpts.aButtons List of buttons to be used */ TableTools = function( oDT, oOpts ) { /* Santiy check that we are a new instance */ if ( ! this instanceof TableTools ) { alert( "Warning: TableTools must be initialised with the keyword 'new'" ); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Public class variables * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * @namespace Settings object which contains customisable information for TableTools instance */ this.s = { /** * Store 'this' so the instance can be retrieved from the settings object * @property that * @type object * @default this */ "that": this, /** * DataTables settings objects * @property dt * @type object * @default <i>From the oDT init option</i> */ "dt": oDT.fnSettings(), /** * @namespace Print specific information */ "print": { /** * DataTables draw 'start' point before the printing display was shown * @property saveStart * @type int * @default -1 */ "saveStart": -1, /** * DataTables draw 'length' point before the printing display was shown * @property saveLength * @type int * @default -1 */ "saveLength": -1, /** * Page scrolling point before the printing display was shown so it can be restored * @property saveScroll * @type int * @default -1 */ "saveScroll": -1, /** * Wrapped function to end the print display (to maintain scope) * @property funcEnd * @type Function * @default function () {} */ "funcEnd": function () {} }, /** * A unique ID is assigned to each button in each instance * @property buttonCounter * @type int * @default 0 */ "buttonCounter": 0, /** * @namespace Select rows specific information */ "select": { /** * Select type - can be 'none', 'single' or 'multi' * @property type * @type string * @default "" */ "type": "", /** * Array of nodes which are currently selected * @property selected * @type array * @default [] */ "selected": [], /** * Function to run before the selection can take place. Will cancel the select if the * function returns false * @property preRowSelect * @type Function * @default null */ "preRowSelect": null, /** * Function to run when a row is selected * @property postSelected * @type Function * @default null */ "postSelected": null, /** * Function to run when a row is deselected * @property postDeselected * @type Function * @default null */ "postDeselected": null, /** * Indicate if all rows are selected (needed for server-side processing) * @property all * @type boolean * @default false */ "all": false, /** * Class name to add to selected TR nodes * @property selectedClass * @type String * @default "" */ "selectedClass": "" }, /** * Store of the user input customisation object * @property custom * @type object * @default {} */ "custom": {}, /** * SWF movie path * @property swfPath * @type string * @default "" */ "swfPath": "", /** * Default button set * @property buttonSet * @type array * @default [] */ "buttonSet": [], /** * When there is more than one TableTools instance for a DataTable, there must be a * master which controls events (row selection etc) * @property master * @type boolean * @default false */ "master": false, /** * Tag names that are used for creating collections and buttons * @namesapce */ "tags": {} }; /** * @namespace Common and useful DOM elements for the class instance */ this.dom = { /** * DIV element that is create and all TableTools buttons (and their children) put into * @property container * @type node * @default null */ "container": null, /** * The table node to which TableTools will be applied * @property table * @type node * @default null */ "table": null, /** * @namespace Nodes used for the print display */ "print": { /** * Nodes which have been removed from the display by setting them to display none * @property hidden * @type array * @default [] */ "hidden": [], /** * The information display saying telling the user about the print display * @property message * @type node * @default null */ "message": null }, /** * @namespace Nodes used for a collection display. This contains the currently used collection */ "collection": { /** * The div wrapper containing the buttons in the collection (i.e. the menu) * @property collection * @type node * @default null */ "collection": null, /** * Background display to provide focus and capture events * @property background * @type node * @default null */ "background": null } }; /** * @namespace Name space for the classes that this TableTools instance will use * @extends TableTools.classes */ this.classes = $.extend( true, {}, TableTools.classes ); if ( this.s.dt.bJUI ) { $.extend( true, this.classes, TableTools.classes_themeroller ); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Public class methods * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * Retreieve the settings object from an instance * @method fnSettings * @returns {object} TableTools settings object */ this.fnSettings = function () { return this.s; }; /* Constructor logic */ if ( typeof oOpts == 'undefined' ) { oOpts = {}; } this._fnConstruct( oOpts ); return this; }; TableTools.prototype = { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Public methods * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * Retreieve the settings object from an instance * @returns {array} List of TR nodes which are currently selected * @param {boolean} [filtered=false] Get only selected rows which are * available given the filtering applied to the table. By default * this is false - i.e. all rows, regardless of filtering are selected. */ "fnGetSelected": function ( filtered ) { var out = [], data = this.s.dt.aoData, displayed = this.s.dt.aiDisplay, i, iLen; if ( filtered ) { // Only consider filtered rows for ( i=0, iLen=displayed.length ; i<iLen ; i++ ) { if ( data[ displayed[i] ]._DTTT_selected ) { out.push( data[ displayed[i] ].nTr ); } } } else { // Use all rows for ( i=0, iLen=data.length ; i<iLen ; i++ ) { if ( data[i]._DTTT_selected ) { out.push( data[i].nTr ); } } } return out; }, /** * Get the data source objects/arrays from DataTables for the selected rows (same as * fnGetSelected followed by fnGetData on each row from the table) * @returns {array} Data from the TR nodes which are currently selected */ "fnGetSelectedData": function () { var out = []; var data=this.s.dt.aoData; var i, iLen; for ( i=0, iLen=data.length ; i<iLen ; i++ ) { if ( data[i]._DTTT_selected ) { out.push( this.s.dt.oInstance.fnGetData(i) ); } } return out; }, /** * Check to see if a current row is selected or not * @param {Node} n TR node to check if it is currently selected or not * @returns {Boolean} true if select, false otherwise */ "fnIsSelected": function ( n ) { var pos = this.s.dt.oInstance.fnGetPosition( n ); return (this.s.dt.aoData[pos]._DTTT_selected===true) ? true : false; }, /** * Select all rows in the table * @param {boolean} [filtered=false] Select only rows which are available * given the filtering applied to the table. By default this is false - * i.e. all rows, regardless of filtering are selected. */ "fnSelectAll": function ( filtered ) { var s = this._fnGetMasterSettings(); this._fnRowSelect( (filtered === true) ? s.dt.aiDisplay : s.dt.aoData ); }, /** * Deselect all rows in the table * @param {boolean} [filtered=false] Deselect only rows which are available * given the filtering applied to the table. By default this is false - * i.e. all rows, regardless of filtering are deselected. */ "fnSelectNone": function ( filtered ) { var s = this._fnGetMasterSettings(); this._fnRowDeselect( this.fnGetSelected(filtered) ); }, /** * Select row(s) * @param {node|object|array} n The row(s) to select. Can be a single DOM * TR node, an array of TR nodes or a jQuery object. */ "fnSelect": function ( n ) { if ( this.s.select.type == "single" ) { this.fnSelectNone(); this._fnRowSelect( n ); } else if ( this.s.select.type == "multi" ) { this._fnRowSelect( n ); } }, /** * Deselect row(s) * @param {node|object|array} n The row(s) to deselect. Can be a single DOM * TR node, an array of TR nodes or a jQuery object. */ "fnDeselect": function ( n ) { this._fnRowDeselect( n ); }, /** * Get the title of the document - useful for file names. The title is retrieved from either * the configuration object's 'title' parameter, or the HTML document title * @param {Object} oConfig Button configuration object * @returns {String} Button title */ "fnGetTitle": function( oConfig ) { var sTitle = ""; if ( typeof oConfig.sTitle != 'undefined' && oConfig.sTitle !== "" ) { sTitle = oConfig.sTitle; } else { var anTitle = document.getElementsByTagName('title'); if ( anTitle.length > 0 ) { sTitle = anTitle[0].innerHTML; } } /* Strip characters which the OS will object to - checking for UTF8 support in the scripting * engine */ if ( "\u00A1".toString().length < 4 ) { return sTitle.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, ""); } else { return sTitle.replace(/[^a-zA-Z0-9_\.,\-_ !\(\)]/g, ""); } }, /** * Calculate a unity array with the column width by proportion for a set of columns to be * included for a button. This is particularly useful for PDF creation, where we can use the * column widths calculated by the browser to size the columns in the PDF. * @param {Object} oConfig Button configuration object * @returns {Array} Unity array of column ratios */ "fnCalcColRatios": function ( oConfig ) { var aoCols = this.s.dt.aoColumns, aColumnsInc = this._fnColumnTargets( oConfig.mColumns ), aColWidths = [], iWidth = 0, iTotal = 0, i, iLen; for ( i=0, iLen=aColumnsInc.length ; i<iLen ; i++ ) { if ( aColumnsInc[i] ) { iWidth = aoCols[i].nTh.offsetWidth; iTotal += iWidth; aColWidths.push( iWidth ); } } for ( i=0, iLen=aColWidths.length ; i<iLen ; i++ ) { aColWidths[i] = aColWidths[i] / iTotal; } return aColWidths.join('\t'); }, /** * Get the information contained in a table as a string * @param {Object} oConfig Button configuration object * @returns {String} Table data as a string */ "fnGetTableData": function ( oConfig ) { /* In future this could be used to get data from a plain HTML source as well as DataTables */ if ( this.s.dt ) { return this._fnGetDataTablesData( oConfig ); } }, /** * Pass text to a flash button instance, which will be used on the button's click handler * @param {Object} clip Flash button object * @param {String} text Text to set */ "fnSetText": function ( clip, text ) { this._fnFlashSetText( clip, text ); }, /** * Resize the flash elements of the buttons attached to this TableTools instance - this is * useful for when initialising TableTools when it is hidden (display:none) since sizes can't * be calculated at that time. */ "fnResizeButtons": function () { for ( var cli in ZeroClipboard_TableTools.clients ) { if ( cli ) { var client = ZeroClipboard_TableTools.clients[cli]; if ( typeof client.domElement != 'undefined' && client.domElement.parentNode ) { client.positionElement(); } } } }, /** * Check to see if any of the ZeroClipboard client's attached need to be resized */ "fnResizeRequired": function () { for ( var cli in ZeroClipboard_TableTools.clients ) { if ( cli ) { var client = ZeroClipboard_TableTools.clients[cli]; if ( typeof client.domElement != 'undefined' && client.domElement.parentNode == this.dom.container && client.sized === false ) { return true; } } } return false; }, /** * Programmatically enable or disable the print view * @param {boolean} [bView=true] Show the print view if true or not given. If false, then * terminate the print view and return to normal. * @param {object} [oConfig={}] Configuration for the print view * @param {boolean} [oConfig.bShowAll=false] Show all rows in the table if true * @param {string} [oConfig.sInfo] Information message, displayed as an overlay to the * user to let them know what the print view is. * @param {string} [oConfig.sMessage] HTML string to show at the top of the document - will * be included in the printed document. */ "fnPrint": function ( bView, oConfig ) { if ( oConfig === undefined ) { oConfig = {}; } if ( bView === undefined || bView ) { this._fnPrintStart( oConfig ); } else { this._fnPrintEnd(); } }, /** * Show a message to the end user which is nicely styled * @param {string} message The HTML string to show to the user * @param {int} time The duration the message is to be shown on screen for (mS) */ "fnInfo": function ( message, time ) { var nInfo = document.createElement( "div" ); nInfo.className = this.classes.print.info; nInfo.innerHTML = message; document.body.appendChild( nInfo ); setTimeout( function() { $(nInfo).fadeOut( "normal", function() { document.body.removeChild( nInfo ); } ); }, time ); }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Private methods (they are of course public in JS, but recommended as private) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * Constructor logic * @method _fnConstruct * @param {Object} oOpts Same as TableTools constructor * @returns void * @private */ "_fnConstruct": function ( oOpts ) { var that = this; this._fnCustomiseSettings( oOpts ); /* Container element */ this.dom.container = document.createElement( this.s.tags.container ); this.dom.container.className = this.classes.container; /* Row selection config */ if ( this.s.select.type != 'none' ) { this._fnRowSelectConfig(); } /* Buttons */ this._fnButtonDefinations( this.s.buttonSet, this.dom.container ); /* Destructor */ this.s.dt.aoDestroyCallback.push( { "sName": "TableTools", "fn": function () { $(that.s.dt.nTBody).off( 'click.DTTT_Select', 'tr' ); $(that.dom.container).empty(); } } ); }, /** * Take the user defined settings and the default settings and combine them. * @method _fnCustomiseSettings * @param {Object} oOpts Same as TableTools constructor * @returns void * @private */ "_fnCustomiseSettings": function ( oOpts ) { /* Is this the master control instance or not? */ if ( typeof this.s.dt._TableToolsInit == 'undefined' ) { this.s.master = true; this.s.dt._TableToolsInit = true; } /* We can use the table node from comparisons to group controls */ this.dom.table = this.s.dt.nTable; /* Clone the defaults and then the user options */ this.s.custom = $.extend( {}, TableTools.DEFAULTS, oOpts ); /* Flash file location */ this.s.swfPath = this.s.custom.sSwfPath; if ( typeof ZeroClipboard_TableTools != 'undefined' ) { ZeroClipboard_TableTools.moviePath = this.s.swfPath; } /* Table row selecting */ this.s.select.type = this.s.custom.sRowSelect; this.s.select.preRowSelect = this.s.custom.fnPreRowSelect; this.s.select.postSelected = this.s.custom.fnRowSelected; this.s.select.postDeselected = this.s.custom.fnRowDeselected; // Backwards compatibility - allow the user to specify a custom class in the initialiser if ( this.s.custom.sSelectedClass ) { this.classes.select.row = this.s.custom.sSelectedClass; } this.s.tags = this.s.custom.oTags; /* Button set */ this.s.buttonSet = this.s.custom.aButtons; }, /** * Take the user input arrays and expand them to be fully defined, and then add them to a given * DOM element * @method _fnButtonDefinations * @param {array} buttonSet Set of user defined buttons * @param {node} wrapper Node to add the created buttons to * @returns void * @private */ "_fnButtonDefinations": function ( buttonSet, wrapper ) { var buttonDef; for ( var i=0, iLen=buttonSet.length ; i<iLen ; i++ ) { if ( typeof buttonSet[i] == "string" ) { if ( typeof TableTools.BUTTONS[ buttonSet[i] ] == 'undefined' ) { alert( "TableTools: Warning - unknown button type: "+buttonSet[i] ); continue; } buttonDef = $.extend( {}, TableTools.BUTTONS[ buttonSet[i] ], true ); } else { if ( typeof TableTools.BUTTONS[ buttonSet[i].sExtends ] == 'undefined' ) { alert( "TableTools: Warning - unknown button type: "+buttonSet[i].sExtends ); continue; } var o = $.extend( {}, TableTools.BUTTONS[ buttonSet[i].sExtends ], true ); buttonDef = $.extend( o, buttonSet[i], true ); } wrapper.appendChild( this._fnCreateButton( buttonDef, $(wrapper).hasClass(this.classes.collection.container) ) ); } }, /** * Create and configure a TableTools button * @method _fnCreateButton * @param {Object} oConfig Button configuration object * @returns {Node} Button element * @private */ "_fnCreateButton": function ( oConfig, bCollectionButton ) { var nButton = this._fnButtonBase( oConfig, bCollectionButton ); if ( oConfig.sAction.match(/flash/) ) { this._fnFlashConfig( nButton, oConfig ); } else if ( oConfig.sAction == "text" ) { this._fnTextConfig( nButton, oConfig ); } else if ( oConfig.sAction == "div" ) { this._fnTextConfig( nButton, oConfig ); } else if ( oConfig.sAction == "collection" ) { this._fnTextConfig( nButton, oConfig ); this._fnCollectionConfig( nButton, oConfig ); } return nButton; }, /** * Create the DOM needed for the button and apply some base properties. All buttons start here * @method _fnButtonBase * @param {o} oConfig Button configuration object * @returns {Node} DIV element for the button * @private */ "_fnButtonBase": function ( o, bCollectionButton ) { var sTag, sLiner, sClass; if ( bCollectionButton ) { sTag = o.sTag !== "default" ? o.sTag : this.s.tags.collection.button; sLiner = o.sLinerTag !== "default" ? o.sLiner : this.s.tags.collection.liner; sClass = this.classes.collection.buttons.normal; } else { sTag = o.sTag !== "default" ? o.sTag : this.s.tags.button; sLiner = o.sLinerTag !== "default" ? o.sLiner : this.s.tags.liner; sClass = this.classes.buttons.normal; } var nButton = document.createElement( sTag ), nSpan = document.createElement( sLiner ), masterS = this._fnGetMasterSettings(); nButton.className = sClass+" "+o.sButtonClass; nButton.setAttribute('id', "ToolTables_"+this.s.dt.sInstance+"_"+masterS.buttonCounter ); nButton.appendChild( nSpan ); nSpan.innerHTML = o.sButtonText; masterS.buttonCounter++; return nButton; }, /** * Get the settings object for the master instance. When more than one TableTools instance is * assigned to a DataTable, only one of them can be the 'master' (for the select rows). As such, * we will typically want to interact with that master for global properties. * @method _fnGetMasterSettings * @returns {Object} TableTools settings object * @private */ "_fnGetMasterSettings": function () { if ( this.s.master ) { return this.s; } else { /* Look for the master which has the same DT as this one */ var instances = TableTools._aInstances; for ( var i=0, iLen=instances.length ; i<iLen ; i++ ) { if ( this.dom.table == instances[i].s.dt.nTable ) { return instances[i].s; } } } }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Button collection functions */ /** * Create a collection button, when activated will present a drop down list of other buttons * @param {Node} nButton Button to use for the collection activation * @param {Object} oConfig Button configuration object * @returns void * @private */ "_fnCollectionConfig": function ( nButton, oConfig ) { var nHidden = document.createElement( this.s.tags.collection.container ); nHidden.style.display = "none"; nHidden.className = this.classes.collection.container; oConfig._collection = nHidden; document.body.appendChild( nHidden ); this._fnButtonDefinations( oConfig.aButtons, nHidden ); }, /** * Show a button collection * @param {Node} nButton Button to use for the collection * @param {Object} oConfig Button configuration object * @returns void * @private */ "_fnCollectionShow": function ( nButton, oConfig ) { var that = this, oPos = $(nButton).offset(), nHidden = oConfig._collection, iDivX = oPos.left, iDivY = oPos.top + $(nButton).outerHeight(), iWinHeight = $(window).height(), iDocHeight = $(document).height(), iWinWidth = $(window).width(), iDocWidth = $(document).width(); nHidden.style.position = "absolute"; nHidden.style.left = iDivX+"px"; nHidden.style.top = iDivY+"px"; nHidden.style.display = "block"; $(nHidden).css('opacity',0); var nBackground = document.createElement('div'); nBackground.style.position = "absolute"; nBackground.style.left = "0px"; nBackground.style.top = "0px"; nBackground.style.height = ((iWinHeight>iDocHeight)? iWinHeight : iDocHeight) +"px"; nBackground.style.width = ((iWinWidth>iDocWidth)? iWinWidth : iDocWidth) +"px"; nBackground.className = this.classes.collection.background; $(nBackground).css('opacity',0); document.body.appendChild( nBackground ); document.body.appendChild( nHidden ); /* Visual corrections to try and keep the collection visible */ var iDivWidth = $(nHidden).outerWidth(); var iDivHeight = $(nHidden).outerHeight(); if ( iDivX + iDivWidth > iDocWidth ) { nHidden.style.left = (iDocWidth-iDivWidth)+"px"; } if ( iDivY + iDivHeight > iDocHeight ) { nHidden.style.top = (iDivY-iDivHeight-$(nButton).outerHeight())+"px"; } this.dom.collection.collection = nHidden; this.dom.collection.background = nBackground; /* This results in a very small delay for the end user but it allows the animation to be * much smoother. If you don't want the animation, then the setTimeout can be removed */ setTimeout( function () { $(nHidden).animate({"opacity": 1}, 500); $(nBackground).animate({"opacity": 0.25}, 500); }, 10 ); /* Resize the buttons to the Flash contents fit */ this.fnResizeButtons(); /* Event handler to remove the collection display */ $(nBackground).click( function () { that._fnCollectionHide.call( that, null, null ); } ); }, /** * Hide a button collection * @param {Node} nButton Button to use for the collection * @param {Object} oConfig Button configuration object * @returns void * @private */ "_fnCollectionHide": function ( nButton, oConfig ) { if ( oConfig !== null && oConfig.sExtends == 'collection' ) { return; } if ( this.dom.collection.collection !== null ) { $(this.dom.collection.collection).animate({"opacity": 0}, 500, function (e) { this.style.display = "none"; } ); $(this.dom.collection.background).animate({"opacity": 0}, 500, function (e) { this.parentNode.removeChild( this ); } ); this.dom.collection.collection = null; this.dom.collection.background = null; } }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Row selection functions */ /** * Add event handlers to a table to allow for row selection * @method _fnRowSelectConfig * @returns void * @private */ "_fnRowSelectConfig": function () { if ( this.s.master ) { var that = this, i, iLen, dt = this.s.dt, aoOpenRows = this.s.dt.aoOpenRows; $(dt.nTable).addClass( this.classes.select.table ); $(dt.nTBody).on( 'click.DTTT_Select', 'tr', function(e) { /* Sub-table must be ignored (odd that the selector won't do this with >) */ if ( this.parentNode != dt.nTBody ) { return; } /* Check that we are actually working with a DataTables controlled row */ if ( dt.oInstance.fnGetData(this) === null ) { return; } if ( that.fnIsSelected( this ) ) { that._fnRowDeselect( this, e ); } else if ( that.s.select.type == "single" ) { that.fnSelectNone(); that._fnRowSelect( this, e ); } else if ( that.s.select.type == "multi" ) { that._fnRowSelect( this, e ); } } ); // Bind a listener to the DataTable for when new rows are created. // This allows rows to be visually selected when they should be and // deferred rendering is used. dt.oApi._fnCallbackReg( dt, 'aoRowCreatedCallback', function (tr, data, index) { if ( dt.aoData[index]._DTTT_selected ) { $(tr).addClass( that.classes.select.row ); } }, 'TableTools-SelectAll' ); } }, /** * Select rows * @param {*} src Rows to select - see _fnSelectData for a description of valid inputs * @private */ "_fnRowSelect": function ( src, e ) { var that = this, data = this._fnSelectData( src ), firstTr = data.length===0 ? null : data[0].nTr, anSelected = [], i, len; // Get all the rows that will be selected for ( i=0, len=data.length ; i<len ; i++ ) { if ( data[i].nTr ) { anSelected.push( data[i].nTr ); } } // User defined pre-selection function if ( this.s.select.preRowSelect !== null && !this.s.select.preRowSelect.call(this, e, anSelected, true) ) { return; } // Mark them as selected for ( i=0, len=data.length ; i<len ; i++ ) { data[i]._DTTT_selected = true; if ( data[i].nTr ) { $(data[i].nTr).addClass( that.classes.select.row ); } } // Post-selection function if ( this.s.select.postSelected !== null ) { this.s.select.postSelected.call( this, anSelected ); } TableTools._fnEventDispatch( this, 'select', anSelected, true ); }, /** * Deselect rows * @param {*} src Rows to deselect - see _fnSelectData for a description of valid inputs * @private */ "_fnRowDeselect": function ( src, e ) { var that = this, data = this._fnSelectData( src ), firstTr = data.length===0 ? null : data[0].nTr, anDeselectedTrs = [], i, len; // Get all the rows that will be deselected for ( i=0, len=data.length ; i<len ; i++ ) { if ( data[i].nTr ) { anDeselectedTrs.push( data[i].nTr ); } } // User defined pre-selection function if ( this.s.select.preRowSelect !== null && !this.s.select.preRowSelect.call(this, e, anDeselectedTrs, false) ) { return; } // Mark them as deselected for ( i=0, len=data.length ; i<len ; i++ ) { data[i]._DTTT_selected = false; if ( data[i].nTr ) { $(data[i].nTr).removeClass( that.classes.select.row ); } } // Post-deselection function if ( this.s.select.postDeselected !== null ) { this.s.select.postDeselected.call( this, anDeselectedTrs ); } TableTools._fnEventDispatch( this, 'select', anDeselectedTrs, false ); }, /** * Take a data source for row selection and convert it into aoData points for the DT * @param {*} src Can be a single DOM TR node, an array of TR nodes (including a * a jQuery object), a single aoData point from DataTables, an array of aoData * points or an array of aoData indexes * @returns {array} An array of aoData points */ "_fnSelectData": function ( src ) { var out = [], pos, i, iLen; if ( src.nodeName ) { // Single node pos = this.s.dt.oInstance.fnGetPosition( src ); out.push( this.s.dt.aoData[pos] ); } else if ( typeof src.length !== 'undefined' ) { // jQuery object or an array of nodes, or aoData points for ( i=0, iLen=src.length ; i<iLen ; i++ ) { if ( src[i].nodeName ) { pos = this.s.dt.oInstance.fnGetPosition( src[i] ); out.push( this.s.dt.aoData[pos] ); } else if ( typeof src[i] === 'number' ) { out.push( this.s.dt.aoData[ src[i] ] ); } else { out.push( src[i] ); } } return out; } else { // A single aoData point out.push( src ); } return out; }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Text button functions */ /** * Configure a text based button for interaction events * @method _fnTextConfig * @param {Node} nButton Button element which is being considered * @param {Object} oConfig Button configuration object * @returns void * @private */ "_fnTextConfig": function ( nButton, oConfig ) { var that = this; if ( oConfig.fnInit !== null ) { oConfig.fnInit.call( this, nButton, oConfig ); } if ( oConfig.sToolTip !== "" ) { nButton.title = oConfig.sToolTip; } $(nButton).hover( function () { if ( oConfig.fnMouseover !== null ) { oConfig.fnMouseover.call( this, nButton, oConfig, null ); } }, function () { if ( oConfig.fnMouseout !== null ) { oConfig.fnMouseout.call( this, nButton, oConfig, null ); } } ); if ( oConfig.fnSelect !== null ) { TableTools._fnEventListen( this, 'select', function (n) { oConfig.fnSelect.call( that, nButton, oConfig, n ); } ); } $(nButton).click( function (e) { //e.preventDefault(); if ( oConfig.fnClick !== null ) { oConfig.fnClick.call( that, nButton, oConfig, null, e ); } /* Provide a complete function to match the behaviour of the flash elements */ if ( oConfig.fnComplete !== null ) { oConfig.fnComplete.call( that, nButton, oConfig, null, null ); } that._fnCollectionHide( nButton, oConfig ); } ); }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Flash button functions */ /** * Configure a flash based button for interaction events * @method _fnFlashConfig * @param {Node} nButton Button element which is being considered * @param {o} oConfig Button configuration object * @returns void * @private */ "_fnFlashConfig": function ( nButton, oConfig ) { var that = this; var flash = new ZeroClipboard_TableTools.Client(); if ( oConfig.fnInit !== null ) { oConfig.fnInit.call( this, nButton, oConfig ); } flash.setHandCursor( true ); if ( oConfig.sAction == "flash_save" ) { flash.setAction( 'save' ); flash.setCharSet( (oConfig.sCharSet=="utf16le") ? 'UTF16LE' : 'UTF8' ); flash.setBomInc( oConfig.bBomInc ); flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) ); } else if ( oConfig.sAction == "flash_pdf" ) { flash.setAction( 'pdf' ); flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) ); } else { flash.setAction( 'copy' ); } flash.addEventListener('mouseOver', function(client) { if ( oConfig.fnMouseover !== null ) { oConfig.fnMouseover.call( that, nButton, oConfig, flash ); } } ); flash.addEventListener('mouseOut', function(client) { if ( oConfig.fnMouseout !== null ) { oConfig.fnMouseout.call( that, nButton, oConfig, flash ); } } ); flash.addEventListener('mouseDown', function(client) { if ( oConfig.fnClick !== null ) { oConfig.fnClick.call( that, nButton, oConfig, flash ); } } ); flash.addEventListener('complete', function (client, text) { if ( oConfig.fnComplete !== null ) { oConfig.fnComplete.call( that, nButton, oConfig, flash, text ); } that._fnCollectionHide( nButton, oConfig ); } ); this._fnFlashGlue( flash, nButton, oConfig.sToolTip ); }, /** * Wait until the id is in the DOM before we "glue" the swf. Note that this function will call * itself (using setTimeout) until it completes successfully * @method _fnFlashGlue * @param {Object} clip Zero clipboard object * @param {Node} node node to glue swf to * @param {String} text title of the flash movie * @returns void * @private */ "_fnFlashGlue": function ( flash, node, text ) { var that = this; var id = node.getAttribute('id'); if ( document.getElementById(id) ) { flash.glue( node, text ); } else { setTimeout( function () { that._fnFlashGlue( flash, node, text ); }, 100 ); } }, /** * Set the text for the flash clip to deal with * * This function is required for large information sets. There is a limit on the * amount of data that can be transferred between Javascript and Flash in a single call, so * we use this method to build up the text in Flash by sending over chunks. It is estimated * that the data limit is around 64k, although it is undocumented, and appears to be different * between different flash versions. We chunk at 8KiB. * @method _fnFlashSetText * @param {Object} clip the ZeroClipboard object * @param {String} sData the data to be set * @returns void * @private */ "_fnFlashSetText": function ( clip, sData ) { var asData = this._fnChunkData( sData, 8192 ); clip.clearText(); for ( var i=0, iLen=asData.length ; i<iLen ; i++ ) { clip.appendText( asData[i] ); } }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Data retrieval functions */ /** * Convert the mixed columns variable into a boolean array the same size as the columns, which * indicates which columns we want to include * @method _fnColumnTargets * @param {String|Array} mColumns The columns to be included in data retrieval. If a string * then it can take the value of "visible" or "hidden" (to include all visible or * hidden columns respectively). Or an array of column indexes * @returns {Array} A boolean array the length of the columns of the table, which each value * indicating if the column is to be included or not * @private */ "_fnColumnTargets": function ( mColumns ) { var aColumns = []; var dt = this.s.dt; if ( typeof mColumns == "object" ) { for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ ) { aColumns.push( false ); } for ( i=0, iLen=mColumns.length ; i<iLen ; i++ ) { aColumns[ mColumns[i] ] = true; } } else if ( mColumns == "visible" ) { for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ ) { aColumns.push( dt.aoColumns[i].bVisible ? true : false ); } } else if ( mColumns == "hidden" ) { for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ ) { aColumns.push( dt.aoColumns[i].bVisible ? false : true ); } } else if ( mColumns == "sortable" ) { for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ ) { aColumns.push( dt.aoColumns[i].bSortable ? true : false ); } } else /* all */ { for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ ) { aColumns.push( true ); } } return aColumns; }, /** * New line character(s) depend on the platforms * @method method * @param {Object} oConfig Button configuration object - only interested in oConfig.sNewLine * @returns {String} Newline character */ "_fnNewline": function ( oConfig ) { if ( oConfig.sNewLine == "auto" ) { return navigator.userAgent.match(/Windows/) ? "\r\n" : "\n"; } else { return oConfig.sNewLine; } }, /** * Get data from DataTables' internals and format it for output * @method _fnGetDataTablesData * @param {Object} oConfig Button configuration object * @param {String} oConfig.sFieldBoundary Field boundary for the data cells in the string * @param {String} oConfig.sFieldSeperator Field separator for the data cells * @param {String} oConfig.sNewline New line options * @param {Mixed} oConfig.mColumns Which columns should be included in the output * @param {Boolean} oConfig.bHeader Include the header * @param {Boolean} oConfig.bFooter Include the footer * @param {Boolean} oConfig.bSelectedOnly Include only the selected rows in the output * @returns {String} Concatenated string of data * @private */ "_fnGetDataTablesData": function ( oConfig ) { var i, iLen, j, jLen; var aRow, aData=[], sLoopData='', arr; var dt = this.s.dt, tr, child; var regex = new RegExp(oConfig.sFieldBoundary, "g"); /* Do it here for speed */ var aColumnsInc = this._fnColumnTargets( oConfig.mColumns ); var bSelectedOnly = (typeof oConfig.bSelectedOnly != 'undefined') ? oConfig.bSelectedOnly : false; /* * Header */ if ( oConfig.bHeader ) { aRow = []; for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ ) { if ( aColumnsInc[i] ) { sLoopData = dt.aoColumns[i].sTitle.replace(/\n/g," ").replace( /<.*?>/g, "" ).replace(/^\s+|\s+$/g,""); sLoopData = this._fnHtmlDecode( sLoopData ); aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) ); } } aData.push( aRow.join(oConfig.sFieldSeperator) ); } /* * Body */ var aDataIndex = dt.aiDisplay; var aSelected = this.fnGetSelected(); if ( this.s.select.type !== "none" && bSelectedOnly && aSelected.length !== 0 ) { aDataIndex = []; for ( i=0, iLen=aSelected.length ; i<iLen ; i++ ) { aDataIndex.push( dt.oInstance.fnGetPosition( aSelected[i] ) ); } } for ( j=0, jLen=aDataIndex.length ; j<jLen ; j++ ) { tr = dt.aoData[ aDataIndex[j] ].nTr; aRow = []; /* Columns */ for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ ) { if ( aColumnsInc[i] ) { /* Convert to strings (with small optimisation) */ var mTypeData = dt.oApi._fnGetCellData( dt, aDataIndex[j], i, 'display' ); if ( oConfig.fnCellRender ) { sLoopData = oConfig.fnCellRender( mTypeData, i, tr, aDataIndex[j] )+""; } else if ( typeof mTypeData == "string" ) { /* Strip newlines, replace img tags with alt attr. and finally strip html... */ sLoopData = mTypeData.replace(/\n/g," "); sLoopData = sLoopData.replace(/<img.*?\s+alt\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+)).*?>/gi, '$1$2$3'); sLoopData = sLoopData.replace( /<.*?>/g, "" ); } else { sLoopData = mTypeData+""; } /* Trim and clean the data */ sLoopData = sLoopData.replace(/^\s+/, '').replace(/\s+$/, ''); sLoopData = this._fnHtmlDecode( sLoopData ); /* Bound it and add it to the total data */ aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) ); } } aData.push( aRow.join(oConfig.sFieldSeperator) ); /* Details rows from fnOpen */ if ( oConfig.bOpenRows ) { arr = $.grep(dt.aoOpenRows, function(o) { return o.nParent === tr; }); if ( arr.length === 1 ) { sLoopData = this._fnBoundData( $('td', arr[0].nTr).html(), oConfig.sFieldBoundary, regex ); aData.push( sLoopData ); } } } /* * Footer */ if ( oConfig.bFooter && dt.nTFoot !== null ) { aRow = []; for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ ) { if ( aColumnsInc[i] && dt.aoColumns[i].nTf !== null ) { sLoopData = dt.aoColumns[i].nTf.innerHTML.replace(/\n/g," ").replace( /<.*?>/g, "" ); sLoopData = this._fnHtmlDecode( sLoopData ); aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) ); } } aData.push( aRow.join(oConfig.sFieldSeperator) ); } _sLastData = aData.join( this._fnNewline(oConfig) ); return _sLastData; }, /** * Wrap data up with a boundary string * @method _fnBoundData * @param {String} sData data to bound * @param {String} sBoundary bounding char(s) * @param {RegExp} regex search for the bounding chars - constructed outside for efficiency * in the loop * @returns {String} bound data * @private */ "_fnBoundData": function ( sData, sBoundary, regex ) { if ( sBoundary === "" ) { return sData; } else { return sBoundary + sData.replace(regex, sBoundary+sBoundary) + sBoundary; } }, /** * Break a string up into an array of smaller strings * @method _fnChunkData * @param {String} sData data to be broken up * @param {Int} iSize chunk size * @returns {Array} String array of broken up text * @private */ "_fnChunkData": function ( sData, iSize ) { var asReturn = []; var iStrlen = sData.length; for ( var i=0 ; i<iStrlen ; i+=iSize ) { if ( i+iSize < iStrlen ) { asReturn.push( sData.substring( i, i+iSize ) ); } else { asReturn.push( sData.substring( i, iStrlen ) ); } } return asReturn; }, /** * Decode HTML entities * @method _fnHtmlDecode * @param {String} sData encoded string * @returns {String} decoded string * @private */ "_fnHtmlDecode": function ( sData ) { if ( sData.indexOf('&') === -1 ) { return sData; } var n = document.createElement('div'); return sData.replace( /&([^\s]*);/g, function( match, match2 ) { if ( match.substr(1, 1) === '#' ) { return String.fromCharCode( Number(match2.substr(1)) ); } else { n.innerHTML = match; return n.childNodes[0].nodeValue; } } ); }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Printing functions */ /** * Show print display * @method _fnPrintStart * @param {Event} e Event object * @param {Object} oConfig Button configuration object * @returns void * @private */ "_fnPrintStart": function ( oConfig ) { var that = this; var oSetDT = this.s.dt; /* Parse through the DOM hiding everything that isn't needed for the table */ this._fnPrintHideNodes( oSetDT.nTable ); /* Show the whole table */ this.s.print.saveStart = oSetDT._iDisplayStart; this.s.print.saveLength = oSetDT._iDisplayLength; if ( oConfig.bShowAll ) { oSetDT._iDisplayStart = 0; oSetDT._iDisplayLength = -1; oSetDT.oApi._fnCalculateEnd( oSetDT ); oSetDT.oApi._fnDraw( oSetDT ); } /* Adjust the display for scrolling which might be done by DataTables */ if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" ) { this._fnPrintScrollStart( oSetDT ); // If the table redraws while in print view, the DataTables scrolling // setup would hide the header, so we need to readd it on draw $(this.s.dt.nTable).bind('draw.DTTT_Print', function () { that._fnPrintScrollStart( oSetDT ); } ); } /* Remove the other DataTables feature nodes - but leave the table! and info div */ var anFeature = oSetDT.aanFeatures; for ( var cFeature in anFeature ) { if ( cFeature != 'i' && cFeature != 't' && cFeature.length == 1 ) { for ( var i=0, iLen=anFeature[cFeature].length ; i<iLen ; i++ ) { this.dom.print.hidden.push( { "node": anFeature[cFeature][i], "display": "block" } ); anFeature[cFeature][i].style.display = "none"; } } } /* Print class can be used for styling */ $(document.body).addClass( this.classes.print.body ); /* Show information message to let the user know what is happening */ if ( oConfig.sInfo !== "" ) { this.fnInfo( oConfig.sInfo, 3000 ); } /* Add a message at the top of the page */ if ( oConfig.sMessage ) { this.dom.print.message = document.createElement( "div" ); this.dom.print.message.className = this.classes.print.message; this.dom.print.message.innerHTML = oConfig.sMessage; document.body.insertBefore( this.dom.print.message, document.body.childNodes[0] ); } /* Cache the scrolling and the jump to the top of the page */ this.s.print.saveScroll = $(window).scrollTop(); window.scrollTo( 0, 0 ); /* Bind a key event listener to the document for the escape key - * it is removed in the callback */ $(document).bind( "keydown.DTTT", function(e) { /* Only interested in the escape key */ if ( e.keyCode == 27 ) { e.preventDefault(); that._fnPrintEnd.call( that, e ); } } ); }, /** * Printing is finished, resume normal display * @method _fnPrintEnd * @param {Event} e Event object * @returns void * @private */ "_fnPrintEnd": function ( e ) { var that = this; var oSetDT = this.s.dt; var oSetPrint = this.s.print; var oDomPrint = this.dom.print; /* Show all hidden nodes */ this._fnPrintShowNodes(); /* Restore DataTables' scrolling */ if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" ) { $(this.s.dt.nTable).unbind('draw.DTTT_Print'); this._fnPrintScrollEnd(); } /* Restore the scroll */ window.scrollTo( 0, oSetPrint.saveScroll ); /* Drop the print message */ if ( oDomPrint.message !== null ) { document.body.removeChild( oDomPrint.message ); oDomPrint.message = null; } /* Styling class */ $(document.body).removeClass( 'DTTT_Print' ); /* Restore the table length */ oSetDT._iDisplayStart = oSetPrint.saveStart; oSetDT._iDisplayLength = oSetPrint.saveLength; oSetDT.oApi._fnCalculateEnd( oSetDT ); oSetDT.oApi._fnDraw( oSetDT ); $(document).unbind( "keydown.DTTT" ); }, /** * Take account of scrolling in DataTables by showing the full table * @returns void * @private */ "_fnPrintScrollStart": function () { var oSetDT = this.s.dt, nScrollHeadInner = oSetDT.nScrollHead.getElementsByTagName('div')[0], nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0], nScrollBody = oSetDT.nTable.parentNode; /* Copy the header in the thead in the body table, this way we show one single table when * in print view. Note that this section of code is more or less verbatim from DT 1.7.0 */ var nTheadSize = oSetDT.nTable.getElementsByTagName('thead'); if ( nTheadSize.length > 0 ) { oSetDT.nTable.removeChild( nTheadSize[0] ); } if ( oSetDT.nTFoot !== null ) { var nTfootSize = oSetDT.nTable.getElementsByTagName('tfoot'); if ( nTfootSize.length > 0 ) { oSetDT.nTable.removeChild( nTfootSize[0] ); } } nTheadSize = oSetDT.nTHead.cloneNode(true); oSetDT.nTable.insertBefore( nTheadSize, oSetDT.nTable.childNodes[0] ); if ( oSetDT.nTFoot !== null ) { nTfootSize = oSetDT.nTFoot.cloneNode(true); oSetDT.nTable.insertBefore( nTfootSize, oSetDT.nTable.childNodes[1] ); } /* Now adjust the table's viewport so we can actually see it */ if ( oSetDT.oScroll.sX !== "" ) { oSetDT.nTable.style.width = $(oSetDT.nTable).outerWidth()+"px"; nScrollBody.style.width = $(oSetDT.nTable).outerWidth()+"px"; nScrollBody.style.overflow = "visible"; } if ( oSetDT.oScroll.sY !== "" ) { nScrollBody.style.height = $(oSetDT.nTable).outerHeight()+"px"; nScrollBody.style.overflow = "visible"; } }, /** * Take account of scrolling in DataTables by showing the full table. Note that the redraw of * the DataTable that we do will actually deal with the majority of the hard work here * @returns void * @private */ "_fnPrintScrollEnd": function () { var oSetDT = this.s.dt, nScrollBody = oSetDT.nTable.parentNode; if ( oSetDT.oScroll.sX !== "" ) { nScrollBody.style.width = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sX ); nScrollBody.style.overflow = "auto"; } if ( oSetDT.oScroll.sY !== "" ) { nScrollBody.style.height = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sY ); nScrollBody.style.overflow = "auto"; } }, /** * Resume the display of all TableTools hidden nodes * @method _fnPrintShowNodes * @returns void * @private */ "_fnPrintShowNodes": function ( ) { var anHidden = this.dom.print.hidden; for ( var i=0, iLen=anHidden.length ; i<iLen ; i++ ) { anHidden[i].node.style.display = anHidden[i].display; } anHidden.splice( 0, anHidden.length ); }, /** * Hide nodes which are not needed in order to display the table. Note that this function is * recursive * @method _fnPrintHideNodes * @param {Node} nNode Element which should be showing in a 'print' display * @returns void * @private */ "_fnPrintHideNodes": function ( nNode ) { var anHidden = this.dom.print.hidden; var nParent = nNode.parentNode; var nChildren = nParent.childNodes; for ( var i=0, iLen=nChildren.length ; i<iLen ; i++ ) { if ( nChildren[i] != nNode && nChildren[i].nodeType == 1 ) { /* If our node is shown (don't want to show nodes which were previously hidden) */ var sDisplay = $(nChildren[i]).css("display"); if ( sDisplay != "none" ) { /* Cache the node and it's previous state so we can restore it */ anHidden.push( { "node": nChildren[i], "display": sDisplay } ); nChildren[i].style.display = "none"; } } } if ( nParent.nodeName != "BODY" ) { this._fnPrintHideNodes( nParent ); } } }; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Static variables * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * Store of all instances that have been created of TableTools, so one can look up other (when * there is need of a master) * @property _aInstances * @type Array * @default [] * @private */ TableTools._aInstances = []; /** * Store of all listeners and their callback functions * @property _aListeners * @type Array * @default [] */ TableTools._aListeners = []; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Static methods * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /** * Get an array of all the master instances * @method fnGetMasters * @returns {Array} List of master TableTools instances * @static */ TableTools.fnGetMasters = function () { var a = []; for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ ) { if ( TableTools._aInstances[i].s.master ) { a.push( TableTools._aInstances[i] ); } } return a; }; /** * Get the master instance for a table node (or id if a string is given) * @method fnGetInstance * @returns {Object} ID of table OR table node, for which we want the TableTools instance * @static */ TableTools.fnGetInstance = function ( node ) { if ( typeof node != 'object' ) { node = document.getElementById(node); } for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ ) { if ( TableTools._aInstances[i].s.master && TableTools._aInstances[i].dom.table == node ) { return TableTools._aInstances[i]; } } return null; }; /** * Add a listener for a specific event * @method _fnEventListen * @param {Object} that Scope of the listening function (i.e. 'this' in the caller) * @param {String} type Event type * @param {Function} fn Function * @returns void * @private * @static */ TableTools._fnEventListen = function ( that, type, fn ) { TableTools._aListeners.push( { "that": that, "type": type, "fn": fn } ); }; /** * An event has occurred - look up every listener and fire it off. We check that the event we are * going to fire is attached to the same table (using the table node as reference) before firing * @method _fnEventDispatch * @param {Object} that Scope of the listening function (i.e. 'this' in the caller) * @param {String} type Event type * @param {Node} node Element that the event occurred on (may be null) * @param {boolean} [selected] Indicate if the node was selected (true) or deselected (false) * @returns void * @private * @static */ TableTools._fnEventDispatch = function ( that, type, node, selected ) { var listeners = TableTools._aListeners; for ( var i=0, iLen=listeners.length ; i<iLen ; i++ ) { if ( that.dom.table == listeners[i].that.dom.table && listeners[i].type == type ) { listeners[i].fn( node, selected ); } } }; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Constants * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ TableTools.buttonBase = { // Button base "sAction": "text", "sTag": "default", "sLinerTag": "default", "sButtonClass": "DTTT_button_text", "sButtonText": "Button text", "sTitle": "", "sToolTip": "", // Common button specific options "sCharSet": "utf8", "bBomInc": false, "sFileName": "*.csv", "sFieldBoundary": "", "sFieldSeperator": "\t", "sNewLine": "auto", "mColumns": "all", /* "all", "visible", "hidden" or array of column integers */ "bHeader": true, "bFooter": true, "bOpenRows": false, "bSelectedOnly": false, // Callbacks "fnMouseover": null, "fnMouseout": null, "fnClick": null, "fnSelect": null, "fnComplete": null, "fnInit": null, "fnCellRender": null }; /** * @namespace Default button configurations */ TableTools.BUTTONS = { "csv": $.extend( {}, TableTools.buttonBase, { "sAction": "flash_save", "sButtonClass": "DTTT_button_csv", "sButtonText": "CSV", "sFieldBoundary": '"', "sFieldSeperator": ",", "fnClick": function( nButton, oConfig, flash ) { this.fnSetText( flash, this.fnGetTableData(oConfig) ); } } ), "xls": $.extend( {}, TableTools.buttonBase, { "sAction": "flash_save", "sCharSet": "utf16le", "bBomInc": true, "sButtonClass": "DTTT_button_xls", "sButtonText": "Excel", "fnClick": function( nButton, oConfig, flash ) { this.fnSetText( flash, this.fnGetTableData(oConfig) ); } } ), "copy": $.extend( {}, TableTools.buttonBase, { "sAction": "flash_copy", "sButtonClass": "DTTT_button_copy", "sButtonText": "Copy", "fnClick": function( nButton, oConfig, flash ) { this.fnSetText( flash, this.fnGetTableData(oConfig) ); }, "fnComplete": function(nButton, oConfig, flash, text) { var lines = text.split('\n').length, len = this.s.dt.nTFoot === null ? lines-1 : lines-2, plural = (len==1) ? "" : "s"; this.fnInfo( '<h6>Table copied</h6>'+ '<p>Copied '+len+' row'+plural+' to the clipboard.</p>', 1500 ); } } ), "pdf": $.extend( {}, TableTools.buttonBase, { "sAction": "flash_pdf", "sNewLine": "\n", "sFileName": "*.pdf", "sButtonClass": "DTTT_button_pdf", "sButtonText": "PDF", "sPdfOrientation": "portrait", "sPdfSize": "A4", "sPdfMessage": "", "fnClick": function( nButton, oConfig, flash ) { this.fnSetText( flash, "title:"+ this.fnGetTitle(oConfig) +"\n"+ "message:"+ oConfig.sPdfMessage +"\n"+ "colWidth:"+ this.fnCalcColRatios(oConfig) +"\n"+ "orientation:"+ oConfig.sPdfOrientation +"\n"+ "size:"+ oConfig.sPdfSize +"\n"+ "--/TableToolsOpts--\n" + this.fnGetTableData(oConfig) ); } } ), "print": $.extend( {}, TableTools.buttonBase, { "sInfo": "<h6>Print view</h6><p>Please use your browser's print function to "+ "print this table. Press escape when finished.", "sMessage": null, "bShowAll": true, "sToolTip": "View print view", "sButtonClass": "DTTT_button_print", "sButtonText": "Print", "fnClick": function ( nButton, oConfig ) { this.fnPrint( true, oConfig ); } } ), "text": $.extend( {}, TableTools.buttonBase ), "select": $.extend( {}, TableTools.buttonBase, { "sButtonText": "Select button", "fnSelect": function( nButton, oConfig ) { if ( this.fnGetSelected().length !== 0 ) { $(nButton).removeClass( this.classes.buttons.disabled ); } else { $(nButton).addClass( this.classes.buttons.disabled ); } }, "fnInit": function( nButton, oConfig ) { $(nButton).addClass( this.classes.buttons.disabled ); } } ), "select_single": $.extend( {}, TableTools.buttonBase, { "sButtonText": "Select button", "fnSelect": function( nButton, oConfig ) { var iSelected = this.fnGetSelected().length; if ( iSelected == 1 ) { $(nButton).removeClass( this.classes.buttons.disabled ); } else { $(nButton).addClass( this.classes.buttons.disabled ); } }, "fnInit": function( nButton, oConfig ) { $(nButton).addClass( this.classes.buttons.disabled ); } } ), "select_all": $.extend( {}, TableTools.buttonBase, { "sButtonText": "Select all", "fnClick": function( nButton, oConfig ) { this.fnSelectAll(); }, "fnSelect": function( nButton, oConfig ) { if ( this.fnGetSelected().length == this.s.dt.fnRecordsDisplay() ) { $(nButton).addClass( this.classes.buttons.disabled ); } else { $(nButton).removeClass( this.classes.buttons.disabled ); } } } ), "select_none": $.extend( {}, TableTools.buttonBase, { "sButtonText": "Deselect all", "fnClick": function( nButton, oConfig ) { this.fnSelectNone(); }, "fnSelect": function( nButton, oConfig ) { if ( this.fnGetSelected().length !== 0 ) { $(nButton).removeClass( this.classes.buttons.disabled ); } else { $(nButton).addClass( this.classes.buttons.disabled ); } }, "fnInit": function( nButton, oConfig ) { $(nButton).addClass( this.classes.buttons.disabled ); } } ), "ajax": $.extend( {}, TableTools.buttonBase, { "sAjaxUrl": "/xhr.php", "sButtonText": "Ajax button", "fnClick": function( nButton, oConfig ) { var sData = this.fnGetTableData(oConfig); $.ajax( { "url": oConfig.sAjaxUrl, "data": [ { "name": "tableData", "value": sData } ], "success": oConfig.fnAjaxComplete, "dataType": "json", "type": "POST", "cache": false, "error": function () { alert( "Error detected when sending table data to server" ); } } ); }, "fnAjaxComplete": function( json ) { alert( 'Ajax complete' ); } } ), "div": $.extend( {}, TableTools.buttonBase, { "sAction": "div", "sTag": "div", "sButtonClass": "DTTT_nonbutton", "sButtonText": "Text button" } ), "collection": $.extend( {}, TableTools.buttonBase, { "sAction": "collection", "sButtonClass": "DTTT_button_collection", "sButtonText": "Collection", "fnClick": function( nButton, oConfig ) { this._fnCollectionShow(nButton, oConfig); } } ) }; /* * on* callback parameters: * 1. node - button element * 2. object - configuration object for this button * 3. object - ZeroClipboard reference (flash button only) * 4. string - Returned string from Flash (flash button only - and only on 'complete') */ /** * @namespace Classes used by TableTools - allows the styles to be override easily. * Note that when TableTools initialises it will take a copy of the classes object * and will use its internal copy for the remainder of its run time. */ TableTools.classes = { "container": "DTTT_container", "buttons": { "normal": "DTTT_button", "disabled": "DTTT_disabled" }, "collection": { "container": "DTTT_collection", "background": "DTTT_collection_background", "buttons": { "normal": "DTTT_button", "disabled": "DTTT_disabled" } }, "select": { "table": "DTTT_selectable", "row": "DTTT_selected" }, "print": { "body": "DTTT_Print", "info": "DTTT_print_info", "message": "DTTT_PrintMessage" } }; /** * @namespace ThemeRoller classes - built in for compatibility with DataTables' * bJQueryUI option. */ TableTools.classes_themeroller = { "container": "DTTT_container ui-buttonset ui-buttonset-multi", "buttons": { "normal": "DTTT_button ui-button ui-state-default" }, "collection": { "container": "DTTT_collection ui-buttonset ui-buttonset-multi" } }; /** * @namespace TableTools default settings for initialisation */ TableTools.DEFAULTS = { "sSwfPath": "media/swf/copy_csv_xls_pdf.swf", "sRowSelect": "none", "sSelectedClass": null, "fnPreRowSelect": null, "fnRowSelected": null, "fnRowDeselected": null, "aButtons": [ "copy", "csv", "xls", "pdf", "print" ], "oTags": { "container": "div", "button": "a", // We really want to use buttons here, but Firefox and IE ignore the // click on the Flash element in the button (but not mouse[in|out]). "liner": "span", "collection": { "container": "div", "button": "a", "liner": "span" } } }; /** * Name of this class * @constant CLASS * @type String * @default TableTools */ TableTools.prototype.CLASS = "TableTools"; /** * TableTools version * @constant VERSION * @type String * @default See code */ TableTools.VERSION = "2.1.5"; TableTools.prototype.VERSION = TableTools.VERSION; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Initialisation * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Register a new feature with DataTables */ if ( typeof $.fn.dataTable == "function" && typeof $.fn.dataTableExt.fnVersionCheck == "function" && $.fn.dataTableExt.fnVersionCheck('1.9.0') ) { $.fn.dataTableExt.aoFeatures.push( { "fnInit": function( oDTSettings ) { var oOpts = typeof oDTSettings.oInit.oTableTools != 'undefined' ? oDTSettings.oInit.oTableTools : {}; var oTT = new TableTools( oDTSettings.oInstance, oOpts ); TableTools._aInstances.push( oTT ); return oTT.dom.container; }, "cFeature": "T", "sFeature": "TableTools" } ); } else { alert( "Warning: TableTools 2 requires DataTables 1.9.0 or newer - www.datatables.net/download"); } $.fn.DataTable.TableTools = TableTools; })(jQuery, window, document);