effective_datatables 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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);