effective_datatables 2.6.6 → 2.6.7

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/dataTables/buttons/buttons.bootstrap.js +47 -47
  3. data/app/assets/javascripts/dataTables/buttons/buttons.colVis.js +182 -181
  4. data/app/assets/javascripts/dataTables/buttons/buttons.html5.js +1201 -684
  5. data/app/assets/javascripts/dataTables/buttons/buttons.print.js +144 -127
  6. data/app/assets/javascripts/dataTables/buttons/dataTables.buttons.js +1463 -1405
  7. data/app/assets/javascripts/dataTables/colreorder/dataTables.colReorder.js +27 -0
  8. data/app/assets/javascripts/dataTables/dataTables.bootstrap.js +182 -0
  9. data/app/assets/javascripts/dataTables/jquery.dataTables.js +15278 -0
  10. data/app/assets/javascripts/dataTables/jszip/jszip.js +9155 -0
  11. data/app/assets/javascripts/dataTables/responsive/dataTables.responsive.js +1232 -0
  12. data/app/assets/javascripts/dataTables/responsive/responsive.bootstrap.js +81 -0
  13. data/app/assets/javascripts/effective_datatables.js +7 -8
  14. data/app/assets/stylesheets/dataTables/buttons/{buttons.bootstrap.min.css → buttons.bootstrap.css} +0 -0
  15. data/app/assets/stylesheets/dataTables/colReorder/colReorder.bootstrap.css +11 -0
  16. data/app/assets/stylesheets/dataTables/{dataTables.bootstrap.min.css → dataTables.bootstrap.css} +1 -3
  17. data/app/assets/stylesheets/dataTables/responsive/responsive.bootstrap.css +181 -0
  18. data/app/assets/stylesheets/effective_datatables.scss +4 -10
  19. data/app/assets/stylesheets/effective_datatables/_overrides.scss.erb +5 -25
  20. data/lib/effective_datatables/version.rb +1 -1
  21. metadata +12 -16
  22. data/app/assets/javascripts/dataTables/dataTables.colReorder.min.js +0 -26
  23. data/app/assets/javascripts/dataTables/jquery.dataTables.bootstrap.js +0 -220
  24. data/app/assets/javascripts/dataTables/jquery.dataTables.min.js +0 -164
  25. data/app/assets/javascripts/dataTables/responsive/dataTables.responsive.min.js +0 -23
  26. data/app/assets/javascripts/dataTables/responsive/responsive.bootstrap.min.js +0 -7
  27. data/app/assets/javascripts/vendor/jszip.min.js +0 -14
  28. data/app/assets/stylesheets/dataTables/buttons/buttons.dataTables.min.css +0 -298
  29. data/app/assets/stylesheets/dataTables/colReorder/colReorder.bootstrap.min.css +0 -1
  30. data/app/assets/stylesheets/dataTables/colReorder/colReorder.dataTables.min.css +0 -1
  31. data/app/assets/stylesheets/dataTables/jquery.dataTables.min.css +0 -453
  32. data/app/assets/stylesheets/dataTables/responsive/responsive.bootstrap.min.css +0 -1
  33. data/app/assets/stylesheets/dataTables/responsive/responsive.dataTables.min.css +0 -1
@@ -1,37 +1,37 @@
1
1
  /*!
2
2
  * Print button for Buttons and DataTables.
3
- * 2015 SpryMedia Ltd - datatables.net/license
3
+ * 2016 SpryMedia Ltd - datatables.net/license
4
4
  */
5
5
 
6
6
  (function( factory ){
7
- if ( typeof define === 'function' && define.amd ) {
8
- // AMD
9
- define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
10
- return factory( $, window, document );
11
- } );
12
- }
13
- else if ( typeof exports === 'object' ) {
14
- // CommonJS
15
- module.exports = function (root, $) {
16
- if ( ! root ) {
17
- root = window;
18
- }
19
-
20
- if ( ! $ || ! $.fn.dataTable ) {
21
- $ = require('datatables.net')(root, $).$;
22
- }
23
-
24
- if ( ! $.fn.dataTable.Buttons ) {
25
- require('datatables.net-buttons')(root, $);
26
- }
27
-
28
- return factory( $, root, root.document );
29
- };
30
- }
31
- else {
32
- // Browser
33
- factory( jQuery, window, document );
34
- }
7
+ if ( typeof define === 'function' && define.amd ) {
8
+ // AMD
9
+ define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
10
+ return factory( $, window, document );
11
+ } );
12
+ }
13
+ else if ( typeof exports === 'object' ) {
14
+ // CommonJS
15
+ module.exports = function (root, $) {
16
+ if ( ! root ) {
17
+ root = window;
18
+ }
19
+
20
+ if ( ! $ || ! $.fn.dataTable ) {
21
+ $ = require('datatables.net')(root, $).$;
22
+ }
23
+
24
+ if ( ! $.fn.dataTable.Buttons ) {
25
+ require('datatables.net-buttons')(root, $);
26
+ }
27
+
28
+ return factory( $, root, root.document );
29
+ };
30
+ }
31
+ else {
32
+ // Browser
33
+ factory( jQuery, window, document );
34
+ }
35
35
  }(function( $, window, document, undefined ) {
36
36
  'use strict';
37
37
  var DataTable = $.fn.dataTable;
@@ -46,112 +46,129 @@ var _link = document.createElement( 'a' );
46
46
  * @param {node} el Element to convert
47
47
  */
48
48
  var _relToAbs = function( el ) {
49
- var url;
50
- var clone = $(el).clone()[0];
51
- var linkHost;
49
+ var url;
50
+ var clone = $(el).clone()[0];
51
+ var linkHost;
52
52
 
53
- if ( clone.nodeName.toLowerCase() === 'link' ) {
54
- _link.href = clone.href;
55
- linkHost = _link.host;
53
+ if ( clone.nodeName.toLowerCase() === 'link' ) {
54
+ _link.href = clone.href;
55
+ linkHost = _link.host;
56
56
 
57
- // IE doesn't have a trailing slash on the host
58
- // Chrome has it on the pathname
59
- if ( linkHost.indexOf('/') === -1 && _link.pathname.indexOf('/') !== 0) {
60
- linkHost += '/';
61
- }
57
+ // IE doesn't have a trailing slash on the host
58
+ // Chrome has it on the pathname
59
+ if ( linkHost.indexOf('/') === -1 && _link.pathname.indexOf('/') !== 0) {
60
+ linkHost += '/';
61
+ }
62
62
 
63
- clone.href = _link.protocol+"//"+linkHost+_link.pathname+_link.search;
64
- }
63
+ clone.href = _link.protocol+"//"+linkHost+_link.pathname+_link.search;
64
+ }
65
65
 
66
- return clone.outerHTML;
66
+ return clone.outerHTML;
67
67
  };
68
68
 
69
69
 
70
70
  DataTable.ext.buttons.print = {
71
- className: 'buttons-print',
72
-
73
- text: function ( dt ) {
74
- return dt.i18n( 'buttons.print', 'Print' );
75
- },
76
-
77
- action: function ( e, dt, button, config ) {
78
- var data = dt.buttons.exportData( config.exportOptions );
79
- var addRow = function ( d, tag ) {
80
- var str = '<tr>';
81
-
82
- for ( var i=0, ien=d.length ; i<ien ; i++ ) {
83
- str += '<'+tag+'>'+d[i]+'</'+tag+'>';
84
- }
85
-
86
- return str + '</tr>';
87
- };
88
-
89
- // Construct a table for printing
90
- var html = '<table class="'+dt.table().node().className+'">';
91
-
92
- if ( config.header ) {
93
- html += '<thead>'+ addRow( data.header, 'th' ) +'</thead>';
94
- }
95
-
96
- html += '<tbody>';
97
- for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
98
- html += addRow( data.body[i], 'td' );
99
- }
100
- html += '</tbody>';
101
-
102
- if ( config.footer ) {
103
- html += '<thead>'+ addRow( data.footer, 'th' ) +'</thead>';
104
- }
105
-
106
- // Open a new window for the printable table
107
- var win = window.open( '', '' );
108
- var title = config.title.replace( '*', $('title').text() );
109
-
110
- win.document.close();
111
-
112
- // Inject the title and also a copy of the style and link tags from this
113
- // document so the table can retain its base styling. Note that we have
114
- // to use string manipulation as IE won't allow elements to be created
115
- // in the host document and then appended to the new window.
116
- var head = '<title>'+title+'</title>';
117
- $('style, link').each( function () {
118
- head += _relToAbs( this );
119
- } );
120
-
121
- $(win.document.head).html( head );
122
-
123
- // Inject the table and other surrounding information
124
- $(win.document.body).html(
125
- '<h1>'+title+'</h1>'+
126
- '<div>'+config.message+'</div>'+
127
- html
128
- );
129
-
130
- if ( config.customize ) {
131
- config.customize( win );
132
- }
133
-
134
- setTimeout( function () {
135
- if ( config.autoPrint ) {
136
- win.print(); // blocking - so close will not
137
- win.close(); // execute until this is done
138
- }
139
- }, 250 );
140
- },
141
-
142
- title: '*',
143
-
144
- message: '',
145
-
146
- exportOptions: {},
147
-
148
- header: true,
149
-
150
- footer: false,
151
-
152
- autoPrint: true,
153
-
154
- customize: null
71
+ className: 'buttons-print',
72
+
73
+ text: function ( dt ) {
74
+ return dt.i18n( 'buttons.print', 'Print' );
75
+ },
76
+
77
+ action: function ( e, dt, button, config ) {
78
+ var data = dt.buttons.exportData( config.exportOptions );
79
+ var addRow = function ( d, tag ) {
80
+ var str = '<tr>';
81
+
82
+ for ( var i=0, ien=d.length ; i<ien ; i++ ) {
83
+ str += '<'+tag+'>'+d[i]+'</'+tag+'>';
84
+ }
85
+
86
+ return str + '</tr>';
87
+ };
88
+
89
+ // Construct a table for printing
90
+ var html = '<table class="'+dt.table().node().className+'">';
91
+
92
+ if ( config.header ) {
93
+ html += '<thead>'+ addRow( data.header, 'th' ) +'</thead>';
94
+ }
95
+
96
+ html += '<tbody>';
97
+ for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
98
+ html += addRow( data.body[i], 'td' );
99
+ }
100
+ html += '</tbody>';
101
+
102
+ if ( config.footer && data.footer ) {
103
+ html += '<tfoot>'+ addRow( data.footer, 'th' ) +'</tfoot>';
104
+ }
105
+
106
+ // Open a new window for the printable table
107
+ var win = window.open( '', '' );
108
+ var title = config.title;
109
+
110
+ if ( typeof title === 'function' ) {
111
+ title = title();
112
+ }
113
+
114
+ if ( title.indexOf( '*' ) !== -1 ) {
115
+ title= title.replace( '*', $('title').text() );
116
+ }
117
+
118
+ win.document.close();
119
+
120
+ // Inject the title and also a copy of the style and link tags from this
121
+ // document so the table can retain its base styling. Note that we have
122
+ // to use string manipulation as IE won't allow elements to be created
123
+ // in the host document and then appended to the new window.
124
+ var head = '<title>'+title+'</title>';
125
+ $('style, link').each( function () {
126
+ head += _relToAbs( this );
127
+ } );
128
+
129
+ try {
130
+ win.document.head.innerHTML = head; // Work around for Edge
131
+ }
132
+ catch (e) {
133
+ $(win.document.head).html( head ); // Old IE
134
+ }
135
+
136
+ // Inject the table and other surrounding information
137
+ win.document.body.innerHTML =
138
+ '<h1>'+title+'</h1>'+
139
+ '<div>'+
140
+ (typeof config.message === 'function' ?
141
+ config.message( dt, button, config ) :
142
+ config.message
143
+ )+
144
+ '</div>'+
145
+ html;
146
+
147
+ if ( config.customize ) {
148
+ config.customize( win );
149
+ }
150
+
151
+ setTimeout( function () {
152
+ if ( config.autoPrint ) {
153
+ win.print(); // blocking - so close will not
154
+ win.close(); // execute until this is done
155
+ }
156
+ }, 250 );
157
+ },
158
+
159
+ title: '*',
160
+
161
+ message: '',
162
+
163
+ exportOptions: {},
164
+
165
+ header: true,
166
+
167
+ footer: false,
168
+
169
+ autoPrint: true,
170
+
171
+ customize: null
155
172
  };
156
173
 
157
174
 
@@ -1,32 +1,32 @@
1
- /*! Buttons for DataTables 1.1.0
2
- * ©2015 SpryMedia Ltd - datatables.net/license
1
+ /*! Buttons for DataTables 1.2.2
2
+ * ©2016 SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
5
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
- module.exports = function (root, $) {
15
- if ( ! root ) {
16
- root = window;
17
- }
18
-
19
- if ( ! $ || ! $.fn.dataTable ) {
20
- $ = require('datatables.net')(root, $).$;
21
- }
22
-
23
- return factory( $, root, root.document );
24
- };
25
- }
26
- else {
27
- // Browser
28
- factory( jQuery, window, document );
29
- }
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
+ module.exports = function (root, $) {
15
+ if ( ! root ) {
16
+ root = window;
17
+ }
18
+
19
+ if ( ! $ || ! $.fn.dataTable ) {
20
+ $ = require('datatables.net')(root, $).$;
21
+ }
22
+
23
+ return factory( $, root, root.document );
24
+ };
25
+ }
26
+ else {
27
+ // Browser
28
+ factory( jQuery, window, document );
29
+ }
30
30
  }(function( $, window, document, undefined ) {
31
31
  'use strict';
32
32
  var DataTable = $.fn.dataTable;
@@ -48,799 +48,807 @@ var _dtButtons = DataTable.ext.buttons;
48
48
  */
49
49
  var Buttons = function( dt, config )
50
50
  {
51
- // Allow a boolean true for defaults
52
- if ( config === true ) {
53
- config = {};
54
- }
55
-
56
- // For easy configuration of buttons an array can be given
57
- if ( $.isArray( config ) ) {
58
- config = { buttons: config };
59
- }
60
-
61
- this.c = $.extend( true, {}, Buttons.defaults, config );
62
-
63
- // Don't want a deep copy for the buttons
64
- if ( config.buttons ) {
65
- this.c.buttons = config.buttons;
66
- }
67
-
68
- this.s = {
69
- dt: new DataTable.Api( dt ),
70
- buttons: [],
71
- subButtons: [],
72
- listenKeys: '',
73
- namespace: 'dtb'+(_instCounter++)
74
- };
75
-
76
- this.dom = {
77
- container: $('<'+this.c.dom.container.tag+'/>')
78
- .addClass( this.c.dom.container.className )
79
- };
80
-
81
- this._constructor();
51
+ // Allow a boolean true for defaults
52
+ if ( config === true ) {
53
+ config = {};
54
+ }
55
+
56
+ // For easy configuration of buttons an array can be given
57
+ if ( $.isArray( config ) ) {
58
+ config = { buttons: config };
59
+ }
60
+
61
+ this.c = $.extend( true, {}, Buttons.defaults, config );
62
+
63
+ // Don't want a deep copy for the buttons
64
+ if ( config.buttons ) {
65
+ this.c.buttons = config.buttons;
66
+ }
67
+
68
+ this.s = {
69
+ dt: new DataTable.Api( dt ),
70
+ buttons: [],
71
+ listenKeys: '',
72
+ namespace: 'dtb'+(_instCounter++)
73
+ };
74
+
75
+ this.dom = {
76
+ container: $('<'+this.c.dom.container.tag+'/>')
77
+ .addClass( this.c.dom.container.className )
78
+ };
79
+
80
+ this._constructor();
82
81
  };
83
82
 
84
83
 
85
84
  $.extend( Buttons.prototype, {
86
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
87
- * Public methods
88
- */
89
-
90
- /**
91
- * Get the action of a button
92
- * @param {int|string} Button index
93
- * @return {function}
94
- *//**
95
- * Set the action of a button
96
- * @param {int|string} Button index
97
- * @param {function} Function to set
98
- * @return {Buttons} Self for chaining
99
- */
100
- action: function ( idx, action )
101
- {
102
- var button = this._indexToButton( idx ).conf;
103
-
104
- if ( action === undefined ) {
105
- return button.action;
106
- }
107
-
108
- button.action = action;
109
-
110
- return this;
111
- },
112
-
113
- /**
114
- * Add an active class to the button to make to look active
115
- * @param {int|string} Button index
116
- * @param {boolean} [flag=true] Enable / disable flag
117
- * @return {Buttons} Self for chaining
118
- */
119
- active: function ( idx, flag ) {
120
- var button = this._indexToButton( idx );
121
- button.node.toggleClass(
122
- this.c.dom.button.active,
123
- flag === undefined ? true : flag
124
- );
125
-
126
- return this;
127
- },
128
-
129
- /**
130
- * Add a new button
131
- * @param {int|string} Button index for where to insert the button
132
- * @param {object} Button configuration object, base string name or function
133
- * @return {Buttons} Self for chaining
134
- */
135
- add: function ( idx, config )
136
- {
137
- if ( typeof idx === 'string' && idx.indexOf('-') !== -1 ) {
138
- var idxs = idx.split('-');
139
- this.c.buttons[idxs[0]*1].buttons.splice( idxs[1]*1, 0, config );
140
- }
141
- else {
142
- this.c.buttons.splice( idx*1, 0, config );
143
- }
144
-
145
- this.dom.container.empty();
146
- this._buildButtons( this.c.buttons );
147
-
148
- return this;
149
- },
150
-
151
- /**
152
- * Get the container node for the buttons
153
- * @return {jQuery} Buttons node
154
- */
155
- container: function ()
156
- {
157
- return this.dom.container;
158
- },
159
-
160
- /**
161
- * Disable a button
162
- * @param {int|string} Button index
163
- * @return {Buttons} Self for chaining
164
- */
165
- disable: function ( idx ) {
166
- var button = this._indexToButton( idx );
167
- button.node.addClass( this.c.dom.button.disabled );
168
-
169
- return this;
170
- },
171
-
172
- /**
173
- * Destroy the instance, cleaning up event handlers and removing DOM
174
- * elements
175
- * @return {Buttons} Self for chaining
176
- */
177
- destroy: function ()
178
- {
179
- // Key event listener
180
- $('body').off( 'keyup.'+this.s.namespace );
181
-
182
- // Individual button destroy (so they can remove their own events if
183
- // needed
184
- var buttons = this.s.buttons;
185
- var subButtons = this.s.subButtons;
186
- var i, ien, j, jen;
187
-
188
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
189
- this.removePrep( i );
190
-
191
- for ( j=0, jen=subButtons[i].length ; j<jen ; j++ ) {
192
- this.removePrep( i+'-'+j );
193
- }
194
- }
195
-
196
- this.removeCommit();
197
-
198
- // Container
199
- this.dom.container.remove();
200
-
201
- // Remove from the settings object collection
202
- var buttonInsts = this.s.dt.settings()[0];
203
-
204
- for ( i=0, ien=buttonInsts.length ; i<ien ; i++ ) {
205
- if ( buttonInsts.inst === this ) {
206
- buttonInsts.splice( i, 1 );
207
- break;
208
- }
209
- }
210
-
211
- return this;
212
- },
213
-
214
- /**
215
- * Enable / disable a button
216
- * @param {int|string} Button index
217
- * @param {boolean} [flag=true] Enable / disable flag
218
- * @return {Buttons} Self for chaining
219
- */
220
- enable: function ( idx, flag )
221
- {
222
- if ( flag === false ) {
223
- return this.disable( idx );
224
- }
225
-
226
- var button = this._indexToButton( idx );
227
- button.node.removeClass( this.c.dom.button.disabled );
228
-
229
- return this;
230
- },
231
-
232
- /**
233
- * Get the instance name for the button set selector
234
- * @return {string} Instance name
235
- */
236
- name: function ()
237
- {
238
- return this.c.name;
239
- },
240
-
241
- /**
242
- * Get a button's node
243
- * @param {int|string} Button index
244
- * @return {jQuery} Button element
245
- */
246
- node: function ( idx )
247
- {
248
- var button = this._indexToButton( idx );
249
- return button.node;
250
- },
251
-
252
- /**
253
- * Tidy up any buttons that have been scheduled for removal. This is
254
- * required so multiple buttons can be removed without upsetting the button
255
- * indexes while removing them.
256
- * @param {int|string} Button index
257
- * @return {Buttons} Self for chaining
258
- */
259
- removeCommit: function ()
260
- {
261
- var buttons = this.s.buttons;
262
- var subButtons = this.s.subButtons;
263
- var i, ien, j;
264
-
265
- for ( i=buttons.length-1 ; i>=0 ; i-- ) {
266
- if ( buttons[i] === null ) {
267
- buttons.splice( i, 1 );
268
- subButtons.splice( i, 1 );
269
- this.c.buttons.splice( i, 1 );
270
- }
271
- }
272
-
273
- for ( i=0, ien=subButtons.length ; i<ien ; i++ ) {
274
- for ( j=subButtons[i].length-1 ; j>=0 ; j-- ) {
275
- if ( subButtons[i][j] === null ) {
276
- subButtons[i].splice( j, 1 );
277
- this.c.buttons[i].buttons.splice( j, 1 );
278
- }
279
- }
280
- }
281
-
282
- return this;
283
- },
284
-
285
- /**
286
- * Scheduled a button for removal. This is required so multiple buttons can
287
- * be removed without upsetting the button indexes while removing them.
288
- * @return {Buttons} Self for chaining
289
- */
290
- removePrep: function ( idx )
291
- {
292
- var button;
293
- var dt = this.s.dt;
294
-
295
- if ( typeof idx === 'number' || idx.indexOf('-') === -1 ) {
296
- // Top level button
297
- button = this.s.buttons[ idx*1 ];
298
-
299
- if ( button.conf.destroy ) {
300
- button.conf.destroy.call( dt.button(idx), dt, button, button.conf );
301
- }
302
-
303
- button.node.remove();
304
- this._removeKey( button.conf );
305
- this.s.buttons[ idx*1 ] = null;
306
- }
307
- else {
308
- // Collection button
309
- var idxs = idx.split('-');
310
- button = this.s.subButtons[ idxs[0]*1 ][ idxs[1]*1 ];
311
-
312
- if ( button.conf.destroy ) {
313
- button.conf.destroy.call( dt.button(idx), dt, button, button.conf );
314
- }
315
-
316
- button.node.remove();
317
- this._removeKey( button.conf );
318
- this.s.subButtons[ idxs[0]*1 ][ idxs[1]*1 ] = null;
319
- }
320
-
321
- return this;
322
- },
323
-
324
- /**
325
- * Get the text for a button
326
- * @param {int|string} Button index
327
- * @return {string} Button text
328
- *//**
329
- * Set the text for a button
330
- * @param {int|string|function} Button index
331
- * @param {string} Text
332
- * @return {Buttons} Self for chaining
333
- */
334
- text: function ( idx, label )
335
- {
336
- var button = this._indexToButton( idx );
337
- var buttonLiner = this.c.dom.collection.buttonLiner;
338
- var linerTag = typeof idx === 'string' && idx.indexOf( '-' ) !== -1 && buttonLiner && buttonLiner.tag ?
339
- buttonLiner.tag :
340
- this.c.dom.buttonLiner.tag;
341
- var dt = this.s.dt;
342
- var text = function ( opt ) {
343
- return typeof opt === 'function' ?
344
- opt( dt, button.node, button.conf ) :
345
- opt;
346
- };
347
-
348
- if ( label === undefined ) {
349
- return text( button.conf.text );
350
- }
351
-
352
- button.conf.text = label;
353
-
354
- if ( linerTag ) {
355
- button.node.children( linerTag ).html( text(label) );
356
- }
357
- else {
358
- button.node.html( text(label) );
359
- }
360
-
361
- return this;
362
- },
363
-
364
- /**
365
- * Calculate button index from a node
366
- * @param {node} Button node (_not_ a jQuery object)
367
- * @return {string} Index. Undefined if not found
368
- */
369
- toIndex: function ( node )
370
- {
371
- var i, ien, j, jen;
372
- var buttons = this.s.buttons;
373
- var subButtons = this.s.subButtons;
374
-
375
- // Loop the main buttons first
376
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
377
- if ( buttons[i].node[0] === node ) {
378
- return i+'';
379
- }
380
- }
381
-
382
- // Then the sub-buttons
383
- for ( i=0, ien=subButtons.length ; i<ien ; i++ ) {
384
- for ( j=0, jen=subButtons[i].length ; j<jen ; j++ ) {
385
- if ( subButtons[i][j].node[0] === node ) {
386
- return i+'-'+j;
387
- }
388
- }
389
- }
390
- },
391
-
392
-
393
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
394
- * Constructor
395
- */
396
-
397
- /**
398
- * Buttons constructor
399
- * @private
400
- */
401
- _constructor: function ()
402
- {
403
- var that = this;
404
- var dt = this.s.dt;
405
- var dtSettings = dt.settings()[0];
406
-
407
- if ( ! dtSettings._buttons ) {
408
- dtSettings._buttons = [];
409
- }
410
-
411
- dtSettings._buttons.push( {
412
- inst: this,
413
- name: this.c.name
414
- } );
415
-
416
- this._buildButtons( this.c.buttons );
417
-
418
- dt.on( 'destroy', function () {
419
- that.destroy();
420
- } );
421
-
422
- // Global key event binding to listen for button keys
423
- $('body').on( 'keyup.'+this.s.namespace, function ( e ) {
424
- if ( ! document.activeElement || document.activeElement === document.body ) {
425
- // SUse a string of characters for fast lookup of if we need to
426
- // handle this
427
- var character = String.fromCharCode(e.keyCode).toLowerCase();
428
-
429
- if ( that.s.listenKeys.toLowerCase().indexOf( character ) !== -1 ) {
430
- that._keypress( character, e );
431
- }
432
- }
433
- } );
434
- },
435
-
436
-
437
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
438
- * Private methods
439
- */
440
-
441
- /**
442
- * Add a new button to the key press listener
443
- * @param {object} Resolved button configuration object
444
- * @private
445
- */
446
- _addKey: function ( conf )
447
- {
448
- if ( conf.key ) {
449
- this.s.listenKeys += $.isPlainObject( conf.key ) ?
450
- conf.key.key :
451
- conf.key;
452
- }
453
- },
454
-
455
- /**
456
- * Create buttons from an array of buttons
457
- * @param {array} Buttons to create
458
- * @param {jQuery} Container node into which the created button should be
459
- * inserted.
460
- * @param {int} Counter for sub-buttons to be stored in a collection
461
- * @private
462
- */
463
- _buildButtons: function ( buttons, container, collectionCounter )
464
- {
465
- var dt = this.s.dt;
466
-
467
- if ( ! container ) {
468
- container = this.dom.container;
469
- this.s.buttons = [];
470
- this.s.subButtons = [];
471
- }
472
-
473
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
474
- var conf = this._resolveExtends( buttons[i] );
475
-
476
- if ( ! conf ) {
477
- continue;
478
- }
479
-
480
- // If the configuration is an array, then expand the buttons at this
481
- // point
482
- if ( $.isArray( conf ) ) {
483
- this._buildButtons( conf, container, collectionCounter );
484
- continue;
485
- }
486
-
487
- var button = this._buildButton(
488
- conf,
489
- collectionCounter!==undefined ? true : false
490
- );
491
-
492
- if ( ! button ) {
493
- continue;
494
- }
495
-
496
- var buttonNode = button.node;
497
- container.append( button.inserter );
498
-
499
- if ( collectionCounter === undefined ) {
500
- this.s.buttons.push( {
501
- node: buttonNode,
502
- conf: conf,
503
- inserter: button.inserter
504
- } );
505
- this.s.subButtons.push( [] );
506
- }
507
- else {
508
- this.s.subButtons[ collectionCounter ].push( {
509
- node: buttonNode,
510
- conf: conf,
511
- inserter: button.inserter
512
- } );
513
- }
514
-
515
- if ( conf.buttons ) {
516
- var collectionDom = this.c.dom.collection;
517
- conf._collection = $('<'+collectionDom.tag+'/>')
518
- .addClass( collectionDom.className );
519
-
520
- this._buildButtons( conf.buttons, conf._collection, i );
521
- }
522
-
523
- // init call is made here, rather than buildButton as it needs to
524
- // have been added to the buttons / subButtons array first
525
- if ( conf.init ) {
526
- conf.init.call( dt.button( buttonNode ), dt, buttonNode, conf );
527
- }
528
- }
529
- },
530
-
531
- /**
532
- * Create an individual button
533
- * @param {object} config Resolved button configuration
534
- * @param {boolean} collectionButton `true` if a collection button
535
- * @return {jQuery} Created button node (jQuery)
536
- * @private
537
- */
538
- _buildButton: function ( config, collectionButton )
539
- {
540
- var that = this;
541
- var buttonDom = this.c.dom.button;
542
- var linerDom = this.c.dom.buttonLiner;
543
- var collectionDom = this.c.dom.collection;
544
- var dt = this.s.dt;
545
- var text = function ( opt ) {
546
- return typeof opt === 'function' ?
547
- opt( dt, button, config ) :
548
- opt;
549
- };
550
-
551
- if ( collectionButton && collectionDom.button ) {
552
- buttonDom = collectionDom.button;
553
- }
554
-
555
- if ( collectionButton && collectionDom.buttonLiner ) {
556
- linerDom = collectionDom.buttonLiner;
557
- }
558
-
559
- // Make sure that the button is available based on whatever requirements
560
- // it has. For example, Flash buttons require Flash
561
- if ( config.available && ! config.available( dt, config ) ) {
562
- return false;
563
- }
564
-
565
- var button = $('<'+buttonDom.tag+'/>')
566
- .addClass( buttonDom.className )
567
- .attr( 'tabindex', this.s.dt.settings()[0].iTabIndex )
568
- .attr( 'aria-controls', this.s.dt.table().node().id )
569
- .on( 'click.dtb', function (e) {
570
- e.preventDefault();
571
-
572
- if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
573
- config.action.call( dt.button( button ), e, dt, button, config );
574
- }
575
-
576
- button.blur();
577
- } )
578
- .on( 'keyup.dtb', function (e) {
579
- if ( e.keyCode === 13 ) {
580
- if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
581
- config.action.call( dt.button( button ), e, dt, button, config );
582
- }
583
- }
584
- } );
585
-
586
- if ( linerDom.tag ) {
587
- button.append(
588
- $('<'+linerDom.tag+'/>')
589
- .html( text( config.text ) )
590
- .addClass( linerDom.className )
591
- );
592
- }
593
- else {
594
- button.html( text( config.text ) );
595
- }
596
-
597
- if ( config.enabled === false ) {
598
- button.addClass( buttonDom.disabled );
599
- }
600
-
601
- if ( config.className ) {
602
- button.addClass( config.className );
603
- }
604
-
605
- if ( config.titleAttr ) {
606
- button.attr( 'title', config.titleAttr );
607
- }
608
-
609
- if ( ! config.namespace ) {
610
- config.namespace = '.dt-button-'+(_buttonCounter++);
611
- }
612
-
613
- var buttonContainer = this.c.dom.buttonContainer;
614
- var inserter;
615
- if ( buttonContainer ) {
616
- inserter = $('<'+buttonContainer.tag+'/>')
617
- .addClass( buttonContainer.className )
618
- .append( button );
619
- }
620
- else {
621
- inserter = button;
622
- }
623
-
624
- this._addKey( config );
625
-
626
- return {
627
- node: button,
628
- inserter: inserter
629
- };
630
- },
631
-
632
- /**
633
- * Get a button's host information from a button index
634
- * @param {int|string} Button index
635
- * @return {object} Button information - object contains `node` and `conf`
636
- * properties
637
- * @private
638
- */
639
- _indexToButton: function ( idx )
640
- {
641
- if ( typeof idx === 'number' || idx.indexOf('-') === -1 ) {
642
- return this.s.buttons[ idx*1 ];
643
- }
644
-
645
- var idxs = idx.split('-');
646
- return this.s.subButtons[ idxs[0]*1 ][ idxs[1]*1 ];
647
- },
648
-
649
- /**
650
- * Handle a key press - determine if any button's key configured matches
651
- * what was typed and trigger the action if so.
652
- * @param {string} The character pressed
653
- * @param {object} Key event that triggered this call
654
- * @private
655
- */
656
- _keypress: function ( character, e )
657
- {
658
- var i, ien, j, jen;
659
- var buttons = this.s.buttons;
660
- var subButtons = this.s.subButtons;
661
- var run = function ( conf, node ) {
662
- if ( ! conf.key ) {
663
- return;
664
- }
665
-
666
- if ( conf.key === character ) {
667
- node.click();
668
- }
669
- else if ( $.isPlainObject( conf.key ) ) {
670
- if ( conf.key.key !== character ) {
671
- return;
672
- }
673
-
674
- if ( conf.key.shiftKey && ! e.shiftKey ) {
675
- return;
676
- }
677
-
678
- if ( conf.key.altKey && ! e.altKey ) {
679
- return;
680
- }
681
-
682
- if ( conf.key.ctrlKey && ! e.ctrlKey ) {
683
- return;
684
- }
685
-
686
- if ( conf.key.metaKey && ! e.metaKey ) {
687
- return;
688
- }
689
-
690
- // Made it this far - it is good
691
- node.click();
692
- }
693
- };
694
-
695
- // Loop the main buttons first
696
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
697
- run( buttons[i].conf, buttons[i].node );
698
- }
699
-
700
- // Then the sub-buttons
701
- for ( i=0, ien=subButtons.length ; i<ien ; i++ ) {
702
- for ( j=0, jen=subButtons[i].length ; j<jen ; j++ ) {
703
- run( subButtons[i][j].conf, subButtons[i][j].node );
704
- }
705
- }
706
- },
707
-
708
- /**
709
- * Remove a key from the key listener for this instance (to be used when a
710
- * button is removed)
711
- * @param {object} Button configuration
712
- */
713
- _removeKey: function ( conf )
714
- {
715
- if ( conf.key ) {
716
- var character = $.isPlainObject( conf.key ) ?
717
- conf.key.key :
718
- conf.key;
719
-
720
- // Remove only one character, as multiple buttons could have the
721
- // same listening key
722
- var a = this.s.listenKeys.split('');
723
- var idx = $.inArray( character, a );
724
- a.splice( idx, 1 );
725
- this.s.listenKeys = a.join('');
726
- }
727
- },
728
-
729
- /**
730
- * Resolve a button configuration
731
- * @param {string|function|object} Button config to resolve
732
- * @return {object} Button configuration
733
- */
734
- _resolveExtends: function ( conf )
735
- {
736
- var dt = this.s.dt;
737
- var i, ien;
738
- var toConfObject = function ( base ) {
739
- var loop = 0;
740
-
741
- // Loop until we have resolved to a button configuration, or an
742
- // array of button configurations (which will be iterated
743
- // separately)
744
- while ( ! $.isPlainObject(base) && ! $.isArray(base) ) {
745
- if ( base === undefined ) {
746
- return;
747
- }
748
-
749
- if ( typeof base === 'function' ) {
750
- base = base( dt, conf );
751
-
752
- if ( ! base ) {
753
- return false;
754
- }
755
- }
756
- else if ( typeof base === 'string' ) {
757
- if ( ! _dtButtons[ base ] ) {
758
- throw 'Unknown button type: '+base;
759
- }
760
-
761
- base = _dtButtons[ base ];
762
- }
763
-
764
- loop++;
765
- if ( loop > 30 ) {
766
- // Protect against misconfiguration killing the browser
767
- throw 'Buttons: Too many iterations';
768
- }
769
- }
770
-
771
- return $.isArray( base ) ?
772
- base :
773
- $.extend( {}, base );
774
- };
775
-
776
- conf = toConfObject( conf );
777
-
778
- while ( conf && conf.extend ) {
779
- // Use `toConfObject` in case the button definition being extended
780
- // is itself a string or a function
781
- if ( ! _dtButtons[ conf.extend ] ) {
782
- throw 'Cannot extend unknown button type: '+conf.extend;
783
- }
784
-
785
- var objArray = toConfObject( _dtButtons[ conf.extend ] );
786
- if ( $.isArray( objArray ) ) {
787
- return objArray;
788
- }
789
- else if ( ! objArray ) {
790
- // This is a little brutal as it might be possible to have a
791
- // valid button without the extend, but if there is no extend
792
- // then the host button would be acting in an undefined state
793
- return false;
794
- }
795
-
796
- // Stash the current class name
797
- var originalClassName = objArray.className;
798
-
799
- conf = $.extend( {}, objArray, conf );
800
-
801
- // The extend will have overwritten the original class name if the
802
- // `conf` object also assigned a class, but we want to concatenate
803
- // them so they are list that is combined from all extended buttons
804
- if ( originalClassName && conf.className !== originalClassName ) {
805
- conf.className = originalClassName+' '+conf.className;
806
- }
807
-
808
- // Buttons to be added to a collection -gives the ability to define
809
- // if buttons should be added to the start or end of a collection
810
- var postfixButtons = conf.postfixButtons;
811
- if ( postfixButtons ) {
812
- if ( ! conf.buttons ) {
813
- conf.buttons = [];
814
- }
815
-
816
- for ( i=0, ien=postfixButtons.length ; i<ien ; i++ ) {
817
- conf.buttons.push( postfixButtons[i] );
818
- }
819
-
820
- conf.postfixButtons = null;
821
- }
822
-
823
- var prefixButtons = conf.prefixButtons;
824
- if ( prefixButtons ) {
825
- if ( ! conf.buttons ) {
826
- conf.buttons = [];
827
- }
828
-
829
- for ( i=0, ien=prefixButtons.length ; i<ien ; i++ ) {
830
- conf.buttons.splice( i, 0, prefixButtons[i] );
831
- }
832
-
833
- conf.prefixButtons = null;
834
- }
835
-
836
- // Although we want the `conf` object to overwrite almost all of
837
- // the properties of the object being extended, the `extend`
838
- // property should come from the object being extended
839
- conf.extend = objArray.extend;
840
- }
841
-
842
- return conf;
843
- }
85
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
86
+ * Public methods
87
+ */
88
+
89
+ /**
90
+ * Get the action of a button
91
+ * @param {int|string} Button index
92
+ * @return {function}
93
+ *//**
94
+ * Set the action of a button
95
+ * @param {node} node Button element
96
+ * @param {function} action Function to set
97
+ * @return {Buttons} Self for chaining
98
+ */
99
+ action: function ( node, action )
100
+ {
101
+ var button = this._nodeToButton( node );
102
+
103
+ if ( action === undefined ) {
104
+ return button.conf.action;
105
+ }
106
+
107
+ button.conf.action = action;
108
+
109
+ return this;
110
+ },
111
+
112
+ /**
113
+ * Add an active class to the button to make to look active or get current
114
+ * active state.
115
+ * @param {node} node Button element
116
+ * @param {boolean} [flag] Enable / disable flag
117
+ * @return {Buttons} Self for chaining or boolean for getter
118
+ */
119
+ active: function ( node, flag ) {
120
+ var button = this._nodeToButton( node );
121
+ var klass = this.c.dom.button.active;
122
+ var jqNode = $(button.node);
123
+
124
+ if ( flag === undefined ) {
125
+ return jqNode.hasClass( klass );
126
+ }
127
+
128
+ jqNode.toggleClass( klass, flag === undefined ? true : flag );
129
+
130
+ return this;
131
+ },
132
+
133
+ /**
134
+ * Add a new button
135
+ * @param {object} config Button configuration object, base string name or function
136
+ * @param {int|string} [idx] Button index for where to insert the button
137
+ * @return {Buttons} Self for chaining
138
+ */
139
+ add: function ( config, idx )
140
+ {
141
+ var buttons = this.s.buttons;
142
+
143
+ if ( typeof idx === 'string' ) {
144
+ var split = idx.split('-');
145
+ var base = this.s;
146
+
147
+ for ( var i=0, ien=split.length-1 ; i<ien ; i++ ) {
148
+ base = base.buttons[ split[i]*1 ];
149
+ }
150
+
151
+ buttons = base.buttons;
152
+ idx = split[ split.length-1 ]*1;
153
+ }
154
+
155
+ this._expandButton( buttons, config, false, idx );
156
+ this._draw();
157
+
158
+ return this;
159
+ },
160
+
161
+ /**
162
+ * Get the container node for the buttons
163
+ * @return {jQuery} Buttons node
164
+ */
165
+ container: function ()
166
+ {
167
+ return this.dom.container;
168
+ },
169
+
170
+ /**
171
+ * Disable a button
172
+ * @param {node} node Button node
173
+ * @return {Buttons} Self for chaining
174
+ */
175
+ disable: function ( node ) {
176
+ var button = this._nodeToButton( node );
177
+
178
+ $(button.node).addClass( this.c.dom.button.disabled );
179
+
180
+ return this;
181
+ },
182
+
183
+ /**
184
+ * Destroy the instance, cleaning up event handlers and removing DOM
185
+ * elements
186
+ * @return {Buttons} Self for chaining
187
+ */
188
+ destroy: function ()
189
+ {
190
+ // Key event listener
191
+ $('body').off( 'keyup.'+this.s.namespace );
192
+
193
+ // Individual button destroy (so they can remove their own events if
194
+ // needed). Take a copy as the array is modified by `remove`
195
+ var buttons = this.s.buttons.slice();
196
+ var i, ien;
197
+
198
+ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
199
+ this.remove( buttons[i].node );
200
+ }
201
+
202
+ // Container
203
+ this.dom.container.remove();
204
+
205
+ // Remove from the settings object collection
206
+ var buttonInsts = this.s.dt.settings()[0];
207
+
208
+ for ( i=0, ien=buttonInsts.length ; i<ien ; i++ ) {
209
+ if ( buttonInsts.inst === this ) {
210
+ buttonInsts.splice( i, 1 );
211
+ break;
212
+ }
213
+ }
214
+
215
+ return this;
216
+ },
217
+
218
+ /**
219
+ * Enable / disable a button
220
+ * @param {node} node Button node
221
+ * @param {boolean} [flag=true] Enable / disable flag
222
+ * @return {Buttons} Self for chaining
223
+ */
224
+ enable: function ( node, flag )
225
+ {
226
+ if ( flag === false ) {
227
+ return this.disable( node );
228
+ }
229
+
230
+ var button = this._nodeToButton( node );
231
+ $(button.node).removeClass( this.c.dom.button.disabled );
232
+
233
+ return this;
234
+ },
235
+
236
+ /**
237
+ * Get the instance name for the button set selector
238
+ * @return {string} Instance name
239
+ */
240
+ name: function ()
241
+ {
242
+ return this.c.name;
243
+ },
244
+
245
+ /**
246
+ * Get a button's node
247
+ * @param {node} node Button node
248
+ * @return {jQuery} Button element
249
+ */
250
+ node: function ( node )
251
+ {
252
+ var button = this._nodeToButton( node );
253
+ return $(button.node);
254
+ },
255
+
256
+ /**
257
+ * Remove a button.
258
+ * @param {node} node Button node
259
+ * @return {Buttons} Self for chaining
260
+ */
261
+ remove: function ( node )
262
+ {
263
+ var button = this._nodeToButton( node );
264
+ var host = this._nodeToHost( node );
265
+ var dt = this.s.dt;
266
+
267
+ // Remove any child buttons first
268
+ if ( button.buttons.length ) {
269
+ for ( var i=button.buttons.length-1 ; i>=0 ; i-- ) {
270
+ this.remove( button.buttons[i].node );
271
+ }
272
+ }
273
+
274
+ // Allow the button to remove event handlers, etc
275
+ if ( button.conf.destroy ) {
276
+ button.conf.destroy.call( dt.button(node), dt, $(node), button.conf );
277
+ }
278
+
279
+ this._removeKey( button.conf );
280
+
281
+ $(button.node).remove();
282
+
283
+ var idx = $.inArray( button, host );
284
+ host.splice( idx, 1 );
285
+
286
+ return this;
287
+ },
288
+
289
+ /**
290
+ * Get the text for a button
291
+ * @param {int|string} node Button index
292
+ * @return {string} Button text
293
+ *//**
294
+ * Set the text for a button
295
+ * @param {int|string|function} node Button index
296
+ * @param {string} label Text
297
+ * @return {Buttons} Self for chaining
298
+ */
299
+ text: function ( node, label )
300
+ {
301
+ var button = this._nodeToButton( node );
302
+ var buttonLiner = this.c.dom.collection.buttonLiner;
303
+ var linerTag = button.inCollection && buttonLiner && buttonLiner.tag ?
304
+ buttonLiner.tag :
305
+ this.c.dom.buttonLiner.tag;
306
+ var dt = this.s.dt;
307
+ var jqNode = $(button.node);
308
+ var text = function ( opt ) {
309
+ return typeof opt === 'function' ?
310
+ opt( dt, jqNode, button.conf ) :
311
+ opt;
312
+ };
313
+
314
+ if ( label === undefined ) {
315
+ return text( button.conf.text );
316
+ }
317
+
318
+ button.conf.text = label;
319
+
320
+ if ( linerTag ) {
321
+ jqNode.children( linerTag ).html( text(label) );
322
+ }
323
+ else {
324
+ jqNode.html( text(label) );
325
+ }
326
+
327
+ return this;
328
+ },
329
+
330
+
331
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
332
+ * Constructor
333
+ */
334
+
335
+ /**
336
+ * Buttons constructor
337
+ * @private
338
+ */
339
+ _constructor: function ()
340
+ {
341
+ var that = this;
342
+ var dt = this.s.dt;
343
+ var dtSettings = dt.settings()[0];
344
+ var buttons = this.c.buttons;
345
+
346
+ if ( ! dtSettings._buttons ) {
347
+ dtSettings._buttons = [];
348
+ }
349
+
350
+ dtSettings._buttons.push( {
351
+ inst: this,
352
+ name: this.c.name
353
+ } );
354
+
355
+ for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
356
+ this.add( buttons[i] );
357
+ }
358
+
359
+ dt.on( 'destroy', function () {
360
+ that.destroy();
361
+ } );
362
+
363
+ // Global key event binding to listen for button keys
364
+ $('body').on( 'keyup.'+this.s.namespace, function ( e ) {
365
+ if ( ! document.activeElement || document.activeElement === document.body ) {
366
+ // SUse a string of characters for fast lookup of if we need to
367
+ // handle this
368
+ var character = String.fromCharCode(e.keyCode).toLowerCase();
369
+
370
+ if ( that.s.listenKeys.toLowerCase().indexOf( character ) !== -1 ) {
371
+ that._keypress( character, e );
372
+ }
373
+ }
374
+ } );
375
+ },
376
+
377
+
378
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
379
+ * Private methods
380
+ */
381
+
382
+ /**
383
+ * Add a new button to the key press listener
384
+ * @param {object} conf Resolved button configuration object
385
+ * @private
386
+ */
387
+ _addKey: function ( conf )
388
+ {
389
+ if ( conf.key ) {
390
+ this.s.listenKeys += $.isPlainObject( conf.key ) ?
391
+ conf.key.key :
392
+ conf.key;
393
+ }
394
+ },
395
+
396
+ /**
397
+ * Insert the buttons into the container. Call without parameters!
398
+ * @param {node} [container] Recursive only - Insert point
399
+ * @param {array} [buttons] Recursive only - Buttons array
400
+ * @private
401
+ */
402
+ _draw: function ( container, buttons )
403
+ {
404
+ if ( ! container ) {
405
+ container = this.dom.container;
406
+ buttons = this.s.buttons;
407
+ }
408
+
409
+ container.children().detach();
410
+
411
+ for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
412
+ container.append( buttons[i].inserter );
413
+
414
+ if ( buttons[i].buttons && buttons[i].buttons.length ) {
415
+ this._draw( buttons[i].collection, buttons[i].buttons );
416
+ }
417
+ }
418
+ },
419
+
420
+ /**
421
+ * Create buttons from an array of buttons
422
+ * @param {array} attachTo Buttons array to attach to
423
+ * @param {object} button Button definition
424
+ * @param {boolean} inCollection true if the button is in a collection
425
+ * @private
426
+ */
427
+ _expandButton: function ( attachTo, button, inCollection, attachPoint )
428
+ {
429
+ var dt = this.s.dt;
430
+ var buttonCounter = 0;
431
+ var buttons = ! $.isArray( button ) ?
432
+ [ button ] :
433
+ button;
434
+
435
+ for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
436
+ var conf = this._resolveExtends( buttons[i] );
437
+
438
+ if ( ! conf ) {
439
+ continue;
440
+ }
441
+
442
+ // If the configuration is an array, then expand the buttons at this
443
+ // point
444
+ if ( $.isArray( conf ) ) {
445
+ this._expandButton( attachTo, conf, inCollection, attachPoint );
446
+ continue;
447
+ }
448
+
449
+ var built = this._buildButton( conf, inCollection );
450
+ if ( ! built ) {
451
+ continue;
452
+ }
453
+
454
+ if ( attachPoint !== undefined ) {
455
+ attachTo.splice( attachPoint, 0, built );
456
+ attachPoint++;
457
+ }
458
+ else {
459
+ attachTo.push( built );
460
+ }
461
+
462
+ if ( built.conf.buttons ) {
463
+ var collectionDom = this.c.dom.collection;
464
+ built.collection = $('<'+collectionDom.tag+'/>')
465
+ .addClass( collectionDom.className );
466
+ built.conf._collection = built.collection;
467
+
468
+ this._expandButton( built.buttons, built.conf.buttons, true, attachPoint );
469
+ }
470
+
471
+ // init call is made here, rather than buildButton as it needs to
472
+ // be selectable, and for that it needs to be in the buttons array
473
+ if ( conf.init ) {
474
+ conf.init.call( dt.button( built.node ), dt, $(built.node), conf );
475
+ }
476
+
477
+ buttonCounter++;
478
+ }
479
+ },
480
+
481
+ /**
482
+ * Create an individual button
483
+ * @param {object} config Resolved button configuration
484
+ * @param {boolean} inCollection `true` if a collection button
485
+ * @return {jQuery} Created button node (jQuery)
486
+ * @private
487
+ */
488
+ _buildButton: function ( config, inCollection )
489
+ {
490
+ var buttonDom = this.c.dom.button;
491
+ var linerDom = this.c.dom.buttonLiner;
492
+ var collectionDom = this.c.dom.collection;
493
+ var dt = this.s.dt;
494
+ var text = function ( opt ) {
495
+ return typeof opt === 'function' ?
496
+ opt( dt, button, config ) :
497
+ opt;
498
+ };
499
+
500
+ if ( inCollection && collectionDom.button ) {
501
+ buttonDom = collectionDom.button;
502
+ }
503
+
504
+ if ( inCollection && collectionDom.buttonLiner ) {
505
+ linerDom = collectionDom.buttonLiner;
506
+ }
507
+
508
+ // Make sure that the button is available based on whatever requirements
509
+ // it has. For example, Flash buttons require Flash
510
+ if ( config.available && ! config.available( dt, config ) ) {
511
+ return false;
512
+ }
513
+
514
+ var action = function ( e, dt, button, config ) {
515
+ config.action.call( dt.button( button ), e, dt, button, config );
516
+
517
+ $(dt.table().node()).triggerHandler( 'buttons-action.dt', [
518
+ dt.button( button ), dt, button, config
519
+ ] );
520
+ };
521
+
522
+ var button = $('<'+buttonDom.tag+'/>')
523
+ .addClass( buttonDom.className )
524
+ .attr( 'tabindex', this.s.dt.settings()[0].iTabIndex )
525
+ .attr( 'aria-controls', this.s.dt.table().node().id )
526
+ .on( 'click.dtb', function (e) {
527
+ e.preventDefault();
528
+
529
+ if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
530
+ action( e, dt, button, config );
531
+ }
532
+
533
+ button.blur();
534
+ } )
535
+ .on( 'keyup.dtb', function (e) {
536
+ if ( e.keyCode === 13 ) {
537
+ if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
538
+ action( e, dt, button, config );
539
+ }
540
+ }
541
+ } );
542
+
543
+ // Make `a` tags act like a link
544
+ if ( buttonDom.tag.toLowerCase() === 'a' ) {
545
+ button.attr( 'href', '#' );
546
+ }
547
+
548
+ if ( linerDom.tag ) {
549
+ var liner = $('<'+linerDom.tag+'/>')
550
+ .html( text( config.text ) )
551
+ .addClass( linerDom.className );
552
+
553
+ if ( linerDom.tag.toLowerCase() === 'a' ) {
554
+ liner.attr( 'href', '#' );
555
+ }
556
+
557
+ button.append( liner );
558
+ }
559
+ else {
560
+ button.html( text( config.text ) );
561
+ }
562
+
563
+ if ( config.enabled === false ) {
564
+ button.addClass( buttonDom.disabled );
565
+ }
566
+
567
+ if ( config.className ) {
568
+ button.addClass( config.className );
569
+ }
570
+
571
+ if ( config.titleAttr ) {
572
+ button.attr( 'title', config.titleAttr );
573
+ }
574
+
575
+ if ( ! config.namespace ) {
576
+ config.namespace = '.dt-button-'+(_buttonCounter++);
577
+ }
578
+
579
+ var buttonContainer = this.c.dom.buttonContainer;
580
+ var inserter;
581
+ if ( buttonContainer && buttonContainer.tag ) {
582
+ inserter = $('<'+buttonContainer.tag+'/>')
583
+ .addClass( buttonContainer.className )
584
+ .append( button );
585
+ }
586
+ else {
587
+ inserter = button;
588
+ }
589
+
590
+ this._addKey( config );
591
+
592
+ return {
593
+ conf: config,
594
+ node: button.get(0),
595
+ inserter: inserter,
596
+ buttons: [],
597
+ inCollection: inCollection,
598
+ collection: null
599
+ };
600
+ },
601
+
602
+ /**
603
+ * Get the button object from a node (recursive)
604
+ * @param {node} node Button node
605
+ * @param {array} [buttons] Button array, uses base if not defined
606
+ * @return {object} Button object
607
+ * @private
608
+ */
609
+ _nodeToButton: function ( node, buttons )
610
+ {
611
+ if ( ! buttons ) {
612
+ buttons = this.s.buttons;
613
+ }
614
+
615
+ for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
616
+ if ( buttons[i].node === node ) {
617
+ return buttons[i];
618
+ }
619
+
620
+ if ( buttons[i].buttons.length ) {
621
+ var ret = this._nodeToButton( node, buttons[i].buttons );
622
+
623
+ if ( ret ) {
624
+ return ret;
625
+ }
626
+ }
627
+ }
628
+ },
629
+
630
+ /**
631
+ * Get container array for a button from a button node (recursive)
632
+ * @param {node} node Button node
633
+ * @param {array} [buttons] Button array, uses base if not defined
634
+ * @return {array} Button's host array
635
+ * @private
636
+ */
637
+ _nodeToHost: function ( node, buttons )
638
+ {
639
+ if ( ! buttons ) {
640
+ buttons = this.s.buttons;
641
+ }
642
+
643
+ for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
644
+ if ( buttons[i].node === node ) {
645
+ return buttons;
646
+ }
647
+
648
+ if ( buttons[i].buttons.length ) {
649
+ var ret = this._nodeToHost( node, buttons[i].buttons );
650
+
651
+ if ( ret ) {
652
+ return ret;
653
+ }
654
+ }
655
+ }
656
+ },
657
+
658
+ /**
659
+ * Handle a key press - determine if any button's key configured matches
660
+ * what was typed and trigger the action if so.
661
+ * @param {string} character The character pressed
662
+ * @param {object} e Key event that triggered this call
663
+ * @private
664
+ */
665
+ _keypress: function ( character, e )
666
+ {
667
+ var run = function ( conf, node ) {
668
+ if ( ! conf.key ) {
669
+ return;
670
+ }
671
+
672
+ if ( conf.key === character ) {
673
+ $(node).click();
674
+ }
675
+ else if ( $.isPlainObject( conf.key ) ) {
676
+ if ( conf.key.key !== character ) {
677
+ return;
678
+ }
679
+
680
+ if ( conf.key.shiftKey && ! e.shiftKey ) {
681
+ return;
682
+ }
683
+
684
+ if ( conf.key.altKey && ! e.altKey ) {
685
+ return;
686
+ }
687
+
688
+ if ( conf.key.ctrlKey && ! e.ctrlKey ) {
689
+ return;
690
+ }
691
+
692
+ if ( conf.key.metaKey && ! e.metaKey ) {
693
+ return;
694
+ }
695
+
696
+ // Made it this far - it is good
697
+ $(node).click();
698
+ }
699
+ };
700
+
701
+ var recurse = function ( a ) {
702
+ for ( var i=0, ien=a.length ; i<ien ; i++ ) {
703
+ run( a[i].conf, a[i].node );
704
+
705
+ if ( a[i].buttons.length ) {
706
+ recurse( a[i].buttons );
707
+ }
708
+ }
709
+ };
710
+
711
+ recurse( this.s.buttons );
712
+ },
713
+
714
+ /**
715
+ * Remove a key from the key listener for this instance (to be used when a
716
+ * button is removed)
717
+ * @param {object} conf Button configuration
718
+ * @private
719
+ */
720
+ _removeKey: function ( conf )
721
+ {
722
+ if ( conf.key ) {
723
+ var character = $.isPlainObject( conf.key ) ?
724
+ conf.key.key :
725
+ conf.key;
726
+
727
+ // Remove only one character, as multiple buttons could have the
728
+ // same listening key
729
+ var a = this.s.listenKeys.split('');
730
+ var idx = $.inArray( character, a );
731
+ a.splice( idx, 1 );
732
+ this.s.listenKeys = a.join('');
733
+ }
734
+ },
735
+
736
+ /**
737
+ * Resolve a button configuration
738
+ * @param {string|function|object} conf Button config to resolve
739
+ * @return {object} Button configuration
740
+ * @private
741
+ */
742
+ _resolveExtends: function ( conf )
743
+ {
744
+ var dt = this.s.dt;
745
+ var i, ien;
746
+ var toConfObject = function ( base ) {
747
+ var loop = 0;
748
+
749
+ // Loop until we have resolved to a button configuration, or an
750
+ // array of button configurations (which will be iterated
751
+ // separately)
752
+ while ( ! $.isPlainObject(base) && ! $.isArray(base) ) {
753
+ if ( base === undefined ) {
754
+ return;
755
+ }
756
+
757
+ if ( typeof base === 'function' ) {
758
+ base = base( dt, conf );
759
+
760
+ if ( ! base ) {
761
+ return false;
762
+ }
763
+ }
764
+ else if ( typeof base === 'string' ) {
765
+ if ( ! _dtButtons[ base ] ) {
766
+ throw 'Unknown button type: '+base;
767
+ }
768
+
769
+ base = _dtButtons[ base ];
770
+ }
771
+
772
+ loop++;
773
+ if ( loop > 30 ) {
774
+ // Protect against misconfiguration killing the browser
775
+ throw 'Buttons: Too many iterations';
776
+ }
777
+ }
778
+
779
+ return $.isArray( base ) ?
780
+ base :
781
+ $.extend( {}, base );
782
+ };
783
+
784
+ conf = toConfObject( conf );
785
+
786
+ while ( conf && conf.extend ) {
787
+ // Use `toConfObject` in case the button definition being extended
788
+ // is itself a string or a function
789
+ if ( ! _dtButtons[ conf.extend ] ) {
790
+ throw 'Cannot extend unknown button type: '+conf.extend;
791
+ }
792
+
793
+ var objArray = toConfObject( _dtButtons[ conf.extend ] );
794
+ if ( $.isArray( objArray ) ) {
795
+ return objArray;
796
+ }
797
+ else if ( ! objArray ) {
798
+ // This is a little brutal as it might be possible to have a
799
+ // valid button without the extend, but if there is no extend
800
+ // then the host button would be acting in an undefined state
801
+ return false;
802
+ }
803
+
804
+ // Stash the current class name
805
+ var originalClassName = objArray.className;
806
+
807
+ conf = $.extend( {}, objArray, conf );
808
+
809
+ // The extend will have overwritten the original class name if the
810
+ // `conf` object also assigned a class, but we want to concatenate
811
+ // them so they are list that is combined from all extended buttons
812
+ if ( originalClassName && conf.className !== originalClassName ) {
813
+ conf.className = originalClassName+' '+conf.className;
814
+ }
815
+
816
+ // Buttons to be added to a collection -gives the ability to define
817
+ // if buttons should be added to the start or end of a collection
818
+ var postfixButtons = conf.postfixButtons;
819
+ if ( postfixButtons ) {
820
+ if ( ! conf.buttons ) {
821
+ conf.buttons = [];
822
+ }
823
+
824
+ for ( i=0, ien=postfixButtons.length ; i<ien ; i++ ) {
825
+ conf.buttons.push( postfixButtons[i] );
826
+ }
827
+
828
+ conf.postfixButtons = null;
829
+ }
830
+
831
+ var prefixButtons = conf.prefixButtons;
832
+ if ( prefixButtons ) {
833
+ if ( ! conf.buttons ) {
834
+ conf.buttons = [];
835
+ }
836
+
837
+ for ( i=0, ien=prefixButtons.length ; i<ien ; i++ ) {
838
+ conf.buttons.splice( i, 0, prefixButtons[i] );
839
+ }
840
+
841
+ conf.prefixButtons = null;
842
+ }
843
+
844
+ // Although we want the `conf` object to overwrite almost all of
845
+ // the properties of the object being extended, the `extend`
846
+ // property should come from the object being extended
847
+ conf.extend = objArray.extend;
848
+ }
849
+
850
+ return conf;
851
+ }
844
852
  } );
