jquery-datatables-rails 3.3.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (24) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/dataTables/bootstrap/3/jquery.dataTables.bootstrap.js +64 -29
  3. data/app/assets/javascripts/dataTables/extras/dataTables.autoFill.js +806 -648
  4. data/app/assets/javascripts/dataTables/extras/dataTables.buttons.js +1607 -0
  5. data/app/assets/javascripts/dataTables/extras/dataTables.colReorder.js +220 -267
  6. data/app/assets/javascripts/dataTables/extras/dataTables.fixedColumns.js +164 -69
  7. data/app/assets/javascripts/dataTables/extras/dataTables.fixedHeader.js +469 -870
  8. data/app/assets/javascripts/dataTables/extras/dataTables.keyTable.js +636 -972
  9. data/app/assets/javascripts/dataTables/extras/dataTables.responsive.js +472 -187
  10. data/app/assets/javascripts/dataTables/extras/dataTables.rowReorder.js +619 -0
  11. data/app/assets/javascripts/dataTables/extras/dataTables.scroller.js +146 -111
  12. data/app/assets/javascripts/dataTables/extras/dataTables.select.js +1038 -0
  13. data/app/assets/javascripts/dataTables/jquery.dataTables.api.fnGetColumnData.js +0 -0
  14. data/app/assets/javascripts/dataTables/jquery.dataTables.api.fnReloadAjax.js +0 -0
  15. data/app/assets/javascripts/dataTables/jquery.dataTables.foundation.js +37 -61
  16. data/app/assets/javascripts/dataTables/jquery.dataTables.js +720 -387
  17. data/app/assets/javascripts/dataTables/jquery.dataTables.sorting.ipAddress.js +44 -0
  18. data/app/assets/javascripts/dataTables/jquery.dataTables.sorting.numbersHtml.js +0 -0
  19. data/app/assets/javascripts/dataTables/jquery.dataTables.typeDetection.numbersHtml.js +0 -0
  20. data/app/assets/stylesheets/dataTables/jquery.dataTables.scss +34 -66
  21. data/app/assets/stylesheets/dataTables/src/demo_table.css +1 -1
  22. data/app/assets/stylesheets/dataTables/src/demo_table_jui.css.scss +4 -4
  23. data/lib/jquery/datatables/rails/version.rb +1 -1
  24. metadata +24 -19
