UNPKG

admin-lte

Version:
1,896 lines (1,624 loc) 84.6 kB
/*! TableTools 2.2.4 * 2009-2015 SpryMedia Ltd - datatables.net/license * * ZeroClipboard 1.0.4 * Author: Joseph Huckaby - MIT licensed */ /** * @summary TableTools * @description Tools and buttons for DataTables * @version 2.2.4 * @file dataTables.tableTools.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact * @copyright Copyright 2009-2015 SpryMedia Ltd. * * This source file is free software, available under the following license: * MIT license - http://datatables.net/license/mit * * This source file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. * * For details please refer to: http://www.datatables.net */ /* Global scope for TableTools for backwards compatibility. * Will be removed in 2.3 */ var TableTools; (function(window, document, undefined) { var factory = function( $, DataTable ) { "use strict"; //include ZeroClipboard.js /* ZeroClipboard 1.0.4 * Author: Joseph Huckaby */ var ZeroClipboard_TableTools = { version: "1.0.4-TableTools2", clients: {}, // registered upload clients on page, indexed by id moviePath: '', // URL to movie nextId: 1, // ID of next movie $: function(thingy) { // simple DOM lookup utility function if (typeof(thingy) == 'string') { thingy = document.getElementById(thingy); } if (!thingy.addClass) { // extend element with a few useful methods thingy.hide = function() { this.style.display = 'none'; }; thingy.show = function() { this.style.display = ''; }; thingy.addClass = function(name) { this.removeClass(name); this.className += ' ' + name; }; thingy.removeClass = function(name) { this.className = this.className.replace( new RegExp("\\s*" + name + "\\s*"), " ").replace(/^\s+/, '').replace(/\s+$/, ''); }; thingy.hasClass = function(name) { return !!this.className.match( new RegExp("\\s*" + name + "\\s*") ); }; } return thingy; }, setMoviePath: function(path) { // set path to ZeroClipboard.swf this.moviePath = path; }, dispatch: function(id, eventName, args) { // receive event from flash movie, send to client var client = this.clients[id]; if (client) { client.receiveEvent(eventName, args); } }, register: function(id, client) { // register new client to receive events this.clients[id] = client; }, getDOMObjectPosition: function(obj) { // get absolute coordinates for dom element var info = { left: 0, top: 0, width: obj.width ? obj.width : obj.offsetWidth, height: obj.height ? obj.height : obj.offsetHeight }; if ( obj.style.width !== "" ) { info.width = obj.style.width.replace("px",""); } if ( obj.style.height !== "" ) { info.height = obj.style.height.replace("px",""); } while (obj) { info.left += obj.offsetLeft; info.top += obj.offsetTop; obj = obj.offsetParent; } return info; }, Client: function(elem) { // constructor for new simple upload client this.handlers = {}; // unique ID this.id = ZeroClipboard_TableTools.nextId++; this.movieId = 'ZeroClipboard_TableToolsMovie_' + this.id; // register client with singleton to receive flash events ZeroClipboard_TableTools.register(this.id, this); // create movie if (elem) { this.glue(elem); } } }; ZeroClipboard_TableTools.Client.prototype = { id: 0, // unique ID for us ready: false, // whether movie is ready to receive events or not movie: null, // reference to movie object clipText: '', // text to copy to clipboard fileName: '', // default file save name action: 'copy', // action to perform handCursorEnabled: true, // whether to show hand cursor, or default pointer cursor cssEffects: true, // enable CSS mouse effects on dom container handlers: null, // user event handlers sized: false, glue: function(elem, title) { // glue to DOM element // elem can be ID or actual DOM element object this.domElement = ZeroClipboard_TableTools.$(elem); // float just above object, or zIndex 99 if dom element isn't set var zIndex = 99; if (this.domElement.style.zIndex) { zIndex = parseInt(this.domElement.style.zIndex, 10) + 1; } // find X/Y position of domElement var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement); // create floating DIV above element this.div = document.createElement('div'); var style = this.div.style; style.position = 'absolute'; style.left = '0px'; style.top = '0px'; style.width = (box.width) + 'px'; style.height = box.height + 'px'; style.zIndex = zIndex; if ( typeof title != "undefined" && title !== "" ) { this.div.title = title; } if ( box.width !== 0 && box.height !== 0 ) { this.sized = true; } // style.backgroundColor = '#f00'; // debug if ( this.domElement ) { this.domElement.appendChild(this.div); this.div.innerHTML = this.getHTML( box.width, box.height ).replace(/&/g, '&amp;'); } }, positionElement: function() { var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement); var style = this.div.style; style.position = 'absolute'; //style.left = (this.domElement.offsetLeft)+'px'; //style.top = this.domElement.offsetTop+'px'; style.width = box.width + 'px'; style.height = box.height + 'px'; if ( box.width !== 0 && box.height !== 0 ) { this.sized = true; } else { return; } var flash = this.div.childNodes[0]; flash.width = box.width; flash.height = box.height; }, getHTML: function(width, height) { // return HTML for movie var html = ''; var flashvars = 'id=' + this.id + '&width=' + width + '&height=' + height; if (navigator.userAgent.match(/MSIE/)) { // IE gets an OBJECT tag var protocol = location.href.match(/^https/i) ? 'https://' : 'http://'; 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>'; } else { // all other browsers get an EMBED tag 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" />'; } return html; }, hide: function() { // temporarily hide floater offscreen if (this.div) { this.div.style.left = '-2000px'; } }, show: function() { // show ourselves after a call to hide() this.reposition(); }, destroy: function() { // destroy control and floater if (this.domElement && this.div) { this.hide(); this.div.innerHTML = ''; var body = document.getElementsByTagName('body')[0]; try { body.removeChild( this.div ); } catch(e) {} this.domElement = null; this.div = null; } }, reposition: function(elem) { // reposition our floating div, optionally to new container // warning: container CANNOT change size, only position if (elem) { this.domElement = ZeroClipboard_TableTools.$(elem); if (!this.domElement) { this.hide(); } } if (this.domElement && this.div) { var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement); var style = this.div.style; style.left = '' + box.left + 'px'; style.top = '' + box.top + 'px'; } }, clearText: function() { // clear the text to be copy / saved this.clipText = ''; if (this.ready) { this.movie.clearText(); } }, appendText: function(newText) { // append text to that which is to be copied / saved this.clipText += newText; if (this.ready) { this.movie.appendText(newText) ;} }, setText: function(newText) { // set text to be copied to be copied / saved this.clipText = newText; if (this.ready) { this.movie.setText(newText) ;} }, setCharSet: function(charSet) { // set the character set (UTF16LE or UTF8) this.charSet = charSet; if (this.ready) { this.movie.setCharSet(charSet) ;} }, setBomInc: function(bomInc) { // set if the BOM should be included or not this.incBom = bomInc; if (this.ready) { this.movie.setBomInc(bomInc) ;} }, setFileName: function(newText) { // set the file name this.fileName = newText; if (this.ready) { this.movie.setFileName(newText); } }, setAction: function(newText) { // set action (save or copy) this.action = newText; if (this.ready) { this.movie.setAction(newText); } }, addEventListener: function(eventName, func) { // add user event listener for event // event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel eventName = eventName.toString().toLowerCase().replace(/^on/, ''); if (!this.handlers[eventName]) { this.handlers[eventName] = []; } this.handlers[eventName].push(func); }, setHandCursor: function(enabled) { // enable hand cursor (true), or default arrow cursor (false) this.handCursorEnabled = enabled; if (this.ready) { this.movie.setHandCursor(enabled); } }, setCSSEffects: function(enabled) { // enable or disable CSS effects on DOM container this.cssEffects = !!enabled; }, receiveEvent: function(eventName, args) { var self; // receive event from flash eventName = eventName.toString().toLowerCase().replace(/^on/, ''); // special behavior for certain events switch (eventName) { case 'load': // movie claims it is ready, but in IE this isn't always the case... // bug fix: Cannot extend EMBED DOM elements in Firefox, must use traditional function this.movie = document.getElementById(this.movieId); if (!this.movie) { self = this; setTimeout( function() { self.receiveEvent('load', null); }, 1 ); return; } // firefox on pc needs a "kick" in order to set these in certain cases if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) { self = this; setTimeout( function() { self.receiveEvent('load', null); }, 100 ); this.ready = true; return; } this.ready = true; this.movie.clearText(); this.movie.appendText( this.clipText ); this.movie.setFileName( this.fileName ); this.movie.setAction( this.action ); this.movie.setCharSet( this.charSet ); this.movie.setBomInc( this.incBom ); this.movie.setHandCursor( this.handCursorEnabled ); break; case 'mouseover': if (this.domElement && this.cssEffects) { //this.domElement.addClass('hover'); if (this.recoverActive) { this.domElement.addClass('active'); } } break; case 'mouseout': if (this.domElement && this.cssEffects) { this.recoverActive = false; if (this.domElement.hasClass('active')) { this.domElement.removeClass('active'); this.recoverActive = true; } //this.domElement.removeClass('hover'); } break; case 'mousedown': if (this.domElement && this.cssEffects) { this.domElement.addClass('active'); } break; case 'mouseup': if (this.domElement && this.cssEffects) { this.domElement.removeClass('active'); this.recoverActive = false; } break; } // switch eventName if (this.handlers[eventName]) { for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) { var func = this.handlers[eventName][idx]; if (typeof(func) == 'function') { // actual function reference func(this, args); } else if ((typeof(func) == 'object') && (func.length == 2)) { // PHP style object + method, i.e. [myObject, 'myMethod'] func[0][ func[1] ](this, args); } else if (typeof(func) == 'string') { // name of function window[func](this, args); } } // foreach event handler defined } // user defined handler for event } }; // For the Flash binding to work, ZeroClipboard_TableTools must be on the global // object list window.ZeroClipboard_TableTools = ZeroClipboard_TableTools; //include TableTools.js /* TableTools * 2009-2015 SpryMedia Ltd - datatables.net/license */ /*globals TableTools,ZeroClipboard_TableTools*/ (function($, window, document) { /** * TableTools provides flexible buttons and other tools for a DataTables enhanced table * @class TableTools * @constructor * @param {Object} oDT DataTables instance. When using DataTables 1.10 this can * also be a jQuery collection, jQuery selector, table node, DataTables API * instance or DataTables settings object. * @param {Object} oOpts TableTools options * @param {String} oOpts.sSwfPath ZeroClipboard SWF path * @param {String} oOpts.sRowSelect Row selection options - 'none', 'single', 'multi' or 'os' * @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'" ); } // In 1.10 we can use the API to get the settings object from a number of // sources var dtSettings = $.fn.dataTable.Api ? new $.fn.dataTable.Api( oDT ).settings()[0] : oDT.fnSettings(); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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": dtSettings, /** * @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 = {}; } TableTools._aInstances.push( this ); 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; }, /** * Get the indexes of the selected rows * @returns {array} List of row indexes * @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. */ "fnGetSelectedIndexes": 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( displayed[i] ); } } } else { // Use all rows for ( i=0, iLen=data.length ; i<iLen ; i++ ) { if ( data[i]._DTTT_selected ) { out.push( 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 ) { this._fnRowSelect( filtered ? this.s.dt.aiDisplay : this.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 ) { this._fnRowDeselect( this.fnGetSelectedIndexes(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 { 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 info = $('<div/>') .addClass( this.classes.print.info ) .html( message ) .appendTo( 'body' ); setTimeout( function() { info.fadeOut( "normal", function() { info.remove(); } ); }, time ); }, /** * Get the container element of the instance for attaching to the DOM * @returns {node} DOM node */ "fnContainer": function () { return this.dom.container; }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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', that.s.custom.sRowSelector ) .off( 'mousedown.DTTT_Select', 'tr' ) .off( 'mouseup.DTTT_Select', 'tr' ); $(that.dom.container).empty(); // Remove the instance var idx = $.inArray( that, TableTools._aInstances ); if ( idx !== -1 ) { TableTools._aInstances.splice( idx, 1 ); } } } ); }, /** * 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 ); } var button = this._fnCreateButton( buttonDef, $(wrapper).hasClass(this.classes.collection.container) ); if ( button ) { wrapper.appendChild( button ); } } }, /** * 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/) ) { if ( ! this._fnHasFlash() ) { return false; } 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 ); } if ( this.s.dt.iTabIndex !== -1 ) { $(nButton) .attr( 'tabindex', this.s.dt.iTabIndex ) .attr( 'aria-controls', this.s.dt.sTableId ) .on( 'keyup.DTTT', function (e) { // Trigger the click event on return key when focused. // Note that for Flash buttons this has no effect since we // can't programmatically trigger the Flash export if ( e.keyCode === 13 ) { e.stopPropagation(); $(this).trigger( 'click' ); } } ) .on( 'mousedown.DTTT', function (e) { // On mousedown we want to stop the focus occurring on the // button, focus is used only for the keyboard navigation. // But using preventDefault for the flash buttons stops the // flash action. However, it is not the button that gets // focused but the flash element for flash buttons, so this // works if ( ! oConfig.sAction.match(/flash/) ) { e.preventDefault(); } } ); } 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 && o.sTag !== "default" ? o.sTag : this.s.tags.collection.button; sLiner = o.sLinerTag && o.sLinerTag !== "default" ? o.sLiner : this.s.tags.collection.liner; sClass = this.classes.collection.buttons.normal; } else { sTag = o.sTag && o.sTag !== "default" ? o.sTag : this.s.tags.button; sLiner = o.sLinerTag && 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 ); // When using OS style selection, we want to cancel the shift text // selection, but only when the shift key is used (so you can // actually still select text in the table) if ( this.s.select.type === 'os' ) { $(dt.nTBody).on( 'mousedown.DTTT_Select', 'tr', function(e) { if ( e.shiftKey ) { $(dt.nTBody) .css( '-moz-user-select', 'none' ) .one('selectstart.DTTT_Select', 'tr', function () { return false; } ); } } ); $(dt.nTBody).on( 'mouseup.DTTT_Select', 'tr', function(e) { $(dt.nTBody).css( '-moz-user-select', '' ); } ); } // Row selection $(dt.nTBody).on( 'click.DTTT_Select', this.s.custom.sRowSelector, function(e) { var row = this.nodeName.toLowerCase() === 'tr' ? this : $(this).parents('tr')[0]; var select = that.s.select; var pos = that.s.dt.oInstance.fnGetPosition( row ); /* Sub-table must be ignored (odd that the selector won't do this with >) */ if ( row.parentNode != dt.nTBody ) { return; } /* Check that we are actually working with a DataTables controlled row */ if ( dt.oInstance.fnGetData(row) === null ) { return; } // Shift click, ctrl click and simple click handling to make // row selection a lot like a file system in desktop OSs if ( select.type == 'os' ) { if ( e.ctrlKey || e.metaKey ) { // Add or remove from the selection if ( that.fnIsSelected( row ) ) { that._fnRowDeselect( row, e ); } else { that._fnRowSelect( row, e ); } } else if ( e.shiftKey ) { // Add a range of rows, from the last selected row to // this one var rowIdxs = that.s.dt.aiDisplay.slice(); // visible rows var idx1 = $.inArray( select.lastRow, rowIdxs ); var idx2 = $.inArray( pos, rowIdxs ); if ( that.fnGetSelected().length === 0 || idx1 === -1 ) { // select from top to here - slightly odd, but both // Windows and Mac OS do this rowIdxs.splice( $.inArray( pos, rowIdxs )+1, rowIdxs.length ); } else { // reverse so we can shift click 'up' as well as down if ( idx1 > idx2 ) { var tmp = idx2; idx2 = idx1; idx1 = tmp; } rowIdxs.splice( idx2+1, rowIdxs.length ); rowIdxs.splice( 0, idx1 ); } if ( ! that.fnIsSelected( row ) ) { // Select range that._fnRowSelect( rowIdxs, e ); } else { // Deselect range - need to keep the clicked on row selected rowIdxs.splice( $.inArray( pos, rowIdxs ), 1 ); that._fnRowDeselect( rowIdxs, e ); } } else { // No cmd or shift click. Deselect current if selected, // or select this row only if ( that.fnIsSelected( row ) && that.fnGetSelected().length === 1 ) { that._fnRowDeselect( row, e ); } else { that.fnSelectNone(); that._fnRowSelect( row, e ); } } } else if ( that.fnIsSelected( row ) ) { that._fnRowDeselect( row, e ); } else if ( select.type == "single" ) { that.fnSelectNone(); that._fnRowSelect( row, e ); } else if ( select.type == "multi" ) { that._fnRowSelect( row, e ); } select.lastRow = pos; } );//.on('selectstart', function () { return false; } ); // 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 if ( typeof src === 'number' ) { out.push(this.s.dt.aoData[src]); } 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 */ /** * Ch