jquery-datatables 1.10.19.1 → 1.10.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -0
  3. data/README.md +28 -1
  4. data/app/assets/javascripts/datatables/dataTables.uikit.js +2 -2
  5. data/app/assets/javascripts/datatables/extensions/AutoFill/dataTables.autoFill.js +42 -29
  6. data/app/assets/javascripts/datatables/extensions/Buttons/buttons.bootstrap.js +1 -1
  7. data/app/assets/javascripts/datatables/extensions/Buttons/buttons.bootstrap4.js +8 -2
  8. data/app/assets/javascripts/datatables/extensions/Buttons/buttons.colVis.js +6 -3
  9. data/app/assets/javascripts/datatables/extensions/Buttons/buttons.foundation.js +5 -4
  10. data/app/assets/javascripts/datatables/extensions/Buttons/buttons.html5.js +58 -6
  11. data/app/assets/javascripts/datatables/extensions/Buttons/buttons.print.js +16 -5
  12. data/app/assets/javascripts/datatables/extensions/Buttons/buttons.semanticui.js +1 -1
  13. data/app/assets/javascripts/datatables/extensions/Buttons/dataTables.buttons.js +267 -152
  14. data/app/assets/javascripts/datatables/extensions/ColReorder/colReorder.foundation.js +1 -1
  15. data/app/assets/javascripts/datatables/extensions/ColReorder/dataTables.colReorder.js +121 -52
  16. data/app/assets/javascripts/datatables/extensions/FixedColumns/dataTables.fixedColumns.js +32 -5
  17. data/app/assets/javascripts/datatables/extensions/KeyTable/dataTables.keyTable.js +166 -63
  18. data/app/assets/javascripts/datatables/extensions/KeyTable/keyTable.foundation.js +1 -1
  19. data/app/assets/javascripts/datatables/extensions/RowGroup/dataTables.rowGroup.js +105 -53
  20. data/app/assets/javascripts/datatables/extensions/RowGroup/rowGroup.foundation.js +1 -1
  21. data/app/assets/javascripts/datatables/extensions/RowGroup/{rowGroup.semanicui.js → rowGroup.semanticui.js} +0 -0
  22. data/app/assets/javascripts/datatables/extensions/RowReorder/dataTables.rowReorder.js +10 -9
  23. data/app/assets/javascripts/datatables/extensions/RowReorder/rowReorder.foundation.js +1 -1
  24. data/app/assets/javascripts/datatables/extensions/Scroller/dataTables.scroller.js +519 -636
  25. data/app/assets/javascripts/datatables/extensions/Scroller/scroller.foundation.js +1 -1
  26. data/app/assets/javascripts/datatables/extensions/Select/dataTables.select.js +49 -18
  27. data/app/assets/javascripts/datatables/extensions/Select/select.foundation.js +1 -1
  28. data/app/assets/javascripts/datatables/jquery.dataTables.js +97 -60
  29. data/app/assets/javascripts/datatables/plugins/api/average.js +7 -6
  30. data/app/assets/javascripts/datatables/plugins/api/sum.js +7 -6
  31. data/app/assets/javascripts/datatables/plugins/pagination/ellipses.js +160 -0
  32. data/app/assets/javascripts/datatables/plugins/pagination/extjs.js +137 -0
  33. data/app/assets/javascripts/datatables/plugins/pagination/four_button.js +110 -0
  34. data/app/assets/javascripts/datatables/plugins/pagination/full_numbers_no_ellipses.js +59 -0
  35. data/app/assets/javascripts/datatables/plugins/pagination/input.js +22 -19
  36. data/app/assets/javascripts/datatables/plugins/pagination/scrolling.js +130 -0
  37. data/app/assets/javascripts/datatables/plugins/pagination/select.js +97 -0
  38. data/app/assets/javascripts/datatables/plugins/pagination/simple_incremental_bootstrap.js +154 -0
  39. data/app/assets/javascripts/datatables/plugins/pagination/simple_numbers_no_ellipses.js +59 -0
  40. data/app/assets/javascripts/datatables/plugins/search/dataTables.alphabetSearch.js +440 -399
  41. data/app/assets/javascripts/datatables/plugins/sorting/enum.js +51 -0
  42. data/app/assets/javascripts/datatables/plugins/type-detection/date-dd-MMM-yyyy.js +63 -0
  43. data/app/assets/javascripts/datatables/plugins/type-detection/date-de.js +125 -0
  44. data/app/assets/javascripts/datatables/plugins/type-detection/date-eu.js +64 -0
  45. data/app/assets/javascripts/datatables/plugins/type-detection/date-euro.js +48 -0
  46. data/app/assets/javascripts/datatables/plugins/type-detection/date-uk.js +35 -12
  47. data/app/assets/javascripts/datatables/plugins/type-detection/datetime-moment.js +74 -0
  48. data/app/assets/javascripts/datatables/plugins/type-detection/datetime-us.js +86 -0
  49. data/app/assets/javascripts/datatables/plugins/type-detection/file-size.js +37 -13
  50. data/app/assets/javascripts/datatables/plugins/type-detection/ip-address.js +113 -11
  51. data/app/assets/javascripts/datatables/plugins/type-detection/numString.js +63 -0
  52. data/app/assets/javascripts/datatables/plugins/type-detection/percent.js +34 -0
  53. data/app/assets/javascripts/datatables/plugins/type-detection/time-elapsed-dhms.js +42 -0
  54. data/app/assets/javascripts/datatables/plugins/type-detection/time.js +56 -0
  55. data/app/assets/javascripts/datatables/plugins/type-detection/title-numeric.js +40 -0
  56. data/app/assets/javascripts/datatables/plugins/type-detection/title-string.js +36 -0
  57. data/app/assets/stylesheets/datatables/extensions/AutoFill/autoFill.dataTables.scss +10 -3
  58. data/app/assets/stylesheets/datatables/extensions/Buttons/buttons.bootstrap.scss +12 -3
  59. data/app/assets/stylesheets/datatables/extensions/Buttons/buttons.bootstrap4.scss +13 -6
  60. data/app/assets/stylesheets/datatables/extensions/Buttons/buttons.dataTables.scss +2 -0
  61. data/app/assets/stylesheets/datatables/extensions/Buttons/buttons.foundation.scss +5 -1
  62. data/app/assets/stylesheets/datatables/extensions/Buttons/buttons.jqueryui.scss +1 -0
  63. data/app/assets/stylesheets/datatables/extensions/Buttons/buttons.semanticui.scss +2 -1
  64. data/app/assets/stylesheets/datatables/extensions/Buttons/common.scss +10 -0
  65. data/app/assets/stylesheets/datatables/extensions/Buttons/mixins.scss +42 -30
  66. data/app/assets/stylesheets/datatables/extensions/FixedColumns/fixedColumns.bootstrap.scss +11 -7
  67. data/app/assets/stylesheets/datatables/extensions/FixedColumns/fixedColumns.foundation.scss +1 -0
  68. data/app/assets/stylesheets/datatables/extensions/KeyTable/keyTable.dataTables.scss +13 -5
  69. data/app/assets/stylesheets/datatables/extensions/RowGroup/rowGroup.dataTables.scss +20 -2
  70. data/app/assets/stylesheets/datatables/extensions/Scroller/scroller.dataTables.scss +15 -2
  71. data/lib/jquery-datatables/version.rb +1 -1
  72. metadata +26 -12
  73. data/app/assets/javascripts/datatables/dataTables.bootstrap2.js +0 -162
  74. data/app/assets/javascripts/datatables/extensions/ColReorder/colReorder.semanicui.js +0 -38
  75. data/app/assets/javascripts/datatables/extensions/FixedColumns/fixedColumns.semanicui.js +0 -38
  76. data/app/assets/javascripts/datatables/extensions/FixedHeader/fixedHeader.semanicui.js +0 -38
  77. data/app/assets/javascripts/datatables/extensions/KeyTable/keyTable.semanicui.js +0 -38
  78. data/app/assets/javascripts/datatables/extensions/RowReorder/rowReorder.semanicui.js +0 -38
  79. data/app/assets/stylesheets/datatables/dataTables.bootstrap2.scss +0 -178
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Plug-in offers the same functionality as `simple_numbers` pagination type
3
+ * (see `pagingType` option) but without ellipses.
4
+ *
5
+ * See [example](http://www.gyrocode.com/articles/jquery-datatables-pagination-without-ellipses) for demonstration.
6
+ *
7
+ * @name Simple Numbers - No Ellipses
8
+ * @summary Same pagination as 'simple_numbers' but without ellipses
9
+ * @author [Michael Ryvkin](http://www.gyrocode.com)
10
+ *
11
+ * @example
12
+ * $(document).ready(function() {
13
+ * $('#example').dataTable( {
14
+ * "pagingType": "simple_numbers_no_ellipses"
15
+ * } );
16
+ * } );
17
+ */
18
+
19
+ $.fn.DataTable.ext.pager.simple_numbers_no_ellipses = function(page, pages){
20
+ var numbers = [];
21
+ var buttons = $.fn.DataTable.ext.pager.numbers_length;
22
+ var half = Math.floor( buttons / 2 );
23
+
24
+ var _range = function ( len, start ){
25
+ var end;
26
+
27
+ if ( typeof start === "undefined" ){
28
+ start = 0;
29
+ end = len;
30
+
31
+ } else {
32
+ end = start;
33
+ start = len;
34
+ }
35
+
36
+ var out = [];
37
+ for ( var i = start ; i < end; i++ ){ out.push(i); }
38
+
39
+ return out;
40
+ };
41
+
42
+
43
+ if ( pages <= buttons ) {
44
+ numbers = _range( 0, pages );
45
+
46
+ } else if ( page <= half ) {
47
+ numbers = _range( 0, buttons);
48
+
49
+ } else if ( page >= pages - 1 - half ) {
50
+ numbers = _range( pages - buttons, pages );
51
+
52
+ } else {
53
+ numbers = _range( page - half, page + half + 1);
54
+ }
55
+
56
+ numbers.DT_el = 'span';
57
+
58
+ return [ 'previous', numbers, 'next' ];
59
+ };
@@ -1,399 +1,440 @@
1
- /*! AlphabetSearch for DataTables v1.2.4
2
- * 2014 SpryMedia Ltd - datatables.net/license
3
- * Gyrocode - MIT License
4
- */
5
-
6
- /**
7
- * @summary AlphabetSearch
8
- * @description Show an set of alphabet buttons alongside a table providing
9
- * search input options
10
- * @version 1.2.4
11
- * @file dataTables.alphabetSearch.js
12
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
13
- * @contact www.sprymedia.co.uk/contact
14
- * @copyright Copyright 2014 SpryMedia Ltd.
15
- * @author Gyrocode (www.gyrocode.com)
16
- * @contact www.gyrocode.com/contacts
17
- * @copyright Copyright (c) Gyrocode
18
- *
19
- * License MIT - http://datatables.net/license/mit
20
- *
21
- * For more detailed information please see:
22
- * https://www.gyrocode.com/projects/jquery-datatables-alphabetsearch/
23
- */
24
- (function(){
25
-
26
-
27
- // Search function
28
- $.fn.dataTable.Api.register( 'alphabetSearch()', function ( searchTerm ) {
29
- this.iterator( 'table', function ( context ) {
30
- context.alphabetSearch.letter = searchTerm;
31
- } );
32
-
33
- return this;
34
- } );
35
-
36
- // Recalculate the alphabet display for updated data
37
- $.fn.dataTable.Api.register( 'alphabetSearch.recalc()', function () {
38
- this.iterator( 'table', function ( context ) {
39
- draw(
40
- new $.fn.dataTable.Api( context ),
41
- $('div.alphabet', this.table().container()),
42
- context
43
- );
44
- } );
45
-
46
- return this;
47
- } );
48
-
49
-
50
- // Search plug-in
51
- $.fn.dataTable.ext.search.push( function ( context, searchData ) {
52
- // Ensure that table has alphabet search feature enabled
53
- if ( ! context.hasOwnProperty('alphabetSearch') ) {
54
- return true;
55
- }
56
-
57
- // Ensure that there is a search applied to this table before running it
58
- if ( ! context.alphabetSearch.letterSearch ) {
59
- return true;
60
- }
61
-
62
- var letter = searchData[context.alphabetSearch.column]
63
- .toString()
64
- .replace(/<.*?>/g, '')
65
- .charAt(0).toUpperCase();
66
-
67
-
68
- if(context.alphabetSearch.letterSearch !== '#'){
69
- if ( letter === context.alphabetSearch.letterSearch ) {
70
- return true;
71
- }
72
- } else {
73
- if(/\d/.test(letter)){
74
- return true;
75
- }
76
- }
77
-
78
- return false;
79
- } );
80
-
81
-
82
- // Order plug-in
83
- //
84
- // NOTES: If sorting by alphabetized column
85
- // there would be two calls to this method
86
- $.fn.dataTable.ext.order['alphabetSearch'] = function ( context, col )
87
- {
88
- var order_col = this.api().order()[0][0];
89
- var order_method = this.api().order()[0][1];
90
-
91
- // If sorting by column other than the one being alphabetized
92
- if(order_col !== context.alphabetSearch.column){
93
- context.alphabetSearch.pass = 0;
94
- }
95
-
96
- var data = this.api().column( col, { order: 'index' } ).data().map( function (value, index) {
97
- var letter = value.replace(/<.*?>/g, '').charAt(0).toUpperCase();
98
-
99
- // If sorting by alphabetized column
100
- return (order_col === context.alphabetSearch.column)
101
- ? (
102
- // If first pass
103
- ( !context.alphabetSearch.pass )
104
- // Ignore
105
- ?
106
- ''
107
- // Otherwise, if it's a second pass
108
- :
109
- (
110
- // If method is ascending sort
111
- ( order_method === 'asc' )
112
- // Return first letter
113
- ? letter
114
- : String.fromCharCode(65535 - letter.charCodeAt(0))
115
- )
116
- )
117
- // Otherwise, if sorting by column other than the one being alphabetized,
118
- // return first letter
119
- : letter;
120
- } );
121
-
122
- // If sorting by alphabetized column
123
- if(order_col === context.alphabetSearch.column){
124
- // If pass is not defined, set it to 0
125
- if(!context.alphabetSearchPass){ context.alphabetSearch.pass = 0; }
126
- // Increment pass counter and reset it to 0 if it's a second pass
127
- context.alphabetSearch.pass = (context.alphabetSearch.pass + 1) % 2;
128
- }
129
-
130
- return data;
131
- };
132
-
133
-
134
- // Private support methods
135
- function bin ( data ) {
136
- var letter, bins = {};
137
-
138
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
139
- letter = data[i]
140
- .toString()
141
- .replace(/<.*?>/g, '')
142
- .charAt(0).toUpperCase();
143
-
144
- if(/\d/.test(letter)){ letter = '#'; }
145
-
146
- if ( bins[letter] ) {
147
- bins[letter]++;
148
- }
149
- else {
150
- bins[letter] = 1;
151
- }
152
- }
153
-
154
- return bins;
155
- }
156
-
157
- function draw ( table, alphabet, context )
158
- {
159
- alphabet.empty();
160
-
161
- if(context.oLanguage.alphabetSearch.infoDisplay !== ''){
162
- $('<span class="alphabet-info-display"></span>')
163
- .html(context.oLanguage.alphabetSearch.infoDisplay)
164
- .appendTo( alphabet );
165
- }
166
-
167
- var columnData = table.column(context.alphabetSearch.column, { search: 'applied' } ).data();
168
- var bins = bin( columnData );
169
-
170
- var alphabetList = $('<ul/>');
171
-
172
- $('<a/>')
173
- .attr( 'href', 'javascript:;' )
174
- .data( 'letter', '' )
175
- .data( 'match-count', columnData.length )
176
- .addClass(
177
- ((!context.alphabetSearch.letter) ? 'active' : '')
178
- )
179
- .html( '<span>' + context.oLanguage.alphabetSearch.infoAll + '</span>' )
180
- .wrap( '<li/>' )
181
- .parent()
182
- .appendTo( alphabetList );
183
-
184
- for ( var i=0 ; i<context.oLanguage.alphabetSearch.alphabet.length ; i++ ) {
185
- var letter = context.oLanguage.alphabetSearch.alphabet[i];
186
-
187
- $('<a/>')
188
- .attr( 'href', 'javascript:;' )
189
- .data( 'letter', letter )
190
- .data( 'match-count', bins[letter] || 0 )
191
- .addClass(
192
- (! bins[letter] ? 'empty' : '')
193
- + ((context.alphabetSearch.letter === letter) ? ' active' : '')
194
- )
195
- .html( '<span>' + letter + '</span>' )
196
- .wrap( '<li/>' )
197
- .parent()
198
- .appendTo( alphabetList );
199
- }
200
-
201
- alphabetList.appendTo( alphabet );
202
-
203
- $('<div class="alphabet-info"></div>')
204
- .appendTo( alphabet );
205
-
206
-
207
- // Perform second rendering
208
- // needed to filter search results by letter
209
- // NOTE: Future optimization is needed to avoid rendering twice
210
- // when no search is performed
211
-
212
- // If letter is selected
213
- if(context.alphabetSearch.letter){
214
- // Apply filtering by letter
215
- context.alphabetSearch.letterSearch = context.alphabetSearch.letter;
216
-
217
- // Redraw table
218
- table.draw();
219
-
220
- // Remove filtering by letter
221
- context.alphabetSearch.letterSearch = '';
222
- }
223
-
224
- // Handle search event here only once
225
- // when alphabet panel has been drawn
226
- // because we are performing two-step rendering
227
- // that could trigger search hanlder when not needed
228
- table.one('search', function (e, context) {
229
- var api = new $.fn.dataTable.Api( context );
230
-
231
- // Redraw alphabet panel
232
- api.alphabetSearch.recalc();
233
- });
234
- }
235
-
236
-
237
- $.fn.dataTable.AlphabetSearch = function ( context ) {
238
- var table = new $.fn.dataTable.Api( context );
239
- var alphabet = $('<div class="alphabet"/>');
240
-
241
- // Language
242
- context.oLanguage.alphabetSearch =
243
- $.extend(
244
- {
245
- 'alphabet': '#ABCDEFGHIJKLMNOPQRSTUVWXYZ',
246
- 'infoDisplay': 'Display:',
247
- 'infoAll': 'All'
248
- },
249
- ((context.oLanguage.alphabetSearch)
250
- ? context.oLanguage.alphabetSearch
251
- : {}
252
- )
253
- );
254
- // Convert alphabet to uppercase
255
- context.oLanguage.alphabetSearch.alphabet.toUpperCase();
256
-
257
- context.alphabetSearch =
258
- $.extend(
259
- {
260
- column: 0
261
- },
262
- $.isPlainObject(context.oInit.alphabetSearch) ? context.oInit.alphabetSearch : {},
263
- {
264
- letter: '',
265
- letterSearch: '',
266
- pass: 0
267
- }
268
- );
269
-
270
- // Set required "orderDataType" ("sSortDataType") for a column
271
- if(context.alphabetSearch.column >= 0 && context.alphabetSearch.column < context.aoColumns.length){
272
- context.aoColumns[context.alphabetSearch.column].sSortDataType = 'alphabetSearch';
273
- }
274
-
275
- // Add column containing names to a list of columns
276
- // where ordering will be always applied to the table
277
- if( context.hasOwnProperty('aaSortingFixed')
278
- && typeof context.aaSortingFixed === 'object' )
279
- {
280
- if( $.isArray(context.aaSortingFixed) ){
281
- if( context.aaSortingFixed.length && !$.isArray( context.aaSortingFixed[0] ) ) {
282
- // 1D array
283
- context.aaSortingFixed = [[context.alphabetSearch.column, 'asc'], context.aaSortingFixed];
284
-
285
- } else {
286
- // 2D array
287
- context.aaSortingFixed.unshift([context.alphabetSearch.column, 'asc']);
288
- }
289
- } else {
290
- if( !context.aaSortingFixed.hasOwnProperty('pre') ){
291
- context.aaSortingFixed.pre = [];
292
- }
293
-
294
- if( context.aaSortingFixed.pre.length && !$.isArray( context.aaSortingFixed.pre[0] ) ) {
295
- // 1D array
296
- context.aaSortingFixed.pre = [[context.alphabetSearch.column, 'asc'], context.aaSortingFixed.pre];
297
-
298
- } else {
299
- // 2D array
300
- context.aaSortingFixed.pre.unshift([context.alphabetSearch.column, 'asc']);
301
- }
302
- }
303
-
304
- } else {
305
- context.aaSortingFixed = [context.alphabetSearch.column, 'asc'];
306
- }
307
-
308
-
309
- draw( table, alphabet, context );
310
-
311
-
312
- // Trigger a search
313
- alphabet.on( 'click', 'a', function (e) {
314
- // Prevent default behavior
315
- e.preventDefault();
316
-
317
- alphabet.find( '.active' ).removeClass( 'active' );
318
- $(this).addClass( 'active' );
319
-
320
- table
321
- .alphabetSearch( $(this).data('letter') )
322
- .draw();
323
- } );
324
-
325
- // Mouse events to show helper information
326
- alphabet
327
- .on( 'mouseenter', 'a', function () {
328
- var $el = $(this);
329
- var el_pos = $el.position();
330
-
331
- var $alphabet_info = $('.alphabet-info', alphabet);
332
-
333
- $alphabet_info.html( $el.data('match-count') );
334
-
335
- // Display helper centered horizontally under the letter
336
- $alphabet_info
337
- .css( {
338
- opacity: 1,
339
- left: el_pos.left + Math.round(($el.outerWidth() - $alphabet_info.outerWidth())/2),
340
- top: $(this).position().top + $el.outerHeight()
341
- } );
342
- } )
343
- .on( 'mouseleave', 'a', function () {
344
- alphabet
345
- .find('div.alphabet-info')
346
- .css('opacity', 0);
347
- } );
348
-
349
- table.on('draw', function (e, context) {
350
- var api = new $.fn.dataTable.Api( context );
351
-
352
- // Total number of column nodes
353
- var col_total = api.columns().nodes().length;
354
-
355
- var rows = api.rows({ page: 'current' }).nodes();
356
- var group_last = null;
357
-
358
- api.column(context.alphabetSearch.column, { page: 'current' }).data().each(function (name, index){
359
- var group = name.replace(/<.*?>/g, '').charAt(0).toUpperCase();
360
-
361
- if (group_last !== group) {
362
- $(rows).eq(index).before(
363
- '<tr class="alphabet-group"><td colspan="' + col_total + '">' + group + '</td></tr>'
364
- );
365
-
366
- group_last = group;
367
- }
368
- });
369
-
370
- // If there are no rows found and letter is selected
371
- if(!rows.length && context.alphabetSearch){
372
- var letter = context.alphabetSearch.letter;
373
-
374
- $(api.table().body()).prepend(
375
- '<tr class="alphabet-group"><td colspan="' + col_total + '">' + letter + '</td></tr>'
376
- );
377
- }
378
- });
379
-
380
- // API method to get the alphabet container node
381
- this.node = function () {
382
- return alphabet;
383
- };
384
- };
385
-
386
- $.fn.DataTable.AlphabetSearch = $.fn.dataTable.AlphabetSearch;
387
-
388
-
389
- // Register a search plug-in
390
- $.fn.dataTable.ext.feature.push( {
391
- fnInit: function ( settings ) {
392
- var search = new $.fn.dataTable.AlphabetSearch( settings );
393
- return search.node();
394
- },
395
- cFeature: 'A'
396
- } );
397
-
398
-
399
- }());
1
+ /*! AlphabetSearch for DataTables v1.2.7
2
+ * 2014 SpryMedia Ltd - datatables.net/license
3
+ * Gyrocode LLC - MIT License
4
+ */
5
+
6
+ /**
7
+ * @summary AlphabetSearch
8
+ * @description Show an set of alphabet buttons alongside a table providing
9
+ * search input options
10
+ * @version 1.2.7
11
+ * @file dataTables.alphabetSearch.js
12
+ * @author SpryMedia Ltd (www.sprymedia.co.uk)
13
+ * @contact www.sprymedia.co.uk/contact
14
+ * @copyright Copyright 2014 SpryMedia Ltd.
15
+ * @author Gyrocode LLC (www.gyrocode.com)
16
+ * @contact www.gyrocode.com/contacts
17
+ * @copyright Copyright (c) Gyrocode LLC
18
+ *
19
+ * License MIT - http://datatables.net/license/mit
20
+ *
21
+ * For more detailed information please see:
22
+ * https://www.gyrocode.com/projects/jquery-datatables-alphabetsearch/
23
+ */
24
+ (function($){
25
+
26
+
27
+ // Search function
28
+ $.fn.dataTable.Api.register( 'alphabetSearch()', function ( searchTerm ) {
29
+ this.iterator( 'table', function ( context ) {
30
+ context.alphabetSearch.letter = searchTerm;
31
+ } );
32
+
33
+ return this;
34
+ } );
35
+
36
+ // Recalculate the alphabet display for updated data
37
+ $.fn.dataTable.Api.register( 'alphabetSearch.recalc()', function () {
38
+ this.iterator( 'table', function ( context ) {
39
+ draw(
40
+ new $.fn.dataTable.Api( context ),
41
+ $('div.alphabet', this.table().container()),
42
+ context
43
+ );
44
+ } );
45
+
46
+ return this;
47
+ } );
48
+
49
+
50
+ // Search plug-in
51
+ $.fn.dataTable.ext.search.push( function ( context, searchData ) {
52
+ // Ensure that table has alphabet search feature enabled
53
+ if ( ! context.hasOwnProperty('alphabetSearch') ) {
54
+ return true;
55
+ }
56
+
57
+ // Ensure that there is a search applied to this table before running it
58
+ if ( ! context.alphabetSearch.letterSearch ) {
59
+ return true;
60
+ }
61
+
62
+ var letter = searchData[context.alphabetSearch.column]
63
+ .toString()
64
+ .replace(/<.*?>/g, '')
65
+ .charAt(0).toUpperCase();
66
+
67
+
68
+ if(context.alphabetSearch.letterSearch !== '#'){
69
+ if ( letter === context.alphabetSearch.letterSearch ) {
70
+ return true;
71
+ }
72
+ } else {
73
+ if(/\d/.test(letter)){
74
+ return true;
75
+ }
76
+ }
77
+
78
+ return false;
79
+ } );
80
+
81
+
82
+ // Order plug-in
83
+ //
84
+ // NOTE: If sorting by alphabetized column there would be two calls to this method.
85
+ $.fn.dataTable.ext.order['alphabetSearch'] = function ( context, col )
86
+ {
87
+ var orderColumn = this.api().order()[0][0];
88
+ var orderMethod = this.api().order()[0][1];
89
+
90
+ // If sorting by column other than the one being alphabetized
91
+ if(orderColumn !== context.alphabetSearch.column){
92
+ context.alphabetSearch.pass = 0;
93
+ }
94
+
95
+ var data = this.api().column( col, { order: 'index' } ).data().map( function (value, index) {
96
+ var text = value.toString().replace(/<.*?>/g, '');
97
+ var letter = text.charAt(0).toUpperCase();
98
+
99
+ // If sorting by alphabetized column
100
+ if(orderColumn === context.alphabetSearch.column) {
101
+
102
+ // If this is a first pass
103
+ if(context.alphabetSearch.pass === 0){
104
+ // Ignore
105
+ return '';
106
+
107
+ // Otherwise, if this is a second pass
108
+ } else {
109
+ // When sorting in ascending order
110
+ if (orderMethod === 'asc'){
111
+ // Return actual content
112
+ return text;
113
+
114
+ // Otherwise, when sorting in descending order
115
+ } else {
116
+ var textReversed = '';
117
+
118
+ // If letter search is applied to the table
119
+ if(context.alphabetSearch.letterSearch) {
120
+ // Reverse (take characters from the oposite side of the character table) all characters.
121
+ //
122
+ // NOTE: Allows to sort alphabetized column in descending order
123
+ // by returning reversed text
124
+ // to compensate fixed ascending order applied during initialization.
125
+ //
126
+ // TODO: Better solution would be to find a way to manipulate first element
127
+ // of the context.aaSortingFixed array before each sort.
128
+
129
+ for(var i = 0; i < text.length; i++){
130
+ textReversed += String.fromCharCode(65535 - text.charCodeAt(i));
131
+ }
132
+
133
+ // Otherwise, if letter search is not applied to the table
134
+ } else {
135
+ // Reverse (take characters from the oposite side of the character table) first character.
136
+ //
137
+ // NOTE: Allows to sort group of rows by first letter in descending order
138
+ // but preserve ascending order within each group.
139
+
140
+ for(var i = 0; i < text.length; i++){
141
+ textReversed += (i) ? text.charAt(i) : String.fromCharCode(65535 - text.charCodeAt(i));
142
+ }
143
+ }
144
+
145
+ return textReversed;
146
+ }
147
+ }
148
+
149
+ // Otherwise, if sorting by column other than the one being alphabetized,
150
+ } else {
151
+ // Return first letter only
152
+ return letter;
153
+ }
154
+ } );
155
+
156
+ // If sorting by alphabetized column
157
+ if(orderColumn === context.alphabetSearch.column){
158
+ // If pass is not defined, set it to 0
159
+ if(!context.alphabetSearch.pass){ context.alphabetSearch.pass = 0; }
160
+ // Increment pass counter and reset it to 0 if it's a second pass
161
+ context.alphabetSearch.pass = (context.alphabetSearch.pass + 1) % 2;
162
+ }
163
+
164
+ return data;
165
+ };
166
+
167
+
168
+ // Private support methods
169
+ function bin ( data ) {
170
+ var letter, bins = {};
171
+
172
+ for ( var i=0, ien=data.length ; i<ien ; i++ ) {
173
+ letter = data[i]
174
+ .toString()
175
+ .replace(/<.*?>/g, '')
176
+ .charAt(0).toUpperCase();
177
+
178
+ if(/\d/.test(letter)){ letter = '#'; }
179
+
180
+ if ( bins[letter] ) {
181
+ bins[letter]++;
182
+ }
183
+ else {
184
+ bins[letter] = 1;
185
+ }
186
+ }
187
+
188
+ return bins;
189
+ }
190
+
191
+ function draw ( table, alphabet, context )
192
+ {
193
+ alphabet.empty();
194
+
195
+ if(context.oLanguage.alphabetSearch.infoDisplay !== ''){
196
+ $('<span class="alphabet-info-display"></span>')
197
+ .html(context.oLanguage.alphabetSearch.infoDisplay)
198
+ .appendTo( alphabet );
199
+ }
200
+
201
+ var columnData = table.column(context.alphabetSearch.column, { search: 'applied' } ).data();
202
+ var bins = bin( columnData );
203
+
204
+ var alphabetList = $('<ul/>');
205
+
206
+ $('<a/>')
207
+ .attr( 'href', 'javascript:;' )
208
+ .data( 'letter', '' )
209
+ .data( 'match-count', columnData.length )
210
+ .addClass(
211
+ ((!context.alphabetSearch.letter) ? 'active' : '')
212
+ )
213
+ .html( '<span>' + context.oLanguage.alphabetSearch.infoAll + '</span>' )
214
+ .wrap( '<li/>' )
215
+ .parent()
216
+ .appendTo( alphabetList );
217
+
218
+ for ( var i=0 ; i<context.oLanguage.alphabetSearch.alphabet.length ; i++ ) {
219
+ var letter = context.oLanguage.alphabetSearch.alphabet[i];
220
+
221
+ $('<a/>')
222
+ .attr( 'href', 'javascript:;' )
223
+ .data( 'letter', letter )
224
+ .data( 'match-count', bins[letter] || 0 )
225
+ .addClass(
226
+ (! bins[letter] ? 'empty' : '')
227
+ + ((context.alphabetSearch.letter === letter) ? ' active' : '')
228
+ )
229
+ .html( '<span>' + letter + '</span>' )
230
+ .wrap( '<li/>' )
231
+ .parent()
232
+ .appendTo( alphabetList );
233
+ }
234
+
235
+ alphabetList.appendTo( alphabet );
236
+
237
+ $('<div class="alphabet-info"></div>')
238
+ .appendTo( alphabet );
239
+
240
+
241
+ // Perform second rendering
242
+ // needed to filter search results by letter
243
+ // NOTE: Future optimization is needed to avoid rendering twice
244
+ // when no search is performed
245
+
246
+ // If letter is selected
247
+ if(context.alphabetSearch.letter){
248
+ // Apply filtering by letter
249
+ context.alphabetSearch.letterSearch = context.alphabetSearch.letter;
250
+
251
+ // Redraw table
252
+ table.draw();
253
+
254
+ // Remove filtering by letter
255
+ context.alphabetSearch.letterSearch = '';
256
+ }
257
+
258
+ // Handle search event here only once
259
+ // when alphabet panel has been drawn
260
+ // because we are performing two-step rendering
261
+ // that could trigger search hanlder when not needed
262
+ table.one('search.dt.dtAlphabetSearch', function (e, context) {
263
+ var api = new $.fn.dataTable.Api( context );
264
+
265
+ // Redraw alphabet panel
266
+ api.alphabetSearch.recalc();
267
+ });
268
+ }
269
+
270
+
271
+ $.fn.dataTable.AlphabetSearch = function ( context ) {
272
+ var table = new $.fn.dataTable.Api( context );
273
+ var alphabet = $('<div class="alphabet"/>');
274
+
275
+ // Language
276
+ context.oLanguage.alphabetSearch =
277
+ $.extend(
278
+ {
279
+ 'alphabet': '#ABCDEFGHIJKLMNOPQRSTUVWXYZ',
280
+ 'infoDisplay': 'Display:',
281
+ 'infoAll': 'All'
282
+ },
283
+ ((context.oLanguage.alphabetSearch)
284
+ ? context.oLanguage.alphabetSearch
285
+ : {}
286
+ )
287
+ );
288
+ // Convert alphabet to uppercase
289
+ context.oLanguage.alphabetSearch.alphabet.toUpperCase();
290
+
291
+ context.alphabetSearch =
292
+ $.extend(
293
+ {
294
+ column: 0
295
+ },
296
+ $.isPlainObject(context.oInit.alphabetSearch) ? context.oInit.alphabetSearch : {},
297
+ {
298
+ letter: '',
299
+ letterSearch: '',
300
+ pass: 0
301
+ }
302
+ );
303
+
304
+ // Set required "orderDataType" ("sSortDataType") for a column
305
+ if(context.alphabetSearch.column >= 0 && context.alphabetSearch.column < context.aoColumns.length){
306
+ context.aoColumns[context.alphabetSearch.column].sSortDataType = 'alphabetSearch';
307
+ }
308
+
309
+ // Add column containing names to a list of columns
310
+ // where ordering will be always applied to the table
311
+ if( context.hasOwnProperty('aaSortingFixed')
312
+ && typeof context.aaSortingFixed === 'object' )
313
+ {
314
+ if( $.isArray(context.aaSortingFixed) ){
315
+ if( context.aaSortingFixed.length && !$.isArray( context.aaSortingFixed[0] ) ) {
316
+ // 1D array
317
+ context.aaSortingFixed = [[context.alphabetSearch.column, 'asc'], context.aaSortingFixed];
318
+
319
+ } else {
320
+ // 2D array
321
+ context.aaSortingFixed.unshift([context.alphabetSearch.column, 'asc']);
322
+ }
323
+ } else {
324
+ if( !context.aaSortingFixed.hasOwnProperty('pre') ){
325
+ context.aaSortingFixed.pre = [];
326
+ }
327
+
328
+ if( context.aaSortingFixed.pre.length && !$.isArray( context.aaSortingFixed.pre[0] ) ) {
329
+ // 1D array
330
+ context.aaSortingFixed.pre = [[context.alphabetSearch.column, 'asc'], context.aaSortingFixed.pre];
331
+
332
+ } else {
333
+ // 2D array
334
+ context.aaSortingFixed.pre.unshift([context.alphabetSearch.column, 'asc']);
335
+ }
336
+ }
337
+
338
+ } else {
339
+ context.aaSortingFixed = [context.alphabetSearch.column, 'asc'];
340
+ }
341
+
342
+
343
+ draw( table, alphabet, context );
344
+
345
+
346
+ // Trigger a search
347
+ alphabet.on( 'click', 'a', function (e) {
348
+ // Prevent default behavior
349
+ e.preventDefault();
350
+
351
+ alphabet.find( '.active' ).removeClass( 'active' );
352
+ $(this).addClass( 'active' );
353
+
354
+ table
355
+ .alphabetSearch( $(this).data('letter') )
356
+ .draw();
357
+ } );
358
+
359
+ // Mouse events to show helper information
360
+ alphabet
361
+ .on( 'mouseenter', 'a', function () {
362
+ var $el = $(this);
363
+ var el_pos = $el.position();
364
+
365
+ var $alphabet_info = $('.alphabet-info', alphabet);
366
+
367
+ $alphabet_info.html( $el.data('match-count') );
368
+
369
+ // Display helper centered horizontally under the letter
370
+ $alphabet_info
371
+ .css( {
372
+ opacity: 1,
373
+ left: el_pos.left + Math.round(($el.outerWidth() - $alphabet_info.outerWidth())/2),
374
+ top: $(this).position().top + $el.outerHeight()
375
+ } );
376
+ } )
377
+ .on( 'mouseleave', 'a', function () {
378
+ alphabet
379
+ .find('div.alphabet-info')
380
+ .css('opacity', 0);
381
+ } );
382
+
383
+ // Handle table draw event
384
+ table.on('draw.dt.dtAlphabetSearch', function (e, context) {
385
+ var api = new $.fn.dataTable.Api( context );
386
+
387
+ // Total number of column nodes
388
+ var col_total = api.columns().nodes().length;
389
+
390
+ var rows = api.rows({ page: 'current' }).nodes();
391
+ var group_last = null;
392
+
393
+ api.column(context.alphabetSearch.column, { page: 'current' }).data().each(function (name, index){
394
+ var group = name.toString().replace(/<.*?>/g, '').charAt(0).toUpperCase();
395
+
396
+ if (group_last !== group) {
397
+ $(rows).eq(index).before(
398
+ '<tr class="alphabet-group"><td colspan="' + col_total + '">' + group + '</td></tr>'
399
+ );
400
+
401
+ group_last = group;
402
+ }
403
+ });
404
+
405
+ // If there are no rows found and letter is selected
406
+ if(!rows.length && context.alphabetSearch){
407
+ var letter = context.alphabetSearch.letter;
408
+
409
+ $(api.table().body()).prepend(
410
+ '<tr class="alphabet-group"><td colspan="' + col_total + '">' + letter + '</td></tr>'
411
+ );
412
+ }
413
+ });
414
+
415
+ // Handle table destroy event
416
+ table.on('destroy.dt.dtAlphabetSearch', function(e, context){
417
+ var api = new $.fn.dataTable.Api( context );
418
+ api.off('.dtAlphabetSearch');
419
+ });
420
+
421
+ // API method to get the alphabet container node
422
+ this.node = function () {
423
+ return alphabet;
424
+ };
425
+ };
426
+
427
+ $.fn.DataTable.AlphabetSearch = $.fn.dataTable.AlphabetSearch;
428
+
429
+
430
+ // Register a search plug-in
431
+ $.fn.dataTable.ext.feature.push( {
432
+ fnInit: function ( settings ) {
433
+ var search = new $.fn.dataTable.AlphabetSearch( settings );
434
+ return search.node();
435
+ },
436
+ cFeature: 'A'
437
+ } );
438
+
439
+
440
+ }(jQuery));