@openui5/sap.ui.documentation 1.124.2 → 1.126.0

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