845
853
 
846
854
 
@@ -852,28 +860,30 @@ $.extend( Buttons.prototype, {
852
860
  /**
853
861
  * Show / hide a background layer behind a collection
854
862
  * @param {boolean} Flag to indicate if the background should be shown or
855
- * hidden
863
+ * hidden
856
864
  * @param {string} Class to assign to the background
857
865
  * @static
858
866
  */
859
867
  Buttons.background = function ( show, className, fade ) {
860
- if ( fade === undefined ) {
861
- fade = 400;
862
- }
863
-
864
- if ( show ) {
865
- $('<div/>')
866
- .addClass( className )
867
- .css( 'display', 'none' )
868
- .appendTo( 'body' )
869
- .fadeIn( fade );
870
- }
871
- else {
872
- $('body > div.'+className)
873
- .fadeOut( fade, function () {
874
- $(this).remove();
875
- } );
876
- }
868
+ if ( fade === undefined ) {
869
+ fade = 400;
870
+ }
871
+
872
+ if ( show ) {
873
+ $('<div/>')
874
+ .addClass( className )
875
+ .css( 'display', 'none' )
876
+ .appendTo( 'body' )
877
+ .fadeIn( fade );
878
+ }
879
+ else {
880
+ $('body > div.'+className)
881
+ .fadeOut( fade, function () {
882
+ $(this)
883
+ .removeClass( className )
884
+ .remove();
885
+ } );
886
+ }
877
887
  };
878
888
 
879
889
  /**
@@ -889,49 +899,49 @@ Buttons.background = function ( show, className, fade ) {
889
899
  */
890
900
  Buttons.instanceSelector = function ( group, buttons )
891
901
  {
892
- if ( ! group ) {
893
- return $.map( buttons, function ( v ) {
894
- return v.inst;
895
- } );
896
- }
897
-
898
- var ret = [];
899
- var names = $.map( buttons, function ( v ) {
900
- return v.name;
901
- } );
902
-
903
- // Flatten the group selector into an array of single options
904
- var process = function ( input ) {
905
- if ( $.isArray( input ) ) {
906
- for ( var i=0, ien=input.length ; i<ien ; i++ ) {
907
- process( input[i] );
908
- }
909
- return;
910
- }
911
-
912
- if ( typeof input === 'string' ) {
913
- if ( input.indexOf( ',' ) !== -1 ) {
914
- // String selector, list of names
915
- process( input.split(',') );
916
- }
917
- else {
918
- // String selector individual name
919
- var idx = $.inArray( $.trim(input), names );
920
-
921
- if ( idx !== -1 ) {
922
- ret.push( buttons[ idx ].inst );
923
- }
924
- }
925
- }
926
- else if ( typeof input === 'number' ) {
927
- // Index selector
928
- ret.push( buttons[ input ].inst );
929
- }
930
- };
931
-
932
- process( group );
933
-
934
- return ret;
902
+ if ( ! group ) {
903
+ return $.map( buttons, function ( v ) {
904
+ return v.inst;
905
+ } );
906
+ }
907
+
908
+ var ret = [];
909
+ var names = $.map( buttons, function ( v ) {
910
+ return v.name;
911
+ } );
912
+
913
+ // Flatten the group selector into an array of single options
914
+ var process = function ( input ) {
915
+ if ( $.isArray( input ) ) {
916
+ for ( var i=0, ien=input.length ; i<ien ; i++ ) {
917
+ process( input[i] );
918
+ }
919
+ return;
920
+ }
921
+
922
+ if ( typeof input === 'string' ) {
923
+ if ( input.indexOf( ',' ) !== -1 ) {
924
+ // String selector, list of names
925
+ process( input.split(',') );
926
+ }
927
+ else {
928
+ // String selector individual name
929
+ var idx = $.inArray( $.trim(input), names );
930
+
931
+ if ( idx !== -1 ) {
932
+ ret.push( buttons[ idx ].inst );
933
+ }
934
+ }
935
+ }
936
+ else if ( typeof input === 'number' ) {
937
+ // Index selector
938
+ ret.push( buttons[ input ].inst );
939
+ }
940
+ };
941
+
942
+ process( group );
943
+
944
+ return ret;
935
945
  };
936
946
 
937
947
  /**
@@ -946,118 +956,128 @@ Buttons.instanceSelector = function ( group, buttons )
946
956
  */
947
957
  Buttons.buttonSelector = function ( insts, selector )
948
958
  {
949
- var ret = [];
950
- var run = function ( selector, inst ) {
951
- var i, ien, j, jen;
952
- var buttons = [];
953
-
954
- $.each( inst.s.buttons, function (i, v) {
955
- if ( v !== null ) {
956
- buttons.push( {
957
- node: v.node[0],
958
- name: v.name
959
- } );
960
- }
961
- } );
962
-
963
- $.each( inst.s.subButtons, function (i, v) {
964
- $.each( v, function (j, w) {
965
- if ( w !== null ) {
966
- buttons.push( {
967
- node: w.node[0],
968
- name: w.name
969
- } );
970
- }
971
- } );
972
- } );
973
-
974
- var nodes = $.map( buttons, function (v) {
975
- return v.node;
976
- } );
977
-
978
- if ( $.isArray( selector ) || selector instanceof $ ) {
979
- for ( i=0, ien=selector.length ; i<ien ; i++ ) {
980
- run( selector[i], inst );
981
- }
982
- return;
983
- }
984
-
985
- if ( selector === null || selector === undefined || selector === '*' ) {
986
- // Select all
987
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
988
- ret.push( {
989
- inst: inst,
990
- idx: inst.toIndex( buttons[i].node )
991
- } );
992
- }
993
- }
994
- else if ( typeof selector === 'number' ) {
995
- // Main button index selector
996
- ret.push( {
997
- inst: inst,
998
- idx: selector
999
- } );
1000
- }
1001
- else if ( typeof selector === 'string' ) {
1002
- if ( selector.indexOf( ',' ) !== -1 ) {
1003
- // Split
1004
- var a = selector.split(',');
1005
-
1006
- for ( i=0, ien=a.length ; i<ien ; i++ ) {
1007
- run( $.trim(a[i]), inst );
1008
- }
1009
- }
1010
- else if ( selector.match( /^\d+(\-\d+)?$/ ) ) {
1011
- // Sub-button index selector
1012
- ret.push( {
1013
- inst: inst,
1014
- idx: selector
1015
- } );
1016
- }
1017
- else if ( selector.indexOf( ':name' ) !== -1 ) {
1018
- // Button name selector
1019
- var name = selector.replace( ':name', '' );
1020
-
1021
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
1022
- if ( buttons[i].name === name ) {
1023
- ret.push( {
1024
- inst: inst,
1025
- idx: inst.toIndex( buttons[i].node )
1026
- } );
1027
- }
1028
- }
1029
- }
1030
- else {
1031
- // jQuery selector on the nodes
1032
- $( nodes ).filter( selector ).each( function () {
1033
- ret.push( {
1034
- inst: inst,
1035
- idx: inst.toIndex( this )
1036
- } );
1037
- } );
1038
- }
1039
- }
1040
- else if ( typeof selector === 'object' && selector.nodeName ) {
1041
- // Node selector
1042
- var idx = $.inArray( selector, nodes );
1043
-
1044
- if ( idx !== -1 ) {
1045
- ret.push( {
1046
- inst: inst,
1047
- idx: inst.toIndex( nodes[ idx ] )
1048
- } );
1049
- }
1050
- }
1051
- };
1052
-
1053
-
1054
- for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
1055
- var inst = insts[i];
1056
-
1057
- run( selector, inst );
1058
- }
1059
-
1060
- return ret;
959
+ var ret = [];
960
+ var nodeBuilder = function ( a, buttons, baseIdx ) {
961
+ var button;
962
+ var idx;
963
+
964
+ for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
965
+ button = buttons[i];
966
+
967
+ if ( button ) {
968
+ idx = baseIdx !== undefined ?
969
+ baseIdx+i :
970
+ i+'';
971
+
972
+ a.push( {
973
+ node: button.node,
974
+ name: button.conf.name,
975
+ idx: idx
976
+ } );
977
+
978
+ if ( button.buttons ) {
979
+ nodeBuilder( a, button.buttons, idx+'-' );
980
+ }
981
+ }
982
+ }
983
+ };
984
+
985
+ var run = function ( selector, inst ) {
986
+ var i, ien;
987
+ var buttons = [];
988
+ nodeBuilder( buttons, inst.s.buttons );
989
+
990
+ var nodes = $.map( buttons, function (v) {
991
+ return v.node;
992
+ } );
993
+
994
+ if ( $.isArray( selector ) || selector instanceof $ ) {
995
+ for ( i=0, ien=selector.length ; i<ien ; i++ ) {
996
+ run( selector[i], inst );
997
+ }
998
+ return;
999
+ }
1000
+
1001
+ if ( selector === null || selector === undefined || selector === '*' ) {
1002
+ // Select all
1003
+ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
1004
+ ret.push( {
1005
+ inst: inst,
1006
+ node: buttons[i].node
1007
+ } );
1008
+ }
1009
+ }
1010
+ else if ( typeof selector === 'number' ) {
1011
+ // Main button index selector
1012
+ ret.push( {
1013
+ inst: inst,
1014
+ node: inst.s.buttons[ selector ].node
1015
+ } );
1016
+ }
1017
+ else if ( typeof selector === 'string' ) {
1018
+ if ( selector.indexOf( ',' ) !== -1 ) {
1019
+ // Split
1020
+ var a = selector.split(',');
1021
+
1022
+ for ( i=0, ien=a.length ; i<ien ; i++ ) {
1023
+ run( $.trim(a[i]), inst );
1024
+ }
1025
+ }
1026
+ else if ( selector.match( /^\d+(\-\d+)*$/ ) ) {
1027
+ // Sub-button index selector
1028
+ var indexes = $.map( buttons, function (v) {
1029
+ return v.idx;
1030
+ } );
1031
+
1032
+ ret.push( {
1033
+ inst: inst,
1034
+ node: buttons[ $.inArray( selector, indexes ) ].node
1035
+ } );
1036
+ }
1037
+ else if ( selector.indexOf( ':name' ) !== -1 ) {
1038
+ // Button name selector
1039
+ var name = selector.replace( ':name', '' );
1040
+
1041
+ for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
1042
+ if ( buttons[i].name === name ) {
1043
+ ret.push( {
1044
+ inst: inst,
1045
+ node: buttons[i].node
1046
+ } );
1047
+ }
1048
+ }
1049
+ }
1050
+ else {
1051
+ // jQuery selector on the nodes
1052
+ $( nodes ).filter( selector ).each( function () {
1053
+ ret.push( {
1054
+ inst: inst,
1055
+ node: this
1056
+ } );
1057
+ } );
1058
+ }
1059
+ }
1060
+ else if ( typeof selector === 'object' && selector.nodeName ) {
1061
+ // Node selector
1062
+ var idx = $.inArray( selector, nodes );
1063
+
1064
+ if ( idx !== -1 ) {
1065
+ ret.push( {
1066
+ inst: inst,
1067
+ node: nodes[ idx ]
1068
+ } );
1069
+ }
1070
+ }
1071
+ };
1072
+
1073
+
1074
+ for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
1075
+ var inst = insts[i];
1076
+
1077
+ run( selector, inst );
1078
+ }
1079
+
1080
+ return ret;
1061
1081
  };
1062
1082
 
1063
1083
 
@@ -1068,29 +1088,29 @@ Buttons.buttonSelector = function ( insts, selector )
1068
1088
  * @static
1069
1089
  */
1070
1090
  Buttons.defaults = {
1071
- buttons: [ 'copy', 'excel', 'csv', 'pdf', 'print' ],
1072
- name: 'main',
1073
- tabIndex: 0,
1074
- dom: {
1075
- container: {
1076
- tag: 'div',
1077
- className: 'dt-buttons'
1078
- },
1079
- collection: {
1080
- tag: 'div',
1081
- className: 'dt-button-collection'
1082
- },
1083
- button: {
1084
- tag: 'a',
1085
- className: 'dt-button',
1086
- active: 'active',
1087
- disabled: 'disabled'
1088
- },
1089
- buttonLiner: {
1090
- tag: 'span',
1091
- className: ''
1092
- }
1093
- }
1091
+ buttons: [ 'copy', 'excel', 'csv', 'pdf', 'print' ],
1092
+ name: 'main',
1093
+ tabIndex: 0,
1094
+ dom: {
1095
+ container: {
1096
+ tag: 'div',
1097
+ className: 'dt-buttons'
1098
+ },
1099
+ collection: {
1100
+ tag: 'div',
1101
+ className: 'dt-button-collection'
1102
+ },
1103
+ button: {
1104
+ tag: 'a',
1105
+ className: 'dt-button',
1106
+ active: 'active',
1107
+ disabled: 'disabled'
1108
+ },
1109
+ buttonLiner: {
1110
+ tag: 'span',
1111
+ className: ''
1112
+ }
1113
+ }
1094
1114
  };
1095
1115
 
1096
1116
  /**
@@ -1098,178 +1118,189 @@ Buttons.defaults = {
1098
1118
  * @type {string}
1099
1119
  * @static
1100
1120
  */
1101
- Buttons.version = '1.1.0';
1121
+ Buttons.version = '1.2.2';
1102
1122
 
1103
1123
 
1104
1124
  $.extend( _dtButtons, {
1105
- collection: {
1106
- text: function ( dt, button, config ) {
1107
- return dt.i18n( 'buttons.collection', 'Collection' );
1108
- },
1109
- className: 'buttons-collection',
1110
- action: function ( e, dt, button, config ) {
1111
- var background;
1112
- var host = button;
1113
- var hostOffset = host.offset();
1114
- var tableContainer = $( dt.table().container() );
1115
- var multiLevel = false;
1116
-
1117
- // Remove any old collection
1118
- if ( $('div.dt-button-background').length ) {
1119
- multiLevel = $('div.dt-button-collection').offset();
1120
- $(document).trigger( 'click.dtb-collection' );
1121
- }
1122
-
1123
- config._collection
1124
- .addClass( config.collectionLayout )
1125
- .css( 'display', 'none' )
1126
- .appendTo( 'body' )
1127
- .fadeIn( config.fade );
1128
-
1129
- var position = config._collection.css( 'position' );
1130
-
1131
- if ( multiLevel && position === 'absolute' ) {
1132
- config._collection.css( {
1133
- top: multiLevel.top + 5, // magic numbers for a little offset
1134
- left: multiLevel.left + 5
1135
- } );
1136
- }
1137
- else if ( position === 'absolute' ) {
1138
- config._collection.css( {
1139
- top: hostOffset.top + host.outerHeight(),
1140
- left: hostOffset.left
1141
- } );
1142
-
1143
- var listRight = hostOffset.left + config._collection.outerWidth();
1144
- var tableRight = tableContainer.offset().left + tableContainer.width();
1145
- if ( listRight > tableRight ) {
1146
- config._collection.css( 'left', hostOffset.left - ( listRight - tableRight ) );
1147
- }
1148
- }
1149
- else {
1150
- // Fix position - centre on screen
1151
- var top = config._collection.height() / 2;
1152
- if ( top > $(window).height() / 2 ) {
1153
- top = $(window).height() / 2;
1154
- }
1155
-
1156
- config._collection.css( 'marginTop', top*-1 );
1157
- }
1158
-
1159
- if ( config.background ) {
1160
- Buttons.background( true, config.backgroundClassName, config.fade );
1161
- }
1162
-
1163
- // Need to break the 'thread' for the collection button being
1164
- // activated by a click - it would also trigger this event
1165
- setTimeout( function () {
1166
- // This is bonkers, but if we don't have a click listener on the
1167
- // background element, iOS Safari will ignore the body click
1168
- // listener below. An empty function here is all that is
1169
- // required to make it work...
1170
- $('div.dt-button-background').on( 'click.dtb-collection', function () {} );
1171
-
1172
- $('body').on( 'click.dtb-collection', function (e) {
1173
- if ( ! $(e.target).parents().andSelf().filter( config._collection ).length ) {
1174
- config._collection
1175
- .fadeOut( config.fade, function () {
1176
- config._collection.detach();
1177
- } );
1178
-
1179
- $('div.dt-button-background').off( 'click.dtb-collection' );
1180
- Buttons.background( false, config.backgroundClassName, config.fade );
1181
-
1182
- $('body').off( 'click.dtb-collection' );
1183
- }
1184
- } );
1185
- }, 10 );
1186
- },
1187
- background: true,
1188
- collectionLayout: '',
1189
- backgroundClassName: 'dt-button-background',
1190
- fade: 400
1191
- },
1192
- copy: function ( dt, conf ) {
1193
- if ( _dtButtons.copyHtml5 ) {
1194
- return 'copyHtml5';
1195
- }
1196
- if ( _dtButtons.copyFlash && _dtButtons.copyFlash.available( dt, conf ) ) {
1197
- return 'copyFlash';
1198
- }
1199
- },
1200
- csv: function ( dt, conf ) {
1201
- // Common option that will use the HTML5 or Flash export buttons
1202
- if ( _dtButtons.csvHtml5 && _dtButtons.csvHtml5.available( dt, conf ) ) {
1203
- return 'csvHtml5';
1204
- }
1205
- if ( _dtButtons.csvFlash && _dtButtons.csvFlash.available( dt, conf ) ) {
1206
- return 'csvFlash';
1207
- }
1208
- },
1209
- excel: function ( dt, conf ) {
1210
- // Common option that will use the HTML5 or Flash export buttons
1211
- if ( _dtButtons.excelHtml5 && _dtButtons.excelHtml5.available( dt, conf ) ) {
1212
- return 'excelHtml5';
1213
- }
1214
- if ( _dtButtons.excelFlash && _dtButtons.excelFlash.available( dt, conf ) ) {
1215
- return 'excelFlash';
1216
- }
1217
- },
1218
- pdf: function ( dt, conf ) {
1219
- // Common option that will use the HTML5 or Flash export buttons
1220
- if ( _dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available( dt, conf ) ) {
1221
- return 'pdfHtml5';
1222
- }
1223
- if ( _dtButtons.pdfFlash && _dtButtons.pdfFlash.available( dt, conf ) ) {
1224
- return 'pdfFlash';
1225
- }
1226
- },
1227
- pageLength: function ( dt, conf ) {
1228
- var lengthMenu = dt.settings()[0].aLengthMenu;
1229
- var vals = $.isArray( lengthMenu[0] ) ? lengthMenu[0] : lengthMenu;
1230
- var lang = $.isArray( lengthMenu[0] ) ? lengthMenu[1] : lengthMenu;
1231
- var text = function ( dt ) {
1232
- return dt.i18n( 'buttons.pageLength', {
1233
- "-1": 'Show all rows',
1234
- _: 'Show %d rows'
1235
- }, dt.page.len() );
1236
- };
1237
-
1238
- return {
1239
- extend: 'collection',
1240
- text: text,
1241
- className: 'buttons-page-length',
1242
- buttons: $.map( vals, function ( val, i ) {
1243
- return {
1244
- text: lang[i],
1245
- action: function ( e, dt, button, conf ) {
1246
- dt.page.len( val ).draw();
1247
- },
1248
- init: function ( dt, node, conf ) {
1249
- var that = this;
1250
- var fn = function () {
1251
- that.active( dt.page.len() === val );
1252
- };
1253
-
1254
- dt.on( 'length.dt'+conf.namespace, fn );
1255
- fn();
1256
- },
1257
- destroy: function ( dt, node, conf ) {
1258
- dt.off( 'length.dt'+conf.namespace );
1259
- }
1260
- };
1261
- } ),
1262
- init: function ( dt, node, conf ) {
1263
- var that = this;
1264
- dt.on( 'length.dt'+conf.namespace, function () {
1265
- that.text( text( dt ) );
1266
- } );
1267
- },
1268
- destroy: function ( dt, node, conf ) {
1269
- dt.off( 'length.dt'+conf.namespace );
1270
- }
1271
- };
1272
- }
1125
+ collection: {
1126
+ text: function ( dt ) {
1127
+ return dt.i18n( 'buttons.collection', 'Collection' );
1128
+ },
1129
+ className: 'buttons-collection',
1130
+ action: function ( e, dt, button, config ) {
1131
+ var host = button;
1132
+ var hostOffset = host.offset();
1133
+ var tableContainer = $( dt.table().container() );
1134
+ var multiLevel = false;
1135
+
1136
+ // Remove any old collection
1137
+ if ( $('div.dt-button-background').length ) {
1138
+ multiLevel = $('div.dt-button-collection').offset();
1139
+ $('body').trigger( 'click.dtb-collection' );
1140
+ }
1141
+
1142
+ config._collection
1143
+ .addClass( config.collectionLayout )
1144
+ .css( 'display', 'none' )
1145
+ .appendTo( 'body' )
1146
+ .fadeIn( config.fade );
1147
+
1148
+ var position = config._collection.css( 'position' );
1149
+
1150
+ if ( multiLevel && position === 'absolute' ) {
1151
+ config._collection.css( {
1152
+ top: multiLevel.top + 5, // magic numbers for a little offset
1153
+ left: multiLevel.left + 5
1154
+ } );
1155
+ }
1156
+ else if ( position === 'absolute' ) {
1157
+ config._collection.css( {
1158
+ top: hostOffset.top + host.outerHeight(),
1159
+ left: hostOffset.left
1160
+ } );
1161
+
1162
+ var listRight = hostOffset.left + config._collection.outerWidth();
1163
+ var tableRight = tableContainer.offset().left + tableContainer.width();
1164
+ if ( listRight > tableRight ) {
1165
+ config._collection.css( 'left', hostOffset.left - ( listRight - tableRight ) );
1166
+ }
1167
+ }
1168
+ else {
1169
+ // Fix position - centre on screen
1170
+ var top = config._collection.height() / 2;
1171
+ if ( top > $(window).height() / 2 ) {
1172
+ top = $(window).height() / 2;
1173
+ }
1174
+
1175
+ config._collection.css( 'marginTop', top*-1 );
1176
+ }
1177
+
1178
+ if ( config.background ) {
1179
+ Buttons.background( true, config.backgroundClassName, config.fade );
1180
+ }
1181
+
1182
+ // Need to break the 'thread' for the collection button being
1183
+ // activated by a click - it would also trigger this event
1184
+ setTimeout( function () {
1185
+ // This is bonkers, but if we don't have a click listener on the
1186
+ // background element, iOS Safari will ignore the body click
1187
+ // listener below. An empty function here is all that is
1188
+ // required to make it work...
1189
+ $('div.dt-button-background').on( 'click.dtb-collection', function () {} );
1190
+
1191
+ $('body').on( 'click.dtb-collection', function (e) {
1192
+ // andSelf is deprecated in jQ1.8, but we want 1.7 compat
1193
+ var back = $.fn.addBack ? 'addBack' : 'andSelf';
1194
+
1195
+ if ( ! $(e.target).parents()[back]().filter( config._collection ).length ) {
1196
+ config._collection
1197
+ .fadeOut( config.fade, function () {
1198
+ config._collection.detach();
1199
+ } );
1200
+
1201
+ $('div.dt-button-background').off( 'click.dtb-collection' );
1202
+ Buttons.background( false, config.backgroundClassName, config.fade );
1203
+
1204
+ $('body').off( 'click.dtb-collection' );
1205
+ dt.off( 'buttons-action.b-internal' );
1206
+ }
1207
+ } );
1208
+ }, 10 );
1209
+
1210
+ if ( config.autoClose ) {
1211
+ dt.on( 'buttons-action.b-internal', function () {
1212
+ $('div.dt-button-background').click();
1213
+ } );
1214
+ }
1215
+ },
1216
+ background: true,
1217
+ collectionLayout: '',
1218
+ backgroundClassName: 'dt-button-background',
1219
+ autoClose: false,
1220
+ fade: 400
1221
+ },
1222
+ copy: function ( dt, conf ) {
1223
+ if ( _dtButtons.copyHtml5 ) {
1224
+ return 'copyHtml5';
1225
+ }
1226
+ if ( _dtButtons.copyFlash && _dtButtons.copyFlash.available( dt, conf ) ) {
1227
+ return 'copyFlash';
1228
+ }
1229
+ },
1230
+ csv: function ( dt, conf ) {
1231
+ // Common option that will use the HTML5 or Flash export buttons
1232
+ if ( _dtButtons.csvHtml5 && _dtButtons.csvHtml5.available( dt, conf ) ) {
1233
+ return 'csvHtml5';
1234
+ }
1235
+ if ( _dtButtons.csvFlash && _dtButtons.csvFlash.available( dt, conf ) ) {
1236
+ return 'csvFlash';
1237
+ }
1238
+ },
1239
+ excel: function ( dt, conf ) {
1240
+ // Common option that will use the HTML5 or Flash export buttons
1241
+ if ( _dtButtons.excelHtml5 && _dtButtons.excelHtml5.available( dt, conf ) ) {
1242
+ return 'excelHtml5';
1243
+ }
1244
+ if ( _dtButtons.excelFlash && _dtButtons.excelFlash.available( dt, conf ) ) {
1245
+ return 'excelFlash';
1246
+ }
1247
+ },
1248
+ pdf: function ( dt, conf ) {
1249
+ // Common option that will use the HTML5 or Flash export buttons
1250
+ if ( _dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available( dt, conf ) ) {
1251
+ return 'pdfHtml5';
1252
+ }
1253
+ if ( _dtButtons.pdfFlash && _dtButtons.pdfFlash.available( dt, conf ) ) {
1254
+ return 'pdfFlash';
1255
+ }
1256
+ },
1257
+ pageLength: function ( dt ) {
1258
+ var lengthMenu = dt.settings()[0].aLengthMenu;
1259
+ var vals = $.isArray( lengthMenu[0] ) ? lengthMenu[0] : lengthMenu;
1260
+ var lang = $.isArray( lengthMenu[0] ) ? lengthMenu[1] : lengthMenu;
1261
+ var text = function ( dt ) {
1262
+ return dt.i18n( 'buttons.pageLength', {
1263
+ "-1": 'Show all rows',
1264
+ _: 'Show %d rows'
1265
+ }, dt.page.len() );
1266
+ };
1267
+
1268
+ return {
1269
+ extend: 'collection',
1270
+ text: text,
1271
+ className: 'buttons-page-length',
1272
+ autoClose: true,
1273
+ buttons: $.map( vals, function ( val, i ) {
1274
+ return {
1275
+ text: lang[i],
1276
+ action: function ( e, dt ) {
1277
+ dt.page.len( val ).draw();
1278
+ },
1279
+ init: function ( dt, node, conf ) {
1280
+ var that = this;
1281
+ var fn = function () {
1282
+ that.active( dt.page.len() === val );
1283
+ };
1284
+
1285
+ dt.on( 'length.dt'+conf.namespace, fn );
1286
+ fn();
1287
+ },
1288
+ destroy: function ( dt, node, conf ) {
1289
+ dt.off( 'length.dt'+conf.namespace );
1290
+ }
1291
+ };
1292
+ } ),
1293
+ init: function ( dt, node, conf ) {
1294
+ var that = this;
1295
+ dt.on( 'length.dt'+conf.namespace, function () {
1296
+ that.text( text( dt ) );
1297
+ } );
1298
+ },
1299
+ destroy: function ( dt, node, conf ) {
1300
+ dt.off( 'length.dt'+conf.namespace );
1301
+ }
1302
+ };
1303
+ }
1273
1304
  } );
1274
1305
 
1275
1306
 
@@ -1282,285 +1313,312 @@ $.extend( _dtButtons, {
1282
1313
 
1283
1314
  // Buttons group and individual button selector
1284
1315
  DataTable.Api.register( 'buttons()', function ( group, selector ) {
1285
- // Argument shifting
1286
- if ( selector === undefined ) {
1287
- selector = group;
1288
- group = undefined;
1289
- }
1290
-
1291
- return this.iterator( true, 'table', function ( ctx ) {
1292
- if ( ctx._buttons ) {
1293
- return Buttons.buttonSelector(
1294
- Buttons.instanceSelector( group, ctx._buttons ),
1295
- selector
1296
- );
1297
- }
1298
- }, true );
1316
+ // Argument shifting
1317
+ if ( selector === undefined ) {
1318
+ selector = group;
1319
+ group = undefined;
1320
+ }
1321
+
1322
+ this.selector.buttonGroup = group;
1323
+
1324
+ var res = this.iterator( true, 'table', function ( ctx ) {
1325
+ if ( ctx._buttons ) {
1326
+ return Buttons.buttonSelector(
1327
+ Buttons.instanceSelector( group, ctx._buttons ),
1328
+ selector
1329
+ );
1330
+ }
1331
+ }, true );
1332
+
1333
+ res._groupSelector = group;
1334
+ return res;
1299
1335
  } );
1300
1336
 
1301
1337
  // Individual button selector
1302
1338
  DataTable.Api.register( 'button()', function ( group, selector ) {
1303
- // just run buttons() and truncate
1304
- var buttons = this.buttons( group, selector );
1339
+ // just run buttons() and truncate
1340
+ var buttons = this.buttons( group, selector );
1305
1341
 
1306
- if ( buttons.length > 1 ) {
1307
- buttons.splice( 1, buttons.length );
1308
- }
1342
+ if ( buttons.length > 1 ) {
1343
+ buttons.splice( 1, buttons.length );
1344
+ }
1309
1345
 
1310
- return buttons;
1346
+ return buttons;
1311
1347
  } );
1312
1348
 
1313
1349
  // Active buttons
1314
- DataTable.Api.register( ['buttons().active()', 'button().active()'], function ( flag ) {
1315
- return this.each( function ( set ) {
1316
- set.inst.active( set.idx, flag );
1317
- } );
1350
+ DataTable.Api.registerPlural( 'buttons().active()', 'button().active()', function ( flag ) {
1351
+ if ( flag === undefined ) {
1352
+ return this.map( function ( set ) {
1353
+ return set.inst.active( set.node );
1354
+ } );
1355
+ }
1356
+
1357
+ return this.each( function ( set ) {
1358
+ set.inst.active( set.node, flag );
1359
+ } );
1318
1360
  } );
1319
1361
 
1320
1362
  // Get / set button action
1321
1363
  DataTable.Api.registerPlural( 'buttons().action()', 'button().action()', function ( action ) {
1322
- if ( action === undefined ) {
1323
- return this.map( function ( set ) {
1324
- return set.inst.action( set.idx );
1325
- } );
1326
- }
1327
-
1328
- return this.each( function ( set ) {
1329
- set.inst.action( set.idx, action );
1330
- } );
1364
+ if ( action === undefined ) {
1365
+ return this.map( function ( set ) {
1366
+ return set.inst.action( set.node );
1367
+ } );
1368
+ }
1369
+
1370
+ return this.each( function ( set ) {
1371
+ set.inst.action( set.node, action );
1372
+ } );
1331
1373
  } );
1332
1374
 
1333
1375
  // Enable / disable buttons
1334
1376
  DataTable.Api.register( ['buttons().enable()', 'button().enable()'], function ( flag ) {
1335
- return this.each( function ( set ) {
1336
- set.inst.enable( set.idx, flag );
1337
- } );
1377
+ return this.each( function ( set ) {
1378
+ set.inst.enable( set.node, flag );
1379
+ } );
1338
1380
  } );
1339
1381
 
1340
1382
  // Disable buttons
1341
1383
  DataTable.Api.register( ['buttons().disable()', 'button().disable()'], function () {
1342
- return this.each( function ( set ) {
1343
- set.inst.disable( set.idx );
1344
- } );
1384
+ return this.each( function ( set ) {
1385
+ set.inst.disable( set.node );
1386
+ } );
1345
1387
  } );
1346
1388
 
1347
1389
  // Get button nodes
1348
1390
  DataTable.Api.registerPlural( 'buttons().nodes()', 'button().node()', function () {
1349
- var jq = $();
1391
+ var jq = $();
1350
1392
 
1351
- // jQuery will automatically reduce duplicates to a single entry
1352
- $( this.each( function ( set ) {
1353
- jq = jq.add( set.inst.node( set.idx ) );
1354
- } ) );
1393
+ // jQuery will automatically reduce duplicates to a single entry
1394
+ $( this.each( function ( set ) {
1395
+ jq = jq.add( set.inst.node( set.node ) );
1396
+ } ) );
1355
1397
 
1356
- return jq;
1398
+ return jq;
1357
1399
  } );
1358
1400
 
1359
1401
  // Get / set button text (i.e. the button labels)
1360
1402
  DataTable.Api.registerPlural( 'buttons().text()', 'button().text()', function ( label ) {
1361
- if ( label === undefined ) {
1362
- return this.map( function ( set ) {
1363
- return set.inst.text( set.idx );
1364
- } );
1365
- }
1366
-
1367
- return this.each( function ( set ) {
1368
- set.inst.text( set.idx, label );
1369
- } );
1403
+ if ( label === undefined ) {
1404
+ return this.map( function ( set ) {
1405
+ return set.inst.text( set.node );
1406
+ } );
1407
+ }
1408
+
1409
+ return this.each( function ( set ) {
1410
+ set.inst.text( set.node, label );
1411
+ } );
1370
1412
  } );
1371
1413
 
1372
1414
  // Trigger a button's action
1373
1415
  DataTable.Api.registerPlural( 'buttons().trigger()', 'button().trigger()', function () {
1374
- return this.each( function ( set ) {
1375
- set.inst.node( set.idx ).trigger( 'click' );
1376
- } );
1416
+ return this.each( function ( set ) {
1417
+ set.inst.node( set.node ).trigger( 'click' );
1418
+ } );
1377
1419
  } );
1378
1420
 
1379
- // Get the container elements for the button sets selected
1421
+ // Get the container elements
1380
1422
  DataTable.Api.registerPlural( 'buttons().containers()', 'buttons().container()', function () {
1381
- var jq = $();
1382
-
1383
- // jQuery will automatically reduce duplicates to a single entry
1384
- $( this.each( function ( set ) {
1385
- jq = jq.add( set.inst.container() );
1386
- } ) );
1387
-
1388
- return jq;
1423
+ var jq = $();
1424
+ var groupSelector = this._groupSelector;
1425
+
1426
+ // We need to use the group selector directly, since if there are no buttons
1427
+ // the result set will be empty
1428
+ this.iterator( true, 'table', function ( ctx ) {
1429
+ if ( ctx._buttons ) {
1430
+ var insts = Buttons.instanceSelector( groupSelector, ctx._buttons );
1431
+
1432
+ for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
1433
+ jq = jq.add( insts[i].container() );
1434
+ }
1435
+ }
1436
+ } );
1437
+
1438
+ return jq;
1389
1439
  } );
1390
1440
 
1391
1441
  // Add a new button
1392
1442
  DataTable.Api.register( 'button().add()', function ( idx, conf ) {
1393
- if ( this.length === 1 ) {
1394
- this[0].inst.add( idx, conf );
1395
- }
1443
+ var ctx = this.context;
1444
+
1445
+ // Don't use `this` as it could be empty - select the instances directly
1446
+ if ( ctx.length ) {
1447
+ var inst = Buttons.instanceSelector( this._groupSelector, ctx[0]._buttons );
1396
1448
 
1397
- return this.button( idx );
1449
+ if ( inst.length ) {
1450
+ inst[0].add( conf, idx );
1451
+ }
1452
+ }
1453
+
1454
+ return this.button( this._groupSelector, idx );
1398
1455
  } );
1399
1456
 
1400
1457
  // Destroy the button sets selected
1401
- DataTable.Api.register( 'buttons().destroy()', function ( idx ) {
1402
- this.pluck( 'inst' ).unique().each( function ( inst ) {
1403
- inst.destroy();
1404
- } );
1458
+ DataTable.Api.register( 'buttons().destroy()', function () {
1459
+ this.pluck( 'inst' ).unique().each( function ( inst ) {
1460
+ inst.destroy();
1461
+ } );
1405
1462
 
1406
- return this;
1463
+ return this;
1407
1464
  } );
1408
1465
 
1409
1466
  // Remove a button
1410
1467
  DataTable.Api.registerPlural( 'buttons().remove()', 'buttons().remove()', function () {
1411
- // Need to split into prep and commit so the indexes remain constant during the remove
1412
- this.each( function ( set ) {
1413
- set.inst.removePrep( set.idx );
1414
- } );
1415
-
1416
- this.pluck( 'inst' ).unique().each( function ( inst ) {
1417
- inst.removeCommit();
1418
- } );
1468
+ this.each( function ( set ) {
1469
+ set.inst.remove( set.node );
1470
+ } );
1419
1471
 
1420
- return this;
1472
+ return this;
1421
1473
  } );
1422
1474
 
1423
1475
  // Information box that can be used by buttons
1424
1476
  var _infoTimer;
1425
1477
  DataTable.Api.register( 'buttons.info()', function ( title, message, time ) {
1426
- var that = this;
1427
-
1428
- if ( title === false ) {
1429
- $('#datatables_buttons_info').fadeOut( function () {
1430
- $(this).remove();
1431
- } );
1432
- clearTimeout( _infoTimer );
1433
- _infoTimer = null;
1434
-
1435
- return this;
1436
- }
1437
-
1438
- if ( _infoTimer ) {
1439
- clearTimeout( _infoTimer );
1440
- }
1441
-
1442
- if ( $('#datatables_buttons_info').length ) {
1443
- $('#datatables_buttons_info').remove();
1444
- }
1445
-
1446
- title = title ? '<h2>'+title+'</h2>' : '';
1447
-
1448
- $('<div id="datatables_buttons_info" class="dt-button-info"/>')
1449
- .html( title )
1450
- .append( $('<div/>')[ typeof message === 'string' ? 'html' : 'append' ]( message ) )
1451
- .css( 'display', 'none' )
1452
- .appendTo( 'body' )
1453
- .fadeIn();
1454
-
1455
- if ( time !== undefined && time !== 0 ) {
1456
- _infoTimer = setTimeout( function () {
1457
- that.buttons.info( false );
1458
- }, time );
1459
- }
1460
-
1461
- return this;
1478
+ var that = this;
1479
+
1480
+ if ( title === false ) {
1481
+ $('#datatables_buttons_info').fadeOut( function () {
1482
+ $(this).remove();
1483
+ } );
1484
+ clearTimeout( _infoTimer );
1485
+ _infoTimer = null;
1486
+
1487
+ return this;
1488
+ }
1489
+
1490
+ if ( _infoTimer ) {
1491
+ clearTimeout( _infoTimer );
1492
+ }
1493
+
1494
+ if ( $('#datatables_buttons_info').length ) {
1495
+ $('#datatables_buttons_info').remove();
1496
+ }
1497
+
1498
+ title = title ? '<h2>'+title+'</h2>' : '';
1499
+
1500
+ $('<div id="datatables_buttons_info" class="dt-button-info"/>')
1501
+ .html( title )
1502
+ .append( $('<div/>')[ typeof message === 'string' ? 'html' : 'append' ]( message ) )
1503
+ .css( 'display', 'none' )
1504
+ .appendTo( 'body' )
1505
+ .fadeIn();
1506
+
1507
+ if ( time !== undefined && time !== 0 ) {
1508
+ _infoTimer = setTimeout( function () {
1509
+ that.buttons.info( false );
1510
+ }, time );
1511
+ }
1512
+
1513
+ return this;
1462
1514
  } );
1463
1515
 
1464
1516
  // Get data from the table for export - this is common to a number of plug-in
1465
1517
  // buttons so it is included in the Buttons core library
1466
1518
  DataTable.Api.register( 'buttons.exportData()', function ( options ) {
1467
- if ( this.context.length ) {
1468
- return _exportData( new DataTable.Api( this.context[0] ), options );
1469
- }
1519
+ if ( this.context.length ) {
1520
+ return _exportData( new DataTable.Api( this.context[0] ), options );
1521
+ }
1470
1522
  } );
1471
1523
 
1472
1524
 
1473
1525
  var _exportTextarea = $('<textarea/>')[0];
1474
1526
  var _exportData = function ( dt, inOpts )
1475
1527
  {
1476
- var config = $.extend( true, {}, {
1477
- rows: null,
1478
- columns: '',
1479
- modifier: {
1480
- search: 'applied',
1481
- order: 'applied'
1482
- },
1483
- orthogonal: 'display',
1484
- stripHtml: true,
1485
- stripNewlines: true,
1486
- decodeEntities: true,
1487
- trim: true,
1488
- format: {
1489
- header: function ( d ) {
1490
- return strip( d );
1491
- },
1492
- footer: function ( d ) {
1493
- return strip( d );
1494
- },
1495
- body: function ( d ) {
1496
- return strip( d );
1497
- }
1498
- }
1499
- }, inOpts );
1500
-
1501
- var strip = function ( str ) {
1502
- if ( typeof str !== 'string' ) {
1503
- return str;
1504
- }
1505
-
1506
- if ( config.stripHtml ) {
1507
- str = str.replace( /<.*?>/g, '' );
1508
- }
1509
-
1510
- if ( config.trim ) {
1511
- str = str.replace( /^\s+|\s+$/g, '' );
1512
- }
1513
-
1514
- if ( config.stripNewlines ) {
1515
- str = str.replace( /\n/g, ' ' );
1516
- }
1517
-
1518
- if ( config.decodeEntities ) {
1519
- _exportTextarea.innerHTML = str;
1520
- str = _exportTextarea.value;
1521
- }
1522
-
1523
- return str;
1524
- };
1525
-
1526
-
1527
- var header = dt.columns( config.columns ).indexes().map( function (idx, i) {
1528
- return config.format.header( dt.column( idx ).header().innerHTML, idx );
1529
- } ).toArray();
1530
-
1531
- var footer = dt.table().footer() ?
1532
- dt.columns( config.columns ).indexes().map( function (idx, i) {
1533
- var el = dt.column( idx ).footer();
1534
- return config.format.footer( el ? el.innerHTML : '', idx );
1535
- } ).toArray() :
1536
- null;
1537
-
1538
- var rowIndexes = dt.rows( config.rows, config.modifier ).indexes().toArray();
1539
- var cells = dt
1540
- .cells( rowIndexes, config.columns )
1541
- .render( config.orthogonal )
1542
- .toArray();
1543
- var columns = header.length;
1544
- var rows = columns > 0 ? cells.length / columns : 0;
1545
- var body = new Array( rows );
1546
- var cellCounter = 0;
1547
-
1548
- for ( var i=0, ien=rows ; i<ien ; i++ ) {
1549
- var row = new Array( columns );
1550
-
1551
- for ( var j=0 ; j<columns ; j++ ) {
1552
- row[j] = config.format.body( cells[ cellCounter ], j, i );
1553
- cellCounter++;
1554
- }
1555
-
1556
- body[i] = row;
1557
- }
1558
-
1559
- return {
1560
- header: header,
1561
- footer: footer,
1562
- body: body
1563
- };
1528
+ var config = $.extend( true, {}, {
1529
+ rows: null,
1530
+ columns: '',
1531
+ modifier: {
1532
+ search: 'applied',
1533
+ order: 'applied'
1534
+ },
1535
+ orthogonal: 'display',
1536
+ stripHtml: true,
1537
+ stripNewlines: true,
1538
+ decodeEntities: true,
1539
+ trim: true,
1540
+ format: {
1541
+ header: function ( d ) {
1542
+ return strip( d );
1543
+ },
1544
+ footer: function ( d ) {
1545
+ return strip( d );
1546
+ },
1547
+ body: function ( d ) {
1548
+ return strip( d );
1549
+ }
1550
+ }
1551
+ }, inOpts );
1552
+
1553
+ var strip = function ( str ) {
1554
+ if ( typeof str !== 'string' ) {
1555
+ return str;
1556
+ }
1557
+
1558
+ if ( config.stripHtml ) {
1559
+ str = str.replace( /<[^>]*>/g, '' );
1560
+ }
1561
+
1562
+ if ( config.trim ) {
1563
+ str = str.replace( /^\s+|\s+$/g, '' );
1564
+ }
1565
+
1566
+ if ( config.stripNewlines ) {
1567
+ str = str.replace( /\n/g, ' ' );
1568
+ }
1569
+
1570
+ if ( config.decodeEntities ) {
1571
+ _exportTextarea.innerHTML = str;
1572
+ str = _exportTextarea.value;
1573
+ }
1574
+
1575
+ return str;
1576
+ };
1577
+
1578
+
1579
+ var header = dt.columns( config.columns ).indexes().map( function (idx) {
1580
+ var el = dt.column( idx ).header();
1581
+ return config.format.header( el.innerHTML, idx, el );
1582
+ } ).toArray();
1583
+
1584
+ var footer = dt.table().footer() ?
1585
+ dt.columns( config.columns ).indexes().map( function (idx) {
1586
+ var el = dt.column( idx ).footer();
1587
+ return config.format.footer( el ? el.innerHTML : '', idx, el );
1588
+ } ).toArray() :
1589
+ null;
1590
+
1591
+ var rowIndexes = dt.rows( config.rows, config.modifier ).indexes().toArray();
1592
+ var cells = dt
1593
+ .cells( rowIndexes, config.columns )
1594
+ .render( config.orthogonal )
1595
+ .toArray();
1596
+ var cellNodes = dt
1597
+ .cells( rowIndexes, config.columns )
1598
+ .nodes()
1599
+ .toArray();
1600
+
1601
+ var columns = header.length;
1602
+ var rows = columns > 0 ? cells.length / columns : 0;
1603
+ var body = new Array( rows );
1604
+ var cellCounter = 0;
1605
+
1606
+ for ( var i=0, ien=rows ; i<ien ; i++ ) {
1607
+ var row = new Array( columns );
1608
+
1609
+ for ( var j=0 ; j<columns ; j++ ) {
1610
+ row[j] = config.format.body( cells[ cellCounter ], i, j, cellNodes[ cellCounter ] );
1611
+ cellCounter++;
1612
+ }
1613
+
1614
+ body[i] = row;
1615
+ }
1616
+
1617
+ return {
1618
+ header: header,
1619
+ footer: footer,
1620
+ body: body
1621
+ };
1564
1622
  };
1565
1623
 
1566
1624
 
@@ -1579,27 +1637,27 @@ $.fn.DataTable.Buttons = Buttons;
1579
1637
  // create the buttons instance here so they can be inserted into the document
1580
1638
  // using the API. Listen for `init` for compatibility with pre 1.10.10, but to
1581
1639
  // be removed in future.
1582
- $(document).on( 'init.dt plugin-init.dt', function (e, settings, json) {
1583
- if ( e.namespace !== 'dt' ) {
1584
- return;
1585
- }
1640
+ $(document).on( 'init.dt plugin-init.dt', function (e, settings) {
1641
+ if ( e.namespace !== 'dt' ) {
1642
+ return;
1643
+ }
1586
1644
 
1587
- var opts = settings.oInit.buttons || DataTable.defaults.buttons;
1645
+ var opts = settings.oInit.buttons || DataTable.defaults.buttons;
1588
1646
 
1589
- if ( opts && ! settings._buttons ) {
1590
- new Buttons( settings, opts ).container();
1591
- }
1647
+ if ( opts && ! settings._buttons ) {
1648
+ new Buttons( settings, opts ).container();
1649
+ }
1592
1650
  } );
1593
1651
 
1594
1652
  // DataTables `dom` feature option
1595
1653
  DataTable.ext.feature.push( {
1596
- fnInit: function( settings ) {
1597
- var api = new DataTable.Api( settings );
1598
- var opts = api.init().buttons || DataTable.defaults.buttons;
1654
+ fnInit: function( settings ) {
1655
+ var api = new DataTable.Api( settings );
1656
+ var opts = api.init().buttons || DataTable.defaults.buttons;
1599
1657
 
1600
- return new Buttons( api, opts ).container();
1601
- },
1602
- cFeature: "B"
1658
+ return new Buttons( api, opts ).container();
1659
+ },
1660
+ cFeature: "B"
1603
1661
  } );
1604
1662
 
1605
1663