jquery-datatables-rails 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ }));