effective_datatables 1.0.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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +581 -0
  4. data/Rakefile +23 -0
  5. data/app/assets/images/effective_datatables/copy_csv_xls_pdf.swf +0 -0
  6. data/app/assets/javascripts/effective_datatables.bootstrap2.js +11 -0
  7. data/app/assets/javascripts/effective_datatables.js +12 -0
  8. data/app/assets/javascripts/effective_datatables/initialize.js.coffee +65 -0
  9. data/app/assets/javascripts/vendor/jquery.dataTables.columnFilter.js +829 -0
  10. data/app/assets/stylesheets/effective_datatables.bootstrap2.css.scss +8 -0
  11. data/app/assets/stylesheets/effective_datatables.css.scss +8 -0
  12. data/app/assets/stylesheets/effective_datatables/_overrides.scss +72 -0
  13. data/app/controllers/effective/datatables_controller.rb +36 -0
  14. data/app/helpers/effective_datatables_helper.rb +74 -0
  15. data/app/models/effective/access_denied.rb +17 -0
  16. data/app/models/effective/active_record_datatable_tool.rb +87 -0
  17. data/app/models/effective/array_datatable_tool.rb +66 -0
  18. data/app/models/effective/datatable.rb +352 -0
  19. data/app/views/effective/datatables/_datatable.html.haml +11 -0
  20. data/app/views/effective/datatables/_spacer_template.html +1 -0
  21. data/config/routes.rb +11 -0
  22. data/lib/effective_datatables.rb +32 -0
  23. data/lib/effective_datatables/engine.rb +20 -0
  24. data/lib/effective_datatables/version.rb +3 -0
  25. data/lib/generators/effective_datatables/install_generator.rb +17 -0
  26. data/lib/generators/templates/README +1 -0
  27. data/lib/generators/templates/effective_datatables.rb +23 -0
  28. data/lib/tasks/effective_datatables_tasks.rake +17 -0
  29. data/spec/dummy/README.rdoc +261 -0
  30. data/spec/dummy/Rakefile +7 -0
  31. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  32. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  33. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  34. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  35. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  36. data/spec/dummy/config.ru +4 -0
  37. data/spec/dummy/config/application.rb +59 -0
  38. data/spec/dummy/config/boot.rb +10 -0
  39. data/spec/dummy/config/database.yml +25 -0
  40. data/spec/dummy/config/environment.rb +5 -0
  41. data/spec/dummy/config/environments/development.rb +37 -0
  42. data/spec/dummy/config/environments/production.rb +67 -0
  43. data/spec/dummy/config/environments/test.rb +37 -0
  44. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  45. data/spec/dummy/config/initializers/inflections.rb +15 -0
  46. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  47. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  48. data/spec/dummy/config/initializers/session_store.rb +8 -0
  49. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  50. data/spec/dummy/config/locales/en.yml +5 -0
  51. data/spec/dummy/config/routes.rb +58 -0
  52. data/spec/dummy/db/development.sqlite3 +0 -0
  53. data/spec/dummy/db/schema.rb +16 -0
  54. data/spec/dummy/db/test.sqlite3 +0 -0
  55. data/spec/dummy/log/development.log +17 -0
  56. data/spec/dummy/log/test.log +1 -0
  57. data/spec/dummy/public/404.html +26 -0
  58. data/spec/dummy/public/422.html +26 -0
  59. data/spec/dummy/public/500.html +25 -0
  60. data/spec/dummy/public/favicon.ico +0 -0
  61. data/spec/dummy/script/rails +6 -0
  62. data/spec/effective_datatables_spec.rb +7 -0
  63. data/spec/spec_helper.rb +34 -0
  64. data/spec/support/factories.rb +1 -0
  65. metadata +217 -0
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ # Our tasks
9
+ load 'lib/tasks/effective_datatables_tasks.rake'
10
+
11
+ # Testing tasks
12
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
13
+ load 'rails/tasks/engine.rake'
14
+
15
+ Bundler::GemHelper.install_tasks
16
+
17
+ require 'rspec/core'
18
+ require 'rspec/core/rake_task'
19
+
20
+ desc "Run all specs in spec directory (excluding plugin specs)"
21
+ RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
22
+
23
+ task :default => :spec
@@ -0,0 +1,11 @@
1
+ //= require dataTables/jquery.dataTables
2
+ //= require dataTables/bootstrap/2/jquery.dataTables.bootstrap
3
+ //= require dataTables/extras/dataTables.tableTools
4
+ //= require dataTables/extras/dataTables.colVis
5
+ //= require vendor/jquery.dataTables.columnFilter
6
+
7
+ //= require_tree ./effective_datatables
8
+
9
+ $.extend( $.fn.dataTable.defaults, {
10
+ 'dom': "<'row'<'span4'l><'span4'T><'span4'f>r>t<'row'<'span6'i><'span6'p>>"
11
+ });
@@ -0,0 +1,12 @@
1
+ //= require dataTables/jquery.dataTables
2
+ //= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
3
+ //= require dataTables/extras/dataTables.tableTools
4
+ //= require dataTables/extras/dataTables.colVis
5
+ //= require vendor/jquery.dataTables.columnFilter
6
+
7
+ //= require_tree ./effective_datatables
8
+
9
+ $.extend( $.fn.dataTable.defaults, {
10
+ 'dom': "<'row'<'col-xs-4'l><'col-xs-4'T><'col-xs-4'fC>r>t<'row'<'col-xs-6'i><'col-xs-6'p>>"
11
+ });
12
+
@@ -0,0 +1,65 @@
1
+ initializeDataTables = ->
2
+ $('table[data-effective-datatables-table]').each ->
3
+ unless $.fn.DataTable.fnIsDataTable(this)
4
+ datatable = $(this)
5
+
6
+ init_options =
7
+ bServerSide: true
8
+ bProcessing: true
9
+ bSaveState: true
10
+ bAutoWidth: false
11
+ deferRender: true
12
+ order: datatable.data('default-order')
13
+ sAjaxSource: datatable.data('source')
14
+ pagingType: 'simple_numbers'
15
+ lengthMenu: [[10, 25, 50, 100, 250, 1000, -1], [10, 25, 50, 100, 250, 1000, 'All']]
16
+ fnServerParams: (aoData, a, b) ->
17
+ table = this.DataTable()
18
+ table.columns().flatten().each (index) ->
19
+ aoData.push({'name': "sVisible_#{index}", 'value': table.column(index).visible()})
20
+ aoColumnDefs:
21
+ [
22
+ {
23
+ bSortable: false,
24
+ aTargets: datatable.data('non-sortable')
25
+ },
26
+ {
27
+ bVisible: false,
28
+ aTargets: datatable.data('non-visible')
29
+ }
30
+ ]
31
+ aoColumns: datatable.data('widths')
32
+ oTableTools:
33
+ sSwfPath: '/assets/effective_datatables/copy_csv_xls_pdf.swf',
34
+ aButtons: ['copy', 'csv', 'pdf', 'print']
35
+ colVis:
36
+ showAll: 'Show all'
37
+ restore: 'Restore default visible'
38
+ activate: 'mouseover'
39
+ fnStateChange: (iCol, bVisible) ->
40
+ table = $(this.dom.button).closest('.dataTables_wrapper').children('table').first().DataTable()
41
+ table.draw()
42
+
43
+ simple = datatable.data('effective-datatables-table') == 'simple'
44
+ filter = datatable.data('filter')
45
+
46
+ if simple
47
+ init_options['lengthMenu'] = [-1] # Show all results
48
+ init_options['dom'] = "<'row'r>t" # Just show the table
49
+
50
+ # Actually initialize it
51
+ datatable = datatable.dataTable(init_options)
52
+
53
+ if filter
54
+ datatable.columnFilter
55
+ sPlaceHolder: 'head:after'
56
+ aoColumns : datatable.data('filter')
57
+ bUseColVis: true
58
+
59
+ $('.dataTables_filter').each ->
60
+ $(this).html("<button class='btn-reset-filters ColVis_Button' data-effective-datatables-reset-filters='true'><span>Reset Filters</span></button>")
61
+
62
+ $ -> initializeDataTables()
63
+ $(document).on 'page:change', -> initializeDataTables()
64
+
65
+ $(document).on 'click', '[data-effective-datatables-reset-filters]', (event) -> window.location.reload()
@@ -0,0 +1,829 @@
1
+ /*
2
+ * http://code.google.com/p/jquery-datatables-column-filter/source/browse/#svn%2Ftrunk%2Fmedia%2Fjs
3
+ * File: jquery.dataTables.columnFilter.js
4
+ * Version: 1.5.6.
5
+ * Author: Jovan Popovic
6
+ *
7
+ * Copyright 2011-2014 Jovan Popovic, all rights reserved.
8
+ *
9
+ * This source file is free software, under either the GPL v2 license or a
10
+ * BSD style license, as supplied with this software.
11
+ *
12
+ * This source file is distributed in the hope that it will be useful, but
13
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14
+ * or FITNESS FOR A PARTICULAR PURPOSE.
15
+ *
16
+ * Parameters:"
17
+ * @sPlaceHolder String Place where inline filtering function should be placed ("tfoot", "thead:before", "thead:after"). Default is "tfoot"
18
+ * @sRangeSeparator String Separator that will be used when range values are sent to the server-side. Default value is "~".
19
+ * @sRangeFormat string Default format of the From ... to ... range inputs. Default is From {from} to {to}
20
+ * @aoColumns Array Array of the filter settings that will be applied on the columns
21
+ */
22
+ (function ($) {
23
+
24
+
25
+ $.fn.columnFilter = function (options) {
26
+
27
+ var asInitVals, i, label, th;
28
+
29
+ //var sTableId = "table";
30
+ var sRangeFormat = "From {from} to {to}";
31
+ //Array of the functions that will override sSearch_ parameters
32
+ var afnSearch_ = new Array();
33
+ var aiCustomSearch_Indexes = new Array();
34
+
35
+ var oFunctionTimeout = null;
36
+
37
+ var filterTimerId = null;
38
+ var filterDelay = 250;
39
+
40
+ var fnOnFiltered = function () { };
41
+
42
+ function _fnGetColumnValues(oSettings, iColumn, bUnique, bFiltered, bIgnoreEmpty) {
43
+ ///<summary>
44
+ ///Return values in the column
45
+ ///</summary>
46
+ ///<param name="oSettings" type="Object">DataTables settings</param>
47
+ ///<param name="iColumn" type="int">Id of the column</param>
48
+ ///<param name="bUnique" type="bool">Return only distinct values</param>
49
+ ///<param name="bFiltered" type="bool">Return values only from the filtered rows</param>
50
+ ///<param name="bIgnoreEmpty" type="bool">Ignore empty cells</param>
51
+
52
+ // check that we have a column id
53
+ if (typeof iColumn == "undefined") return new Array();
54
+
55
+ // by default we only wany unique data
56
+ if (typeof bUnique == "undefined") bUnique = true;
57
+
58
+ // by default we do want to only look at filtered data
59
+ if (typeof bFiltered == "undefined") bFiltered = true;
60
+
61
+ // by default we do not wany to include empty values
62
+ if (typeof bIgnoreEmpty == "undefined") bIgnoreEmpty = true;
63
+
64
+ // list of rows which we're going to loop through
65
+ var aiRows;
66
+
67
+ // use only filtered rows
68
+ if (bFiltered == true) aiRows = oSettings.aiDisplay;
69
+ // use all rows
70
+ else aiRows = oSettings.aiDisplayMaster; // all row numbers
71
+
72
+ // set up data array
73
+ var asResultData = new Array();
74
+
75
+ for (var i = 0, c = aiRows.length; i < c; i++) {
76
+ var iRow = aiRows[i];
77
+ var aData = oTable.fnGetData(iRow);
78
+ var sValue = aData[iColumn];
79
+
80
+ // ignore empty values?
81
+ if (bIgnoreEmpty == true && sValue.length == 0) continue;
82
+
83
+ // ignore unique values?
84
+ else if (bUnique == true && jQuery.inArray(sValue, asResultData) > -1) continue;
85
+
86
+ // else push the value onto the result data array
87
+ else asResultData.push(sValue);
88
+ }
89
+
90
+ return asResultData.sort();
91
+ }
92
+
93
+ function _fnColumnIndex(iColumnIndex) {
94
+ if (properties.bUseColVis)
95
+ return iColumnIndex;
96
+ else
97
+ return oTable.fnSettings().oApi._fnVisibleToColumnIndex(oTable.fnSettings(), iColumnIndex);
98
+ //return iColumnIndex;
99
+ //return oTable.fnSettings().oApi._fnColumnIndexToVisible(oTable.fnSettings(), iColumnIndex);
100
+ }
101
+
102
+ function fnCreateInput(oTable, regex, smart, bIsNumber, iFilterLength, iMaxLenght) {
103
+ var sCSSClass = "text_filter form-control";
104
+ if (bIsNumber)
105
+ sCSSClass = "number_filter form-control";
106
+
107
+ label = label.replace(/(^\s*)|(\s*$)/g, "");
108
+ var currentFilter = oTable.fnSettings().aoPreSearchCols[i].sSearch;
109
+ var search_init = 'search_init ';
110
+ var inputvalue = label;
111
+ if (currentFilter != '' && currentFilter != '^') {
112
+ if (bIsNumber && currentFilter.charAt(0) == '^')
113
+ inputvalue = currentFilter.substr(1); //ignore trailing ^
114
+ else
115
+ inputvalue = currentFilter;
116
+ search_init = '';
117
+ }
118
+
119
+
120
+ var input = $('<input type="text" class="' + search_init + sCSSClass + '" placeholder="' + inputvalue + '" value="' + inputvalue + '" rel="' + i + '"/>');
121
+ if (iMaxLenght != undefined && iMaxLenght != -1) {
122
+ input.attr('maxlength', iMaxLenght);
123
+ }
124
+ th.html(input);
125
+ if (bIsNumber)
126
+ th.wrapInner('<span class="filter_column filter_number" />');
127
+ else
128
+ th.wrapInner('<span class="filter_column filter_text" />');
129
+
130
+ asInitVals[i] = label;
131
+ var index = i;
132
+
133
+ if (bIsNumber && !oTable.fnSettings().oFeatures.bServerSide) {
134
+ input.keyup(function () {
135
+ /* Filter on the column all numbers that starts with the entered value */
136
+ oTable.fnFilter('^' + this.value, _fnColumnIndex(index), true, false); //Issue 37
137
+ fnOnFiltered();
138
+ });
139
+ } else {
140
+ input.keyup(function () {
141
+ var value = this.value;
142
+
143
+ window.clearTimeout(filterTimerId);
144
+ filterTimerId = window.setTimeout(function() {
145
+ oTable.fnFilter(value, _fnColumnIndex(index), regex, smart); //Issue 37
146
+ fnOnFiltered();
147
+ }, filterDelay);
148
+ });
149
+ }
150
+
151
+ input.click(function(e) {
152
+ e.preventDefault();
153
+ return false;
154
+ });
155
+
156
+ input.focus(function () {
157
+ if ($(this).hasClass("search_init")) {
158
+ $(this).removeClass("search_init");
159
+ this.value = "";
160
+ }
161
+ });
162
+ input.blur(function () {
163
+ if (this.value == "") {
164
+ $(this).addClass("search_init");
165
+ this.value = asInitVals[index];
166
+ }
167
+ });
168
+ }
169
+
170
+ function fnCreateRangeInput(oTable) {
171
+
172
+ //var currentFilter = oTable.fnSettings().aoPreSearchCols[i].sSearch;
173
+ th.html(_fnRangeLabelPart(0));
174
+ var sFromId = oTable.attr("id") + '_range_from_' + i;
175
+ var from = $('<input type="text" class="number_range_filter form-control" id="' + sFromId + '" rel="' + i + '"/>');
176
+ th.append(from);
177
+ th.append(_fnRangeLabelPart(1));
178
+ var sToId = oTable.attr("id") + '_range_to_' + i;
179
+ var to = $('<input type="text" class="number_range_filter form-control" id="' + sToId + '" rel="' + i + '"/>');
180
+ th.append(to);
181
+ th.append(_fnRangeLabelPart(2));
182
+ th.wrapInner('<span class="filter_column filter_number_range form-control" />');
183
+ var index = i;
184
+ aiCustomSearch_Indexes.push(i);
185
+
186
+
187
+
188
+ //------------start range filtering function
189
+
190
+
191
+ /* Custom filtering function which will filter data in column four between two values
192
+ * Author: Allan Jardine, Modified by Jovan Popovic
193
+ */
194
+ //$.fn.dataTableExt.afnFiltering.push(
195
+ oTable.dataTableExt.afnFiltering.push(
196
+ function (oSettings, aData, iDataIndex) {
197
+ if (oTable.attr("id") != oSettings.sTableId)
198
+ return true;
199
+ // Try to handle missing nodes more gracefully
200
+ if (document.getElementById(sFromId) == null)
201
+ return true;
202
+ var iMin = document.getElementById(sFromId).value * 1;
203
+ var iMax = document.getElementById(sToId).value * 1;
204
+ var iValue = aData[_fnColumnIndex(index)] == "-" ? 0 : aData[_fnColumnIndex(index)] * 1;
205
+ if (iMin == "" && iMax == "") {
206
+ return true;
207
+ }
208
+ else if (iMin == "" && iValue <= iMax) {
209
+ return true;
210
+ }
211
+ else if (iMin <= iValue && "" == iMax) {
212
+ return true;
213
+ }
214
+ else if (iMin <= iValue && iValue <= iMax) {
215
+ return true;
216
+ }
217
+ return false;
218
+ }
219
+ );
220
+ //------------end range filtering function
221
+
222
+
223
+
224
+ $('#' + sFromId + ',#' + sToId, th).keyup(function () {
225
+
226
+ var iMin = document.getElementById(sFromId).value * 1;
227
+ var iMax = document.getElementById(sToId).value * 1;
228
+ if (iMin != 0 && iMax != 0 && iMin > iMax)
229
+ return;
230
+
231
+ oTable.fnDraw();
232
+ fnOnFiltered();
233
+ });
234
+
235
+
236
+ }
237
+
238
+
239
+ function fnCreateDateRangeInput(oTable) {
240
+
241
+ var aoFragments = sRangeFormat.split(/[}{]/);
242
+
243
+ th.html("");
244
+ //th.html(_fnRangeLabelPart(0));
245
+ var sFromId = oTable.attr("id") + '_range_from_' + i;
246
+ var from = $('<input type="text" class="date_range_filter form-control" id="' + sFromId + '" rel="' + i + '"/>');
247
+ from.datepicker();
248
+ //th.append(from);
249
+ //th.append(_fnRangeLabelPart(1));
250
+ var sToId = oTable.attr("id") + '_range_to_' + i;
251
+ var to = $('<input type="text" class="date_range_filter form-control" id="' + sToId + '" rel="' + i + '"/>');
252
+ //th.append(to);
253
+ //th.append(_fnRangeLabelPart(2));
254
+
255
+ for (ti = 0; ti < aoFragments.length; ti++) {
256
+
257
+ if (aoFragments[ti] == properties.sDateFromToken) {
258
+ th.append(from);
259
+ } else {
260
+ if (aoFragments[ti] == properties.sDateToToken) {
261
+ th.append(to);
262
+ } else {
263
+ th.append(aoFragments[ti]);
264
+ }
265
+ }
266
+
267
+
268
+ }
269
+
270
+
271
+ th.wrapInner('<span class="filter_column filter_date_range" />');
272
+ to.datepicker();
273
+ var index = i;
274
+ aiCustomSearch_Indexes.push(i);
275
+
276
+
277
+ //------------start date range filtering function
278
+
279
+ //$.fn.dataTableExt.afnFiltering.push(
280
+ oTable.dataTableExt.afnFiltering.push(
281
+ function (oSettings, aData, iDataIndex) {
282
+ if (oTable.attr("id") != oSettings.sTableId)
283
+ return true;
284
+
285
+ var dStartDate = from.datepicker("getDate");
286
+
287
+ var dEndDate = to.datepicker("getDate");
288
+
289
+ if (dStartDate == null && dEndDate == null) {
290
+ return true;
291
+ }
292
+
293
+ var dCellDate = null;
294
+ try {
295
+ if (aData[_fnColumnIndex(index)] == null || aData[_fnColumnIndex(index)] == "")
296
+ return false;
297
+ dCellDate = $.datepicker.parseDate($.datepicker.regional[""].dateFormat, aData[_fnColumnIndex(index)]);
298
+ } catch (ex) {
299
+ return false;
300
+ }
301
+ if (dCellDate == null)
302
+ return false;
303
+
304
+
305
+ if (dStartDate == null && dCellDate <= dEndDate) {
306
+ return true;
307
+ }
308
+ else if (dStartDate <= dCellDate && dEndDate == null) {
309
+ return true;
310
+ }
311
+ else if (dStartDate <= dCellDate && dCellDate <= dEndDate) {
312
+ return true;
313
+ }
314
+ return false;
315
+ }
316
+ );
317
+ //------------end date range filtering function
318
+
319
+ $('#' + sFromId + ',#' + sToId, th).change(function () {
320
+ oTable.fnDraw();
321
+ fnOnFiltered();
322
+ });
323
+
324
+
325
+ }
326
+
327
+ function fnCreateColumnSelect(oTable, aData, iColumn, nTh, sLabel, bRegex, oSelected, bMultiselect) {
328
+ if (aData == null)
329
+ aData = _fnGetColumnValues(oTable.fnSettings(), iColumn, true, false, true);
330
+ var index = iColumn;
331
+ var currentFilter = oTable.fnSettings().aoPreSearchCols[i].sSearch;
332
+ if (currentFilter == null || currentFilter == "")//Issue 81
333
+ currentFilter = oSelected;
334
+
335
+ var r = '<select class="search_init select_filter form-control" rel="' + i + '"><option value="" class="search_init">' + sLabel + '</option>';
336
+ if(bMultiselect) {
337
+ r = '<select class="search_init select_filter form-control" rel="' + i + '" multiple>';
338
+ }
339
+ var j = 0;
340
+ var iLen = aData.length;
341
+ for (j = 0; j < iLen; j++) {
342
+
343
+ if (typeof (aData[j]) != 'object') {
344
+ var selected = '';
345
+ if (escape(aData[j]) == currentFilter
346
+ || escape(aData[j]) == escape(currentFilter)
347
+ )
348
+ selected = 'selected '
349
+ r += '<option ' + selected + ' value="' + escape(aData[j]) + '">' + aData[j] + '</option>';
350
+ }
351
+ else {
352
+ var selected = '';
353
+ if (bRegex) {
354
+ //Do not escape values if they are explicitely set to avoid escaping special characters in the regexp
355
+ if (aData[j][0] == currentFilter) selected = 'selected ';
356
+ r += '<option ' + selected + 'value="' + aData[j][0] + '">' + aData[j][1] + '</option>';
357
+ } else {
358
+ if (escape(aData[j][0]) == currentFilter) selected = 'selected ';
359
+ r += '<option ' + selected + 'value="' + escape(aData[j][0]) + '">' + aData[j][1] + '</option>';
360
+ }
361
+ }
362
+ }
363
+
364
+ var select = $(r + '</select>');
365
+ nTh.html(select);
366
+ nTh.wrapInner('<span class="filter_column filter_select" />');
367
+
368
+ select.click(function(e) {
369
+ e.preventDefault();
370
+ return false;
371
+ });
372
+
373
+ if(bMultiselect) {
374
+ select.change(function () {
375
+ if ($(this).val() != "") {
376
+ $(this).removeClass("search_init");
377
+ } else {
378
+ $(this).addClass("search_init");
379
+ }
380
+ var selectedOptions = $(this).val();
381
+ var asEscapedFilters = [];
382
+ if(selectedOptions==null || selectedOptions==[]){
383
+ var re = '^(.*)$';
384
+ }else{
385
+ $.each( selectedOptions, function( i, sFilter ) {
386
+ asEscapedFilters.push( fnRegExpEscape( sFilter ) );
387
+ } );
388
+ var re = '^(' + asEscapedFilters.join('|') + ')$';
389
+ }
390
+
391
+ oTable.fnFilter( re, index, true, false );
392
+ });
393
+ } else {
394
+ select.change(function () {
395
+ //var val = $(this).val();
396
+ if ($(this).val() != "") {
397
+ $(this).removeClass("search_init");
398
+ } else {
399
+ $(this).addClass("search_init");
400
+ }
401
+ if (bRegex)
402
+ oTable.fnFilter($(this).val(), iColumn, bRegex); //Issue 41
403
+ else
404
+ oTable.fnFilter(unescape($(this).val()), iColumn); //Issue 25
405
+ fnOnFiltered();
406
+ });
407
+ if (currentFilter != null && currentFilter != "")//Issue 81
408
+ oTable.fnFilter(unescape(currentFilter), iColumn);
409
+ }
410
+ }
411
+
412
+ function fnCreateSelect(oTable, aData, bRegex, oSelected, bMultiselect) {
413
+ var oSettings = oTable.fnSettings();
414
+ if ( (aData == null || typeof(aData) == 'function' ) && oSettings.sAjaxSource != "" && !oSettings.oFeatures.bServerSide) {
415
+ // Add a function to the draw callback, which will check for the Ajax data having
416
+ // been loaded. Use a closure for the individual column elements that are used to
417
+ // built the column filter, since 'i' and 'th' (etc) are locally "global".
418
+ oSettings.aoDrawCallback.push({
419
+ "fn": (function (iColumn, nTh, sLabel) {
420
+ return function (oSettings) {
421
+ // Only rebuild the select on the second draw - i.e. when the Ajax
422
+ // data has been loaded.
423
+ if (oSettings.iDraw == 2 && oSettings.sAjaxSource != null && oSettings.sAjaxSource != "" && !oSettings.oFeatures.bServerSide) {
424
+ return fnCreateColumnSelect(oTable, aData && aData(oSettings.aoData, oSettings), _fnColumnIndex(iColumn), nTh, sLabel, bRegex, oSelected, bMultiselect); //Issue 37
425
+ }
426
+ };
427
+ })(i, th, label),
428
+ "sName": "column_filter_" + i
429
+ });
430
+ }
431
+ // Regardless of the Ajax state, build the select on first pass
432
+ fnCreateColumnSelect(oTable, typeof(aData) == 'function' ? null: aData, _fnColumnIndex(i), th, label, bRegex, oSelected, bMultiselect); //Issue 37
433
+
434
+ }
435
+
436
+ function fnRegExpEscape( sText ) {
437
+ return sText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
438
+ };
439
+
440
+ function fnCreateDropdown(aData) {
441
+ var index = i;
442
+ var r = '<div class="dropdown select_filter form-control"><a class="dropdown-toggle" data-toggle="dropdown" href="#">' + label + '<b class="caret"></b></a><ul class="dropdown-menu" role="menu"><li data-value=""><a>Show All</a></li>', j, iLen = aData.length;
443
+
444
+ for (j = 0; j < iLen; j++) {
445
+ r += '<li data-value="' + aData[j] + '"><a>' + aData[j] + '</a></li>';
446
+ }
447
+ var select = $(r + '</ul></div>');
448
+ th.html(select);
449
+ th.wrapInner('<span class="filterColumn filter_select" />');
450
+ select.find('li').click(function () {
451
+ oTable.fnFilter($(this).data('value'), index);
452
+ });
453
+ }
454
+
455
+
456
+ function fnCreateCheckbox(oTable, aData) {
457
+
458
+ if (aData == null)
459
+ aData = _fnGetColumnValues(oTable.fnSettings(), i, true, true, true);
460
+ var index = i;
461
+
462
+ var r = '', j, iLen = aData.length;
463
+
464
+ //clean the string
465
+ var localLabel = label.replace('%', 'Perc').replace("&", "AND").replace("$", "DOL").replace("£", "STERL").replace("@", "AT").replace(/\s/g, "_");
466
+ localLabel = localLabel.replace(/[^a-zA-Z 0-9]+/g, '');
467
+ //clean the string
468
+
469
+ //button label override
470
+ var labelBtn = label;
471
+ if (properties.sFilterButtonText != null || properties.sFilterButtonText != undefined) {
472
+ labelBtn = properties.sFilterButtonText;
473
+ }
474
+
475
+ var relativeDivWidthToggleSize = 10;
476
+ var numRow = 12; //numero di checkbox per colonna
477
+ var numCol = Math.floor(iLen / numRow);
478
+ if (iLen % numRow > 0) {
479
+ numCol = numCol + 1;
480
+ };
481
+
482
+ //count how many column should be generated and split the div size
483
+ var divWidth = 100 / numCol - 2;
484
+
485
+ var divWidthToggle = relativeDivWidthToggleSize * numCol;
486
+
487
+ if (numCol == 1) {
488
+ divWidth = 20;
489
+ }
490
+
491
+ var divRowDef = '<div style="float:left; min-width: ' + divWidth + '%; " >';
492
+ var divClose = '</div>';
493
+
494
+ var uniqueId = oTable.attr("id") + localLabel;
495
+ var buttonId = "chkBtnOpen" + uniqueId;
496
+ var checkToggleDiv = uniqueId + "-flt-toggle";
497
+ r += '<button id="' + buttonId + '" class="checkbox_filter btn btn-default" > ' + labelBtn + '</button>'; //filter button witch open dialog
498
+ r += '<div id="' + checkToggleDiv + '" '
499
+ + 'title="' + label + '" '
500
+ + 'rel="' + i + '" '
501
+ + 'class="toggle-check ui-widget-content ui-corner-all" style="width: ' + (divWidthToggle) + '%; " >'; //dialog div
502
+ //r+= '<div align="center" style="margin-top: 5px; "> <button id="'+buttonId+'Reset" class="checkbox_filter" > reset </button> </div>'; //reset button and its div
503
+ r += divRowDef;
504
+
505
+ for (j = 0; j < iLen; j++) {
506
+
507
+ //if last check close div
508
+ if (j % numRow == 0 && j != 0) {
509
+ r += divClose + divRowDef;
510
+ }
511
+
512
+ var sLabel = aData[j];
513
+ var sValue = aData[j];
514
+
515
+ if (typeof (aData[j]) == 'object') {
516
+ sLabel = aData[j].label;
517
+ sValue = aData[j].value;
518
+ }
519
+
520
+ //check button
521
+ r += '<input class="search_init checkbox_filter btn btn-default" type="checkbox" id= "' + uniqueId + '_cb_' + sValue + '" name= "' + localLabel + '" value="' + sValue + '" >' + sLabel + '<br/>';
522
+
523
+ var checkbox = $(r);
524
+ th.html(checkbox);
525
+ th.wrapInner('<span class="filter_column filter_checkbox" />');
526
+ //on every checkbox selection
527
+ checkbox.change(function () {
528
+
529
+ var search = '';
530
+ var or = '|'; //var for select checks in 'or' into the regex
531
+ var resSize = $('input:checkbox[name="' + localLabel + '"]:checked').size();
532
+ $('input:checkbox[name="' + localLabel + '"]:checked').each(function (index) {
533
+
534
+ //search = search + ' ' + $(this).val();
535
+ //concatenation for selected checks in or
536
+ if ((index == 0 && resSize == 1)
537
+ || (index != 0 && index == resSize - 1)) {
538
+ or = '';
539
+ }
540
+ //trim
541
+ search = search.replace(/^\s+|\s+$/g, "");
542
+ search = search + $(this).val() + or;
543
+ or = '|';
544
+
545
+ });
546
+
547
+
548
+ if (search != "") {
549
+ $('input:checkbox[name="' + localLabel + '"]').removeClass("search_init");
550
+ } else {
551
+ $('input:checkbox[name="' + localLabel + '"]').addClass("search_init");
552
+ }
553
+ /* Old code for setting search_init CSS class on checkboxes if any of them is checked
554
+ for (var jj = 0; jj < iLen; jj++) {
555
+ if (search != "") {
556
+ $('#' + aData[jj]).removeClass("search_init");
557
+ } else {
558
+ $('#' + aData[jj]).addClass("search_init");
559
+ }
560
+ }
561
+ */
562
+
563
+ //execute search
564
+ oTable.fnFilter(search, index, true, false);
565
+ fnOnFiltered();
566
+ });
567
+ }
568
+
569
+ //filter button
570
+ $('#' + buttonId).button();
571
+ //dialog
572
+ $('#' + checkToggleDiv).dialog({
573
+ //height: 140,
574
+ autoOpen: false,
575
+ //show: "blind",
576
+ hide: "blind",
577
+ buttons: [{
578
+ text: "Reset",
579
+ click: function () {
580
+ //$('#'+buttonId).removeClass("filter_selected"); //LM remove border if filter selected
581
+ $('input:checkbox[name="' + localLabel + '"]:checked').each(function (index3) {
582
+ $(this).attr('checked', false);
583
+ $(this).addClass("search_init");
584
+ });
585
+ oTable.fnFilter('', index, true, false);
586
+ fnOnFiltered();
587
+ return false;
588
+ }
589
+ },
590
+ {
591
+ text: "Close",
592
+ click: function () { $(this).dialog("close"); }
593
+ }
594
+ ]
595
+ });
596
+
597
+
598
+ $('#' + buttonId).click(function () {
599
+
600
+ $('#' + checkToggleDiv).dialog('open');
601
+ var target = $(this);
602
+ $('#' + checkToggleDiv).dialog("widget").position({ my: 'top',
603
+ at: 'bottom',
604
+ of: target
605
+ });
606
+
607
+ return false;
608
+ });
609
+
610
+ var fnOnFilteredCurrent = fnOnFiltered;
611
+
612
+ fnOnFiltered = function () {
613
+ var target = $('#' + buttonId);
614
+ $('#' + checkToggleDiv).dialog("widget").position({ my: 'top',
615
+ at: 'bottom',
616
+ of: target
617
+ });
618
+ fnOnFilteredCurrent();
619
+ };
620
+ //reset
621
+ /*
622
+ $('#'+buttonId+"Reset").button();
623
+ $('#'+buttonId+"Reset").click(function(){
624
+ $('#'+buttonId).removeClass("filter_selected"); //LM remove border if filter selected
625
+ $('input:checkbox[name="'+localLabel+'"]:checked').each(function(index3) {
626
+ $(this).attr('checked', false);
627
+ $(this).addClass("search_init");
628
+ });
629
+ oTable.fnFilter('', index, true, false);
630
+ return false;
631
+ });
632
+ */
633
+ }
634
+
635
+
636
+
637
+
638
+ function _fnRangeLabelPart(iPlace) {
639
+ switch (iPlace) {
640
+ case 0:
641
+ return sRangeFormat.substring(0, sRangeFormat.indexOf("{from}"));
642
+ case 1:
643
+ return sRangeFormat.substring(sRangeFormat.indexOf("{from}") + 6, sRangeFormat.indexOf("{to}"));
644
+ default:
645
+ return sRangeFormat.substring(sRangeFormat.indexOf("{to}") + 4);
646
+ }
647
+ }
648
+
649
+
650
+
651
+
652
+ var oTable = this;
653
+
654
+ var defaults = {
655
+ sPlaceHolder: "foot",
656
+ sRangeSeparator: "~",
657
+ iFilteringDelay: 500,
658
+ aoColumns: null,
659
+ sRangeFormat: "From {from} to {to}",
660
+ sDateFromToken: "from",
661
+ sDateToToken: "to"
662
+ };
663
+
664
+ var properties = $.extend(defaults, options);
665
+
666
+ return this.each(function () {
667
+
668
+ if (!oTable.fnSettings().oFeatures.bFilter)
669
+ return;
670
+ asInitVals = new Array();
671
+
672
+ var aoFilterCells = oTable.fnSettings().aoFooter[0];
673
+
674
+ var oHost = oTable.fnSettings().nTFoot; //Before fix for ColVis
675
+ var sFilterRow = "tr"; //Before fix for ColVis
676
+
677
+ if (properties.sPlaceHolder == "head:after") {
678
+ var tr = $("tr", oTable.fnSettings().nTHead).detach();
679
+ //tr.appendTo($(oTable.fnSettings().nTHead));
680
+ if (oTable.fnSettings().bSortCellsTop) {
681
+ tr.prependTo($(oTable.fnSettings().nTHead));
682
+ //tr.appendTo($("thead", oTable));
683
+ aoFilterCells = oTable.fnSettings().aoHeader[1];
684
+ }
685
+ else {
686
+ tr.appendTo($(oTable.fnSettings().nTHead));
687
+ //tr.prependTo($("thead", oTable));
688
+ aoFilterCells = oTable.fnSettings().aoHeader[0];
689
+ }
690
+
691
+ sFilterRow = "tr:last";
692
+ oHost = oTable.fnSettings().nTHead;
693
+
694
+ } else if (properties.sPlaceHolder == "head:before") {
695
+
696
+ if (oTable.fnSettings().bSortCellsTop) {
697
+ var tr = $("tr:first", oTable.fnSettings().nTHead).detach();
698
+ tr.appendTo($(oTable.fnSettings().nTHead));
699
+ aoFilterCells = oTable.fnSettings().aoHeader[1];
700
+ } else {
701
+ aoFilterCells = oTable.fnSettings().aoHeader[0];
702
+ }
703
+ /*else {
704
+ //tr.prependTo($("thead", oTable));
705
+ sFilterRow = "tr:first";
706
+ }*/
707
+
708
+ sFilterRow = "tr:first";
709
+
710
+ oHost = oTable.fnSettings().nTHead;
711
+
712
+
713
+ }
714
+
715
+ //$(sFilterRow + " th", oHost).each(function (index) {//bug with ColVis
716
+ $(aoFilterCells).each(function (index) {//fix for ColVis
717
+ i = index;
718
+ var aoColumn = { type: "text",
719
+ bRegex: false,
720
+ bSmart: true,
721
+ iMaxLenght: -1,
722
+ iFilterLength: 0
723
+ };
724
+ if (properties.aoColumns != null) {
725
+ if (properties.aoColumns.length < i || properties.aoColumns[i] == null)
726
+ return;
727
+ aoColumn = properties.aoColumns[i];
728
+ }
729
+ //label = $(this).text(); //Before fix for ColVis
730
+ label = $($(this)[0].cell).text(); //Fix for ColVis
731
+ if (aoColumn.sSelector == null) {
732
+ //th = $($(this)[0]);//Before fix for ColVis
733
+ th = $($(this)[0].cell); //Fix for ColVis
734
+ }
735
+ else {
736
+ th = $(aoColumn.sSelector);
737
+ if (th.length == 0)
738
+ th = $($(this)[0].cell);
739
+ }
740
+
741
+ if (aoColumn != null) {
742
+ if (aoColumn.sRangeFormat != null)
743
+ sRangeFormat = aoColumn.sRangeFormat;
744
+ else
745
+ sRangeFormat = properties.sRangeFormat;
746
+ switch (aoColumn.type) {
747
+ case "null":
748
+ break;
749
+ case "number":
750
+ fnCreateInput(oTable, true, false, true, aoColumn.iFilterLength, aoColumn.iMaxLenght);
751
+ break;
752
+ case "select":
753
+ if (aoColumn.bRegex != true)
754
+ aoColumn.bRegex = false;
755
+ fnCreateSelect(oTable, aoColumn.values, aoColumn.bRegex, aoColumn.selected, aoColumn.multiple);
756
+ break;
757
+ case "number-range":
758
+ fnCreateRangeInput(oTable);
759
+ break;
760
+ case "date-range":
761
+ fnCreateDateRangeInput(oTable);
762
+ break;
763
+ case "checkbox":
764
+ fnCreateCheckbox(oTable, aoColumn.values);
765
+ break;
766
+ case "twitter-dropdown":
767
+ case "dropdown":
768
+ fnCreateDropdown(aoColumn.values);
769
+ break;
770
+ case "text":
771
+ default:
772
+ bRegex = (aoColumn.bRegex == null ? false : aoColumn.bRegex);
773
+ bSmart = (aoColumn.bSmart == null ? false : aoColumn.bSmart);
774
+ fnCreateInput(oTable, bRegex, bSmart, false, aoColumn.iFilterLength, aoColumn.iMaxLenght);
775
+ break;
776
+
777
+ }
778
+ }
779
+ });
780
+
781
+ for (j = 0; j < aiCustomSearch_Indexes.length; j++) {
782
+ //var index = aiCustomSearch_Indexes[j];
783
+ var fnSearch_ = function () {
784
+ var id = oTable.attr("id");
785
+ return $("#" + id + "_range_from_" + aiCustomSearch_Indexes[j]).val() + properties.sRangeSeparator + $("#" + id + "_range_to_" + aiCustomSearch_Indexes[j]).val()
786
+ }
787
+ afnSearch_.push(fnSearch_);
788
+ }
789
+
790
+ if (oTable.fnSettings().oFeatures.bServerSide) {
791
+
792
+ var fnServerDataOriginal = oTable.fnSettings().fnServerData;
793
+
794
+ oTable.fnSettings().fnServerData = function (sSource, aoData, fnCallback) {
795
+
796
+ for (j = 0; j < aiCustomSearch_Indexes.length; j++) {
797
+ var index = aiCustomSearch_Indexes[j];
798
+
799
+ for (k = 0; k < aoData.length; k++) {
800
+ if (aoData[k].name == "sSearch_" + index)
801
+ aoData[k].value = afnSearch_[j]();
802
+ }
803
+ }
804
+ aoData.push({ "name": "sRangeSeparator", "value": properties.sRangeSeparator });
805
+
806
+ if (fnServerDataOriginal != null) {
807
+ try {
808
+ fnServerDataOriginal(sSource, aoData, fnCallback, oTable.fnSettings()); //TODO: See Issue 18
809
+ } catch (ex) {
810
+ fnServerDataOriginal(sSource, aoData, fnCallback);
811
+ }
812
+ }
813
+ else {
814
+ $.getJSON(sSource, aoData, function (json) {
815
+ fnCallback(json)
816
+ });
817
+ }
818
+ };
819
+
820
+ }
821
+
822
+ });
823
+
824
+ };
825
+
826
+
827
+
828
+
829
+ })(jQuery);