META-INF.resources.bower_components.datatables.net-buttons.js.dataTables.buttons.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jwebmp-data-tables Show documentation
Show all versions of jwebmp-data-tables Show documentation
The JWebSwing implementation for Data Tables
/*! Buttons for DataTables 1.5.2
* ©2016-2018 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;
/**
* [Buttons description]
* @param {[type]}
* @param {[type]}
*/
var Buttons = function (dt, config) {
// 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 ($.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
* @return {Buttons} Self for chaining
*/
add: function (config, idx) {
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 < ien; i++) {
base = base.buttons[split[i] * 1];
}
buttons = base.buttons;
idx = split[split.length - 1] * 1;
}
this._expandButton(buttons, config, false, idx);
this._draw();
return this;
},
/**
* Get the container node for the buttons
* @return {jQuery} Buttons node
*/
container: function () {
return this.dom.container;
},
/**
* Disable a button
* @param {node} node Button node
* @return {Buttons} Self for chaining
*/
disable: function (node) {
var button = this._nodeToButton(node);
$(button.node).addClass(this.c.dom.button.disabled);
return this;
},
/**
* Destroy the instance, cleaning up event handlers and removing DOM
* elements
* @return {Buttons} Self for chaining
*/
destroy: function () {
// Key event listener
$('body').off('keyup.' + this.s.namespace);
// Individual button destroy (so they can remove their own events if
// needed). Take a copy as the array is modified by `remove`
var buttons = this.s.buttons.slice();
var i, ien;
for (i = 0, ien = buttons.length; i < ien; i++) {
this.remove(buttons[i].node);
}
// Container
this.dom.container.remove();
// Remove from the settings object collection
var buttonInsts = this.s.dt.settings()[0];
for (i = 0, ien = buttonInsts.length; i < ien; i++) {
if (buttonInsts.inst === this) {
buttonInsts.splice(i, 1);
break;
}
}
return this;
},
/**
* Enable / disable a button
* @param {node} node Button node
* @param {boolean} [flag=true] Enable / disable flag
* @return {Buttons} Self for chaining
*/
enable: function (node, flag) {
if (flag === false) {
return this.disable(node);
}
var button = this._nodeToButton(node);
$(button.node).removeClass(this.c.dom.button.disabled);
return this;
},
/**
* Get the instance name for the button set selector
* @return {string} Instance name
*/
name: function () {
return this.c.name;
},
/**
* Get a button's node
* @param {node} node Button node
* @return {jQuery} Button element
*/
node: function (node) {
var button = this._nodeToButton(node);
return $(button.node);
},
/**
* Set / get a processing class on the selected button
* @param {boolean} flag true to add, false to remove, undefined to get
* @return {boolean|Buttons} Getter value or this if a setter.
*/
processing: function (node, flag) {
var button = this._nodeToButton(node);
if (flag === undefined) {
return $(button.node).hasClass('processing');
}
$(button.node).toggleClass('processing', flag);
return this;
},
/**
* Remove a button.
* @param {node} node Button node
* @return {Buttons} Self for chaining
*/
remove: function (node) {
var button = this._nodeToButton(node);
var host = this._nodeToHost(node);
var dt = this.s.dt;
// Remove any child buttons first
if (button.buttons.length) {
for (var i = button.buttons.length - 1; i >= 0; i--) {
this.remove(button.buttons[i].node);
}
}
// 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).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 < ien; i++) {
this.add(buttons[i]);
}
dt.on('destroy', function (e, settings) {
if (settings === dtSettings) {
that.destroy();
}
});
// Global key event binding to listen for button keys
$('body').on('keyup.' + this.s.namespace, function (e) {
if (!document.activeElement || document.activeElement === document.body) {
// SUse a string of characters for fast lookup of if we need to
// handle this
var character = String.fromCharCode(e.keyCode).toLowerCase();
if (that.s.listenKeys.toLowerCase().indexOf(character) !== -1) {
that._keypress(character, e);
}
}
});
},
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Private methods
*/
/**
* Add a new button to the key press listener
* @param {object} conf Resolved button configuration object
* @private
*/
_addKey: function (conf) {
if (conf.key) {
this.s.listenKeys += $.isPlainObject(conf.key) ?
conf.key.key :
conf.key;
}
},
/**
* Insert the buttons into the container. Call without parameters!
* @param {node} [container] Recursive only - Insert point
* @param {array} [buttons] Recursive only - Buttons array
* @private
*/
_draw: function (container, buttons) {
if (!container) {
container = this.dom.container;
buttons = this.s.buttons;
}
container.children().detach();
for (var i = 0, ien = buttons.length; i < ien; i++) {
container.append(buttons[i].inserter);
container.append(' ');
if (buttons[i].buttons && buttons[i].buttons.length) {
this._draw(buttons[i].collection, buttons[i].buttons);
}
}
},
/**
* Create buttons from an array of buttons
* @param {array} attachTo Buttons array to attach to
* @param {object} button Button definition
* @param {boolean} inCollection true if the button is in a collection
* @private
*/
_expandButton: function (attachTo, button, inCollection, attachPoint) {
var dt = this.s.dt;
var buttonCounter = 0;
var buttons = !$.isArray(button) ?
[button] :
button;
for (var i = 0, ien = buttons.length; i < ien; i++) {
var conf = this._resolveExtends(buttons[i]);
if (!conf) {
continue;
}
// If the configuration is an array, then expand the buttons at this
// point
if ($.isArray(conf)) {
this._expandButton(attachTo, conf, inCollection, attachPoint);
continue;
}
var built = this._buildButton(conf, inCollection);
if (!built) {
continue;
}
if (attachPoint !== undefined) {
attachTo.splice(attachPoint, 0, built);
attachPoint++;
}
else {
attachTo.push(built);
}
if (built.conf.buttons) {
var collectionDom = this.c.dom.collection;
built.collection = $('<' + collectionDom.tag + '/>')
.addClass(collectionDom.className)
.attr('role', 'menu');
built.conf._collection = built.collection;
this._expandButton(built.buttons, built.conf.buttons, true, attachPoint);
}
// 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) {
var buttonDom = this.c.dom.button;
var linerDom = this.c.dom.buttonLiner;
var collectionDom = this.c.dom.collection;
var dt = this.s.dt;
var text = function (opt) {
return typeof opt === 'function' ?
opt(dt, button, config) :
opt;
};
if (inCollection && collectionDom.button) {
buttonDom = collectionDom.button;
}
if (inCollection && collectionDom.buttonLiner) {
linerDom = collectionDom.buttonLiner;
}
// Make sure that the button is available based on whatever requirements
// it has. For example, Flash buttons require Flash
if (config.available && !config.available(dt, config)) {
return false;
}
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 button = $('<' + tag + '/>')
.addClass(buttonDom.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);
}
button.blur();
})
.on('keyup.dtb', function (e) {
if (e.keyCode === 13) {
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++);
}
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);
return {
conf: config,
node: button.get(0),
inserter: inserter,
buttons: [],
inCollection: inCollection,
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 < ien; i++) {
if (buttons[i].node === node) {
return buttons[i];
}
if (buttons[i].buttons.length) {
var ret = this._nodeToButton(node, buttons[i].buttons);
if (ret) {
return ret;
}
}
}
},
/**
* Get container array for a button from a button node (recursive)
* @param {node} node Button node
* @param {array} [buttons] Button array, uses base if not defined
* @return {array} Button's host array
* @private
*/
_nodeToHost: function (node, buttons) {
if (!buttons) {
buttons = this.s.buttons;
}
for (var i = 0, ien = buttons.length; i < ien; i++) {
if (buttons[i].node === node) {
return buttons;
}
if (buttons[i].buttons.length) {
var ret = this._nodeToHost(node, buttons[i].buttons);
if (ret) {
return ret;
}
}
}
},
/**
* Handle a key press - determine if any button's key configured matches
* what was typed and trigger the action if so.
* @param {string} character The character pressed
* @param {object} e Key event that triggered this call
* @private
*/
_keypress: function (character, e) {
// Check if this button press already activated on another instance of Buttons
if (e._buttonsHandled) {
return;
}
var run = function (conf, node) {
if (!conf.key) {
return;
}
if (conf.key === character) {
e._buttonsHandled = true;
$(node).click();
}
else if ($.isPlainObject(conf.key)) {
if (conf.key.key !== character) {
return;
}
if (conf.key.shiftKey && !e.shiftKey) {
return;
}
if (conf.key.altKey && !e.altKey) {
return;
}
if (conf.key.ctrlKey && !e.ctrlKey) {
return;
}
if (conf.key.metaKey && !e.metaKey) {
return;
}
// Made it this far - it is good
e._buttonsHandled = true;
$(node).click();
}
};
var recurse = function (a) {
for (var i = 0, ien = a.length; i < ien; i++) {
run(a[i].conf, a[i].node);
if (a[i].buttons.length) {
recurse(a[i].buttons);
}
}
};
recurse(this.s.buttons);
},
/**
* Remove a key from the key listener for this instance (to be used when a
* button is removed)
* @param {object} conf Button configuration
* @private
*/
_removeKey: function (conf) {
if (conf.key) {
var character = $.isPlainObject(conf.key) ?
conf.key.key :
conf.key;
// Remove only one character, as multiple buttons could have the
// same listening key
var a = this.s.listenKeys.split('');
var idx = $.inArray(character, a);
a.splice(idx, 1);
this.s.listenKeys = a.join('');
}
},
/**
* Resolve a button configuration
* @param {string|function|object} conf Button config to resolve
* @return {object} Button configuration
* @private
*/
_resolveExtends: function (conf) {
var dt = this.s.dt;
var i, ien;
var toConfObject = function (base) {
var loop = 0;
// Loop until we have resolved to a button configuration, or an
// array of button configurations (which will be iterated
// separately)
while (!$.isPlainObject(base) && !$.isArray(base)) {
if (base === undefined) {
return;
}
if (typeof base === 'function') {
base = base(dt, conf);
if (!base) {
return false;
}
}
else if (typeof base === 'string') {
if (!_dtButtons[base]) {
throw 'Unknown button type: ' + base;
}
base = _dtButtons[base];
}
loop++;
if (loop > 30) {
// Protect against misconfiguration killing the browser
throw 'Buttons: Too many iterations';
}
}
return $.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 ($.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;
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 < ien; i++) {
conf.buttons.push(postfixButtons[i]);
}
conf.postfixButtons = null;
}
var prefixButtons = conf.prefixButtons;
if (prefixButtons) {
if (!conf.buttons) {
conf.buttons = [];
}
for (i = 0, ien = prefixButtons.length; i < ien; i++) {
conf.buttons.splice(i, 0, prefixButtons[i]);
}
conf.prefixButtons = null;
}
// Although we want the `conf` object to overwrite almost all of
// the properties of the object being extended, the `extend`
// property should come from the object being extended
conf.extend = objArray.extend;
}
return conf;
}
});
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* 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) {
if (fade === undefined) {
fade = 400;
}
if (show) {
$('')
.addClass(className)
.css('display', 'none')
.appendTo('body')
.fadeIn(fade);
}
else {
$('body > div.' + className)
.fadeOut(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) {
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 ($.isArray(input)) {
for (var i = 0, ien = input.length; i < ien; i++) {
process(input[i]);
}
return;
}
if (typeof input === 'string') {
if (input.indexOf(',') !== -1) {
// String selector, list of names
process(input.split(','));
}
else {
// String selector individual name
var idx = $.inArray($.trim(input), names);
if (idx !== -1) {
ret.push(buttons[idx].inst);
}
}
}
else if (typeof input === 'number') {
// Index selector
ret.push(buttons[input].inst);
}
};
process(group);
return ret;
};
/**
* Button selector - select one or more buttons from a selector input so some
* operation can be performed on them.
* @param {array} Button instances array that the selector should operate on
* @param {string|int|node|jQuery|array} Button selector - see
* `button-selector` documentation on the DataTables site
* @return {array} Array of objects containing `inst` and `idx` properties of
* the selected buttons so you know which instance each button belongs to.
* @static
*/
Buttons.buttonSelector = function (insts, selector) {
var ret = [];
var nodeBuilder = function (a, buttons, baseIdx) {
var button;
var idx;
for (var i = 0, ien = buttons.length; i < ien; i++) {
button = buttons[i];
if (button) {
idx = baseIdx !== undefined ?
baseIdx + i :
i + '';
a.push({
node: button.node,
name: button.conf.name,
idx: idx
});
if (button.buttons) {
nodeBuilder(a, button.buttons, idx + '-');
}
}
}
};
var run = function (selector, inst) {
var i, ien;
var buttons = [];
nodeBuilder(buttons, inst.s.buttons);
var nodes = $.map(buttons, function (v) {
return v.node;
});
if ($.isArray(selector) || selector instanceof $) {
for (i = 0, ien = selector.length; i < ien; i++) {
run(selector[i], inst);
}
return;
}
if (selector === null || selector === undefined || selector === '*') {
// Select all
for (i = 0, ien = buttons.length; i < ien; i++) {
ret.push({
inst: inst,
node: buttons[i].node
});
}
}
else if (typeof selector === 'number') {
// Main button index selector
ret.push({
inst: inst,
node: inst.s.buttons[selector].node
});
}
else if (typeof selector === 'string') {
if (selector.indexOf(',') !== -1) {
// Split
var a = selector.split(',');
for (i = 0, ien = a.length; i < ien; i++) {
run($.trim(a[i]), inst);
}
}
else if (selector.match(/^\d+(\-\d+)*$/)) {
// Sub-button index selector
var indexes = $.map(buttons, function (v) {
return v.idx;
});
ret.push({
inst: inst,
node: buttons[$.inArray(selector, indexes)].node
});
}
else if (selector.indexOf(':name') !== -1) {
// Button name selector
var name = selector.replace(':name', '');
for (i = 0, ien = buttons.length; i < ien; i++) {
if (buttons[i].name === name) {
ret.push({
inst: inst,
node: buttons[i].node
});
}
}
}
else {
// jQuery selector on the nodes
$(nodes).filter(selector).each(function () {
ret.push({
inst: inst,
node: this
});
});
}
}
else if (typeof selector === 'object' && selector.nodeName) {
// Node selector
var idx = $.inArray(selector, nodes);
if (idx !== -1) {
ret.push({
inst: inst,
node: nodes[idx]
});
}
}
};
for (var i = 0, ien = insts.length; i < ien; i++) {
var inst = insts[i];
run(selector, inst);
}
return ret;
};
/**
* 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: 'dt-button-collection'
},
button: {
tag: 'button',
className: 'dt-button',
active: 'active',
disabled: 'disabled'
},
buttonLiner: {
tag: 'span',
className: ''
}
}
};
/**
* Version information
* @type {string}
* @static
*/
Buttons.version = '1.5.2';
$.extend(_dtButtons, {
collection: {
text: function (dt) {
return dt.i18n('buttons.collection', 'Collection');
},
className: 'buttons-collection',
action: function (e, dt, button, config) {
var host = button;
var collectionParent = $(button).parents('div.dt-button-collection');
var hostPosition = host.position();
var tableContainer = $(dt.table().container());
var multiLevel = false;
var insertPoint = host;
// Remove any old collection
if (collectionParent.length) {
multiLevel = $('.dt-button-collection').position();
insertPoint = collectionParent;
$('body').trigger('click.dtb-collection');
}
if (insertPoint.parents('body')[0] !== document.body) {
insertPoint = document.body.lastChild;
}
config._collection
.addClass(config.collectionLayout)
.css('display', 'none')
.insertAfter(insertPoint)
.fadeIn(config.fade);
var position = config._collection.css('position');
if (multiLevel && position === 'absolute') {
config._collection.css({
top: multiLevel.top,
left: multiLevel.left
});
}
else if (position === 'absolute') {
config._collection.css({
top: hostPosition.top + host.outerHeight(),
left: hostPosition.left
});
// calculate overflow when positioned beneath
var tableBottom = tableContainer.offset().top + tableContainer.height();
var listBottom = hostPosition.top + host.outerHeight() + config._collection.outerHeight();
var bottomOverflow = listBottom - tableBottom;
// calculate overflow when positioned above
var listTop = hostPosition.top - config._collection.outerHeight();
var tableTop = tableContainer.offset().top;
var topOverflow = tableTop - listTop;
// if bottom overflow is larger, move to the top because it fits better, or if dropup is requested
if (bottomOverflow > topOverflow || config.dropup) {
config._collection.css('top', hostPosition.top - config._collection.outerHeight() - 5);
}
// Right alignment in table container
var listRight = hostPosition.left + config._collection.outerWidth();
var tableRight = tableContainer.offset().left + tableContainer.width();
if (listRight > tableRight) {
config._collection.css('left', hostPosition.left - (listRight - tableRight));
}
// Right alignment to window
var listOffsetRight = host.offset().left + config._collection.outerWidth();
if (listOffsetRight > $(window).width()) {
config._collection.css('left', hostPosition.left - (listOffsetRight - $(window).width()));
}
}
else {
// Fix position - centre on screen
var top = config._collection.height() / 2;
if (top > $(window).height() / 2) {
top = $(window).height() / 2;
}
config._collection.css('marginTop', top * -1);
}
if (config.background) {
Buttons.background(true, config.backgroundClassName, config.fade);
}
var close = function () {
config._collection
.fadeOut(config.fade, function () {
config._collection.detach();
});
$('div.dt-button-background').off('click.dtb-collection');
Buttons.background(false, config.backgroundClassName, config.fade);
$('body').off('.dtb-collection');
dt.off('buttons-action.b-internal');
};
// Need to break the 'thread' for the collection button being
// activated by a click - it would also trigger this event
setTimeout(function () {
// 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 () {
});
$('body')
.on('click.dtb-collection', function (e) {
// andSelf is deprecated in jQ1.8, but we want 1.7 compat
var back = $.fn.addBack ? 'addBack' : 'andSelf';
if (!$(e.target).parents()[back]().filter(config._collection).length) {
close();
}
})
.on('keyup.dtb-collection', function (e) {
if (e.keyCode === 27) {
close();
}
});
if (config.autoClose) {
dt.on('buttons-action.b-internal', function () {
close();
});
}
}, 10);
},
background: true,
collectionLayout: '',
backgroundClassName: 'dt-button-background',
autoClose: false,
fade: 400,
attr: {
'aria-haspopup': true
}
},
copy: function (dt, conf) {
if (_dtButtons.copyHtml5) {
return 'copyHtml5';
}
if (_dtButtons.copyFlash && _dtButtons.copyFlash.available(dt, conf)) {
return 'copyFlash';
}
},
csv: function (dt, conf) {
// Common option that will use the HTML5 or Flash export buttons
if (_dtButtons.csvHtml5 && _dtButtons.csvHtml5.available(dt, conf)) {
return 'csvHtml5';
}
if (_dtButtons.csvFlash && _dtButtons.csvFlash.available(dt, conf)) {
return 'csvFlash';
}
},
excel: function (dt, conf) {
// Common option that will use the HTML5 or Flash export buttons
if (_dtButtons.excelHtml5 && _dtButtons.excelHtml5.available(dt, conf)) {
return 'excelHtml5';
}
if (_dtButtons.excelFlash && _dtButtons.excelFlash.available(dt, conf)) {
return 'excelFlash';
}
},
pdf: function (dt, conf) {
// Common option that will use the HTML5 or Flash export buttons
if (_dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available(dt, conf)) {
return 'pdfHtml5';
}
if (_dtButtons.pdfFlash && _dtButtons.pdfFlash.available(dt, conf)) {
return 'pdfFlash';
}
},
pageLength: function (dt) {
var lengthMenu = dt.settings()[0].aLengthMenu;
var vals = $.isArray(lengthMenu[0]) ? lengthMenu[0] : lengthMenu;
var lang = $.isArray(lengthMenu[0]) ? lengthMenu[1] : lengthMenu;
var text = function (dt) {
return dt.i18n('buttons.pageLength', {
"-1": 'Show all rows',
_: 'Show %d rows'
}, dt.page.len());
};
return {
extend: 'collection',
text: text,
className: 'buttons-page-length',
autoClose: true,
buttons: $.map(vals, function (val, i) {
return {
text: lang[i],
className: 'button-page-length',
action: function (e, dt) {
dt.page.len(val).draw();
},
init: function (dt, node, conf) {
var that = this;
var fn = function () {
that.active(dt.page.len() === val);
};
dt.on('length.dt' + conf.namespace, fn);
fn();
},
destroy: function (dt, node, conf) {
dt.off('length.dt' + conf.namespace);
}
};
}),
init: function (dt, node, conf) {
var that = this;
dt.on('length.dt' + conf.namespace, function () {
that.text(text(dt));
});
},
destroy: function (dt, node, conf) {
dt.off('length.dt' + conf.namespace);
}
};
}
});
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* DataTables API
*
* For complete documentation, please refer to the docs/api directory or the
* DataTables site
*/
// Buttons group and individual button selector
DataTable.Api.register('buttons()', function (group, selector) {
// Argument shifting
if (selector === undefined) {
selector = group;
group = undefined;
}
this.selector.buttonGroup = group;
var res = this.iterator(true, 'table', function (ctx) {
if (ctx._buttons) {
return Buttons.buttonSelector(
Buttons.instanceSelector(group, ctx._buttons),
selector
);
}
}, true);
res._groupSelector = group;
return res;
});
// Individual button selector
DataTable.Api.register('button()', function (group, selector) {
// just run buttons() and truncate
var buttons = this.buttons(group, selector);
if (buttons.length > 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);
});
});
// 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);
});
});
// 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');
});
});
// Get the container elements
DataTable.Api.registerPlural('buttons().containers()', 'buttons().container()', 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 < ien; i++) {
jq = jq.add(insts[i].container());
}
}
});
return jq;
});
// Add a new button
DataTable.Api.register('button().add()', function (idx, conf) {
var ctx = this.context;
// Don't use `this` as it could be empty - select the instances directly
if (ctx.length) {
var inst = Buttons.instanceSelector(this._groupSelector, ctx[0]._buttons);
if (inst.length) {
inst[0].add(conf, idx);
}
}
return this.button(this._groupSelector, idx);
});
// Destroy the button sets selected
DataTable.Api.register('buttons().destroy()', function () {
this.pluck('inst').unique().each(function (inst) {
inst.destroy();
});
return this;
});
// Remove a button
DataTable.Api.registerPlural('buttons().remove()', 'buttons().remove()', function () {
this.each(function (set) {
set.inst.remove(set.node);
});
return this;
});
// Information box that can be used by buttons
var _infoTimer;
DataTable.Api.register('buttons.info()', function (title, message, time) {
var that = this;
if (title === false) {
$('#datatables_buttons_info').fadeOut(function () {
$(this).remove();
});
clearTimeout(_infoTimer);
_infoTimer = null;
return this;
}
if (_infoTimer) {
clearTimeout(_infoTimer);
}
if ($('#datatables_buttons_info').length) {
$('#datatables_buttons_info').remove();
}
title = title ? '' + title + '
' : '';
$('')
.html(title)
.append($('')[typeof message === 'string' ? 'html' : 'append'](message))
.css('display', 'none')
.appendTo('body')
.fadeIn();
if (time !== undefined && time !== 0) {
_infoTimer = setTimeout(function () {
that.buttons.info(false);
}, time);
}
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 = $.trim(filename.replace('*', $('head > title').text()));
}
// 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 = $('')[0];
var _exportData = function (dt, inOpts) {
var config = $.extend(true, {}, {
rows: null,
columns: '',
modifier: {
search: 'applied',
order: 'applied'
},
orthogonal: 'display',
stripHtml: true,
stripNewlines: true,
decodeEntities: true,
trim: true,
format: {
header: function (d) {
return strip(d);
},
footer: function (d) {
return strip(d);
},
body: function (d) {
return strip(d);
}
},
customizeData: null
}, inOpts);
var strip = function (str) {
if (typeof str !== 'string') {
return str;
}
// Always remove script tags
str = str.replace(/