@@ -0,0 +1,1607 @@
1
+ /*! Buttons for DataTables 1.1.0
2
+ * ©2015 SpryMedia Ltd - datatables.net/license
3
+ */
4
+
5
+ (function( factory ){
6
+ if ( typeof define === 'function' && define.amd ) {
7
+ // AMD
8
+ define( ['jquery', 'datatables.net'], function ( $ ) {
9
+ return factory( $, window, document );
10
+ } );
11
+ }
12
+ else if ( typeof exports === 'object' ) {
13
+ // CommonJS
14
+ 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
+ }(function( $, window, document, undefined ) {
31
+ 'use strict';
32
+ var DataTable = $.fn.dataTable;
33
+
34
+
35
+ // Used for namespacing events added to the document by each instance, so they
36
+ // can be removed on destroy
37
+ var _instCounter = 0;
38
+
39
+ // Button namespacing counter for namespacing events on individual buttons
40
+ var _buttonCounter = 0;
41
+
42
+ var _dtButtons = DataTable.ext.buttons;
43
+
44
+ /**
45
+ * [Buttons description]
46
+ * @param {[type]}
47
+ * @param {[type]}
48
+ */
49
+ var Buttons = function( dt, config )
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();
82
+ };
83
+
84
+
85
+ $.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
+ }
844
+ } );
845
+
846
+
847
+
848
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
849
+ * Statics
850
+ */
851
+
852
+ /**
853
+ * Show / hide a background layer behind a collection
854
+ * @param {boolean} Flag to indicate if the background should be shown or
855
+ * hidden
856
+ * @param {string} Class to assign to the background
857
+ * @static
858
+ */
859
+ 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
+ }
877
+ };
878
+
879
+ /**
880
+ * Instance selector - select Buttons instances based on an instance selector
881
+ * value from the buttons assigned to a DataTable. This is only useful if
882
+ * multiple instances are attached to a DataTable.
883
+ * @param {string|int|array} Instance selector - see `instance-selector`
884
+ * documentation on the DataTables site
885
+ * @param {array} Button instance array that was attached to the DataTables
886
+ * settings object
887
+ * @return {array} Buttons instances
888
+ * @static
889
+ */
890
+ Buttons.instanceSelector = function ( group, buttons )
891
+ {
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;
935
+ };
936
+
937
+ /**
938
+ * Button selector - select one or more buttons from a selector input so some
939
+ * operation can be performed on them.
940
+ * @param {array} Button instances array that the selector should operate on
941
+ * @param {string|int|node|jQuery|array} Button selector - see
942
+ * `button-selector` documentation on the DataTables site
943
+ * @return {array} Array of objects containing `inst` and `idx` properties of
944
+ * the selected buttons so you know which instance each button belongs to.
945
+ * @static
946
+ */
947
+ Buttons.buttonSelector = function ( insts, selector )
948
+ {
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;
1061
+ };
1062
+
1063
+
1064
+ /**
1065
+ * Buttons defaults. For full documentation, please refer to the docs/option
1066
+ * directory or the DataTables site.
1067
+ * @type {Object}
1068
+ * @static
1069
+ */
1070
+ 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
+ }
1094
+ };
1095
+
1096
+ /**
1097
+ * Version information
1098
+ * @type {string}
1099
+ * @static
1100
+ */
1101
+ Buttons.version = '1.1.0';
1102
+
1103
+
1104
+ $.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
+ }
1273
+ } );
1274
+
1275
+
1276
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1277
+ * DataTables API
1278
+ *
1279
+ * For complete documentation, please refer to the docs/api directory or the
1280
+ * DataTables site
1281
+ */
1282
+
1283
+ // Buttons group and individual button selector
1284
+ 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 );
1299
+ } );
1300
+
1301
+ // Individual button selector
1302
+ DataTable.Api.register( 'button()', function ( group, selector ) {
1303
+ // just run buttons() and truncate
1304
+ var buttons = this.buttons( group, selector );
1305
+
1306
+ if ( buttons.length > 1 ) {
1307
+ buttons.splice( 1, buttons.length );
1308
+ }
1309
+
1310
+ return buttons;
1311
+ } );
1312
+
1313
+ // 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
+ } );
1318
+ } );
1319
+
1320
+ // Get / set button action
1321
+ 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
+ } );
1331
+ } );
1332
+
1333
+ // Enable / disable buttons
1334
+ DataTable.Api.register( ['buttons().enable()', 'button().enable()'], function ( flag ) {
1335
+ return this.each( function ( set ) {
1336
+ set.inst.enable( set.idx, flag );
1337
+ } );
1338
+ } );
1339
+
1340
+ // Disable buttons
1341
+ DataTable.Api.register( ['buttons().disable()', 'button().disable()'], function () {
1342
+ return this.each( function ( set ) {
1343
+ set.inst.disable( set.idx );
1344
+ } );
1345
+ } );
1346
+
1347
+ // Get button nodes
1348
+ DataTable.Api.registerPlural( 'buttons().nodes()', 'button().node()', function () {
1349
+ var jq = $();
1350
+
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
+ } ) );
1355
+
1356
+ return jq;
1357
+ } );
1358
+
1359
+ // Get / set button text (i.e. the button labels)
1360
+ 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
+ } );
1370
+ } );
1371
+
1372
+ // Trigger a button's action
1373
+ DataTable.Api.registerPlural( 'buttons().trigger()', 'button().trigger()', function () {
1374
+ return this.each( function ( set ) {
1375
+ set.inst.node( set.idx ).trigger( 'click' );
1376
+ } );
1377
+ } );
1378
+
1379
+ // Get the container elements for the button sets selected
1380
+ 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;
1389
+ } );
1390
+
1391
+ // Add a new button
1392
+ DataTable.Api.register( 'button().add()', function ( idx, conf ) {
1393
+ if ( this.length === 1 ) {
1394
+ this[0].inst.add( idx, conf );
1395
+ }
1396
+
1397
+ return this.button( idx );
1398
+ } );
1399
+
1400
+ // 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
+ } );
1405
+
1406
+ return this;
1407
+ } );
1408
+
1409
+ // Remove a button
1410
+ 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
+ } );
1419
+
1420
+ return this;
1421
+ } );
1422
+
1423
+ // Information box that can be used by buttons
1424
+ var _infoTimer;
1425
+ 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;
1462
+ } );
1463
+
1464
+ // Get data from the table for export - this is common to a number of plug-in
1465
+ // buttons so it is included in the Buttons core library
1466
+ DataTable.Api.register( 'buttons.exportData()', function ( options ) {
1467
+ if ( this.context.length ) {
1468
+ return _exportData( new DataTable.Api( this.context[0] ), options );
1469
+ }
1470
+ } );
1471
+
1472
+
1473
+ var _exportTextarea = $('<textarea/>')[0];
1474
+ var _exportData = function ( dt, inOpts )
1475
+ {
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
+ };
1564
+ };
1565
+
1566
+
1567
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1568
+ * DataTables interface
1569
+ */
1570
+
1571
+ // Attach to DataTables objects for global access
1572
+ $.fn.dataTable.Buttons = Buttons;
1573
+ $.fn.DataTable.Buttons = Buttons;
1574
+
1575
+
1576
+
1577
+ // DataTables creation - check if the buttons have been defined for this table,
1578
+ // they will have been if the `B` option was used in `dom`, otherwise we should
1579
+ // create the buttons instance here so they can be inserted into the document
1580
+ // using the API. Listen for `init` for compatibility with pre 1.10.10, but to
1581
+ // be removed in future.
1582
+ $(document).on( 'init.dt plugin-init.dt', function (e, settings, json) {
1583
+ if ( e.namespace !== 'dt' ) {
1584
+ return;
1585
+ }
1586
+
1587
+ var opts = settings.oInit.buttons || DataTable.defaults.buttons;
1588
+
1589
+ if ( opts && ! settings._buttons ) {
1590
+ new Buttons( settings, opts ).container();
1591
+ }
1592
+ } );
1593
+
1594
+ // DataTables `dom` feature option
1595
+ DataTable.ext.feature.push( {
1596
+ fnInit: function( settings ) {
1597
+ var api = new DataTable.Api( settings );
1598
+ var opts = api.init().buttons || DataTable.defaults.buttons;
1599
+
1600
+ return new Buttons( api, opts ).container();
1601
+ },
1602
+ cFeature: "B"
1603
+ } );
1604
+
1605
+
1606
+ return Buttons;
1607
+ }));