cherrypy-foundation 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cherrypy_foundation/__init__.py +0 -0
- cherrypy_foundation/components/ColorModes.jinja +70 -0
- cherrypy_foundation/components/Datatable.css +47 -0
- cherrypy_foundation/components/Datatable.jinja +63 -0
- cherrypy_foundation/components/Datatable.js +358 -0
- cherrypy_foundation/components/Field.css +10 -0
- cherrypy_foundation/components/Field.jinja +66 -0
- cherrypy_foundation/components/Field.js +56 -0
- cherrypy_foundation/components/Fields.jinja +4 -0
- cherrypy_foundation/components/Flash.jinja +13 -0
- cherrypy_foundation/components/Icon.jinja +3 -0
- cherrypy_foundation/components/LocaleSelection.jinja +13 -0
- cherrypy_foundation/components/LocaleSelection.js +26 -0
- cherrypy_foundation/components/SideBySideMultiSelect.css +25 -0
- cherrypy_foundation/components/SideBySideMultiSelect.jinja +9 -0
- cherrypy_foundation/components/SideBySideMultiSelect.js +9 -0
- cherrypy_foundation/components/Typeahead.css +55 -0
- cherrypy_foundation/components/Typeahead.jinja +106 -0
- cherrypy_foundation/components/Typeahead.js +8 -0
- cherrypy_foundation/components/__init__.py +51 -0
- cherrypy_foundation/components/tests/__init__.py +0 -0
- cherrypy_foundation/components/tests/test_static.py +90 -0
- cherrypy_foundation/components/vendor/bootstrap-icons/bootstrap-icons.css +2106 -0
- cherrypy_foundation/components/vendor/bootstrap-icons/bootstrap-icons.min.css +5 -0
- cherrypy_foundation/components/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
- cherrypy_foundation/components/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
- cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.css +9262 -0
- cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.css.map +95 -0
- cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.min.css +6 -0
- cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.min.css.map +7 -0
- cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.js +4846 -0
- cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.js.map +1 -0
- cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.min.js +7 -0
- cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.min.js.map +7 -0
- cherrypy_foundation/components/vendor/bootstrap5/js/color-modes.js +80 -0
- cherrypy_foundation/components/vendor/datatables/css/dataTables.dataTables.css +849 -0
- cherrypy_foundation/components/vendor/datatables/css/dataTables.dataTables.min.css +1 -0
- cherrypy_foundation/components/vendor/datatables/images/sort_asc.png +0 -0
- cherrypy_foundation/components/vendor/datatables/images/sort_asc_disabled.png +0 -0
- cherrypy_foundation/components/vendor/datatables/images/sort_both.png +0 -0
- cherrypy_foundation/components/vendor/datatables/images/sort_desc.png +0 -0
- cherrypy_foundation/components/vendor/datatables/images/sort_desc_disabled.png +0 -0
- cherrypy_foundation/components/vendor/datatables/js/dataTables.js +14073 -0
- cherrypy_foundation/components/vendor/datatables/js/dataTables.min.js +4 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Buttons/css/buttons.dataTables.css +556 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Buttons/css/buttons.dataTables.min.css +1 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/buttons.html5.js +1700 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/buttons.html5.min.js +8 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/dataTables.buttons.js +2944 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/dataTables.buttons.min.js +4 -0
- cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/css/fixedHeader.dataTables.css +13 -0
- cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/css/fixedHeader.dataTables.min.css +1 -0
- cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/js/dataTables.fixedHeader.js +1202 -0
- cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/js/dataTables.fixedHeader.min.js +4 -0
- cherrypy_foundation/components/vendor/datatables-extensions/JSZip/jszip.js +11577 -0
- cherrypy_foundation/components/vendor/datatables-extensions/JSZip/jszip.min.js +13 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Responsive/css/responsive.dataTables.css +194 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Responsive/css/responsive.dataTables.min.css +1 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Responsive/js/dataTables.responsive.js +1861 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Responsive/js/dataTables.responsive.min.js +4 -0
- cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/pdfmake.js +75023 -0
- cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/pdfmake.min.js +3 -0
- cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/vfs_fonts.js +6 -0
- cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/css/rowGroup.dataTables.css +53 -0
- cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/css/rowGroup.dataTables.min.css +1 -0
- cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/js/dataTables.rowGroup.js +485 -0
- cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/js/dataTables.rowGroup.min.js +4 -0
- cherrypy_foundation/components/vendor/jquery/jquery.min.js +2 -0
- cherrypy_foundation/components/vendor/multi/LICENSE +7 -0
- cherrypy_foundation/components/vendor/multi/README.md +109 -0
- cherrypy_foundation/components/vendor/multi/multi.css +95 -0
- cherrypy_foundation/components/vendor/multi/multi.js +328 -0
- cherrypy_foundation/components/vendor/popper/popper.js +1825 -0
- cherrypy_foundation/components/vendor/popper/popper.min.js +6 -0
- cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.css +1 -0
- cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.js +10 -0
- cherrypy_foundation/error_page.py +94 -0
- cherrypy_foundation/flash.py +50 -0
- cherrypy_foundation/form.py +119 -0
- cherrypy_foundation/logging.py +103 -0
- cherrypy_foundation/passwd.py +65 -0
- cherrypy_foundation/plugins/__init__.py +0 -0
- cherrypy_foundation/plugins/db.py +286 -0
- cherrypy_foundation/plugins/ldap.py +257 -0
- cherrypy_foundation/plugins/restapi.py +74 -0
- cherrypy_foundation/plugins/scheduler.py +287 -0
- cherrypy_foundation/plugins/smtp.py +223 -0
- cherrypy_foundation/plugins/tests/__init__.py +0 -0
- cherrypy_foundation/plugins/tests/test_db.py +118 -0
- cherrypy_foundation/plugins/tests/test_ldap.py +451 -0
- cherrypy_foundation/plugins/tests/test_scheduler.py +100 -0
- cherrypy_foundation/plugins/tests/test_scheduler_db.py +107 -0
- cherrypy_foundation/plugins/tests/test_smtp.py +140 -0
- cherrypy_foundation/sessions.py +93 -0
- cherrypy_foundation/tests/__init__.py +72 -0
- cherrypy_foundation/tests/templates/test_flash.html +9 -0
- cherrypy_foundation/tests/templates/test_form.html +16 -0
- cherrypy_foundation/tests/templates/test_url.html +15 -0
- cherrypy_foundation/tests/test_error_page.py +78 -0
- cherrypy_foundation/tests/test_flash.py +61 -0
- cherrypy_foundation/tests/test_form.py +148 -0
- cherrypy_foundation/tests/test_logging.py +78 -0
- cherrypy_foundation/tests/test_passwd.py +51 -0
- cherrypy_foundation/tests/test_sessions.py +89 -0
- cherrypy_foundation/tests/test_url.py +161 -0
- cherrypy_foundation/tools/__init__.py +0 -0
- cherrypy_foundation/tools/auth.py +263 -0
- cherrypy_foundation/tools/auth_mfa.py +249 -0
- cherrypy_foundation/tools/i18n.py +529 -0
- cherrypy_foundation/tools/jinja2.py +158 -0
- cherrypy_foundation/tools/ratelimit.py +265 -0
- cherrypy_foundation/tools/secure_headers.py +119 -0
- cherrypy_foundation/tools/sessions_timeout.py +167 -0
- cherrypy_foundation/tools/tests/__init__.py +0 -0
- cherrypy_foundation/tools/tests/components/Button.jinja +2 -0
- cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.mo +0 -0
- cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.po +15 -0
- cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.mo +0 -0
- cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.po +15 -0
- cherrypy_foundation/tools/tests/locales/messages.pot +2 -0
- cherrypy_foundation/tools/tests/templates/test_jinja2.html +11 -0
- cherrypy_foundation/tools/tests/templates/test_jinjax.html +9 -0
- cherrypy_foundation/tools/tests/templates/test_jinjax_i18n.html +22 -0
- cherrypy_foundation/tools/tests/test_auth.py +110 -0
- cherrypy_foundation/tools/tests/test_auth_mfa.py +369 -0
- cherrypy_foundation/tools/tests/test_i18n.py +247 -0
- cherrypy_foundation/tools/tests/test_jinja2.py +153 -0
- cherrypy_foundation/tools/tests/test_ratelimit.py +109 -0
- cherrypy_foundation/tools/tests/test_secure_headers.py +200 -0
- cherrypy_foundation/url.py +66 -0
- cherrypy_foundation/widgets.py +48 -0
- cherrypy_foundation-1.0.0.dist-info/METADATA +71 -0
- cherrypy_foundation-1.0.0.dist-info/RECORD +136 -0
- cherrypy_foundation-1.0.0.dist-info/WHEEL +5 -0
- cherrypy_foundation-1.0.0.dist-info/licenses/LICENSE.md +674 -0
- cherrypy_foundation-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2944 @@
|
|
|
1
|
+
/*! Buttons for DataTables 3.2.3
|
|
2
|
+
* © SpryMedia Ltd - datatables.net/license
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
(function( factory ){
|
|
6
|
+
if ( typeof define === 'function' && define.amd ) {
|
|
7
|
+
// AMD
|
|
8
|
+
define( ['jquery', 'datatables.net'], function ( $ ) {
|
|
9
|
+
return factory( $, window, document );
|
|
10
|
+
} );
|
|
11
|
+
}
|
|
12
|
+
else if ( typeof exports === 'object' ) {
|
|
13
|
+
// CommonJS
|
|
14
|
+
var jq = require('jquery');
|
|
15
|
+
var cjsRequires = function (root, $) {
|
|
16
|
+
if ( ! $.fn.dataTable ) {
|
|
17
|
+
require('datatables.net')(root, $);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
if (typeof window === 'undefined') {
|
|
22
|
+
module.exports = function (root, $) {
|
|
23
|
+
if ( ! root ) {
|
|
24
|
+
// CommonJS environments without a window global must pass a
|
|
25
|
+
// root. This will give an error otherwise
|
|
26
|
+
root = window;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if ( ! $ ) {
|
|
30
|
+
$ = jq( root );
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
cjsRequires( root, $ );
|
|
34
|
+
return factory( $, root, root.document );
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
cjsRequires( window, jq );
|
|
39
|
+
module.exports = factory( jq, window, window.document );
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// Browser
|
|
44
|
+
factory( jQuery, window, document );
|
|
45
|
+
}
|
|
46
|
+
}(function( $, window, document ) {
|
|
47
|
+
'use strict';
|
|
48
|
+
var DataTable = $.fn.dataTable;
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
// Used for namespacing events added to the document by each instance, so they
|
|
53
|
+
// can be removed on destroy
|
|
54
|
+
var _instCounter = 0;
|
|
55
|
+
|
|
56
|
+
// Button namespacing counter for namespacing events on individual buttons
|
|
57
|
+
var _buttonCounter = 0;
|
|
58
|
+
|
|
59
|
+
var _dtButtons = DataTable.ext.buttons;
|
|
60
|
+
|
|
61
|
+
// Custom entity decoder for data export
|
|
62
|
+
var _entityDecoder = null;
|
|
63
|
+
|
|
64
|
+
// Allow for jQuery slim
|
|
65
|
+
function _fadeIn(el, duration, fn) {
|
|
66
|
+
if ($.fn.animate) {
|
|
67
|
+
el.stop().fadeIn(duration, fn);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
el.css('display', 'block');
|
|
71
|
+
|
|
72
|
+
if (fn) {
|
|
73
|
+
fn.call(el);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function _fadeOut(el, duration, fn) {
|
|
79
|
+
if ($.fn.animate) {
|
|
80
|
+
el.stop().fadeOut(duration, fn);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
el.css('display', 'none');
|
|
84
|
+
|
|
85
|
+
if (fn) {
|
|
86
|
+
fn.call(el);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* [Buttons description]
|
|
93
|
+
* @param {[type]}
|
|
94
|
+
* @param {[type]}
|
|
95
|
+
*/
|
|
96
|
+
var Buttons = function (dt, config) {
|
|
97
|
+
if (!DataTable.versionCheck('2')) {
|
|
98
|
+
throw 'Warning: Buttons requires DataTables 2 or newer';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// If not created with a `new` keyword then we return a wrapper function that
|
|
102
|
+
// will take the settings object for a DT. This allows easy use of new instances
|
|
103
|
+
// with the `layout` option - e.g. `topLeft: $.fn.dataTable.Buttons( ... )`.
|
|
104
|
+
if (!(this instanceof Buttons)) {
|
|
105
|
+
return function (settings) {
|
|
106
|
+
return new Buttons(settings, dt).container();
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// If there is no config set it to an empty object
|
|
111
|
+
if (typeof config === 'undefined') {
|
|
112
|
+
config = {};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Allow a boolean true for defaults
|
|
116
|
+
if (config === true) {
|
|
117
|
+
config = {};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// For easy configuration of buttons an array can be given
|
|
121
|
+
if (Array.isArray(config)) {
|
|
122
|
+
config = { buttons: config };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.c = $.extend(true, {}, Buttons.defaults, config);
|
|
126
|
+
|
|
127
|
+
// Don't want a deep copy for the buttons
|
|
128
|
+
if (config.buttons) {
|
|
129
|
+
this.c.buttons = config.buttons;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this.s = {
|
|
133
|
+
dt: new DataTable.Api(dt),
|
|
134
|
+
buttons: [],
|
|
135
|
+
listenKeys: '',
|
|
136
|
+
namespace: 'dtb' + _instCounter++
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
this.dom = {
|
|
140
|
+
container: $('<' + this.c.dom.container.tag + '/>').addClass(
|
|
141
|
+
this.c.dom.container.className
|
|
142
|
+
)
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
this._constructor();
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
$.extend(Buttons.prototype, {
|
|
149
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
150
|
+
* Public methods
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get the action of a button
|
|
155
|
+
* @param {int|string} Button index
|
|
156
|
+
* @return {function}
|
|
157
|
+
*/ /**
|
|
158
|
+
* Set the action of a button
|
|
159
|
+
* @param {node} node Button element
|
|
160
|
+
* @param {function} action Function to set
|
|
161
|
+
* @return {Buttons} Self for chaining
|
|
162
|
+
*/
|
|
163
|
+
action: function (node, action) {
|
|
164
|
+
var button = this._nodeToButton(node);
|
|
165
|
+
|
|
166
|
+
if (action === undefined) {
|
|
167
|
+
return button.conf.action;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
button.conf.action = action;
|
|
171
|
+
|
|
172
|
+
return this;
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Add an active class to the button to make to look active or get current
|
|
177
|
+
* active state.
|
|
178
|
+
* @param {node} node Button element
|
|
179
|
+
* @param {boolean} [flag] Enable / disable flag
|
|
180
|
+
* @return {Buttons} Self for chaining or boolean for getter
|
|
181
|
+
*/
|
|
182
|
+
active: function (node, flag) {
|
|
183
|
+
var button = this._nodeToButton(node);
|
|
184
|
+
var klass = this.c.dom.button.active;
|
|
185
|
+
var jqNode = $(button.node);
|
|
186
|
+
|
|
187
|
+
if (
|
|
188
|
+
button.inCollection &&
|
|
189
|
+
this.c.dom.collection.button &&
|
|
190
|
+
this.c.dom.collection.button.active !== undefined
|
|
191
|
+
) {
|
|
192
|
+
klass = this.c.dom.collection.button.active;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (flag === undefined) {
|
|
196
|
+
return jqNode.hasClass(klass);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
jqNode.toggleClass(klass, flag === undefined ? true : flag);
|
|
200
|
+
|
|
201
|
+
return this;
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Add a new button
|
|
206
|
+
* @param {object} config Button configuration object, base string name or function
|
|
207
|
+
* @param {int|string} [idx] Button index for where to insert the button
|
|
208
|
+
* @param {boolean} [draw=true] Trigger a draw. Set a false when adding
|
|
209
|
+
* lots of buttons, until the last button.
|
|
210
|
+
* @return {Buttons} Self for chaining
|
|
211
|
+
*/
|
|
212
|
+
add: function (config, idx, draw) {
|
|
213
|
+
var buttons = this.s.buttons;
|
|
214
|
+
|
|
215
|
+
if (typeof idx === 'string') {
|
|
216
|
+
var split = idx.split('-');
|
|
217
|
+
var base = this.s;
|
|
218
|
+
|
|
219
|
+
for (var i = 0, ien = split.length - 1; i < ien; i++) {
|
|
220
|
+
base = base.buttons[split[i] * 1];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
buttons = base.buttons;
|
|
224
|
+
idx = split[split.length - 1] * 1;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
this._expandButton(
|
|
228
|
+
buttons,
|
|
229
|
+
config,
|
|
230
|
+
config !== undefined ? config.split : undefined,
|
|
231
|
+
(config === undefined ||
|
|
232
|
+
config.split === undefined ||
|
|
233
|
+
config.split.length === 0) &&
|
|
234
|
+
base !== undefined,
|
|
235
|
+
false,
|
|
236
|
+
idx
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
if (draw === undefined || draw === true) {
|
|
240
|
+
this._draw();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return this;
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Clear buttons from a collection and then insert new buttons
|
|
248
|
+
*/
|
|
249
|
+
collectionRebuild: function (node, newButtons) {
|
|
250
|
+
var button = this._nodeToButton(node);
|
|
251
|
+
|
|
252
|
+
if (newButtons !== undefined) {
|
|
253
|
+
var i;
|
|
254
|
+
// Need to reverse the array
|
|
255
|
+
for (i = button.buttons.length - 1; i >= 0; i--) {
|
|
256
|
+
this.remove(button.buttons[i].node);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// If the collection has prefix and / or postfix buttons we need to add them in
|
|
260
|
+
if (button.conf.prefixButtons) {
|
|
261
|
+
newButtons.unshift.apply(newButtons, button.conf.prefixButtons);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (button.conf.postfixButtons) {
|
|
265
|
+
newButtons.push.apply(newButtons, button.conf.postfixButtons);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
for (i = 0; i < newButtons.length; i++) {
|
|
269
|
+
var newBtn = newButtons[i];
|
|
270
|
+
|
|
271
|
+
this._expandButton(
|
|
272
|
+
button.buttons,
|
|
273
|
+
newBtn,
|
|
274
|
+
newBtn !== undefined &&
|
|
275
|
+
newBtn.config !== undefined &&
|
|
276
|
+
newBtn.config.split !== undefined,
|
|
277
|
+
true,
|
|
278
|
+
newBtn.parentConf !== undefined &&
|
|
279
|
+
newBtn.parentConf.split !== undefined,
|
|
280
|
+
null,
|
|
281
|
+
newBtn.parentConf
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
this._draw(button.collection, button.buttons);
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Get the container node for the buttons
|
|
291
|
+
* @return {jQuery} Buttons node
|
|
292
|
+
*/
|
|
293
|
+
container: function () {
|
|
294
|
+
return this.dom.container;
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Disable a button
|
|
299
|
+
* @param {node} node Button node
|
|
300
|
+
* @return {Buttons} Self for chaining
|
|
301
|
+
*/
|
|
302
|
+
disable: function (node) {
|
|
303
|
+
var button = this._nodeToButton(node);
|
|
304
|
+
|
|
305
|
+
if (button.isSplit) {
|
|
306
|
+
$(button.node.childNodes[0])
|
|
307
|
+
.addClass(this.c.dom.button.disabled)
|
|
308
|
+
.prop('disabled', true);
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
$(button.node)
|
|
312
|
+
.addClass(this.c.dom.button.disabled)
|
|
313
|
+
.prop('disabled', true);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
button.disabled = true;
|
|
317
|
+
|
|
318
|
+
this._checkSplitEnable();
|
|
319
|
+
|
|
320
|
+
return this;
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Destroy the instance, cleaning up event handlers and removing DOM
|
|
325
|
+
* elements
|
|
326
|
+
* @return {Buttons} Self for chaining
|
|
327
|
+
*/
|
|
328
|
+
destroy: function () {
|
|
329
|
+
// Key event listener
|
|
330
|
+
$('body').off('keyup.' + this.s.namespace);
|
|
331
|
+
|
|
332
|
+
// Individual button destroy (so they can remove their own events if
|
|
333
|
+
// needed). Take a copy as the array is modified by `remove`
|
|
334
|
+
var buttons = this.s.buttons.slice();
|
|
335
|
+
var i, ien;
|
|
336
|
+
|
|
337
|
+
for (i = 0, ien = buttons.length; i < ien; i++) {
|
|
338
|
+
this.remove(buttons[i].node);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Container
|
|
342
|
+
this.dom.container.remove();
|
|
343
|
+
|
|
344
|
+
// Remove from the settings object collection
|
|
345
|
+
var buttonInsts = this.s.dt.settings()[0];
|
|
346
|
+
|
|
347
|
+
for (i = 0, ien = buttonInsts.length; i < ien; i++) {
|
|
348
|
+
if (buttonInsts.inst === this) {
|
|
349
|
+
buttonInsts.splice(i, 1);
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return this;
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Enable / disable a button
|
|
359
|
+
* @param {node} node Button node
|
|
360
|
+
* @param {boolean} [flag=true] Enable / disable flag
|
|
361
|
+
* @return {Buttons} Self for chaining
|
|
362
|
+
*/
|
|
363
|
+
enable: function (node, flag) {
|
|
364
|
+
if (flag === false) {
|
|
365
|
+
return this.disable(node);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
var button = this._nodeToButton(node);
|
|
369
|
+
|
|
370
|
+
if (button.isSplit) {
|
|
371
|
+
$(button.node.childNodes[0])
|
|
372
|
+
.removeClass(this.c.dom.button.disabled)
|
|
373
|
+
.prop('disabled', false);
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
$(button.node)
|
|
377
|
+
.removeClass(this.c.dom.button.disabled)
|
|
378
|
+
.prop('disabled', false);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
button.disabled = false;
|
|
382
|
+
|
|
383
|
+
this._checkSplitEnable();
|
|
384
|
+
|
|
385
|
+
return this;
|
|
386
|
+
},
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Get a button's index
|
|
390
|
+
*
|
|
391
|
+
* This is internally recursive
|
|
392
|
+
* @param {element} node Button to get the index of
|
|
393
|
+
* @return {string} Button index
|
|
394
|
+
*/
|
|
395
|
+
index: function (node, nested, buttons) {
|
|
396
|
+
if (!nested) {
|
|
397
|
+
nested = '';
|
|
398
|
+
buttons = this.s.buttons;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
for (var i = 0, ien = buttons.length; i < ien; i++) {
|
|
402
|
+
var inner = buttons[i].buttons;
|
|
403
|
+
|
|
404
|
+
if (buttons[i].node === node) {
|
|
405
|
+
return nested + i;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (inner && inner.length) {
|
|
409
|
+
var match = this.index(node, i + '-', inner);
|
|
410
|
+
|
|
411
|
+
if (match !== null) {
|
|
412
|
+
return match;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return null;
|
|
418
|
+
},
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Get the instance name for the button set selector
|
|
422
|
+
* @return {string} Instance name
|
|
423
|
+
*/
|
|
424
|
+
name: function () {
|
|
425
|
+
return this.c.name;
|
|
426
|
+
},
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Get a button's node of the buttons container if no button is given
|
|
430
|
+
* @param {node} [node] Button node
|
|
431
|
+
* @return {jQuery} Button element, or container
|
|
432
|
+
*/
|
|
433
|
+
node: function (node) {
|
|
434
|
+
if (!node) {
|
|
435
|
+
return this.dom.container;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
var button = this._nodeToButton(node);
|
|
439
|
+
return $(button.node);
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Set / get a processing class on the selected button
|
|
444
|
+
* @param {element} node Triggering button node
|
|
445
|
+
* @param {boolean} flag true to add, false to remove, undefined to get
|
|
446
|
+
* @return {boolean|Buttons} Getter value or this if a setter.
|
|
447
|
+
*/
|
|
448
|
+
processing: function (node, flag) {
|
|
449
|
+
var dt = this.s.dt;
|
|
450
|
+
var button = this._nodeToButton(node);
|
|
451
|
+
|
|
452
|
+
if (flag === undefined) {
|
|
453
|
+
return $(button.node).hasClass('processing');
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
$(button.node).toggleClass('processing', flag);
|
|
457
|
+
|
|
458
|
+
$(dt.table().node()).triggerHandler('buttons-processing.dt', [
|
|
459
|
+
flag,
|
|
460
|
+
dt.button(node),
|
|
461
|
+
dt,
|
|
462
|
+
$(node),
|
|
463
|
+
button.conf
|
|
464
|
+
]);
|
|
465
|
+
|
|
466
|
+
return this;
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Remove a button.
|
|
471
|
+
* @param {node} node Button node
|
|
472
|
+
* @return {Buttons} Self for chaining
|
|
473
|
+
*/
|
|
474
|
+
remove: function (node) {
|
|
475
|
+
var button = this._nodeToButton(node);
|
|
476
|
+
var host = this._nodeToHost(node);
|
|
477
|
+
var dt = this.s.dt;
|
|
478
|
+
|
|
479
|
+
// Remove any child buttons first
|
|
480
|
+
if (button.buttons.length) {
|
|
481
|
+
for (var i = button.buttons.length - 1; i >= 0; i--) {
|
|
482
|
+
this.remove(button.buttons[i].node);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
button.conf.destroying = true;
|
|
487
|
+
|
|
488
|
+
// Allow the button to remove event handlers, etc
|
|
489
|
+
if (button.conf.destroy) {
|
|
490
|
+
button.conf.destroy.call(dt.button(node), dt, $(node), button.conf);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
this._removeKey(button.conf);
|
|
494
|
+
|
|
495
|
+
$(button.node).remove();
|
|
496
|
+
|
|
497
|
+
if (button.inserter) {
|
|
498
|
+
$(button.inserter).remove();
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
var idx = $.inArray(button, host);
|
|
502
|
+
host.splice(idx, 1);
|
|
503
|
+
|
|
504
|
+
return this;
|
|
505
|
+
},
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Get the text for a button
|
|
509
|
+
* @param {int|string} node Button index
|
|
510
|
+
* @return {string} Button text
|
|
511
|
+
*/ /**
|
|
512
|
+
* Set the text for a button
|
|
513
|
+
* @param {int|string|function} node Button index
|
|
514
|
+
* @param {string} label Text
|
|
515
|
+
* @return {Buttons} Self for chaining
|
|
516
|
+
*/
|
|
517
|
+
text: function (node, label) {
|
|
518
|
+
var button = this._nodeToButton(node);
|
|
519
|
+
var textNode = button.textNode;
|
|
520
|
+
var dt = this.s.dt;
|
|
521
|
+
var jqNode = $(button.node);
|
|
522
|
+
var text = function (opt) {
|
|
523
|
+
return typeof opt === 'function'
|
|
524
|
+
? opt(dt, jqNode, button.conf)
|
|
525
|
+
: opt;
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
if (label === undefined) {
|
|
529
|
+
return text(button.conf.text);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
button.conf.text = label;
|
|
533
|
+
textNode.html(text(label));
|
|
534
|
+
|
|
535
|
+
return this;
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
539
|
+
* Constructor
|
|
540
|
+
*/
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Buttons constructor
|
|
544
|
+
* @private
|
|
545
|
+
*/
|
|
546
|
+
_constructor: function () {
|
|
547
|
+
var that = this;
|
|
548
|
+
var dt = this.s.dt;
|
|
549
|
+
var dtSettings = dt.settings()[0];
|
|
550
|
+
var buttons = this.c.buttons;
|
|
551
|
+
|
|
552
|
+
if (!dtSettings._buttons) {
|
|
553
|
+
dtSettings._buttons = [];
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
dtSettings._buttons.push({
|
|
557
|
+
inst: this,
|
|
558
|
+
name: this.c.name
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
for (var i = 0, ien = buttons.length; i < ien; i++) {
|
|
562
|
+
this.add(buttons[i]);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
dt.on('destroy', function (e, settings) {
|
|
566
|
+
if (settings === dtSettings) {
|
|
567
|
+
that.destroy();
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
// Global key event binding to listen for button keys
|
|
572
|
+
$('body').on('keyup.' + this.s.namespace, function (e) {
|
|
573
|
+
if (
|
|
574
|
+
!document.activeElement ||
|
|
575
|
+
document.activeElement === document.body
|
|
576
|
+
) {
|
|
577
|
+
// SUse a string of characters for fast lookup of if we need to
|
|
578
|
+
// handle this
|
|
579
|
+
var character = String.fromCharCode(e.keyCode).toLowerCase();
|
|
580
|
+
|
|
581
|
+
if (that.s.listenKeys.toLowerCase().indexOf(character) !== -1) {
|
|
582
|
+
that._keypress(character, e);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
},
|
|
587
|
+
|
|
588
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
589
|
+
* Private methods
|
|
590
|
+
*/
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Add a new button to the key press listener
|
|
594
|
+
* @param {object} conf Resolved button configuration object
|
|
595
|
+
* @private
|
|
596
|
+
*/
|
|
597
|
+
_addKey: function (conf) {
|
|
598
|
+
if (conf.key) {
|
|
599
|
+
this.s.listenKeys += $.isPlainObject(conf.key)
|
|
600
|
+
? conf.key.key
|
|
601
|
+
: conf.key;
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Insert the buttons into the container. Call without parameters!
|
|
607
|
+
* @param {node} [container] Recursive only - Insert point
|
|
608
|
+
* @param {array} [buttons] Recursive only - Buttons array
|
|
609
|
+
* @private
|
|
610
|
+
*/
|
|
611
|
+
_draw: function (container, buttons) {
|
|
612
|
+
if (!container) {
|
|
613
|
+
container = this.dom.container;
|
|
614
|
+
buttons = this.s.buttons;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
container.children().detach();
|
|
618
|
+
|
|
619
|
+
for (var i = 0, ien = buttons.length; i < ien; i++) {
|
|
620
|
+
container.append(buttons[i].inserter);
|
|
621
|
+
container.append(' ');
|
|
622
|
+
|
|
623
|
+
if (buttons[i].buttons && buttons[i].buttons.length) {
|
|
624
|
+
this._draw(buttons[i].collection, buttons[i].buttons);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Create buttons from an array of buttons
|
|
631
|
+
* @param {array} attachTo Buttons array to attach to
|
|
632
|
+
* @param {object} button Button definition
|
|
633
|
+
* @param {boolean} inCollection true if the button is in a collection
|
|
634
|
+
* @private
|
|
635
|
+
*/
|
|
636
|
+
_expandButton: function (
|
|
637
|
+
attachTo,
|
|
638
|
+
button,
|
|
639
|
+
split,
|
|
640
|
+
inCollection,
|
|
641
|
+
inSplit,
|
|
642
|
+
attachPoint,
|
|
643
|
+
parentConf
|
|
644
|
+
) {
|
|
645
|
+
var dt = this.s.dt;
|
|
646
|
+
var isSplit = false;
|
|
647
|
+
var domCollection = this.c.dom.collection;
|
|
648
|
+
var buttons = !Array.isArray(button) ? [button] : button;
|
|
649
|
+
|
|
650
|
+
if (button === undefined) {
|
|
651
|
+
buttons = !Array.isArray(split) ? [split] : split;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
for (var i = 0, ien = buttons.length; i < ien; i++) {
|
|
655
|
+
var conf = this._resolveExtends(buttons[i]);
|
|
656
|
+
|
|
657
|
+
if (!conf) {
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
isSplit = conf.config && conf.config.split ? true : false;
|
|
662
|
+
|
|
663
|
+
// If the configuration is an array, then expand the buttons at this
|
|
664
|
+
// point
|
|
665
|
+
if (Array.isArray(conf)) {
|
|
666
|
+
this._expandButton(
|
|
667
|
+
attachTo,
|
|
668
|
+
conf,
|
|
669
|
+
built !== undefined && built.conf !== undefined
|
|
670
|
+
? built.conf.split
|
|
671
|
+
: undefined,
|
|
672
|
+
inCollection,
|
|
673
|
+
parentConf !== undefined && parentConf.split !== undefined,
|
|
674
|
+
attachPoint,
|
|
675
|
+
parentConf
|
|
676
|
+
);
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
var built = this._buildButton(
|
|
681
|
+
conf,
|
|
682
|
+
inCollection,
|
|
683
|
+
conf.split !== undefined ||
|
|
684
|
+
(conf.config !== undefined &&
|
|
685
|
+
conf.config.split !== undefined),
|
|
686
|
+
inSplit
|
|
687
|
+
);
|
|
688
|
+
if (!built) {
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
if (attachPoint !== undefined && attachPoint !== null) {
|
|
693
|
+
attachTo.splice(attachPoint, 0, built);
|
|
694
|
+
attachPoint++;
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
attachTo.push(built);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Any button type can have a drop icon set
|
|
701
|
+
if (built.conf.dropIcon && ! built.conf.split) {
|
|
702
|
+
$(built.node)
|
|
703
|
+
.addClass(this.c.dom.button.dropClass)
|
|
704
|
+
.append(this.c.dom.button.dropHtml);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// Create the dropdown for a collection
|
|
708
|
+
if (built.conf.buttons) {
|
|
709
|
+
built.collection = $(
|
|
710
|
+
'<' + domCollection.container.content.tag + '/>'
|
|
711
|
+
);
|
|
712
|
+
built.conf._collection = built.collection;
|
|
713
|
+
|
|
714
|
+
this._expandButton(
|
|
715
|
+
built.buttons,
|
|
716
|
+
built.conf.buttons,
|
|
717
|
+
built.conf.split,
|
|
718
|
+
!isSplit,
|
|
719
|
+
isSplit,
|
|
720
|
+
attachPoint,
|
|
721
|
+
built.conf
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// And the split collection
|
|
726
|
+
if (built.conf.split) {
|
|
727
|
+
built.collection = $('<' + domCollection.container.tag + '/>');
|
|
728
|
+
built.conf._collection = built.collection;
|
|
729
|
+
|
|
730
|
+
for (var j = 0; j < built.conf.split.length; j++) {
|
|
731
|
+
var item = built.conf.split[j];
|
|
732
|
+
|
|
733
|
+
if (typeof item === 'object') {
|
|
734
|
+
item.parent = parentConf;
|
|
735
|
+
|
|
736
|
+
if (item.collectionLayout === undefined) {
|
|
737
|
+
item.collectionLayout = built.conf.collectionLayout;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if (item.dropup === undefined) {
|
|
741
|
+
item.dropup = built.conf.dropup;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
if (item.fade === undefined) {
|
|
745
|
+
item.fade = built.conf.fade;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
this._expandButton(
|
|
751
|
+
built.buttons,
|
|
752
|
+
built.conf.buttons,
|
|
753
|
+
built.conf.split,
|
|
754
|
+
!isSplit,
|
|
755
|
+
isSplit,
|
|
756
|
+
attachPoint,
|
|
757
|
+
built.conf
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
built.conf.parent = parentConf;
|
|
762
|
+
|
|
763
|
+
// init call is made here, rather than buildButton as it needs to
|
|
764
|
+
// be selectable, and for that it needs to be in the buttons array
|
|
765
|
+
if (conf.init) {
|
|
766
|
+
conf.init.call(dt.button(built.node), dt, $(built.node), conf);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
},
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Create an individual button
|
|
773
|
+
* @param {object} config Resolved button configuration
|
|
774
|
+
* @param {boolean} inCollection `true` if a collection button
|
|
775
|
+
* @return {object} Completed button description object
|
|
776
|
+
* @private
|
|
777
|
+
*/
|
|
778
|
+
_buildButton: function (config, inCollection, isSplit, inSplit) {
|
|
779
|
+
var that = this;
|
|
780
|
+
var configDom = this.c.dom;
|
|
781
|
+
var textNode;
|
|
782
|
+
var dt = this.s.dt;
|
|
783
|
+
var text = function (opt) {
|
|
784
|
+
return typeof opt === 'function' ? opt(dt, button, config) : opt;
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
// Create an object that describes the button which can be in `dom.button`, or
|
|
788
|
+
// `dom.collection.button` or `dom.split.button` or `dom.collection.split.button`!
|
|
789
|
+
// Each should extend from `dom.button`.
|
|
790
|
+
var dom = $.extend(true, {}, configDom.button);
|
|
791
|
+
|
|
792
|
+
if (inCollection && isSplit && configDom.collection.split) {
|
|
793
|
+
$.extend(true, dom, configDom.collection.split.action);
|
|
794
|
+
}
|
|
795
|
+
else if (inSplit || inCollection) {
|
|
796
|
+
$.extend(true, dom, configDom.collection.button);
|
|
797
|
+
}
|
|
798
|
+
else if (isSplit) {
|
|
799
|
+
$.extend(true, dom, configDom.split.button);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// Spacers don't do much other than insert an element into the DOM
|
|
803
|
+
if (config.spacer) {
|
|
804
|
+
var spacer = $('<' + dom.spacer.tag + '/>')
|
|
805
|
+
.addClass(
|
|
806
|
+
'dt-button-spacer ' +
|
|
807
|
+
config.style +
|
|
808
|
+
' ' +
|
|
809
|
+
dom.spacer.className
|
|
810
|
+
)
|
|
811
|
+
.html(text(config.text));
|
|
812
|
+
|
|
813
|
+
return {
|
|
814
|
+
conf: config,
|
|
815
|
+
node: spacer,
|
|
816
|
+
nodeChild: null,
|
|
817
|
+
inserter: spacer,
|
|
818
|
+
buttons: [],
|
|
819
|
+
inCollection: inCollection,
|
|
820
|
+
isSplit: isSplit,
|
|
821
|
+
collection: null,
|
|
822
|
+
textNode: spacer
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Make sure that the button is available based on whatever requirements
|
|
827
|
+
// it has. For example, PDF button require pdfmake
|
|
828
|
+
if (
|
|
829
|
+
config.available &&
|
|
830
|
+
!config.available(dt, config) &&
|
|
831
|
+
!config.html
|
|
832
|
+
) {
|
|
833
|
+
return false;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
var button;
|
|
837
|
+
|
|
838
|
+
if (!config.html) {
|
|
839
|
+
var run = function (e, dt, button, config, done) {
|
|
840
|
+
config.action.call(dt.button(button), e, dt, button, config, done);
|
|
841
|
+
|
|
842
|
+
$(dt.table().node()).triggerHandler('buttons-action.dt', [
|
|
843
|
+
dt.button(button),
|
|
844
|
+
dt,
|
|
845
|
+
button,
|
|
846
|
+
config
|
|
847
|
+
]);
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
var action = function(e, dt, button, config) {
|
|
851
|
+
if (config.async) {
|
|
852
|
+
that.processing(button[0], true);
|
|
853
|
+
|
|
854
|
+
setTimeout(function () {
|
|
855
|
+
run(e, dt, button, config, function () {
|
|
856
|
+
that.processing(button[0], false);
|
|
857
|
+
});
|
|
858
|
+
}, config.async);
|
|
859
|
+
}
|
|
860
|
+
else {
|
|
861
|
+
run(e, dt, button, config, function () {});
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
var tag = config.tag || dom.tag;
|
|
866
|
+
var clickBlurs =
|
|
867
|
+
config.clickBlurs === undefined ? true : config.clickBlurs;
|
|
868
|
+
|
|
869
|
+
button = $('<' + tag + '/>')
|
|
870
|
+
.addClass(dom.className)
|
|
871
|
+
.attr('tabindex', this.s.dt.settings()[0].iTabIndex)
|
|
872
|
+
.attr('aria-controls', this.s.dt.table().node().id)
|
|
873
|
+
.on('click.dtb', function (e) {
|
|
874
|
+
e.preventDefault();
|
|
875
|
+
|
|
876
|
+
if (!button.hasClass(dom.disabled) && config.action) {
|
|
877
|
+
action(e, dt, button, config);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
if (clickBlurs) {
|
|
881
|
+
button.trigger('blur');
|
|
882
|
+
}
|
|
883
|
+
})
|
|
884
|
+
.on('keypress.dtb', function (e) {
|
|
885
|
+
if (e.keyCode === 13) {
|
|
886
|
+
e.preventDefault();
|
|
887
|
+
|
|
888
|
+
if (!button.hasClass(dom.disabled) && config.action) {
|
|
889
|
+
action(e, dt, button, config);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
// Make `a` tags act like a link
|
|
895
|
+
if (tag.toLowerCase() === 'a') {
|
|
896
|
+
button.attr('href', '#');
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// Button tags should have `type=button` so they don't have any default behaviour
|
|
900
|
+
if (tag.toLowerCase() === 'button') {
|
|
901
|
+
button.attr('type', 'button');
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
if (dom.liner.tag) {
|
|
905
|
+
var liner = $('<' + dom.liner.tag + '/>')
|
|
906
|
+
.html(text(config.text))
|
|
907
|
+
.addClass(dom.liner.className);
|
|
908
|
+
|
|
909
|
+
if (dom.liner.tag.toLowerCase() === 'a') {
|
|
910
|
+
liner.attr('href', '#');
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
button.append(liner);
|
|
914
|
+
textNode = liner;
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
button.html(text(config.text));
|
|
918
|
+
textNode = button;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
if (config.enabled === false) {
|
|
922
|
+
button.addClass(dom.disabled);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
if (config.className) {
|
|
926
|
+
button.addClass(config.className);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
if (config.titleAttr) {
|
|
930
|
+
button.attr('title', text(config.titleAttr));
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
if (config.attr) {
|
|
934
|
+
button.attr(config.attr);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (!config.namespace) {
|
|
938
|
+
config.namespace = '.dt-button-' + _buttonCounter++;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
if (config.config !== undefined && config.config.split) {
|
|
942
|
+
config.split = config.config.split;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
else {
|
|
946
|
+
button = $(config.html);
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
var buttonContainer = this.c.dom.buttonContainer;
|
|
950
|
+
var inserter;
|
|
951
|
+
if (buttonContainer && buttonContainer.tag) {
|
|
952
|
+
inserter = $('<' + buttonContainer.tag + '/>')
|
|
953
|
+
.addClass(buttonContainer.className)
|
|
954
|
+
.append(button);
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
inserter = button;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
this._addKey(config);
|
|
961
|
+
|
|
962
|
+
// Style integration callback for DOM manipulation
|
|
963
|
+
// Note that this is _not_ documented. It is currently
|
|
964
|
+
// for style integration only
|
|
965
|
+
if (this.c.buttonCreated) {
|
|
966
|
+
inserter = this.c.buttonCreated(config, inserter);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
var splitDiv;
|
|
970
|
+
|
|
971
|
+
if (isSplit) {
|
|
972
|
+
var dropdownConf = inCollection
|
|
973
|
+
? $.extend(true, this.c.dom.split, this.c.dom.collection.split)
|
|
974
|
+
: this.c.dom.split;
|
|
975
|
+
var wrapperConf = dropdownConf.wrapper;
|
|
976
|
+
|
|
977
|
+
splitDiv = $('<' + wrapperConf.tag + '/>')
|
|
978
|
+
.addClass(wrapperConf.className)
|
|
979
|
+
.append(button);
|
|
980
|
+
|
|
981
|
+
var dropButtonConfig = $.extend(config, {
|
|
982
|
+
autoClose: true,
|
|
983
|
+
align: dropdownConf.dropdown.align,
|
|
984
|
+
attr: {
|
|
985
|
+
'aria-haspopup': 'dialog',
|
|
986
|
+
'aria-expanded': false
|
|
987
|
+
},
|
|
988
|
+
className: dropdownConf.dropdown.className,
|
|
989
|
+
closeButton: false,
|
|
990
|
+
splitAlignClass: dropdownConf.dropdown.splitAlignClass,
|
|
991
|
+
text: dropdownConf.dropdown.text
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
this._addKey(dropButtonConfig);
|
|
995
|
+
|
|
996
|
+
var splitAction = function (e, dt, button, config) {
|
|
997
|
+
_dtButtons.split.action.call(
|
|
998
|
+
dt.button(splitDiv),
|
|
999
|
+
e,
|
|
1000
|
+
dt,
|
|
1001
|
+
button,
|
|
1002
|
+
config
|
|
1003
|
+
);
|
|
1004
|
+
|
|
1005
|
+
$(dt.table().node()).triggerHandler('buttons-action.dt', [
|
|
1006
|
+
dt.button(button),
|
|
1007
|
+
dt,
|
|
1008
|
+
button,
|
|
1009
|
+
config
|
|
1010
|
+
]);
|
|
1011
|
+
button.attr('aria-expanded', true);
|
|
1012
|
+
};
|
|
1013
|
+
|
|
1014
|
+
var dropButton = $(
|
|
1015
|
+
'<button class="' +
|
|
1016
|
+
dropdownConf.dropdown.className +
|
|
1017
|
+
' dt-button"></button>'
|
|
1018
|
+
)
|
|
1019
|
+
.html(this.c.dom.button.dropHtml)
|
|
1020
|
+
.addClass(this.c.dom.button.dropClass)
|
|
1021
|
+
.on('click.dtb', function (e) {
|
|
1022
|
+
e.preventDefault();
|
|
1023
|
+
e.stopPropagation();
|
|
1024
|
+
|
|
1025
|
+
if (!dropButton.hasClass(dom.disabled)) {
|
|
1026
|
+
splitAction(e, dt, dropButton, dropButtonConfig);
|
|
1027
|
+
}
|
|
1028
|
+
if (clickBlurs) {
|
|
1029
|
+
dropButton.trigger('blur');
|
|
1030
|
+
}
|
|
1031
|
+
})
|
|
1032
|
+
.on('keypress.dtb', function (e) {
|
|
1033
|
+
if (e.keyCode === 13) {
|
|
1034
|
+
e.preventDefault();
|
|
1035
|
+
|
|
1036
|
+
if (!dropButton.hasClass(dom.disabled)) {
|
|
1037
|
+
splitAction(e, dt, dropButton, dropButtonConfig);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
if (config.split.length === 0) {
|
|
1043
|
+
dropButton.addClass('dtb-hide-drop');
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
splitDiv.append(dropButton).attr(dropButtonConfig.attr);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
var node = isSplit ? splitDiv.get(0) : button.get(0);
|
|
1050
|
+
|
|
1051
|
+
return {
|
|
1052
|
+
conf: config,
|
|
1053
|
+
node: node,
|
|
1054
|
+
nodeChild: node && node.children && node.children.length ? node.children[0] : null,
|
|
1055
|
+
inserter: isSplit ? splitDiv : inserter,
|
|
1056
|
+
buttons: [],
|
|
1057
|
+
inCollection: inCollection,
|
|
1058
|
+
isSplit: isSplit,
|
|
1059
|
+
inSplit: inSplit,
|
|
1060
|
+
collection: null,
|
|
1061
|
+
textNode: textNode
|
|
1062
|
+
};
|
|
1063
|
+
},
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* Spin over buttons checking if splits should be enabled or not.
|
|
1067
|
+
* @param {*} buttons Array of buttons to check
|
|
1068
|
+
*/
|
|
1069
|
+
_checkSplitEnable: function (buttons) {
|
|
1070
|
+
if (! buttons) {
|
|
1071
|
+
buttons = this.s.buttons;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
for (var i=0 ; i<buttons.length ; i++) {
|
|
1075
|
+
var button = buttons[i];
|
|
1076
|
+
|
|
1077
|
+
// Check if the button is a split one and if so, determine
|
|
1078
|
+
// its state
|
|
1079
|
+
if (button.isSplit) {
|
|
1080
|
+
var splitBtn = button.node.childNodes[1];
|
|
1081
|
+
|
|
1082
|
+
if (this._checkAnyEnabled(button.buttons)) {
|
|
1083
|
+
// Enable the split
|
|
1084
|
+
$(splitBtn)
|
|
1085
|
+
.removeClass(this.c.dom.button.disabled)
|
|
1086
|
+
.prop('disabled', false);
|
|
1087
|
+
}
|
|
1088
|
+
else {
|
|
1089
|
+
$(splitBtn)
|
|
1090
|
+
.addClass(this.c.dom.button.disabled)
|
|
1091
|
+
.prop('disabled', false);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
else if (button.isCollection) {
|
|
1095
|
+
// Nest down into collections
|
|
1096
|
+
this._checkSplitEnable(button.buttons);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
},
|
|
1100
|
+
|
|
1101
|
+
/**
|
|
1102
|
+
* Check an array of buttons and see if any are enabled in it
|
|
1103
|
+
* @param {*} buttons Button array
|
|
1104
|
+
* @returns true if a button is enabled, false otherwise
|
|
1105
|
+
*/
|
|
1106
|
+
_checkAnyEnabled: function (buttons) {
|
|
1107
|
+
for (var i=0 ; i<buttons.length ; i++) {
|
|
1108
|
+
if (! buttons[i].disabled) {
|
|
1109
|
+
return true;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
return false;
|
|
1114
|
+
},
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* Get the button object from a node (recursive)
|
|
1118
|
+
* @param {node} node Button node
|
|
1119
|
+
* @param {array} [buttons] Button array, uses base if not defined
|
|
1120
|
+
* @return {object} Button object
|
|
1121
|
+
* @private
|
|
1122
|
+
*/
|
|
1123
|
+
_nodeToButton: function (node, buttons) {
|
|
1124
|
+
if (!buttons) {
|
|
1125
|
+
buttons = this.s.buttons;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
for (var i = 0, ien = buttons.length; i < ien; i++) {
|
|
1129
|
+
if (buttons[i].node === node || buttons[i].nodeChild === node) {
|
|
1130
|
+
return buttons[i];
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
if (buttons[i].buttons.length) {
|
|
1134
|
+
var ret = this._nodeToButton(node, buttons[i].buttons);
|
|
1135
|
+
|
|
1136
|
+
if (ret) {
|
|
1137
|
+
return ret;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
},
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* Get container array for a button from a button node (recursive)
|
|
1145
|
+
* @param {node} node Button node
|
|
1146
|
+
* @param {array} [buttons] Button array, uses base if not defined
|
|
1147
|
+
* @return {array} Button's host array
|
|
1148
|
+
* @private
|
|
1149
|
+
*/
|
|
1150
|
+
_nodeToHost: function (node, buttons) {
|
|
1151
|
+
if (!buttons) {
|
|
1152
|
+
buttons = this.s.buttons;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
for (var i = 0, ien = buttons.length; i < ien; i++) {
|
|
1156
|
+
if (buttons[i].node === node) {
|
|
1157
|
+
return buttons;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
if (buttons[i].buttons.length) {
|
|
1161
|
+
var ret = this._nodeToHost(node, buttons[i].buttons);
|
|
1162
|
+
|
|
1163
|
+
if (ret) {
|
|
1164
|
+
return ret;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
},
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* Handle a key press - determine if any button's key configured matches
|
|
1172
|
+
* what was typed and trigger the action if so.
|
|
1173
|
+
* @param {string} character The character pressed
|
|
1174
|
+
* @param {object} e Key event that triggered this call
|
|
1175
|
+
* @private
|
|
1176
|
+
*/
|
|
1177
|
+
_keypress: function (character, e) {
|
|
1178
|
+
// Check if this button press already activated on another instance of Buttons
|
|
1179
|
+
if (e._buttonsHandled) {
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
var run = function (conf, node) {
|
|
1184
|
+
if (!conf.key) {
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
if (conf.key === character) {
|
|
1189
|
+
e._buttonsHandled = true;
|
|
1190
|
+
$(node).click();
|
|
1191
|
+
}
|
|
1192
|
+
else if ($.isPlainObject(conf.key)) {
|
|
1193
|
+
if (conf.key.key !== character) {
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
if (conf.key.shiftKey && !e.shiftKey) {
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
if (conf.key.altKey && !e.altKey) {
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
if (conf.key.ctrlKey && !e.ctrlKey) {
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
if (conf.key.metaKey && !e.metaKey) {
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
// Made it this far - it is good
|
|
1214
|
+
e._buttonsHandled = true;
|
|
1215
|
+
$(node).click();
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1219
|
+
var recurse = function (a) {
|
|
1220
|
+
for (var i = 0, ien = a.length; i < ien; i++) {
|
|
1221
|
+
run(a[i].conf, a[i].node);
|
|
1222
|
+
|
|
1223
|
+
if (a[i].buttons.length) {
|
|
1224
|
+
recurse(a[i].buttons);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
|
|
1229
|
+
recurse(this.s.buttons);
|
|
1230
|
+
},
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* Remove a key from the key listener for this instance (to be used when a
|
|
1234
|
+
* button is removed)
|
|
1235
|
+
* @param {object} conf Button configuration
|
|
1236
|
+
* @private
|
|
1237
|
+
*/
|
|
1238
|
+
_removeKey: function (conf) {
|
|
1239
|
+
if (conf.key) {
|
|
1240
|
+
var character = $.isPlainObject(conf.key) ? conf.key.key : conf.key;
|
|
1241
|
+
|
|
1242
|
+
// Remove only one character, as multiple buttons could have the
|
|
1243
|
+
// same listening key
|
|
1244
|
+
var a = this.s.listenKeys.split('');
|
|
1245
|
+
var idx = $.inArray(character, a);
|
|
1246
|
+
a.splice(idx, 1);
|
|
1247
|
+
this.s.listenKeys = a.join('');
|
|
1248
|
+
}
|
|
1249
|
+
},
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* Resolve a button configuration
|
|
1253
|
+
* @param {string|function|object} conf Button config to resolve
|
|
1254
|
+
* @return {object} Button configuration
|
|
1255
|
+
* @private
|
|
1256
|
+
*/
|
|
1257
|
+
_resolveExtends: function (conf) {
|
|
1258
|
+
var that = this;
|
|
1259
|
+
var dt = this.s.dt;
|
|
1260
|
+
var i, ien;
|
|
1261
|
+
var toConfObject = function (base) {
|
|
1262
|
+
var loop = 0;
|
|
1263
|
+
|
|
1264
|
+
// Loop until we have resolved to a button configuration, or an
|
|
1265
|
+
// array of button configurations (which will be iterated
|
|
1266
|
+
// separately)
|
|
1267
|
+
while (!$.isPlainObject(base) && !Array.isArray(base)) {
|
|
1268
|
+
if (base === undefined) {
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
if (typeof base === 'function') {
|
|
1273
|
+
base = base.call(that, dt, conf);
|
|
1274
|
+
|
|
1275
|
+
if (!base) {
|
|
1276
|
+
return false;
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
else if (typeof base === 'string') {
|
|
1280
|
+
if (!_dtButtons[base]) {
|
|
1281
|
+
return { html: base };
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
base = _dtButtons[base];
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
loop++;
|
|
1288
|
+
if (loop > 30) {
|
|
1289
|
+
// Protect against misconfiguration killing the browser
|
|
1290
|
+
throw 'Buttons: Too many iterations';
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
return Array.isArray(base) ? base : $.extend({}, base);
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1297
|
+
conf = toConfObject(conf);
|
|
1298
|
+
|
|
1299
|
+
while (conf && conf.extend) {
|
|
1300
|
+
// Use `toConfObject` in case the button definition being extended
|
|
1301
|
+
// is itself a string or a function
|
|
1302
|
+
if (!_dtButtons[conf.extend]) {
|
|
1303
|
+
throw 'Cannot extend unknown button type: ' + conf.extend;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
var objArray = toConfObject(_dtButtons[conf.extend]);
|
|
1307
|
+
if (Array.isArray(objArray)) {
|
|
1308
|
+
return objArray;
|
|
1309
|
+
}
|
|
1310
|
+
else if (!objArray) {
|
|
1311
|
+
// This is a little brutal as it might be possible to have a
|
|
1312
|
+
// valid button without the extend, but if there is no extend
|
|
1313
|
+
// then the host button would be acting in an undefined state
|
|
1314
|
+
return false;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// Stash the current class name
|
|
1318
|
+
var originalClassName = objArray.className;
|
|
1319
|
+
|
|
1320
|
+
if (conf.config !== undefined && objArray.config !== undefined) {
|
|
1321
|
+
conf.config = $.extend({}, objArray.config, conf.config);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
conf = $.extend({}, objArray, conf);
|
|
1325
|
+
|
|
1326
|
+
// The extend will have overwritten the original class name if the
|
|
1327
|
+
// `conf` object also assigned a class, but we want to concatenate
|
|
1328
|
+
// them so they are list that is combined from all extended buttons
|
|
1329
|
+
if (originalClassName && conf.className !== originalClassName) {
|
|
1330
|
+
conf.className = originalClassName + ' ' + conf.className;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// Although we want the `conf` object to overwrite almost all of
|
|
1334
|
+
// the properties of the object being extended, the `extend`
|
|
1335
|
+
// property should come from the object being extended
|
|
1336
|
+
conf.extend = objArray.extend;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// Buttons to be added to a collection -gives the ability to define
|
|
1340
|
+
// if buttons should be added to the start or end of a collection
|
|
1341
|
+
var postfixButtons = conf.postfixButtons;
|
|
1342
|
+
if (postfixButtons) {
|
|
1343
|
+
if (!conf.buttons) {
|
|
1344
|
+
conf.buttons = [];
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
for (i = 0, ien = postfixButtons.length; i < ien; i++) {
|
|
1348
|
+
conf.buttons.push(postfixButtons[i]);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
var prefixButtons = conf.prefixButtons;
|
|
1353
|
+
if (prefixButtons) {
|
|
1354
|
+
if (!conf.buttons) {
|
|
1355
|
+
conf.buttons = [];
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
for (i = 0, ien = prefixButtons.length; i < ien; i++) {
|
|
1359
|
+
conf.buttons.splice(i, 0, prefixButtons[i]);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
return conf;
|
|
1364
|
+
},
|
|
1365
|
+
|
|
1366
|
+
/**
|
|
1367
|
+
* Display (and replace if there is an existing one) a popover attached to a button
|
|
1368
|
+
* @param {string|node} content Content to show
|
|
1369
|
+
* @param {DataTable.Api} hostButton DT API instance of the button
|
|
1370
|
+
* @param {object} inOpts Options (see object below for all options)
|
|
1371
|
+
*/
|
|
1372
|
+
_popover: function (content, hostButton, inOpts) {
|
|
1373
|
+
var dt = hostButton;
|
|
1374
|
+
var c = this.c;
|
|
1375
|
+
var closed = false;
|
|
1376
|
+
var options = $.extend(
|
|
1377
|
+
{
|
|
1378
|
+
align: 'button-left', // button-right, dt-container, split-left, split-right
|
|
1379
|
+
autoClose: false,
|
|
1380
|
+
background: true,
|
|
1381
|
+
backgroundClassName: 'dt-button-background',
|
|
1382
|
+
closeButton: true,
|
|
1383
|
+
containerClassName: c.dom.collection.container.className,
|
|
1384
|
+
contentClassName: c.dom.collection.container.content.className,
|
|
1385
|
+
collectionLayout: '',
|
|
1386
|
+
collectionTitle: '',
|
|
1387
|
+
dropup: false,
|
|
1388
|
+
fade: 400,
|
|
1389
|
+
popoverTitle: '',
|
|
1390
|
+
rightAlignClassName: 'dt-button-right',
|
|
1391
|
+
tag: c.dom.collection.container.tag
|
|
1392
|
+
},
|
|
1393
|
+
inOpts
|
|
1394
|
+
);
|
|
1395
|
+
|
|
1396
|
+
var containerSelector =
|
|
1397
|
+
options.tag + '.' + options.containerClassName.replace(/ /g, '.');
|
|
1398
|
+
var hostButtonNode = hostButton.node();
|
|
1399
|
+
var hostNode = options.collectionLayout.includes('fixed') ? $('body') : hostButton.node();
|
|
1400
|
+
|
|
1401
|
+
var close = function () {
|
|
1402
|
+
closed = true;
|
|
1403
|
+
|
|
1404
|
+
_fadeOut($(containerSelector), options.fade, function () {
|
|
1405
|
+
$(this).detach();
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
$(
|
|
1409
|
+
dt
|
|
1410
|
+
.buttons('[aria-haspopup="dialog"][aria-expanded="true"]')
|
|
1411
|
+
.nodes()
|
|
1412
|
+
).attr('aria-expanded', 'false');
|
|
1413
|
+
|
|
1414
|
+
$('div.dt-button-background').off('click.dtb-collection');
|
|
1415
|
+
Buttons.background(
|
|
1416
|
+
false,
|
|
1417
|
+
options.backgroundClassName,
|
|
1418
|
+
options.fade,
|
|
1419
|
+
hostNode
|
|
1420
|
+
);
|
|
1421
|
+
|
|
1422
|
+
$(window).off('resize.resize.dtb-collection');
|
|
1423
|
+
$('body').off('.dtb-collection');
|
|
1424
|
+
dt.off('buttons-action.b-internal');
|
|
1425
|
+
dt.off('destroy');
|
|
1426
|
+
|
|
1427
|
+
$('body').trigger('buttons-popover-hide.dt');
|
|
1428
|
+
};
|
|
1429
|
+
|
|
1430
|
+
if (content === false) {
|
|
1431
|
+
close();
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
var existingExpanded = $(
|
|
1436
|
+
dt.buttons('[aria-haspopup="dialog"][aria-expanded="true"]').nodes()
|
|
1437
|
+
);
|
|
1438
|
+
if (existingExpanded.length) {
|
|
1439
|
+
// Reuse the current position if the button that was triggered is inside an existing collection
|
|
1440
|
+
if (hostNode.closest(containerSelector).length) {
|
|
1441
|
+
hostNode = existingExpanded.eq(0);
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
close();
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// Sort buttons if defined
|
|
1448
|
+
if (options.sort) {
|
|
1449
|
+
var elements = $('button', content)
|
|
1450
|
+
.map(function (idx, el) {
|
|
1451
|
+
return {
|
|
1452
|
+
text: $(el).text(),
|
|
1453
|
+
el: el
|
|
1454
|
+
};
|
|
1455
|
+
})
|
|
1456
|
+
.toArray();
|
|
1457
|
+
|
|
1458
|
+
elements.sort(function (a, b) {
|
|
1459
|
+
return a.text.localeCompare(b.text);
|
|
1460
|
+
});
|
|
1461
|
+
|
|
1462
|
+
$(content).append(elements.map(function (v) {
|
|
1463
|
+
return v.el;
|
|
1464
|
+
}));
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// Try to be smart about the layout
|
|
1468
|
+
var cnt = $('.dt-button', content).length;
|
|
1469
|
+
var mod = '';
|
|
1470
|
+
|
|
1471
|
+
if (cnt === 3) {
|
|
1472
|
+
mod = 'dtb-b3';
|
|
1473
|
+
}
|
|
1474
|
+
else if (cnt === 2) {
|
|
1475
|
+
mod = 'dtb-b2';
|
|
1476
|
+
}
|
|
1477
|
+
else if (cnt === 1) {
|
|
1478
|
+
mod = 'dtb-b1';
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
var display = $('<' + options.tag + '/>')
|
|
1482
|
+
.addClass(options.containerClassName)
|
|
1483
|
+
.addClass(options.collectionLayout)
|
|
1484
|
+
.addClass(options.splitAlignClass)
|
|
1485
|
+
.addClass(mod)
|
|
1486
|
+
.css('display', 'none')
|
|
1487
|
+
.attr({
|
|
1488
|
+
'aria-modal': true,
|
|
1489
|
+
role: 'dialog'
|
|
1490
|
+
});
|
|
1491
|
+
|
|
1492
|
+
content = $(content)
|
|
1493
|
+
.addClass(options.contentClassName)
|
|
1494
|
+
.attr('role', 'menu')
|
|
1495
|
+
.appendTo(display);
|
|
1496
|
+
|
|
1497
|
+
hostButtonNode.attr('aria-expanded', 'true');
|
|
1498
|
+
|
|
1499
|
+
if (hostNode.parents('body')[0] !== document.body) {
|
|
1500
|
+
hostNode = $(document.body).children('div, section, p').last();
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
if (options.popoverTitle) {
|
|
1504
|
+
display.prepend(
|
|
1505
|
+
'<div class="dt-button-collection-title">' +
|
|
1506
|
+
options.popoverTitle +
|
|
1507
|
+
'</div>'
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
else if (options.collectionTitle) {
|
|
1511
|
+
display.prepend(
|
|
1512
|
+
'<div class="dt-button-collection-title">' +
|
|
1513
|
+
options.collectionTitle +
|
|
1514
|
+
'</div>'
|
|
1515
|
+
);
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
if (options.closeButton) {
|
|
1519
|
+
display
|
|
1520
|
+
.prepend('<div class="dtb-popover-close">×</div>')
|
|
1521
|
+
.addClass('dtb-collection-closeable');
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
_fadeIn(display.insertAfter(hostNode), options.fade);
|
|
1525
|
+
|
|
1526
|
+
var tableContainer = $(hostButton.table().container());
|
|
1527
|
+
var position = display.css('position');
|
|
1528
|
+
|
|
1529
|
+
if (options.span === 'container' || options.align === 'dt-container') {
|
|
1530
|
+
hostNode = hostNode.parent();
|
|
1531
|
+
display.css('width', tableContainer.width());
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// Align the popover relative to the DataTables container
|
|
1535
|
+
// Useful for wide popovers such as SearchPanes
|
|
1536
|
+
if (position === 'absolute') {
|
|
1537
|
+
// Align relative to the host button
|
|
1538
|
+
var offsetParent = $(hostNode[0].offsetParent);
|
|
1539
|
+
var buttonPosition = hostNode.position();
|
|
1540
|
+
var buttonOffset = hostNode.offset();
|
|
1541
|
+
var tableSizes = offsetParent.offset();
|
|
1542
|
+
var containerPosition = offsetParent.position();
|
|
1543
|
+
var computed = window.getComputedStyle(offsetParent[0]);
|
|
1544
|
+
|
|
1545
|
+
tableSizes.height = offsetParent.outerHeight();
|
|
1546
|
+
tableSizes.width =
|
|
1547
|
+
offsetParent.width() + parseFloat(computed.paddingLeft);
|
|
1548
|
+
tableSizes.right = tableSizes.left + tableSizes.width;
|
|
1549
|
+
tableSizes.bottom = tableSizes.top + tableSizes.height;
|
|
1550
|
+
|
|
1551
|
+
// Set the initial position so we can read height / width
|
|
1552
|
+
var top = buttonPosition.top + hostNode.outerHeight();
|
|
1553
|
+
var left = buttonPosition.left;
|
|
1554
|
+
|
|
1555
|
+
display.css({
|
|
1556
|
+
top: top,
|
|
1557
|
+
left: left
|
|
1558
|
+
});
|
|
1559
|
+
|
|
1560
|
+
// Get the popover position
|
|
1561
|
+
computed = window.getComputedStyle(display[0]);
|
|
1562
|
+
var popoverSizes = display.offset();
|
|
1563
|
+
|
|
1564
|
+
popoverSizes.height = display.outerHeight();
|
|
1565
|
+
popoverSizes.width = display.outerWidth();
|
|
1566
|
+
popoverSizes.right = popoverSizes.left + popoverSizes.width;
|
|
1567
|
+
popoverSizes.bottom = popoverSizes.top + popoverSizes.height;
|
|
1568
|
+
popoverSizes.marginTop = parseFloat(computed.marginTop);
|
|
1569
|
+
popoverSizes.marginBottom = parseFloat(computed.marginBottom);
|
|
1570
|
+
|
|
1571
|
+
// First position per the class requirements - pop up and right align
|
|
1572
|
+
if (options.dropup) {
|
|
1573
|
+
top =
|
|
1574
|
+
buttonPosition.top -
|
|
1575
|
+
popoverSizes.height -
|
|
1576
|
+
popoverSizes.marginTop -
|
|
1577
|
+
popoverSizes.marginBottom;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
if (
|
|
1581
|
+
options.align === 'button-right' ||
|
|
1582
|
+
display.hasClass(options.rightAlignClassName)
|
|
1583
|
+
) {
|
|
1584
|
+
left =
|
|
1585
|
+
buttonPosition.left -
|
|
1586
|
+
popoverSizes.width +
|
|
1587
|
+
hostNode.outerWidth();
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// Container alignment - make sure it doesn't overflow the table container
|
|
1591
|
+
if (
|
|
1592
|
+
options.align === 'dt-container' ||
|
|
1593
|
+
options.align === 'container'
|
|
1594
|
+
) {
|
|
1595
|
+
if (left < buttonPosition.left) {
|
|
1596
|
+
left = -buttonPosition.left;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
// Window adjustment
|
|
1601
|
+
if (
|
|
1602
|
+
containerPosition.left + left + popoverSizes.width >
|
|
1603
|
+
$(window).width()
|
|
1604
|
+
) {
|
|
1605
|
+
// Overflowing the document to the right
|
|
1606
|
+
left =
|
|
1607
|
+
$(window).width() -
|
|
1608
|
+
popoverSizes.width -
|
|
1609
|
+
containerPosition.left;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
if (buttonOffset.left + left < 0) {
|
|
1613
|
+
// Off to the left of the document
|
|
1614
|
+
left = -buttonOffset.left;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
if (
|
|
1618
|
+
containerPosition.top + top + popoverSizes.height >
|
|
1619
|
+
$(window).height() + $(window).scrollTop()
|
|
1620
|
+
) {
|
|
1621
|
+
// Pop up if otherwise we'd need the user to scroll down
|
|
1622
|
+
top =
|
|
1623
|
+
buttonPosition.top -
|
|
1624
|
+
popoverSizes.height -
|
|
1625
|
+
popoverSizes.marginTop -
|
|
1626
|
+
popoverSizes.marginBottom;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
if (offsetParent.offset().top + top < $(window).scrollTop()) {
|
|
1630
|
+
// Correction for when the top is beyond the top of the page
|
|
1631
|
+
top = buttonPosition.top + hostNode.outerHeight();
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
// Calculations all done - now set it
|
|
1635
|
+
display.css({
|
|
1636
|
+
top: top,
|
|
1637
|
+
left: left
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
else {
|
|
1641
|
+
// Fix position - centre on screen
|
|
1642
|
+
var place = function () {
|
|
1643
|
+
var half = $(window).height() / 2;
|
|
1644
|
+
|
|
1645
|
+
var top = display.height() / 2;
|
|
1646
|
+
if (top > half) {
|
|
1647
|
+
top = half;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
display.css('marginTop', top * -1);
|
|
1651
|
+
};
|
|
1652
|
+
|
|
1653
|
+
place();
|
|
1654
|
+
|
|
1655
|
+
$(window).on('resize.dtb-collection', function () {
|
|
1656
|
+
place();
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
if (options.background) {
|
|
1661
|
+
Buttons.background(
|
|
1662
|
+
true,
|
|
1663
|
+
options.backgroundClassName,
|
|
1664
|
+
options.fade,
|
|
1665
|
+
options.backgroundHost || hostNode
|
|
1666
|
+
);
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
// This is bonkers, but if we don't have a click listener on the
|
|
1670
|
+
// background element, iOS Safari will ignore the body click
|
|
1671
|
+
// listener below. An empty function here is all that is
|
|
1672
|
+
// required to make it work...
|
|
1673
|
+
$('div.dt-button-background').on(
|
|
1674
|
+
'click.dtb-collection',
|
|
1675
|
+
function () {}
|
|
1676
|
+
);
|
|
1677
|
+
|
|
1678
|
+
if (options.autoClose) {
|
|
1679
|
+
setTimeout(function () {
|
|
1680
|
+
dt.on('buttons-action.b-internal', function (e, btn, dt, node) {
|
|
1681
|
+
if (node[0] === hostNode[0]) {
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1684
|
+
close();
|
|
1685
|
+
});
|
|
1686
|
+
}, 0);
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
$(display).trigger('buttons-popover.dt');
|
|
1690
|
+
|
|
1691
|
+
dt.on('destroy', close);
|
|
1692
|
+
|
|
1693
|
+
setTimeout(function () {
|
|
1694
|
+
closed = false;
|
|
1695
|
+
$('body')
|
|
1696
|
+
.on('click.dtb-collection', function (e) {
|
|
1697
|
+
if (closed) {
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
// andSelf is deprecated in jQ1.8, but we want 1.7 compat
|
|
1702
|
+
var back = $.fn.addBack ? 'addBack' : 'andSelf';
|
|
1703
|
+
var parent = $(e.target).parent()[0];
|
|
1704
|
+
|
|
1705
|
+
if (
|
|
1706
|
+
(!$(e.target).parents()[back]().filter(content)
|
|
1707
|
+
.length &&
|
|
1708
|
+
!$(parent).hasClass('dt-buttons')) ||
|
|
1709
|
+
$(e.target).hasClass('dt-button-background')
|
|
1710
|
+
) {
|
|
1711
|
+
close();
|
|
1712
|
+
}
|
|
1713
|
+
})
|
|
1714
|
+
.on('keyup.dtb-collection', function (e) {
|
|
1715
|
+
if (e.keyCode === 27) {
|
|
1716
|
+
close();
|
|
1717
|
+
}
|
|
1718
|
+
})
|
|
1719
|
+
.on('keydown.dtb-collection', function (e) {
|
|
1720
|
+
// Focus trap for tab key
|
|
1721
|
+
var elements = $('a, button', content);
|
|
1722
|
+
var active = document.activeElement;
|
|
1723
|
+
|
|
1724
|
+
if (e.keyCode !== 9) {
|
|
1725
|
+
// tab
|
|
1726
|
+
return;
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
if (elements.index(active) === -1) {
|
|
1730
|
+
// If current focus is not inside the popover
|
|
1731
|
+
elements.first().focus();
|
|
1732
|
+
e.preventDefault();
|
|
1733
|
+
}
|
|
1734
|
+
else if (e.shiftKey) {
|
|
1735
|
+
// Reverse tabbing order when shift key is pressed
|
|
1736
|
+
if (active === elements[0]) {
|
|
1737
|
+
elements.last().focus();
|
|
1738
|
+
e.preventDefault();
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
else {
|
|
1742
|
+
if (active === elements.last()[0]) {
|
|
1743
|
+
elements.first().focus();
|
|
1744
|
+
e.preventDefault();
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
});
|
|
1748
|
+
}, 0);
|
|
1749
|
+
}
|
|
1750
|
+
});
|
|
1751
|
+
|
|
1752
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
1753
|
+
* Statics
|
|
1754
|
+
*/
|
|
1755
|
+
|
|
1756
|
+
/**
|
|
1757
|
+
* Show / hide a background layer behind a collection
|
|
1758
|
+
* @param {boolean} Flag to indicate if the background should be shown or
|
|
1759
|
+
* hidden
|
|
1760
|
+
* @param {string} Class to assign to the background
|
|
1761
|
+
* @static
|
|
1762
|
+
*/
|
|
1763
|
+
Buttons.background = function (show, className, fade, insertPoint) {
|
|
1764
|
+
if (fade === undefined) {
|
|
1765
|
+
fade = 400;
|
|
1766
|
+
}
|
|
1767
|
+
if (!insertPoint) {
|
|
1768
|
+
insertPoint = document.body;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
if (show) {
|
|
1772
|
+
_fadeIn(
|
|
1773
|
+
$('<div/>')
|
|
1774
|
+
.addClass(className)
|
|
1775
|
+
.css('display', 'none')
|
|
1776
|
+
.insertAfter(insertPoint),
|
|
1777
|
+
fade
|
|
1778
|
+
);
|
|
1779
|
+
}
|
|
1780
|
+
else {
|
|
1781
|
+
_fadeOut($('div.' + className), fade, function () {
|
|
1782
|
+
$(this).removeClass(className).remove();
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
|
|
1787
|
+
/**
|
|
1788
|
+
* Instance selector - select Buttons instances based on an instance selector
|
|
1789
|
+
* value from the buttons assigned to a DataTable. This is only useful if
|
|
1790
|
+
* multiple instances are attached to a DataTable.
|
|
1791
|
+
* @param {string|int|array} Instance selector - see `instance-selector`
|
|
1792
|
+
* documentation on the DataTables site
|
|
1793
|
+
* @param {array} Button instance array that was attached to the DataTables
|
|
1794
|
+
* settings object
|
|
1795
|
+
* @return {array} Buttons instances
|
|
1796
|
+
* @static
|
|
1797
|
+
*/
|
|
1798
|
+
Buttons.instanceSelector = function (group, buttons) {
|
|
1799
|
+
if (group === undefined || group === null) {
|
|
1800
|
+
return $.map(buttons, function (v) {
|
|
1801
|
+
return v.inst;
|
|
1802
|
+
});
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
var ret = [];
|
|
1806
|
+
var names = $.map(buttons, function (v) {
|
|
1807
|
+
return v.name;
|
|
1808
|
+
});
|
|
1809
|
+
|
|
1810
|
+
// Flatten the group selector into an array of single options
|
|
1811
|
+
var process = function (input) {
|
|
1812
|
+
if (Array.isArray(input)) {
|
|
1813
|
+
for (var i = 0, ien = input.length; i < ien; i++) {
|
|
1814
|
+
process(input[i]);
|
|
1815
|
+
}
|
|
1816
|
+
return;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
if (typeof input === 'string') {
|
|
1820
|
+
if (input.indexOf(',') !== -1) {
|
|
1821
|
+
// String selector, list of names
|
|
1822
|
+
process(input.split(','));
|
|
1823
|
+
}
|
|
1824
|
+
else {
|
|
1825
|
+
// String selector individual name
|
|
1826
|
+
var idx = $.inArray(input.trim(), names);
|
|
1827
|
+
|
|
1828
|
+
if (idx !== -1) {
|
|
1829
|
+
ret.push(buttons[idx].inst);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
else if (typeof input === 'number') {
|
|
1834
|
+
// Index selector
|
|
1835
|
+
ret.push(buttons[input].inst);
|
|
1836
|
+
}
|
|
1837
|
+
else if (typeof input === 'object' && input.nodeName) {
|
|
1838
|
+
// Element selector
|
|
1839
|
+
for (var j = 0; j < buttons.length; j++) {
|
|
1840
|
+
if (buttons[j].inst.dom.container[0] === input) {
|
|
1841
|
+
ret.push(buttons[j].inst);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
else if (typeof input === 'object') {
|
|
1846
|
+
// Actual instance selector
|
|
1847
|
+
ret.push(input);
|
|
1848
|
+
}
|
|
1849
|
+
};
|
|
1850
|
+
|
|
1851
|
+
process(group);
|
|
1852
|
+
|
|
1853
|
+
return ret;
|
|
1854
|
+
};
|
|
1855
|
+
|
|
1856
|
+
/**
|
|
1857
|
+
* Button selector - select one or more buttons from a selector input so some
|
|
1858
|
+
* operation can be performed on them.
|
|
1859
|
+
* @param {array} Button instances array that the selector should operate on
|
|
1860
|
+
* @param {string|int|node|jQuery|array} Button selector - see
|
|
1861
|
+
* `button-selector` documentation on the DataTables site
|
|
1862
|
+
* @return {array} Array of objects containing `inst` and `idx` properties of
|
|
1863
|
+
* the selected buttons so you know which instance each button belongs to.
|
|
1864
|
+
* @static
|
|
1865
|
+
*/
|
|
1866
|
+
Buttons.buttonSelector = function (insts, selector) {
|
|
1867
|
+
var ret = [];
|
|
1868
|
+
var nodeBuilder = function (a, buttons, baseIdx) {
|
|
1869
|
+
var button;
|
|
1870
|
+
var idx;
|
|
1871
|
+
|
|
1872
|
+
for (var i = 0, ien = buttons.length; i < ien; i++) {
|
|
1873
|
+
button = buttons[i];
|
|
1874
|
+
|
|
1875
|
+
if (button) {
|
|
1876
|
+
idx = baseIdx !== undefined ? baseIdx + i : i + '';
|
|
1877
|
+
|
|
1878
|
+
a.push({
|
|
1879
|
+
node: button.node,
|
|
1880
|
+
name: button.conf.name,
|
|
1881
|
+
idx: idx
|
|
1882
|
+
});
|
|
1883
|
+
|
|
1884
|
+
if (button.buttons) {
|
|
1885
|
+
nodeBuilder(a, button.buttons, idx + '-');
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
};
|
|
1890
|
+
|
|
1891
|
+
var run = function (selector, inst) {
|
|
1892
|
+
var i, ien;
|
|
1893
|
+
var buttons = [];
|
|
1894
|
+
nodeBuilder(buttons, inst.s.buttons);
|
|
1895
|
+
|
|
1896
|
+
var nodes = $.map(buttons, function (v) {
|
|
1897
|
+
return v.node;
|
|
1898
|
+
});
|
|
1899
|
+
|
|
1900
|
+
if (Array.isArray(selector) || selector instanceof $) {
|
|
1901
|
+
for (i = 0, ien = selector.length; i < ien; i++) {
|
|
1902
|
+
run(selector[i], inst);
|
|
1903
|
+
}
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
if (selector === null || selector === undefined || selector === '*') {
|
|
1908
|
+
// Select all
|
|
1909
|
+
for (i = 0, ien = buttons.length; i < ien; i++) {
|
|
1910
|
+
ret.push({
|
|
1911
|
+
inst: inst,
|
|
1912
|
+
node: buttons[i].node
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
else if (typeof selector === 'number') {
|
|
1917
|
+
// Main button index selector
|
|
1918
|
+
if (inst.s.buttons[selector]) {
|
|
1919
|
+
ret.push({
|
|
1920
|
+
inst: inst,
|
|
1921
|
+
node: inst.s.buttons[selector].node
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
else if (typeof selector === 'string') {
|
|
1926
|
+
if (selector.indexOf(',') !== -1) {
|
|
1927
|
+
// Split
|
|
1928
|
+
var a = selector.split(',');
|
|
1929
|
+
|
|
1930
|
+
for (i = 0, ien = a.length; i < ien; i++) {
|
|
1931
|
+
run(a[i].trim(), inst);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
else if (selector.match(/^\d+(\-\d+)*$/)) {
|
|
1935
|
+
// Sub-button index selector
|
|
1936
|
+
var indexes = $.map(buttons, function (v) {
|
|
1937
|
+
return v.idx;
|
|
1938
|
+
});
|
|
1939
|
+
|
|
1940
|
+
ret.push({
|
|
1941
|
+
inst: inst,
|
|
1942
|
+
node: buttons[$.inArray(selector, indexes)].node
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1945
|
+
else if (selector.indexOf(':name') !== -1) {
|
|
1946
|
+
// Button name selector
|
|
1947
|
+
var name = selector.replace(':name', '');
|
|
1948
|
+
|
|
1949
|
+
for (i = 0, ien = buttons.length; i < ien; i++) {
|
|
1950
|
+
if (buttons[i].name === name) {
|
|
1951
|
+
ret.push({
|
|
1952
|
+
inst: inst,
|
|
1953
|
+
node: buttons[i].node
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
else {
|
|
1959
|
+
// jQuery selector on the nodes
|
|
1960
|
+
$(nodes)
|
|
1961
|
+
.filter(selector)
|
|
1962
|
+
.each(function () {
|
|
1963
|
+
ret.push({
|
|
1964
|
+
inst: inst,
|
|
1965
|
+
node: this
|
|
1966
|
+
});
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
else if (typeof selector === 'object' && selector.nodeName) {
|
|
1971
|
+
// Node selector
|
|
1972
|
+
var idx = $.inArray(selector, nodes);
|
|
1973
|
+
|
|
1974
|
+
if (idx !== -1) {
|
|
1975
|
+
ret.push({
|
|
1976
|
+
inst: inst,
|
|
1977
|
+
node: nodes[idx]
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
|
|
1983
|
+
for (var i = 0, ien = insts.length; i < ien; i++) {
|
|
1984
|
+
var inst = insts[i];
|
|
1985
|
+
|
|
1986
|
+
run(selector, inst);
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
return ret;
|
|
1990
|
+
};
|
|
1991
|
+
|
|
1992
|
+
/**
|
|
1993
|
+
* Default function used for formatting output data.
|
|
1994
|
+
* @param {*} str Data to strip
|
|
1995
|
+
*/
|
|
1996
|
+
Buttons.stripData = function (str, config) {
|
|
1997
|
+
// If the input is an HTML element, we can use the HTML from it (HTML might be stripped below).
|
|
1998
|
+
if (str !== null && typeof str === 'object' && str.nodeName && str.nodeType) {
|
|
1999
|
+
str = str.innerHTML;
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
if (typeof str !== 'string') {
|
|
2003
|
+
return str;
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
// Always remove script tags
|
|
2007
|
+
str = Buttons.stripHtmlScript(str);
|
|
2008
|
+
|
|
2009
|
+
// Always remove comments
|
|
2010
|
+
str = Buttons.stripHtmlComments(str);
|
|
2011
|
+
|
|
2012
|
+
if (!config || config.stripHtml) {
|
|
2013
|
+
str = DataTable.util.stripHtml(str);
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
if (!config || config.trim) {
|
|
2017
|
+
str = str.trim();
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
if (!config || config.stripNewlines) {
|
|
2021
|
+
str = str.replace(/\n/g, ' ');
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
if (!config || config.decodeEntities) {
|
|
2025
|
+
if (_entityDecoder) {
|
|
2026
|
+
str = _entityDecoder(str);
|
|
2027
|
+
}
|
|
2028
|
+
else {
|
|
2029
|
+
_exportTextarea.innerHTML = str;
|
|
2030
|
+
str = _exportTextarea.value;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
// Prevent Excel from running a formula
|
|
2035
|
+
if (!config || config.escapeExcelFormula) {
|
|
2036
|
+
if (str.match(/^[=+\-@\t\r]/)) {
|
|
2037
|
+
console.log('matching and updateing');
|
|
2038
|
+
str = "'" + str;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
return str;
|
|
2043
|
+
};
|
|
2044
|
+
|
|
2045
|
+
/**
|
|
2046
|
+
* Provide a custom entity decoding function - e.g. a regex one, which can be
|
|
2047
|
+
* much faster than the built in DOM option, but also larger code size.
|
|
2048
|
+
* @param {function} fn
|
|
2049
|
+
*/
|
|
2050
|
+
Buttons.entityDecoder = function (fn) {
|
|
2051
|
+
_entityDecoder = fn;
|
|
2052
|
+
};
|
|
2053
|
+
|
|
2054
|
+
/**
|
|
2055
|
+
* Common function for stripping HTML comments
|
|
2056
|
+
*
|
|
2057
|
+
* @param {*} input
|
|
2058
|
+
* @returns
|
|
2059
|
+
*/
|
|
2060
|
+
Buttons.stripHtmlComments = function (input) {
|
|
2061
|
+
var previous;
|
|
2062
|
+
|
|
2063
|
+
do {
|
|
2064
|
+
previous = input;
|
|
2065
|
+
input = input.replace(/(<!--.*?--!?>)|(<!--[\S\s]+?--!?>)|(<!--[\S\s]*?$)/g, '');
|
|
2066
|
+
} while (input !== previous);
|
|
2067
|
+
|
|
2068
|
+
return input;
|
|
2069
|
+
};
|
|
2070
|
+
|
|
2071
|
+
/**
|
|
2072
|
+
* Common function for stripping HTML script tags
|
|
2073
|
+
*
|
|
2074
|
+
* @param {*} input
|
|
2075
|
+
* @returns
|
|
2076
|
+
*/
|
|
2077
|
+
Buttons.stripHtmlScript = function (input) {
|
|
2078
|
+
var previous;
|
|
2079
|
+
|
|
2080
|
+
do {
|
|
2081
|
+
previous = input;
|
|
2082
|
+
input = input.replace(/<script\b[^<]*(?:(?!<\/script[^>]*>)<[^<]*)*<\/script[^>]*>/gi, '');
|
|
2083
|
+
} while (input !== previous);
|
|
2084
|
+
|
|
2085
|
+
return input;
|
|
2086
|
+
};
|
|
2087
|
+
|
|
2088
|
+
/**
|
|
2089
|
+
* Buttons defaults. For full documentation, please refer to the docs/option
|
|
2090
|
+
* directory or the DataTables site.
|
|
2091
|
+
* @type {Object}
|
|
2092
|
+
* @static
|
|
2093
|
+
*/
|
|
2094
|
+
Buttons.defaults = {
|
|
2095
|
+
buttons: ['copy', 'excel', 'csv', 'pdf', 'print'],
|
|
2096
|
+
name: 'main',
|
|
2097
|
+
tabIndex: 0,
|
|
2098
|
+
dom: {
|
|
2099
|
+
container: {
|
|
2100
|
+
tag: 'div',
|
|
2101
|
+
className: 'dt-buttons'
|
|
2102
|
+
},
|
|
2103
|
+
collection: {
|
|
2104
|
+
container: {
|
|
2105
|
+
// The element used for the dropdown
|
|
2106
|
+
className: 'dt-button-collection',
|
|
2107
|
+
content: {
|
|
2108
|
+
className: '',
|
|
2109
|
+
tag: 'div'
|
|
2110
|
+
},
|
|
2111
|
+
tag: 'div'
|
|
2112
|
+
}
|
|
2113
|
+
// optionally
|
|
2114
|
+
// , button: IButton - buttons inside the collection container
|
|
2115
|
+
// , split: ISplit - splits inside the collection container
|
|
2116
|
+
},
|
|
2117
|
+
button: {
|
|
2118
|
+
tag: 'button',
|
|
2119
|
+
className: 'dt-button',
|
|
2120
|
+
active: 'dt-button-active', // class name
|
|
2121
|
+
disabled: 'disabled', // class name
|
|
2122
|
+
spacer: {
|
|
2123
|
+
className: 'dt-button-spacer',
|
|
2124
|
+
tag: 'span'
|
|
2125
|
+
},
|
|
2126
|
+
liner: {
|
|
2127
|
+
tag: 'span',
|
|
2128
|
+
className: ''
|
|
2129
|
+
},
|
|
2130
|
+
dropClass: '',
|
|
2131
|
+
dropHtml: '<span class="dt-button-down-arrow">▼</span>'
|
|
2132
|
+
},
|
|
2133
|
+
split: {
|
|
2134
|
+
action: {
|
|
2135
|
+
// action button
|
|
2136
|
+
className: 'dt-button-split-drop-button dt-button',
|
|
2137
|
+
tag: 'button'
|
|
2138
|
+
},
|
|
2139
|
+
dropdown: {
|
|
2140
|
+
// button to trigger the dropdown
|
|
2141
|
+
align: 'split-right',
|
|
2142
|
+
className: 'dt-button-split-drop',
|
|
2143
|
+
splitAlignClass: 'dt-button-split-left',
|
|
2144
|
+
tag: 'button'
|
|
2145
|
+
},
|
|
2146
|
+
wrapper: {
|
|
2147
|
+
// wrap around both
|
|
2148
|
+
className: 'dt-button-split',
|
|
2149
|
+
tag: 'div'
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
};
|
|
2154
|
+
|
|
2155
|
+
/**
|
|
2156
|
+
* Version information
|
|
2157
|
+
* @type {string}
|
|
2158
|
+
* @static
|
|
2159
|
+
*/
|
|
2160
|
+
Buttons.version = '3.2.3';
|
|
2161
|
+
|
|
2162
|
+
$.extend(_dtButtons, {
|
|
2163
|
+
collection: {
|
|
2164
|
+
text: function (dt) {
|
|
2165
|
+
return dt.i18n('buttons.collection', 'Collection');
|
|
2166
|
+
},
|
|
2167
|
+
className: 'buttons-collection',
|
|
2168
|
+
closeButton: false,
|
|
2169
|
+
dropIcon: true,
|
|
2170
|
+
init: function (dt, button) {
|
|
2171
|
+
button.attr('aria-expanded', false);
|
|
2172
|
+
},
|
|
2173
|
+
action: function (e, dt, button, config) {
|
|
2174
|
+
if (config._collection.parents('body').length) {
|
|
2175
|
+
this.popover(false, config);
|
|
2176
|
+
}
|
|
2177
|
+
else {
|
|
2178
|
+
this.popover(config._collection, config);
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
// When activated using a key - auto focus on the
|
|
2182
|
+
// first item in the popover
|
|
2183
|
+
if (e.type === 'keypress') {
|
|
2184
|
+
$('a, button', config._collection).eq(0).focus();
|
|
2185
|
+
}
|
|
2186
|
+
},
|
|
2187
|
+
attr: {
|
|
2188
|
+
'aria-haspopup': 'dialog'
|
|
2189
|
+
}
|
|
2190
|
+
// Also the popover options, defined in Buttons.popover
|
|
2191
|
+
},
|
|
2192
|
+
split: {
|
|
2193
|
+
text: function (dt) {
|
|
2194
|
+
return dt.i18n('buttons.split', 'Split');
|
|
2195
|
+
},
|
|
2196
|
+
className: 'buttons-split',
|
|
2197
|
+
closeButton: false,
|
|
2198
|
+
init: function (dt, button) {
|
|
2199
|
+
return button.attr('aria-expanded', false);
|
|
2200
|
+
},
|
|
2201
|
+
action: function (e, dt, button, config) {
|
|
2202
|
+
this.popover(config._collection, config);
|
|
2203
|
+
},
|
|
2204
|
+
attr: {
|
|
2205
|
+
'aria-haspopup': 'dialog'
|
|
2206
|
+
}
|
|
2207
|
+
// Also the popover options, defined in Buttons.popover
|
|
2208
|
+
},
|
|
2209
|
+
copy: function () {
|
|
2210
|
+
if (_dtButtons.copyHtml5) {
|
|
2211
|
+
return 'copyHtml5';
|
|
2212
|
+
}
|
|
2213
|
+
},
|
|
2214
|
+
csv: function (dt, conf) {
|
|
2215
|
+
if (_dtButtons.csvHtml5 && _dtButtons.csvHtml5.available(dt, conf)) {
|
|
2216
|
+
return 'csvHtml5';
|
|
2217
|
+
}
|
|
2218
|
+
},
|
|
2219
|
+
excel: function (dt, conf) {
|
|
2220
|
+
if (
|
|
2221
|
+
_dtButtons.excelHtml5 &&
|
|
2222
|
+
_dtButtons.excelHtml5.available(dt, conf)
|
|
2223
|
+
) {
|
|
2224
|
+
return 'excelHtml5';
|
|
2225
|
+
}
|
|
2226
|
+
},
|
|
2227
|
+
pdf: function (dt, conf) {
|
|
2228
|
+
if (_dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available(dt, conf)) {
|
|
2229
|
+
return 'pdfHtml5';
|
|
2230
|
+
}
|
|
2231
|
+
},
|
|
2232
|
+
pageLength: function (dt) {
|
|
2233
|
+
var lengthMenu = dt.settings()[0].aLengthMenu;
|
|
2234
|
+
var vals = [];
|
|
2235
|
+
var lang = [];
|
|
2236
|
+
var text = function (dt) {
|
|
2237
|
+
return dt.i18n(
|
|
2238
|
+
'buttons.pageLength',
|
|
2239
|
+
{
|
|
2240
|
+
'-1': 'Show all rows',
|
|
2241
|
+
_: 'Show %d rows'
|
|
2242
|
+
},
|
|
2243
|
+
dt.page.len()
|
|
2244
|
+
);
|
|
2245
|
+
};
|
|
2246
|
+
|
|
2247
|
+
// Support for DataTables 1.x 2D array
|
|
2248
|
+
if (Array.isArray(lengthMenu[0])) {
|
|
2249
|
+
vals = lengthMenu[0];
|
|
2250
|
+
lang = lengthMenu[1];
|
|
2251
|
+
}
|
|
2252
|
+
else {
|
|
2253
|
+
for (var i = 0; i < lengthMenu.length; i++) {
|
|
2254
|
+
var option = lengthMenu[i];
|
|
2255
|
+
|
|
2256
|
+
// Support for DataTables 2 object in the array
|
|
2257
|
+
if ($.isPlainObject(option)) {
|
|
2258
|
+
vals.push(option.value);
|
|
2259
|
+
lang.push(option.label);
|
|
2260
|
+
}
|
|
2261
|
+
else {
|
|
2262
|
+
vals.push(option);
|
|
2263
|
+
lang.push(option);
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
return {
|
|
2269
|
+
extend: 'collection',
|
|
2270
|
+
text: text,
|
|
2271
|
+
className: 'buttons-page-length',
|
|
2272
|
+
autoClose: true,
|
|
2273
|
+
buttons: $.map(vals, function (val, i) {
|
|
2274
|
+
return {
|
|
2275
|
+
text: lang[i],
|
|
2276
|
+
className: 'button-page-length',
|
|
2277
|
+
action: function (e, dt) {
|
|
2278
|
+
dt.page.len(val).draw();
|
|
2279
|
+
},
|
|
2280
|
+
init: function (dt, node, conf) {
|
|
2281
|
+
var that = this;
|
|
2282
|
+
var fn = function () {
|
|
2283
|
+
that.active(dt.page.len() === val);
|
|
2284
|
+
};
|
|
2285
|
+
|
|
2286
|
+
dt.on('length.dt' + conf.namespace, fn);
|
|
2287
|
+
fn();
|
|
2288
|
+
},
|
|
2289
|
+
destroy: function (dt, node, conf) {
|
|
2290
|
+
dt.off('length.dt' + conf.namespace);
|
|
2291
|
+
}
|
|
2292
|
+
};
|
|
2293
|
+
}),
|
|
2294
|
+
init: function (dt, node, conf) {
|
|
2295
|
+
var that = this;
|
|
2296
|
+
dt.on('length.dt' + conf.namespace, function () {
|
|
2297
|
+
that.text(conf.text);
|
|
2298
|
+
});
|
|
2299
|
+
},
|
|
2300
|
+
destroy: function (dt, node, conf) {
|
|
2301
|
+
dt.off('length.dt' + conf.namespace);
|
|
2302
|
+
}
|
|
2303
|
+
};
|
|
2304
|
+
},
|
|
2305
|
+
spacer: {
|
|
2306
|
+
style: 'empty',
|
|
2307
|
+
spacer: true,
|
|
2308
|
+
text: function (dt) {
|
|
2309
|
+
return dt.i18n('buttons.spacer', '');
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
});
|
|
2313
|
+
|
|
2314
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
2315
|
+
* DataTables API
|
|
2316
|
+
*
|
|
2317
|
+
* For complete documentation, please refer to the docs/api directory or the
|
|
2318
|
+
* DataTables site
|
|
2319
|
+
*/
|
|
2320
|
+
|
|
2321
|
+
// Buttons group and individual button selector
|
|
2322
|
+
DataTable.Api.register('buttons()', function (group, selector) {
|
|
2323
|
+
// Argument shifting
|
|
2324
|
+
if (selector === undefined) {
|
|
2325
|
+
selector = group;
|
|
2326
|
+
group = undefined;
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
this.selector.buttonGroup = group;
|
|
2330
|
+
|
|
2331
|
+
var res = this.iterator(
|
|
2332
|
+
true,
|
|
2333
|
+
'table',
|
|
2334
|
+
function (ctx) {
|
|
2335
|
+
if (ctx._buttons) {
|
|
2336
|
+
return Buttons.buttonSelector(
|
|
2337
|
+
Buttons.instanceSelector(group, ctx._buttons),
|
|
2338
|
+
selector
|
|
2339
|
+
);
|
|
2340
|
+
}
|
|
2341
|
+
},
|
|
2342
|
+
true
|
|
2343
|
+
);
|
|
2344
|
+
|
|
2345
|
+
res._groupSelector = group;
|
|
2346
|
+
return res;
|
|
2347
|
+
});
|
|
2348
|
+
|
|
2349
|
+
// Individual button selector
|
|
2350
|
+
DataTable.Api.register('button()', function (group, selector) {
|
|
2351
|
+
// just run buttons() and truncate
|
|
2352
|
+
var buttons = this.buttons(group, selector);
|
|
2353
|
+
|
|
2354
|
+
if (buttons.length > 1) {
|
|
2355
|
+
buttons.splice(1, buttons.length);
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
return buttons;
|
|
2359
|
+
});
|
|
2360
|
+
|
|
2361
|
+
// Active buttons
|
|
2362
|
+
DataTable.Api.registerPlural(
|
|
2363
|
+
'buttons().active()',
|
|
2364
|
+
'button().active()',
|
|
2365
|
+
function (flag) {
|
|
2366
|
+
if (flag === undefined) {
|
|
2367
|
+
return this.map(function (set) {
|
|
2368
|
+
return set.inst.active(set.node);
|
|
2369
|
+
});
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
return this.each(function (set) {
|
|
2373
|
+
set.inst.active(set.node, flag);
|
|
2374
|
+
});
|
|
2375
|
+
}
|
|
2376
|
+
);
|
|
2377
|
+
|
|
2378
|
+
// Get / set button action
|
|
2379
|
+
DataTable.Api.registerPlural(
|
|
2380
|
+
'buttons().action()',
|
|
2381
|
+
'button().action()',
|
|
2382
|
+
function (action) {
|
|
2383
|
+
if (action === undefined) {
|
|
2384
|
+
return this.map(function (set) {
|
|
2385
|
+
return set.inst.action(set.node);
|
|
2386
|
+
});
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
return this.each(function (set) {
|
|
2390
|
+
set.inst.action(set.node, action);
|
|
2391
|
+
});
|
|
2392
|
+
}
|
|
2393
|
+
);
|
|
2394
|
+
|
|
2395
|
+
// Collection control
|
|
2396
|
+
DataTable.Api.registerPlural(
|
|
2397
|
+
'buttons().collectionRebuild()',
|
|
2398
|
+
'button().collectionRebuild()',
|
|
2399
|
+
function (buttons) {
|
|
2400
|
+
return this.each(function (set) {
|
|
2401
|
+
for (var i = 0; i < buttons.length; i++) {
|
|
2402
|
+
if (typeof buttons[i] === 'object') {
|
|
2403
|
+
buttons[i].parentConf = set;
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
set.inst.collectionRebuild(set.node, buttons);
|
|
2407
|
+
});
|
|
2408
|
+
}
|
|
2409
|
+
);
|
|
2410
|
+
|
|
2411
|
+
// Enable / disable buttons
|
|
2412
|
+
DataTable.Api.register(
|
|
2413
|
+
['buttons().enable()', 'button().enable()'],
|
|
2414
|
+
function (flag) {
|
|
2415
|
+
return this.each(function (set) {
|
|
2416
|
+
set.inst.enable(set.node, flag);
|
|
2417
|
+
});
|
|
2418
|
+
}
|
|
2419
|
+
);
|
|
2420
|
+
|
|
2421
|
+
// Disable buttons
|
|
2422
|
+
DataTable.Api.register(
|
|
2423
|
+
['buttons().disable()', 'button().disable()'],
|
|
2424
|
+
function () {
|
|
2425
|
+
return this.each(function (set) {
|
|
2426
|
+
set.inst.disable(set.node);
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
2429
|
+
);
|
|
2430
|
+
|
|
2431
|
+
// Button index
|
|
2432
|
+
DataTable.Api.register('button().index()', function () {
|
|
2433
|
+
var idx = null;
|
|
2434
|
+
|
|
2435
|
+
this.each(function (set) {
|
|
2436
|
+
var res = set.inst.index(set.node);
|
|
2437
|
+
|
|
2438
|
+
if (res !== null) {
|
|
2439
|
+
idx = res;
|
|
2440
|
+
}
|
|
2441
|
+
});
|
|
2442
|
+
|
|
2443
|
+
return idx;
|
|
2444
|
+
});
|
|
2445
|
+
|
|
2446
|
+
// Get button nodes
|
|
2447
|
+
DataTable.Api.registerPlural(
|
|
2448
|
+
'buttons().nodes()',
|
|
2449
|
+
'button().node()',
|
|
2450
|
+
function () {
|
|
2451
|
+
var jq = $();
|
|
2452
|
+
|
|
2453
|
+
// jQuery will automatically reduce duplicates to a single entry
|
|
2454
|
+
$(
|
|
2455
|
+
this.each(function (set) {
|
|
2456
|
+
jq = jq.add(set.inst.node(set.node));
|
|
2457
|
+
})
|
|
2458
|
+
);
|
|
2459
|
+
|
|
2460
|
+
return jq;
|
|
2461
|
+
}
|
|
2462
|
+
);
|
|
2463
|
+
|
|
2464
|
+
// Get / set button processing state
|
|
2465
|
+
DataTable.Api.registerPlural(
|
|
2466
|
+
'buttons().processing()',
|
|
2467
|
+
'button().processing()',
|
|
2468
|
+
function (flag) {
|
|
2469
|
+
if (flag === undefined) {
|
|
2470
|
+
return this.map(function (set) {
|
|
2471
|
+
return set.inst.processing(set.node);
|
|
2472
|
+
});
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
return this.each(function (set) {
|
|
2476
|
+
set.inst.processing(set.node, flag);
|
|
2477
|
+
});
|
|
2478
|
+
}
|
|
2479
|
+
);
|
|
2480
|
+
|
|
2481
|
+
// Get / set button text (i.e. the button labels)
|
|
2482
|
+
DataTable.Api.registerPlural(
|
|
2483
|
+
'buttons().text()',
|
|
2484
|
+
'button().text()',
|
|
2485
|
+
function (label) {
|
|
2486
|
+
if (label === undefined) {
|
|
2487
|
+
return this.map(function (set) {
|
|
2488
|
+
return set.inst.text(set.node);
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
return this.each(function (set) {
|
|
2493
|
+
set.inst.text(set.node, label);
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2496
|
+
);
|
|
2497
|
+
|
|
2498
|
+
// Trigger a button's action
|
|
2499
|
+
DataTable.Api.registerPlural(
|
|
2500
|
+
'buttons().trigger()',
|
|
2501
|
+
'button().trigger()',
|
|
2502
|
+
function () {
|
|
2503
|
+
return this.each(function (set) {
|
|
2504
|
+
set.inst.node(set.node).trigger('click');
|
|
2505
|
+
});
|
|
2506
|
+
}
|
|
2507
|
+
);
|
|
2508
|
+
|
|
2509
|
+
// Button resolver to the popover
|
|
2510
|
+
DataTable.Api.register('button().popover()', function (content, options) {
|
|
2511
|
+
return this.map(function (set) {
|
|
2512
|
+
return set.inst._popover(content, this.button(this[0].node), options);
|
|
2513
|
+
});
|
|
2514
|
+
});
|
|
2515
|
+
|
|
2516
|
+
// Get the container elements
|
|
2517
|
+
DataTable.Api.register('buttons().containers()', function () {
|
|
2518
|
+
var jq = $();
|
|
2519
|
+
var groupSelector = this._groupSelector;
|
|
2520
|
+
|
|
2521
|
+
// We need to use the group selector directly, since if there are no buttons
|
|
2522
|
+
// the result set will be empty
|
|
2523
|
+
this.iterator(true, 'table', function (ctx) {
|
|
2524
|
+
if (ctx._buttons) {
|
|
2525
|
+
var insts = Buttons.instanceSelector(groupSelector, ctx._buttons);
|
|
2526
|
+
|
|
2527
|
+
for (var i = 0, ien = insts.length; i < ien; i++) {
|
|
2528
|
+
jq = jq.add(insts[i].container());
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
});
|
|
2532
|
+
|
|
2533
|
+
return jq;
|
|
2534
|
+
});
|
|
2535
|
+
|
|
2536
|
+
DataTable.Api.register('buttons().container()', function () {
|
|
2537
|
+
// API level of nesting is `buttons()` so we can zip into the containers method
|
|
2538
|
+
return this.containers().eq(0);
|
|
2539
|
+
});
|
|
2540
|
+
|
|
2541
|
+
// Add a new button
|
|
2542
|
+
DataTable.Api.register('button().add()', function (idx, conf, draw) {
|
|
2543
|
+
var ctx = this.context;
|
|
2544
|
+
|
|
2545
|
+
// Don't use `this` as it could be empty - select the instances directly
|
|
2546
|
+
if (ctx.length) {
|
|
2547
|
+
var inst = Buttons.instanceSelector(
|
|
2548
|
+
this._groupSelector,
|
|
2549
|
+
ctx[0]._buttons
|
|
2550
|
+
);
|
|
2551
|
+
|
|
2552
|
+
if (inst.length) {
|
|
2553
|
+
inst[0].add(conf, idx, draw);
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
return this.button(this._groupSelector, idx);
|
|
2558
|
+
});
|
|
2559
|
+
|
|
2560
|
+
// Destroy the button sets selected
|
|
2561
|
+
DataTable.Api.register('buttons().destroy()', function () {
|
|
2562
|
+
this.pluck('inst')
|
|
2563
|
+
.unique()
|
|
2564
|
+
.each(function (inst) {
|
|
2565
|
+
inst.destroy();
|
|
2566
|
+
});
|
|
2567
|
+
|
|
2568
|
+
return this;
|
|
2569
|
+
});
|
|
2570
|
+
|
|
2571
|
+
// Remove a button
|
|
2572
|
+
DataTable.Api.registerPlural(
|
|
2573
|
+
'buttons().remove()',
|
|
2574
|
+
'buttons().remove()',
|
|
2575
|
+
function () {
|
|
2576
|
+
this.each(function (set) {
|
|
2577
|
+
set.inst.remove(set.node);
|
|
2578
|
+
});
|
|
2579
|
+
|
|
2580
|
+
return this;
|
|
2581
|
+
}
|
|
2582
|
+
);
|
|
2583
|
+
|
|
2584
|
+
// Information box that can be used by buttons
|
|
2585
|
+
var _infoTimer;
|
|
2586
|
+
DataTable.Api.register('buttons.info()', function (title, message, time) {
|
|
2587
|
+
var that = this;
|
|
2588
|
+
|
|
2589
|
+
if (title === false) {
|
|
2590
|
+
this.off('destroy.btn-info');
|
|
2591
|
+
_fadeOut($('#datatables_buttons_info'), 400, function () {
|
|
2592
|
+
$(this).remove();
|
|
2593
|
+
});
|
|
2594
|
+
clearTimeout(_infoTimer);
|
|
2595
|
+
_infoTimer = null;
|
|
2596
|
+
|
|
2597
|
+
return this;
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
if (_infoTimer) {
|
|
2601
|
+
clearTimeout(_infoTimer);
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
if ($('#datatables_buttons_info').length) {
|
|
2605
|
+
$('#datatables_buttons_info').remove();
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2608
|
+
title = title ? '<h2>' + title + '</h2>' : '';
|
|
2609
|
+
|
|
2610
|
+
_fadeIn(
|
|
2611
|
+
$('<div id="datatables_buttons_info" class="dt-button-info"/>')
|
|
2612
|
+
.html(title)
|
|
2613
|
+
.append(
|
|
2614
|
+
$('<div/>')[typeof message === 'string' ? 'html' : 'append'](
|
|
2615
|
+
message
|
|
2616
|
+
)
|
|
2617
|
+
)
|
|
2618
|
+
.css('display', 'none')
|
|
2619
|
+
.appendTo('body')
|
|
2620
|
+
);
|
|
2621
|
+
|
|
2622
|
+
if (time !== undefined && time !== 0) {
|
|
2623
|
+
_infoTimer = setTimeout(function () {
|
|
2624
|
+
that.buttons.info(false);
|
|
2625
|
+
}, time);
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
this.on('destroy.btn-info', function () {
|
|
2629
|
+
that.buttons.info(false);
|
|
2630
|
+
});
|
|
2631
|
+
|
|
2632
|
+
return this;
|
|
2633
|
+
});
|
|
2634
|
+
|
|
2635
|
+
// Get data from the table for export - this is common to a number of plug-in
|
|
2636
|
+
// buttons so it is included in the Buttons core library
|
|
2637
|
+
DataTable.Api.register('buttons.exportData()', function (options) {
|
|
2638
|
+
if (this.context.length) {
|
|
2639
|
+
return _exportData(new DataTable.Api(this.context[0]), options);
|
|
2640
|
+
}
|
|
2641
|
+
});
|
|
2642
|
+
|
|
2643
|
+
// Get information about the export that is common to many of the export data
|
|
2644
|
+
// types (DRY)
|
|
2645
|
+
DataTable.Api.register('buttons.exportInfo()', function (conf) {
|
|
2646
|
+
if (!conf) {
|
|
2647
|
+
conf = {};
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
return {
|
|
2651
|
+
filename: _filename(conf, this),
|
|
2652
|
+
title: _title(conf, this),
|
|
2653
|
+
messageTop: _message(this, conf, conf.message || conf.messageTop, 'top'),
|
|
2654
|
+
messageBottom: _message(this, conf, conf.messageBottom, 'bottom')
|
|
2655
|
+
};
|
|
2656
|
+
});
|
|
2657
|
+
|
|
2658
|
+
/**
|
|
2659
|
+
* Get the file name for an exported file.
|
|
2660
|
+
*
|
|
2661
|
+
* @param {object} config Button configuration
|
|
2662
|
+
* @param {object} dt DataTable instance
|
|
2663
|
+
*/
|
|
2664
|
+
var _filename = function (config, dt) {
|
|
2665
|
+
// Backwards compatibility
|
|
2666
|
+
var filename =
|
|
2667
|
+
config.filename === '*' &&
|
|
2668
|
+
config.title !== '*' &&
|
|
2669
|
+
config.title !== undefined &&
|
|
2670
|
+
config.title !== null &&
|
|
2671
|
+
config.title !== ''
|
|
2672
|
+
? config.title
|
|
2673
|
+
: config.filename;
|
|
2674
|
+
|
|
2675
|
+
if (typeof filename === 'function') {
|
|
2676
|
+
filename = filename(config, dt);
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2679
|
+
if (filename === undefined || filename === null) {
|
|
2680
|
+
return null;
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2683
|
+
if (filename.indexOf('*') !== -1) {
|
|
2684
|
+
filename = filename.replace(/\*/g, $('head > title').text()).trim();
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
// Strip characters which the OS will object to
|
|
2688
|
+
filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, '');
|
|
2689
|
+
|
|
2690
|
+
var extension = _stringOrFunction(config.extension, config, dt);
|
|
2691
|
+
if (!extension) {
|
|
2692
|
+
extension = '';
|
|
2693
|
+
}
|
|
2694
|
+
|
|
2695
|
+
return filename + extension;
|
|
2696
|
+
};
|
|
2697
|
+
|
|
2698
|
+
/**
|
|
2699
|
+
* Simply utility method to allow parameters to be given as a function
|
|
2700
|
+
*
|
|
2701
|
+
* @param {undefined|string|function} option Option
|
|
2702
|
+
* @return {null|string} Resolved value
|
|
2703
|
+
*/
|
|
2704
|
+
var _stringOrFunction = function (option, config, dt) {
|
|
2705
|
+
if (option === null || option === undefined) {
|
|
2706
|
+
return null;
|
|
2707
|
+
}
|
|
2708
|
+
else if (typeof option === 'function') {
|
|
2709
|
+
return option(config, dt);
|
|
2710
|
+
}
|
|
2711
|
+
return option;
|
|
2712
|
+
};
|
|
2713
|
+
|
|
2714
|
+
/**
|
|
2715
|
+
* Get the title for an exported file.
|
|
2716
|
+
*
|
|
2717
|
+
* @param {object} config Button configuration
|
|
2718
|
+
*/
|
|
2719
|
+
var _title = function (config, dt) {
|
|
2720
|
+
var title = _stringOrFunction(config.title, config, dt);
|
|
2721
|
+
|
|
2722
|
+
return title === null
|
|
2723
|
+
? null
|
|
2724
|
+
: title.indexOf('*') !== -1
|
|
2725
|
+
? title.replace(/\*/g, $('head > title').text() || 'Exported data')
|
|
2726
|
+
: title;
|
|
2727
|
+
};
|
|
2728
|
+
|
|
2729
|
+
var _message = function (dt, config, option, position) {
|
|
2730
|
+
var message = _stringOrFunction(option, config, dt);
|
|
2731
|
+
if (message === null) {
|
|
2732
|
+
return null;
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2735
|
+
var caption = $('caption', dt.table().container()).eq(0);
|
|
2736
|
+
if (message === '*') {
|
|
2737
|
+
var side = caption.css('caption-side');
|
|
2738
|
+
if (side !== position) {
|
|
2739
|
+
return null;
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
return caption.length ? caption.text() : '';
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2745
|
+
return message;
|
|
2746
|
+
};
|
|
2747
|
+
|
|
2748
|
+
var _exportTextarea = $('<textarea/>')[0];
|
|
2749
|
+
var _exportData = function (dt, inOpts) {
|
|
2750
|
+
var config = $.extend(
|
|
2751
|
+
true,
|
|
2752
|
+
{},
|
|
2753
|
+
{
|
|
2754
|
+
rows: null,
|
|
2755
|
+
columns: '',
|
|
2756
|
+
modifier: {
|
|
2757
|
+
search: 'applied',
|
|
2758
|
+
order: 'applied'
|
|
2759
|
+
},
|
|
2760
|
+
orthogonal: 'display',
|
|
2761
|
+
stripHtml: true,
|
|
2762
|
+
stripNewlines: true,
|
|
2763
|
+
decodeEntities: true,
|
|
2764
|
+
escapeExcelFormula: false,
|
|
2765
|
+
trim: true,
|
|
2766
|
+
format: {
|
|
2767
|
+
header: function (d) {
|
|
2768
|
+
return Buttons.stripData(d, config);
|
|
2769
|
+
},
|
|
2770
|
+
footer: function (d) {
|
|
2771
|
+
return Buttons.stripData(d, config);
|
|
2772
|
+
},
|
|
2773
|
+
body: function (d) {
|
|
2774
|
+
return Buttons.stripData(d, config);
|
|
2775
|
+
}
|
|
2776
|
+
},
|
|
2777
|
+
customizeData: null,
|
|
2778
|
+
customizeZip: null
|
|
2779
|
+
},
|
|
2780
|
+
inOpts
|
|
2781
|
+
);
|
|
2782
|
+
|
|
2783
|
+
var header = dt
|
|
2784
|
+
.columns(config.columns)
|
|
2785
|
+
.indexes()
|
|
2786
|
+
.map(function (idx) {
|
|
2787
|
+
var col = dt.column(idx);
|
|
2788
|
+
return config.format.header(col.title(), idx, col.header());
|
|
2789
|
+
})
|
|
2790
|
+
.toArray();
|
|
2791
|
+
|
|
2792
|
+
var footer = dt.table().footer()
|
|
2793
|
+
? dt
|
|
2794
|
+
.columns(config.columns)
|
|
2795
|
+
.indexes()
|
|
2796
|
+
.map(function (idx) {
|
|
2797
|
+
var el = dt.column(idx).footer();
|
|
2798
|
+
var val = '';
|
|
2799
|
+
|
|
2800
|
+
if (el) {
|
|
2801
|
+
var inner = $('.dt-column-title', el);
|
|
2802
|
+
|
|
2803
|
+
val = inner.length
|
|
2804
|
+
? inner.html()
|
|
2805
|
+
: $(el).html();
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2808
|
+
return config.format.footer(val, idx, el);
|
|
2809
|
+
})
|
|
2810
|
+
.toArray()
|
|
2811
|
+
: null;
|
|
2812
|
+
|
|
2813
|
+
// If Select is available on this table, and any rows are selected, limit the export
|
|
2814
|
+
// to the selected rows. If no rows are selected, all rows will be exported. Specify
|
|
2815
|
+
// a `selected` modifier to control directly.
|
|
2816
|
+
var modifier = $.extend({}, config.modifier);
|
|
2817
|
+
if (
|
|
2818
|
+
dt.select &&
|
|
2819
|
+
typeof dt.select.info === 'function' &&
|
|
2820
|
+
modifier.selected === undefined
|
|
2821
|
+
) {
|
|
2822
|
+
if (
|
|
2823
|
+
dt.rows(config.rows, $.extend({ selected: true }, modifier)).any()
|
|
2824
|
+
) {
|
|
2825
|
+
$.extend(modifier, { selected: true });
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2829
|
+
var rowIndexes = dt.rows(config.rows, modifier).indexes().toArray();
|
|
2830
|
+
var selectedCells = dt.cells(rowIndexes, config.columns, {
|
|
2831
|
+
order: modifier.order
|
|
2832
|
+
});
|
|
2833
|
+
var cells = selectedCells.render(config.orthogonal).toArray();
|
|
2834
|
+
var cellNodes = selectedCells.nodes().toArray();
|
|
2835
|
+
var cellIndexes = selectedCells.indexes().toArray();
|
|
2836
|
+
|
|
2837
|
+
var columns = dt.columns(config.columns).count();
|
|
2838
|
+
var rows = columns > 0 ? cells.length / columns : 0;
|
|
2839
|
+
var body = [];
|
|
2840
|
+
var cellCounter = 0;
|
|
2841
|
+
|
|
2842
|
+
for (var i = 0, ien = rows; i < ien; i++) {
|
|
2843
|
+
var row = [columns];
|
|
2844
|
+
|
|
2845
|
+
for (var j = 0; j < columns; j++) {
|
|
2846
|
+
row[j] = config.format.body(
|
|
2847
|
+
cells[cellCounter],
|
|
2848
|
+
cellIndexes[cellCounter].row,
|
|
2849
|
+
cellIndexes[cellCounter].column,
|
|
2850
|
+
cellNodes[cellCounter]
|
|
2851
|
+
);
|
|
2852
|
+
cellCounter++;
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
body[i] = row;
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
var data = {
|
|
2859
|
+
header: header,
|
|
2860
|
+
headerStructure: _headerFormatter(
|
|
2861
|
+
config.format.header,
|
|
2862
|
+
dt.table().header.structure(config.columns)
|
|
2863
|
+
),
|
|
2864
|
+
footer: footer,
|
|
2865
|
+
footerStructure: _headerFormatter(
|
|
2866
|
+
config.format.footer,
|
|
2867
|
+
dt.table().footer.structure(config.columns)
|
|
2868
|
+
),
|
|
2869
|
+
body: body
|
|
2870
|
+
};
|
|
2871
|
+
|
|
2872
|
+
if (config.customizeData) {
|
|
2873
|
+
config.customizeData(data);
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2876
|
+
return data;
|
|
2877
|
+
};
|
|
2878
|
+
|
|
2879
|
+
function _headerFormatter(formatter, struct) {
|
|
2880
|
+
for (var i=0 ; i<struct.length ; i++) {
|
|
2881
|
+
for (var j=0 ; j<struct[i].length ; j++) {
|
|
2882
|
+
var item = struct[i][j];
|
|
2883
|
+
|
|
2884
|
+
if (item) {
|
|
2885
|
+
item.title = formatter(
|
|
2886
|
+
item.title,
|
|
2887
|
+
j,
|
|
2888
|
+
item.cell
|
|
2889
|
+
);
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
return struct;
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
2898
|
+
* DataTables interface
|
|
2899
|
+
*/
|
|
2900
|
+
|
|
2901
|
+
// Attach to DataTables objects for global access
|
|
2902
|
+
$.fn.dataTable.Buttons = Buttons;
|
|
2903
|
+
$.fn.DataTable.Buttons = Buttons;
|
|
2904
|
+
|
|
2905
|
+
// DataTables creation - check if the buttons have been defined for this table,
|
|
2906
|
+
// they will have been if the `B` option was used in `dom`, otherwise we should
|
|
2907
|
+
// create the buttons instance here so they can be inserted into the document
|
|
2908
|
+
// using the API. Listen for `init` for compatibility with pre 1.10.10, but to
|
|
2909
|
+
// be removed in future.
|
|
2910
|
+
$(document).on('init.dt plugin-init.dt', function (e, settings) {
|
|
2911
|
+
if (e.namespace !== 'dt') {
|
|
2912
|
+
return;
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
var opts = settings.oInit.buttons || DataTable.defaults.buttons;
|
|
2916
|
+
|
|
2917
|
+
if (opts && !settings._buttons) {
|
|
2918
|
+
new Buttons(settings, opts).container();
|
|
2919
|
+
}
|
|
2920
|
+
});
|
|
2921
|
+
|
|
2922
|
+
function _init(settings, options) {
|
|
2923
|
+
var api = new DataTable.Api(settings);
|
|
2924
|
+
var opts = options
|
|
2925
|
+
? options
|
|
2926
|
+
: api.init().buttons || DataTable.defaults.buttons;
|
|
2927
|
+
|
|
2928
|
+
return new Buttons(api, opts).container();
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2931
|
+
// DataTables 1 `dom` feature option
|
|
2932
|
+
DataTable.ext.feature.push({
|
|
2933
|
+
fnInit: _init,
|
|
2934
|
+
cFeature: 'B'
|
|
2935
|
+
});
|
|
2936
|
+
|
|
2937
|
+
// DataTables 2 layout feature
|
|
2938
|
+
if (DataTable.feature) {
|
|
2939
|
+
DataTable.feature.register('buttons', _init);
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
|
|
2943
|
+
return DataTable;
|
|
2944
|
+
}));
|