From 0225bdb772d1334cc1aa7ab0fc3678df0864df6b Mon Sep 17 00:00:00 2001 From: AlisaLinUwU Date: Sun, 26 Jan 2025 10:42:28 +0500 Subject: Initialize --- .../datatables-buttons/js/dataTables.buttons.js | 2478 ++++++++++++++++++++ 1 file changed, 2478 insertions(+) create mode 100644 build/resources/main/static/plugins/datatables-buttons/js/dataTables.buttons.js (limited to 'build/resources/main/static/plugins/datatables-buttons/js/dataTables.buttons.js') diff --git a/build/resources/main/static/plugins/datatables-buttons/js/dataTables.buttons.js b/build/resources/main/static/plugins/datatables-buttons/js/dataTables.buttons.js new file mode 100644 index 0000000..f35a9a8 --- /dev/null +++ b/build/resources/main/static/plugins/datatables-buttons/js/dataTables.buttons.js @@ -0,0 +1,2478 @@ +/*! Buttons for DataTables 2.2.2 + * ©2016-2022 SpryMedia Ltd - datatables.net/license + */ + +(function( factory ){ + if ( typeof define === 'function' && define.amd ) { + // AMD + define( ['jquery', 'datatables.net'], function ( $ ) { + return factory( $, window, document ); + } ); + } + else if ( typeof exports === 'object' ) { + // CommonJS + module.exports = function (root, $) { + if ( ! root ) { + root = window; + } + + if ( ! $ || ! $.fn.dataTable ) { + $ = require('datatables.net')(root, $).$; + } + + return factory( $, root, root.document ); + }; + } + else { + // Browser + factory( jQuery, window, document ); + } +}(function( $, window, document, undefined ) { +'use strict'; +var DataTable = $.fn.dataTable; + + +// Used for namespacing events added to the document by each instance, so they +// can be removed on destroy +var _instCounter = 0; + +// Button namespacing counter for namespacing events on individual buttons +var _buttonCounter = 0; + +var _dtButtons = DataTable.ext.buttons; + +// Allow for jQuery slim +function _fadeIn(el, duration, fn) { + if ($.fn.animate) { + el + .stop() + .fadeIn( duration, fn ); + + } + else { + el.css('display', 'block'); + + if (fn) { + fn.call(el); + } + } +} + +function _fadeOut(el, duration, fn) { + if ($.fn.animate) { + el + .stop() + .fadeOut( duration, fn ); + } + else { + el.css('display', 'none'); + + if (fn) { + fn.call(el); + } + } +} + +/** + * [Buttons description] + * @param {[type]} + * @param {[type]} + */ +var Buttons = function( dt, config ) +{ + // If not created with a `new` keyword then we return a wrapper function that + // will take the settings object for a DT. This allows easy use of new instances + // with the `layout` option - e.g. `topLeft: $.fn.dataTable.Buttons( ... )`. + if ( !(this instanceof Buttons) ) { + return function (settings) { + return new Buttons( settings, dt ).container(); + }; + } + + // If there is no config set it to an empty object + if ( typeof( config ) === 'undefined' ) { + config = {}; + } + + // Allow a boolean true for defaults + if ( config === true ) { + config = {}; + } + + // For easy configuration of buttons an array can be given + if ( Array.isArray( config ) ) { + config = { buttons: config }; + } + + this.c = $.extend( true, {}, Buttons.defaults, config ); + + // Don't want a deep copy for the buttons + if ( config.buttons ) { + this.c.buttons = config.buttons; + } + + this.s = { + dt: new DataTable.Api( dt ), + buttons: [], + listenKeys: '', + namespace: 'dtb'+(_instCounter++) + }; + + this.dom = { + container: $('<'+this.c.dom.container.tag+'/>') + .addClass( this.c.dom.container.className ) + }; + + this._constructor(); +}; + + +$.extend( Buttons.prototype, { + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Public methods + */ + + /** + * Get the action of a button + * @param {int|string} Button index + * @return {function} + *//** + * Set the action of a button + * @param {node} node Button element + * @param {function} action Function to set + * @return {Buttons} Self for chaining + */ + action: function ( node, action ) + { + var button = this._nodeToButton( node ); + + if ( action === undefined ) { + return button.conf.action; + } + + button.conf.action = action; + + return this; + }, + + /** + * Add an active class to the button to make to look active or get current + * active state. + * @param {node} node Button element + * @param {boolean} [flag] Enable / disable flag + * @return {Buttons} Self for chaining or boolean for getter + */ + active: function ( node, flag ) { + var button = this._nodeToButton( node ); + var klass = this.c.dom.button.active; + var jqNode = $(button.node); + + if ( flag === undefined ) { + return jqNode.hasClass( klass ); + } + + jqNode.toggleClass( klass, flag === undefined ? true : flag ); + + return this; + }, + + /** + * Add a new button + * @param {object} config Button configuration object, base string name or function + * @param {int|string} [idx] Button index for where to insert the button + * @param {boolean} [draw=true] Trigger a draw. Set a false when adding + * lots of buttons, until the last button. + * @return {Buttons} Self for chaining + */ + add: function ( config, idx, draw ) + { + var buttons = this.s.buttons; + + if ( typeof idx === 'string' ) { + var split = idx.split('-'); + var base = this.s; + + for ( var i=0, ien=split.length-1 ; i=0; i--) { + this.remove(button.buttons[i].node); + } + + for (i=0; i=0 ; i-- ) { + this.remove( button.buttons[i].node ); + } + } + + button.conf.destroying = true; + + // Allow the button to remove event handlers, etc + if ( button.conf.destroy ) { + button.conf.destroy.call( dt.button(node), dt, $(node), button.conf ); + } + + this._removeKey( button.conf ); + + $(button.node).remove(); + + var idx = $.inArray( button, host ); + host.splice( idx, 1 ); + + return this; + }, + + /** + * Get the text for a button + * @param {int|string} node Button index + * @return {string} Button text + *//** + * Set the text for a button + * @param {int|string|function} node Button index + * @param {string} label Text + * @return {Buttons} Self for chaining + */ + text: function ( node, label ) + { + var button = this._nodeToButton( node ); + var buttonLiner = this.c.dom.collection.buttonLiner; + var linerTag = button.inCollection && buttonLiner && buttonLiner.tag ? + buttonLiner.tag : + this.c.dom.buttonLiner.tag; + var dt = this.s.dt; + var jqNode = $(button.node); + var text = function ( opt ) { + return typeof opt === 'function' ? + opt( dt, jqNode, button.conf ) : + opt; + }; + + if ( label === undefined ) { + return text( button.conf.text ); + } + + button.conf.text = label; + + if ( linerTag ) { + jqNode + .children( linerTag ) + .eq(0) + .filter(':not(.dt-down-arrow)') + .html( text(label) ); + } + else { + jqNode.html( text(label) ); + } + + return this; + }, + + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Constructor + */ + + /** + * Buttons constructor + * @private + */ + _constructor: function () + { + var that = this; + var dt = this.s.dt; + var dtSettings = dt.settings()[0]; + var buttons = this.c.buttons; + + if ( ! dtSettings._buttons ) { + dtSettings._buttons = []; + } + + dtSettings._buttons.push( { + inst: this, + name: this.c.name + } ); + + for ( var i=0, ien=buttons.length ; i'); + + built.conf._collection = built.collection; + + if(built.conf.split) { + for(var j = 0; j < built.conf.split.length; j++) { + if(typeof built.conf.split[j] === "object") { + built.conf.split[j].parent = parentConf; + if(built.conf.split[j].collectionLayout === undefined) { + built.conf.split[j].collectionLayout = built.conf.collectionLayout; + } + if(built.conf.split[j].dropup === undefined) { + built.conf.split[j].dropup = built.conf.dropup; + } + if(built.conf.split[j].fade === undefined) { + built.conf.split[j].fade = built.conf.fade; + } + } + } + } + else { + $(built.node).append($(''+this.c.dom.splitDropdown.text+'')) + } + + this._expandButton( built.buttons, built.conf.buttons, built.conf.split, !isSplit, isSplit, attachPoint, built.conf ); + } + built.conf.parent = parentConf; + + // init call is made here, rather than buildButton as it needs to + // be selectable, and for that it needs to be in the buttons array + if ( conf.init ) { + conf.init.call( dt.button( built.node ), dt, $(built.node), conf ); + } + + buttonCounter++; + } + }, + + /** + * Create an individual button + * @param {object} config Resolved button configuration + * @param {boolean} inCollection `true` if a collection button + * @return {jQuery} Created button node (jQuery) + * @private + */ + _buildButton: function ( config, inCollection, isSplit, inSplit ) + { + var buttonDom = this.c.dom.button; + var linerDom = this.c.dom.buttonLiner; + var collectionDom = this.c.dom.collection; + var splitDom = this.c.dom.split; + var splitCollectionDom = this.c.dom.splitCollection; + var splitDropdownButton = this.c.dom.splitDropdownButton; + var dt = this.s.dt; + var text = function ( opt ) { + return typeof opt === 'function' ? + opt( dt, button, config ) : + opt; + }; + + // Spacers don't do much other than insert an element into the DOM + if (config.spacer) { + var spacer = $('') + .addClass('dt-button-spacer ' + config.style + ' ' + buttonDom.spacerClass) + .html(text(config.text)); + + return { + conf: config, + node: spacer, + inserter: spacer, + buttons: [], + inCollection: inCollection, + isSplit: isSplit, + inSplit: inSplit, + collection: null + }; + } + + if ( !isSplit && inSplit && splitCollectionDom ) { + buttonDom = splitDropdownButton; + } + else if ( !isSplit && inCollection && collectionDom.button ) { + buttonDom = collectionDom.button; + } + + if ( !isSplit && inSplit && splitCollectionDom.buttonLiner ) { + linerDom = splitCollectionDom.buttonLiner + } + else if ( !isSplit && inCollection && collectionDom.buttonLiner ) { + linerDom = collectionDom.buttonLiner; + } + + // Make sure that the button is available based on whatever requirements + // it has. For example, PDF button require pdfmake + if ( config.available && ! config.available( dt, config ) && !config.hasOwnProperty('html') ) { + return false; + } + + var button; + if(!config.hasOwnProperty('html')) { + var action = function ( e, dt, button, config ) { + config.action.call( dt.button( button ), e, dt, button, config ); + + $(dt.table().node()).triggerHandler( 'buttons-action.dt', [ + dt.button( button ), dt, button, config + ] ); + }; + + var tag = config.tag || buttonDom.tag; + var clickBlurs = config.clickBlurs === undefined + ? true : + config.clickBlurs; + + button = $('<'+tag+'/>') + .addClass( buttonDom.className ) + .addClass( inSplit ? this.c.dom.splitDropdownButton.className : '') + .attr( 'tabindex', this.s.dt.settings()[0].iTabIndex ) + .attr( 'aria-controls', this.s.dt.table().node().id ) + .on( 'click.dtb', function (e) { + e.preventDefault(); + + if ( ! button.hasClass( buttonDom.disabled ) && config.action ) { + action( e, dt, button, config ); + } + if( clickBlurs ) { + button.trigger('blur'); + } + } ) + .on( 'keypress.dtb', function (e) { + if ( e.keyCode === 13 ) { + e.preventDefault(); + + if ( ! button.hasClass( buttonDom.disabled ) && config.action ) { + action( e, dt, button, config ); + } + } + } ); + + // Make `a` tags act like a link + if ( tag.toLowerCase() === 'a' ) { + button.attr( 'href', '#' ); + } + + // Button tags should have `type=button` so they don't have any default behaviour + if ( tag.toLowerCase() === 'button' ) { + button.attr( 'type', 'button' ); + } + + if ( linerDom.tag ) { + var liner = $('<'+linerDom.tag+'/>') + .html( text( config.text ) ) + .addClass( linerDom.className ); + + if ( linerDom.tag.toLowerCase() === 'a' ) { + liner.attr( 'href', '#' ); + } + + button.append( liner ); + } + else { + button.html( text( config.text ) ); + } + + if ( config.enabled === false ) { + button.addClass( buttonDom.disabled ); + } + + if ( config.className ) { + button.addClass( config.className ); + } + + if ( config.titleAttr ) { + button.attr( 'title', text( config.titleAttr ) ); + } + + if ( config.attr ) { + button.attr( config.attr ); + } + + if ( ! config.namespace ) { + config.namespace = '.dt-button-'+(_buttonCounter++); + } + + if ( config.config !== undefined && config.config.split ) { + config.split = config.config.split; + } + } + else { + button = $(config.html) + } + + var buttonContainer = this.c.dom.buttonContainer; + var inserter; + if ( buttonContainer && buttonContainer.tag ) { + inserter = $('<'+buttonContainer.tag+'/>') + .addClass( buttonContainer.className ) + .append( button ); + } + else { + inserter = button; + } + + this._addKey( config ); + + // Style integration callback for DOM manipulation + // Note that this is _not_ documented. It is currently + // for style integration only + if( this.c.buttonCreated ) { + inserter = this.c.buttonCreated( config, inserter ); + } + + var splitDiv; + if(isSplit) { + splitDiv = $('
').addClass(this.c.dom.splitWrapper.className) + splitDiv.append(button); + var dropButtonConfig = $.extend(config, { + text: this.c.dom.splitDropdown.text, + className: this.c.dom.splitDropdown.className, + closeButton: false, + attr: { + 'aria-haspopup': true, + 'aria-expanded': false + }, + align: this.c.dom.splitDropdown.align, + splitAlignClass: this.c.dom.splitDropdown.splitAlignClass + + }) + + this._addKey(dropButtonConfig); + + var splitAction = function ( e, dt, button, config ) { + _dtButtons.split.action.call( dt.button($('div.dt-btn-split-wrapper')[0] ), e, dt, button, config ); + + $(dt.table().node()).triggerHandler( 'buttons-action.dt', [ + dt.button( button ), dt, button, config + ] ); + button.attr('aria-expanded', true) + }; + + var dropButton = $('') + .on( 'click.dtb', function (e) { + e.preventDefault(); + e.stopPropagation(); + + if ( ! dropButton.hasClass( buttonDom.disabled )) { + splitAction( e, dt, dropButton, dropButtonConfig ); + } + if ( clickBlurs ) { + dropButton.trigger('blur'); + } + } ) + .on( 'keypress.dtb', function (e) { + if ( e.keyCode === 13 ) { + e.preventDefault(); + + if ( ! dropButton.hasClass( buttonDom.disabled ) ) { + splitAction( e, dt, dropButton, dropButtonConfig ); + } + } + } ); + + if(config.split.length === 0) { + dropButton.addClass('dtb-hide-drop'); + } + + splitDiv.append(dropButton).attr(dropButtonConfig.attr); + } + + return { + conf: config, + node: isSplit ? splitDiv.get(0) : button.get(0), + inserter: isSplit ? splitDiv : inserter, + buttons: [], + inCollection: inCollection, + isSplit: isSplit, + inSplit: inSplit, + collection: null + }; + }, + + /** + * Get the button object from a node (recursive) + * @param {node} node Button node + * @param {array} [buttons] Button array, uses base if not defined + * @return {object} Button object + * @private + */ + _nodeToButton: function ( node, buttons ) + { + if ( ! buttons ) { + buttons = this.s.buttons; + } + + for ( var i=0, ien=buttons.length ; i 30 ) { + // Protect against misconfiguration killing the browser + throw 'Buttons: Too many iterations'; + } + } + + return Array.isArray( base ) ? + base : + $.extend( {}, base ); + }; + + conf = toConfObject( conf ); + + while ( conf && conf.extend ) { + // Use `toConfObject` in case the button definition being extended + // is itself a string or a function + if ( ! _dtButtons[ conf.extend ] ) { + throw 'Cannot extend unknown button type: '+conf.extend; + } + + var objArray = toConfObject( _dtButtons[ conf.extend ] ); + if ( Array.isArray( objArray ) ) { + return objArray; + } + else if ( ! objArray ) { + // This is a little brutal as it might be possible to have a + // valid button without the extend, but if there is no extend + // then the host button would be acting in an undefined state + return false; + } + + // Stash the current class name + var originalClassName = objArray.className; + + if (conf.config !== undefined && objArray.config !== undefined) { + conf.config = $.extend({}, objArray.config, conf.config) + } + + conf = $.extend( {}, objArray, conf ); + + // The extend will have overwritten the original class name if the + // `conf` object also assigned a class, but we want to concatenate + // them so they are list that is combined from all extended buttons + if ( originalClassName && conf.className !== originalClassName ) { + conf.className = originalClassName+' '+conf.className; + } + + // Buttons to be added to a collection -gives the ability to define + // if buttons should be added to the start or end of a collection + var postfixButtons = conf.postfixButtons; + if ( postfixButtons ) { + if ( ! conf.buttons ) { + conf.buttons = []; + } + + for ( i=0, ien=postfixButtons.length ; i') + .addClass('dt-button-collection') + .addClass(options.collectionLayout) + .addClass(options.splitAlignClass) + .addClass(mod) + .css('display', 'none'); + + content = $(content) + .addClass(options.contentClassName) + .attr('role', 'menu') + .appendTo(display); + + hostNode.attr( 'aria-expanded', 'true' ); + + if ( hostNode.parents('body')[0] !== document.body ) { + hostNode = document.body.lastChild; + } + + if ( options.popoverTitle ) { + display.prepend('
'+options.popoverTitle+'
'); + } + else if ( options.collectionTitle ) { + display.prepend('
'+options.collectionTitle+'
'); + } + + if (options.closeButton) { + display.prepend('
x
').addClass('dtb-collection-closeable') + } + + _fadeIn( display.insertAfter( hostNode ), options.fade ); + + var tableContainer = $( hostButton.table().container() ); + var position = display.css( 'position' ); + + if ( options.span === 'container' || options.align === 'dt-container' ) { + hostNode = hostNode.parent(); + display.css('width', tableContainer.width()); + } + + // Align the popover relative to the DataTables container + // Useful for wide popovers such as SearchPanes + if (position === 'absolute') { + // Align relative to the host button + var offsetParent = $(hostNode[0].offsetParent); + var buttonPosition = hostNode.position(); + var buttonOffset = hostNode.offset(); + var tableSizes = offsetParent.offset(); + var containerPosition = offsetParent.position(); + var computed = window.getComputedStyle(offsetParent[0]); + + tableSizes.height = offsetParent.outerHeight(); + tableSizes.width = offsetParent.width() + parseFloat(computed.paddingLeft); + tableSizes.right = tableSizes.left + tableSizes.width; + tableSizes.bottom = tableSizes.top + tableSizes.height; + + // Set the initial position so we can read height / width + var top = buttonPosition.top + hostNode.outerHeight(); + var left = buttonPosition.left; + + display.css( { + top: top, + left: left + } ); + + // Get the popover position + computed = window.getComputedStyle(display[0]); + var popoverSizes = display.offset(); + + popoverSizes.height = display.outerHeight(); + popoverSizes.width = display.outerWidth(); + popoverSizes.right = popoverSizes.left + popoverSizes.width; + popoverSizes.bottom = popoverSizes.top + popoverSizes.height; + popoverSizes.marginTop = parseFloat(computed.marginTop); + popoverSizes.marginBottom = parseFloat(computed.marginBottom); + + // First position per the class requirements - pop up and right align + if (options.dropup) { + top = buttonPosition.top - popoverSizes.height - popoverSizes.marginTop - popoverSizes.marginBottom; + } + + if (options.align === 'button-right' || display.hasClass( options.rightAlignClassName )) { + left = buttonPosition.left - popoverSizes.width + hostNode.outerWidth(); + } + + // Container alignment - make sure it doesn't overflow the table container + if (options.align === 'dt-container' || options.align === 'container') { + if (left < buttonPosition.left) { + left = -buttonPosition.left; + } + + if (left + popoverSizes.width > tableSizes.width) { + left = tableSizes.width - popoverSizes.width; + } + } + + // Window adjustment + if (containerPosition.left + left + popoverSizes.width > $(window).width()) { + // Overflowing the document to the right + left = $(window).width() - popoverSizes.width - containerPosition.left; + } + + if (buttonOffset.left + left < 0) { + // Off to the left of the document + left = -buttonOffset.left; + } + + if (containerPosition.top + top + popoverSizes.height > $(window).height() + $(window).scrollTop()) { + // Pop up if otherwise we'd need the user to scroll down + top = buttonPosition.top - popoverSizes.height - popoverSizes.marginTop - popoverSizes.marginBottom; + } + + if (containerPosition.top + top < $(window).scrollTop()) { + // Correction for when the top is beyond the top of the page + top = buttonPosition.top + hostNode.outerHeight(); + } + + // Calculations all done - now set it + display.css( { + top: top, + left: left + } ); + } + else { + // Fix position - centre on screen + var position = function () { + var half = $(window).height() / 2; + + var top = display.height() / 2; + if ( top > half ) { + top = half; + } + + display.css( 'marginTop', top*-1 ); + }; + + position(); + + $(window).on('resize.dtb-collection', function () { + position(); + }); + } + + if ( options.background ) { + Buttons.background( + true, + options.backgroundClassName, + options.fade, + options.backgroundHost || hostNode + ); + } + + // This is bonkers, but if we don't have a click listener on the + // background element, iOS Safari will ignore the body click + // listener below. An empty function here is all that is + // required to make it work... + $('div.dt-button-background').on( 'click.dtb-collection', function () {} ); + + if ( options.autoClose ) { + setTimeout( function () { + dt.on( 'buttons-action.b-internal', function (e, btn, dt, node) { + if ( node[0] === hostNode[0] ) { + return; + } + close(); + } ); + }, 0); + } + + $(display).trigger('buttons-popover.dt'); + + + dt.on('destroy', close); + + setTimeout(function() { + closed = false; + $('body') + .on( 'click.dtb-collection', function (e) { + if (closed) { + return; + } + + // andSelf is deprecated in jQ1.8, but we want 1.7 compat + var back = $.fn.addBack ? 'addBack' : 'andSelf'; + var parent = $(e.target).parent()[0]; + + if (( ! $(e.target).parents()[back]().filter( content ).length && !$(parent).hasClass('dt-buttons')) || $(e.target).hasClass('dt-button-background')) { + close(); + } + } ) + .on( 'keyup.dtb-collection', function (e) { + if ( e.keyCode === 27 ) { + close(); + } + } ); + }, 0); + } +} ); + + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Statics + */ + +/** + * Show / hide a background layer behind a collection + * @param {boolean} Flag to indicate if the background should be shown or + * hidden + * @param {string} Class to assign to the background + * @static + */ +Buttons.background = function ( show, className, fade, insertPoint ) { + if ( fade === undefined ) { + fade = 400; + } + if ( ! insertPoint ) { + insertPoint = document.body; + } + + if ( show ) { + _fadeIn( + $('
') + .addClass( className ) + .css( 'display', 'none' ) + .insertAfter( insertPoint ), + fade + ); + } + else { + _fadeOut( + $('div.'+className), + fade, + function () { + $(this) + .removeClass( className ) + .remove(); + } + ); + } +}; + +/** + * Instance selector - select Buttons instances based on an instance selector + * value from the buttons assigned to a DataTable. This is only useful if + * multiple instances are attached to a DataTable. + * @param {string|int|array} Instance selector - see `instance-selector` + * documentation on the DataTables site + * @param {array} Button instance array that was attached to the DataTables + * settings object + * @return {array} Buttons instances + * @static + */ +Buttons.instanceSelector = function ( group, buttons ) +{ + if ( group === undefined || group === null ) { + return $.map( buttons, function ( v ) { + return v.inst; + } ); + } + + var ret = []; + var names = $.map( buttons, function ( v ) { + return v.name; + } ); + + // Flatten the group selector into an array of single options + var process = function ( input ) { + if ( Array.isArray( input ) ) { + for ( var i=0, ien=input.length ; i)<[^<]*)*<\/script>/gi, '' ); + + // Always remove comments + str = str.replace( //g, '' ); + + if ( ! config || config.stripHtml ) { + str = str.replace( /<[^>]*>/g, '' ); + } + + if ( ! config || config.trim ) { + str = str.replace( /^\s+|\s+$/g, '' ); + } + + if ( ! config || config.stripNewlines ) { + str = str.replace( /\n/g, ' ' ); + } + + if ( ! config || config.decodeEntities ) { + _exportTextarea.innerHTML = str; + str = _exportTextarea.value; + } + + return str; +}; + + +/** + * Buttons defaults. For full documentation, please refer to the docs/option + * directory or the DataTables site. + * @type {Object} + * @static + */ +Buttons.defaults = { + buttons: [ 'copy', 'excel', 'csv', 'pdf', 'print' ], + name: 'main', + tabIndex: 0, + dom: { + container: { + tag: 'div', + className: 'dt-buttons' + }, + collection: { + tag: 'div', + className: '' + }, + button: { + tag: 'button', + className: 'dt-button', + active: 'active', + disabled: 'disabled', + spacerClass: '' + }, + buttonLiner: { + tag: 'span', + className: '' + }, + split: { + tag: 'div', + className: 'dt-button-split', + }, + splitWrapper: { + tag: 'div', + className: 'dt-btn-split-wrapper', + }, + splitDropdown: { + tag: 'button', + text: '▼', + className: 'dt-btn-split-drop', + align: 'split-right', + splitAlignClass: 'dt-button-split-left' + }, + splitDropdownButton: { + tag: 'button', + className: 'dt-btn-split-drop-button dt-button', + }, + splitCollection: { + tag: 'div', + className: 'dt-button-split-collection', + } + } +}; + +/** + * Version information + * @type {string} + * @static + */ +Buttons.version = '2.2.2'; + + +$.extend( _dtButtons, { + collection: { + text: function ( dt ) { + return dt.i18n( 'buttons.collection', 'Collection' ); + }, + className: 'buttons-collection', + closeButton: false, + init: function ( dt, button, config ) { + button.attr( 'aria-expanded', false ); + }, + action: function ( e, dt, button, config ) { + if ( config._collection.parents('body').length ) { + this.popover(false, config); + } + else { + this.popover(config._collection, config); + } + }, + attr: { + 'aria-haspopup': true + } + // Also the popover options, defined in Buttons.popover + }, + split: { + text: function ( dt ) { + return dt.i18n( 'buttons.split', 'Split' ); + }, + className: 'buttons-split', + closeButton: false, + init: function ( dt, button, config ) { + return button.attr( 'aria-expanded', false ); + }, + action: function ( e, dt, button, config ) { + this.popover(config._collection, config); + }, + attr: { + 'aria-haspopup': true + } + // Also the popover options, defined in Buttons.popover + }, + copy: function ( dt, conf ) { + if ( _dtButtons.copyHtml5 ) { + return 'copyHtml5'; + } + }, + csv: function ( dt, conf ) { + if ( _dtButtons.csvHtml5 && _dtButtons.csvHtml5.available( dt, conf ) ) { + return 'csvHtml5'; + } + }, + excel: function ( dt, conf ) { + if ( _dtButtons.excelHtml5 && _dtButtons.excelHtml5.available( dt, conf ) ) { + return 'excelHtml5'; + } + }, + pdf: function ( dt, conf ) { + if ( _dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available( dt, conf ) ) { + return 'pdfHtml5'; + } + }, + pageLength: function ( dt ) { + var lengthMenu = dt.settings()[0].aLengthMenu; + var vals = []; + var lang = []; + var text = function ( dt ) { + return dt.i18n( 'buttons.pageLength', { + "-1": 'Show all rows', + _: 'Show %d rows' + }, dt.page.len() ); + }; + + // Support for DataTables 1.x 2D array + if (Array.isArray( lengthMenu[0] )) { + vals = lengthMenu[0]; + lang = lengthMenu[1]; + } + else { + for (var i=0 ; i 1 ) { + buttons.splice( 1, buttons.length ); + } + + return buttons; +} ); + +// Active buttons +DataTable.Api.registerPlural( 'buttons().active()', 'button().active()', function ( flag ) { + if ( flag === undefined ) { + return this.map( function ( set ) { + return set.inst.active( set.node ); + } ); + } + + return this.each( function ( set ) { + set.inst.active( set.node, flag ); + } ); +} ); + +// Get / set button action +DataTable.Api.registerPlural( 'buttons().action()', 'button().action()', function ( action ) { + if ( action === undefined ) { + return this.map( function ( set ) { + return set.inst.action( set.node ); + } ); + } + + return this.each( function ( set ) { + set.inst.action( set.node, action ); + } ); +} ); + +// Collection control +DataTable.Api.registerPlural( 'buttons().collectionRebuild()', 'button().collectionRebuild()', function ( buttons ) { + return this.each( function ( set ) { + for(var i = 0; i < buttons.length; i++) { + if(typeof buttons[i] === 'object') { + buttons[i].parentConf = set; + } + } + set.inst.collectionRebuild( set.node, buttons ); + } ); +} ); + +// Enable / disable buttons +DataTable.Api.register( ['buttons().enable()', 'button().enable()'], function ( flag ) { + return this.each( function ( set ) { + set.inst.enable( set.node, flag ); + } ); +} ); + +// Disable buttons +DataTable.Api.register( ['buttons().disable()', 'button().disable()'], function () { + return this.each( function ( set ) { + set.inst.disable( set.node ); + } ); +} ); + +// Button index +DataTable.Api.register( 'button().index()', function () { + var idx = null; + + this.each( function ( set ) { + var res = set.inst.index( set.node ); + + if (res !== null) { + idx = res; + } + } ); + + return idx; +} ); + +// Get button nodes +DataTable.Api.registerPlural( 'buttons().nodes()', 'button().node()', function () { + var jq = $(); + + // jQuery will automatically reduce duplicates to a single entry + $( this.each( function ( set ) { + jq = jq.add( set.inst.node( set.node ) ); + } ) ); + + return jq; +} ); + +// Get / set button processing state +DataTable.Api.registerPlural( 'buttons().processing()', 'button().processing()', function ( flag ) { + if ( flag === undefined ) { + return this.map( function ( set ) { + return set.inst.processing( set.node ); + } ); + } + + return this.each( function ( set ) { + set.inst.processing( set.node, flag ); + } ); +} ); + +// Get / set button text (i.e. the button labels) +DataTable.Api.registerPlural( 'buttons().text()', 'button().text()', function ( label ) { + if ( label === undefined ) { + return this.map( function ( set ) { + return set.inst.text( set.node ); + } ); + } + + return this.each( function ( set ) { + set.inst.text( set.node, label ); + } ); +} ); + +// Trigger a button's action +DataTable.Api.registerPlural( 'buttons().trigger()', 'button().trigger()', function () { + return this.each( function ( set ) { + set.inst.node( set.node ).trigger( 'click' ); + } ); +} ); + +// Button resolver to the popover +DataTable.Api.register( 'button().popover()', function (content, options) { + return this.map( function ( set ) { + return set.inst._popover( content, this.button(this[0].node), options ); + } ); +} ); + +// Get the container elements +DataTable.Api.register( 'buttons().containers()', function () { + var jq = $(); + var groupSelector = this._groupSelector; + + // We need to use the group selector directly, since if there are no buttons + // the result set will be empty + this.iterator( true, 'table', function ( ctx ) { + if ( ctx._buttons ) { + var insts = Buttons.instanceSelector( groupSelector, ctx._buttons ); + + for ( var i=0, ien=insts.length ; i'+title+'' : ''; + + _fadeIn( + $('
') + .html( title ) + .append( $('
')[ typeof message === 'string' ? 'html' : 'append' ]( message ) ) + .css( 'display', 'none' ) + .appendTo( 'body' ) + ); + + if ( time !== undefined && time !== 0 ) { + _infoTimer = setTimeout( function () { + that.buttons.info( false ); + }, time ); + } + + this.on('destroy.btn-info', function () { + that.buttons.info(false); + }); + + return this; +} ); + +// Get data from the table for export - this is common to a number of plug-in +// buttons so it is included in the Buttons core library +DataTable.Api.register( 'buttons.exportData()', function ( options ) { + if ( this.context.length ) { + return _exportData( new DataTable.Api( this.context[0] ), options ); + } +} ); + +// Get information about the export that is common to many of the export data +// types (DRY) +DataTable.Api.register( 'buttons.exportInfo()', function ( conf ) { + if ( ! conf ) { + conf = {}; + } + + return { + filename: _filename( conf ), + title: _title( conf ), + messageTop: _message(this, conf.message || conf.messageTop, 'top'), + messageBottom: _message(this, conf.messageBottom, 'bottom') + }; +} ); + + + +/** + * Get the file name for an exported file. + * + * @param {object} config Button configuration + * @param {boolean} incExtension Include the file name extension + */ +var _filename = function ( config ) +{ + // Backwards compatibility + var filename = config.filename === '*' && config.title !== '*' && config.title !== undefined && config.title !== null && config.title !== '' ? + config.title : + config.filename; + + if ( typeof filename === 'function' ) { + filename = filename(); + } + + if ( filename === undefined || filename === null ) { + return null; + } + + if ( filename.indexOf( '*' ) !== -1 ) { + filename = filename.replace( '*', $('head > title').text() ).trim(); + } + + // Strip characters which the OS will object to + filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, ""); + + var extension = _stringOrFunction( config.extension ); + if ( ! extension ) { + extension = ''; + } + + return filename + extension; +}; + +/** + * Simply utility method to allow parameters to be given as a function + * + * @param {undefined|string|function} option Option + * @return {null|string} Resolved value + */ +var _stringOrFunction = function ( option ) +{ + if ( option === null || option === undefined ) { + return null; + } + else if ( typeof option === 'function' ) { + return option(); + } + return option; +}; + +/** + * Get the title for an exported file. + * + * @param {object} config Button configuration + */ +var _title = function ( config ) +{ + var title = _stringOrFunction( config.title ); + + return title === null ? + null : title.indexOf( '*' ) !== -1 ? + title.replace( '*', $('head > title').text() || 'Exported data' ) : + title; +}; + +var _message = function ( dt, option, position ) +{ + var message = _stringOrFunction( option ); + if ( message === null ) { + return null; + } + + var caption = $('caption', dt.table().container()).eq(0); + if ( message === '*' ) { + var side = caption.css( 'caption-side' ); + if ( side !== position ) { + return null; + } + + return caption.length ? + caption.text() : + ''; + } + + return message; +}; + + + + +var _exportTextarea = $('