foo_table-rails 0.5.0 → 2.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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/foo_table-rails/version.rb +1 -1
  4. data/vendor/assets/javascripts/footable.core.js +353 -159
  5. data/vendor/assets/javascripts/footable.core.min.js +14 -0
  6. data/vendor/assets/javascripts/footable.filter.js +145 -92
  7. data/vendor/assets/javascripts/footable.filter.min.js +14 -0
  8. data/vendor/assets/javascripts/footable.paginate.js +90 -35
  9. data/vendor/assets/javascripts/footable.paginate.min.js +1 -0
  10. data/vendor/assets/javascripts/footable.sort.js +171 -146
  11. data/vendor/assets/javascripts/footable.sort.min.js +1 -0
  12. data/vendor/assets/stylesheets/fonts/footable.eot +0 -0
  13. data/vendor/assets/stylesheets/fonts/footable.svg +78 -0
  14. data/vendor/assets/stylesheets/fonts/footable.ttf +0 -0
  15. data/vendor/assets/stylesheets/fonts/footable.woff +0 -0
  16. data/vendor/assets/stylesheets/footable.all.css +407 -0
  17. data/vendor/assets/stylesheets/footable.all.min.css +3 -0
  18. data/vendor/assets/stylesheets/footable.core.css +147 -96
  19. data/vendor/assets/stylesheets/footable.core.min.css +1 -0
  20. data/vendor/assets/stylesheets/footable.metro.css +132 -0
  21. data/vendor/assets/stylesheets/footable.metro.min.css +1 -0
  22. data/vendor/assets/stylesheets/footable.standalone.css +106 -0
  23. data/vendor/assets/stylesheets/footable.standalone.min.css +1 -0
  24. metadata +16 -7
  25. data/vendor/assets/javascripts/footable.template.js +0 -44
  26. data/vendor/assets/stylesheets/footable.paginate.css +0 -31
  27. data/vendor/assets/stylesheets/footable.sort.css +0 -24
  28. data/vendor/assets/stylesheets/footable.theme.bootstrap-responsive.css +0 -1109
  29. data/vendor/assets/stylesheets/footable.theme.bootstrap-tab.js +0 -144
  30. data/vendor/assets/stylesheets/footable.theme.bootstrap.css +0 -6158
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * FooTable - Awesome Responsive Tables
3
- * Version : 0.5
4
- * http://themergency.com/footable
3
+ * Version : 2.0
4
+ * http://fooplugins.com/plugins/footable-jquery/
5
5
  *
6
6
  * Requires jQuery - http://jquery.com/
7
7
  *
@@ -9,7 +9,7 @@
9
9
  * Released under the MIT license
10
10
  * You are free to use FooTable in commercial projects as long as this copyright header is left intact.
11
11
  *
12
- * Date: 22 Apr 2013
12
+ * Date: 23 Aug 2013
13
13
  */
14
14
  (function ($, w, undefined) {
15
15
  w.footable = {
@@ -22,12 +22,35 @@
22
22
  parsers: { // The default parser to parse the value out of a cell (values are used in building up row detail)
23
23
  alpha: function (cell) {
24
24
  return $(cell).data('value') || $.trim($(cell).text());
25
+ },
26
+ numeric: function (cell) {
27
+ var val = $(cell).data('value') || $(cell).text().replace(/[^0-9.\-]/g, '');
28
+ val = parseFloat(val);
29
+ if (isNaN(val)) val = 0;
30
+ return val;
25
31
  }
26
32
  },
33
+ addRowToggle: true,
27
34
  calculateWidthAndHeightOverride: null,
28
35
  toggleSelector: ' > tbody > tr:not(.footable-row-detail)', //the selector to show/hide the detail row
29
36
  columnDataSelector: '> thead > tr:last-child > th, > thead > tr:last-child > td', //the selector used to find the column data in the thead
30
- createDetail: function (element, data) {
37
+ detailSeparator: ':', //the seperator character used when building up the detail row
38
+ createGroupedDetail: function (data) {
39
+ var groups = { '_none': { 'name': null, 'data': [] } };
40
+ for (var i = 0; i < data.length; i++) {
41
+ var groupid = data[i].group;
42
+ if (groupid !== null) {
43
+ if (!(groupid in groups))
44
+ groups[groupid] = { 'name': data[i].groupName || data[i].group, 'data': [] };
45
+
46
+ groups[groupid].data.push(data[i]);
47
+ } else {
48
+ groups._none.data.push(data[i]);
49
+ }
50
+ }
51
+ return groups;
52
+ },
53
+ createDetail: function (element, data, createGroupedDetail, separatorChar, classes) {
31
54
  /// <summary>This function is used by FooTable to generate the detail view seen when expanding a collapsed row.</summary>
32
55
  /// <param name="element">This is the div that contains all the detail row information, anything could be added to it.</param>
33
56
  /// <param name="data">
@@ -41,37 +64,60 @@
41
64
  /// 'groupName': String // This is the actual name of the group the column belongs to.
42
65
  /// }
43
66
  /// </param>
67
+ /// <param name="createGroupedDetail">The grouping function to group the data</param>
68
+ /// <param name="separatorChar">The separator charactor used</param>
69
+ /// <param name="classes">The array of class names used to build up the detail row</param>
44
70
 
45
- var groups = { '_none': { 'name': null, 'data': [] } };
46
- for (var i = 0; i < data.length; i++) {
47
- var groupid = data[i].group;
48
- if (groupid != null) {
49
- if (!(groupid in groups))
50
- groups[groupid] = { 'name': data[i].groupName, 'data': [] };
51
-
52
- groups[groupid].data.push(data[i]);
53
- } else {
54
- groups._none.data.push(data[i]);
55
- }
56
- }
57
-
71
+ var groups = createGroupedDetail(data);
58
72
  for (var group in groups) {
59
- if (groups[group].data.length == 0) continue;
60
- if (group != '_none') element.append('<h4>' + groups[group].name + '</h4>');
73
+ if (groups[group].data.length === 0) continue;
74
+ if (group !== '_none') element.append('<div class="' + classes.detailInnerGroup + '">' + groups[group].name + '</div>');
61
75
 
62
76
  for (var j = 0; j < groups[group].data.length; j++) {
63
- element.append('<div><strong>' + groups[group].data[j].name + '</strong> : ' + groups[group].data[j].display + '</div>');
77
+ var separator = (groups[group].data[j].name) ? separatorChar : '';
78
+ element.append('<div class="' + classes.detailInnerRow + '"><div class="' + classes.detailInnerName + '">' + groups[group].data[j].name + separator + '</div><div class="' + classes.detailInnerValue + '">' + groups[group].data[j].display + '</div></div>');
64
79
  }
65
80
  }
66
81
  },
67
82
  classes: {
83
+ main: 'footable',
68
84
  loading: 'footable-loading',
69
85
  loaded: 'footable-loaded',
70
- sorted: 'footable-sorted',
71
- descending: 'footable-sorted-desc',
72
- indicator: 'footable-sort-indicator'
86
+ toggle: 'footable-toggle',
87
+ disabled: 'footable-disabled',
88
+ detail: 'footable-row-detail',
89
+ detailCell: 'footable-row-detail-cell',
90
+ detailInner: 'footable-row-detail-inner',
91
+ detailInnerRow: 'footable-row-detail-row',
92
+ detailInnerGroup: 'footable-row-detail-group',
93
+ detailInnerName: 'footable-row-detail-name',
94
+ detailInnerValue: 'footable-row-detail-value',
95
+ detailShow: 'footable-detail-show'
96
+ },
97
+ triggers: {
98
+ initialize: 'footable_initialize', //trigger this event to force FooTable to reinitialize
99
+ resize: 'footable_resize', //trigger this event to force FooTable to resize
100
+ redraw: 'footable_redraw', //trigger this event to force FooTable to redraw
101
+ toggleRow: 'footable_toggle_row', //trigger this event to force FooTable to toggle a row
102
+ expandFirstRow: 'footable_expand_first_row' //trigger this event to force FooTable to expand the first row
73
103
  },
74
- debug: false // Whether or not to log information to the console.
104
+ events: {
105
+ alreadyInitialized: 'footable_already_initialized', //fires when the FooTable has already been initialized
106
+ initializing: 'footable_initializing', //fires before FooTable starts initializing
107
+ initialized: 'footable_initialized', //fires after FooTable has finished initializing
108
+ resizing: 'footable_resizing', //fires before FooTable resizes
109
+ resized: 'footable_resized', //fires after FooTable has resized
110
+ redrawn: 'footable_redrawn', //fires after FooTable has redrawn
111
+ breakpoint: 'footable_breakpoint', //fires inside the resize function, when a breakpoint is hit
112
+ columnData: 'footable_column_data', //fires when setting up column data. Plugins should use this event to capture their own info about a column
113
+ rowDetailUpdating: 'footable_row_detail_updating', //fires before a detail row is updated
114
+ rowDetailUpdated: 'footable_row_detail_updated', //fires when a detail row is being updated
115
+ rowCollapsed: 'footable_row_collapsed', //fires when a row is collapsed
116
+ rowExpanded: 'footable_row_expanded', //fires when a row is expanded
117
+ rowRemoved: 'footable_row_removed' //fires when a row is removed
118
+ },
119
+ debug: false, // Whether or not to log information to the console.
120
+ log: null
75
121
  },
76
122
 
77
123
  version: {
@@ -82,9 +128,9 @@
82
128
  parse: function (str) {
83
129
  version = /(\d+)\.?(\d+)?\.?(\d+)?/.exec(str);
84
130
  return {
85
- major: parseInt(version[1]) || 0,
86
- minor: parseInt(version[2]) || 0,
87
- patch: parseInt(version[3]) || 0
131
+ major: parseInt(version[1], 10) || 0,
132
+ minor: parseInt(version[2], 10) || 0,
133
+ patch: parseInt(version[3], 10) || 0
88
134
  };
89
135
  }
90
136
  },
@@ -95,14 +141,14 @@
95
141
  ///<param name="plugin">The object defining the plugin, this should implement a string property called "name" and a function called "init".</param>
96
142
 
97
143
  if (typeof plugin['name'] !== 'string') {
98
- if (w.footable.options.debug == true) console.error('Validation failed, plugin does not implement a string property called "name".', plugin);
144
+ if (w.footable.options.debug === true) console.error('Validation failed, plugin does not implement a string property called "name".', plugin);
99
145
  return false;
100
146
  }
101
147
  if (!$.isFunction(plugin['init'])) {
102
- if (w.footable.options.debug == true) console.error('Validation failed, plugin "' + plugin['name'] + '" does not implement a function called "init".', plugin);
148
+ if (w.footable.options.debug === true) console.error('Validation failed, plugin "' + plugin['name'] + '" does not implement a function called "init".', plugin);
103
149
  return false;
104
150
  }
105
- if (w.footable.options.debug == true) console.log('Validation succeeded for plugin "' + plugin['name'] + '".', plugin);
151
+ if (w.footable.options.debug === true) console.log('Validation succeeded for plugin "' + plugin['name'] + '".', plugin);
106
152
  return true;
107
153
  },
108
154
  registered: [], // An array containing all registered plugins.
@@ -113,8 +159,8 @@
113
159
 
114
160
  if (w.footable.plugins._validate(plugin)) {
115
161
  w.footable.plugins.registered.push(plugin);
116
- if (options != undefined && typeof options === 'object') $.extend(true, w.footable.options, options);
117
- if (w.footable.options.debug == true) console.log('Plugin "' + plugin['name'] + '" has been registered with the Foobox.', plugin);
162
+ if (options !== undefined && typeof options === 'object') $.extend(true, w.footable.options, options);
163
+ if (w.footable.options.debug === true) console.log('Plugin "' + plugin['name'] + '" has been registered with the Foobox.', plugin);
118
164
  }
119
165
  },
120
166
  init: function (instance) {
@@ -125,7 +171,7 @@
125
171
  try {
126
172
  w.footable.plugins.registered[i]['init'](instance);
127
173
  } catch (err) {
128
- if (w.footable.options.debug == true) console.error(err);
174
+ if (w.footable.options.debug === true) console.error(err);
129
175
  }
130
176
  }
131
177
  }
@@ -145,7 +191,8 @@
145
191
  var o = $.extend(true, {}, w.footable.options, options); //merge user and default options
146
192
  return this.each(function () {
147
193
  instanceCount++;
148
- this.footable = new Footable(this, o, instanceCount);
194
+ var footable = new Footable(this, o, instanceCount);
195
+ $(this).data('footable', footable);
149
196
  });
150
197
  };
151
198
 
@@ -174,13 +221,13 @@
174
221
  t.stop = function () {
175
222
  ///<summary>Stops the timer if its runnning and resets it back to its starting state.</summary>
176
223
 
177
- if (t.id != null) {
224
+ if (t.id !== null) {
178
225
  clearTimeout(t.id);
179
226
  t.id = null;
180
227
  t.busy = false;
181
228
  }
182
229
  };
183
- };
230
+ }
184
231
 
185
232
  function Footable(t, o, id) {
186
233
  ///<summary>Inits a new instance of the plugin.</summary>
@@ -196,11 +243,13 @@
196
243
  ft.breakpointNames = '';
197
244
  ft.columns = {};
198
245
 
199
- var opt = ft.options;
200
- var cls = opt.classes;
201
- var indexOffset = 0;
246
+ var opt = ft.options,
247
+ cls = opt.classes,
248
+ evt = opt.events,
249
+ trg = opt.triggers,
250
+ indexOffset = 0;
202
251
 
203
- // This object simply houses all the timers used in the footable.
252
+ // This object simply houses all the timers used in the FooTable.
204
253
  ft.timers = {
205
254
  resize: new Timer(),
206
255
  register: function (name) {
@@ -216,29 +265,19 @@
216
265
 
217
266
  if ($table.hasClass(cls.loaded)) {
218
267
  //already loaded FooTable for the table, so don't init again
219
- ft.raise('footable_already_initialized');
268
+ ft.raise(evt.alreadyInitialized);
220
269
  return;
221
270
  }
222
271
 
272
+ //raise the initializing event
273
+ ft.raise(evt.initializing);
274
+
223
275
  $table.addClass(cls.loading);
224
276
 
225
277
  // Get the column data once for the life time of the plugin
226
278
  $table.find(opt.columnDataSelector).each(function () {
227
279
  var data = ft.getColumnData(this);
228
280
  ft.columns[data.index] = data;
229
-
230
- if (data.className != null) {
231
- var selector = '', first = true;
232
- $.each(data.matches, function (m, match) { //support for colspans
233
- if (!first) {
234
- selector += ', ';
235
- }
236
- selector += '> tbody > tr:not(.footable-row-detail) > td:nth-child(' + (parseInt(match) + 1) + ')';
237
- first = false;
238
- });
239
- //add the className to the cells specified by data-class="blah"
240
- $table.find(selector).not('.footable-cell-detail').addClass(data.className);
241
- }
242
281
  });
243
282
 
244
283
  // Create a nice friendly array to work with out of the breakpoints object.
@@ -252,49 +291,110 @@
252
291
  return a['width'] - b['width'];
253
292
  });
254
293
 
255
- //bind the toggle selector click events
256
- ft.bindToggleSelectors();
257
-
258
- ft.raise('footable_initializing');
259
-
260
- $table.bind('footable_initialized', function () {
261
- //resize the footable onload
262
- ft.resize();
263
-
264
- //remove the loading class
265
- $table.removeClass(cls.loading);
266
-
267
- //hides all elements within the table that have the attribute data-hide="init"
268
- //what does this do? LOL
269
- $table.find('[data-init="hide"]').hide();
270
- $table.find('[data-init="show"]').show();
271
-
272
- //add the loaded class
273
- $table.addClass(cls.loaded);
274
- });
294
+ $table
295
+ //bind to FooTable initialize trigger
296
+ .bind(trg.initialize, function () {
297
+ //remove previous "state" (to "force" a resize)
298
+ $table.removeData('footable_info');
299
+ $table.data('breakpoint', '');
300
+
301
+ //trigger the FooTable resize
302
+ $table.trigger(trg.resize);
303
+
304
+ //remove the loading class
305
+ $table.removeClass(cls.loading);
306
+
307
+ //add the FooTable and loaded class
308
+ $table.addClass(cls.loaded).addClass(cls.main);
309
+
310
+ //raise the initialized event
311
+ ft.raise(evt.initialized);
312
+ })
313
+ //bind to FooTable redraw trigger
314
+ .bind(trg.redraw, function () {
315
+ ft.redraw();
316
+ })
317
+
318
+ //bind to FooTable resize trigger
319
+ .bind(trg.resize, function () {
320
+ ft.resize();
321
+ })
322
+ //bind to FooTable expandFirstRow trigger
323
+ .bind(trg.expandFirstRow, function () {
324
+ $table.find(opt.toggleSelector).first().not('.' + cls.detailShow).trigger(trg.toggleRow);
325
+ });
275
326
 
276
- $table.bind('footable_resize', function () {
277
- ft.resize();
278
- });
327
+ //trigger a FooTable initialize
328
+ $table.trigger(trg.initialize);
279
329
 
330
+ //bind to window resize
280
331
  $window
281
332
  .bind('resize.footable', function () {
282
333
  ft.timers.resize.stop();
283
334
  ft.timers.resize.start(function () {
284
- ft.resize();
335
+ ft.raise(trg.resize);
285
336
  }, opt.delay);
286
337
  });
338
+ };
339
+
340
+ ft.addRowToggle = function () {
341
+ if (!opt.addRowToggle) return;
342
+
343
+ var $table = $(ft.table),
344
+ hasToggleColumn = false;
345
+
346
+ //first remove all toggle spans
347
+ $table.find('span.' + cls.toggle).remove();
348
+
349
+ for (var c in ft.columns) {
350
+ var col = ft.columns[c];
351
+ if (col.toggle) {
352
+ hasToggleColumn = true;
353
+ var selector = '> tbody > tr:not(.' + cls.detail + ',.' + cls.disabled + ') > td:nth-child(' + (parseInt(col.index, 10) + 1) + ')';
354
+ $table.find(selector).not('.' + cls.detailCell).prepend($('<span />').addClass(cls.toggle));
355
+ return;
356
+ }
357
+ }
358
+ //check if we have an toggle column. If not then add it to the first column just to be safe
359
+ if (!hasToggleColumn) {
360
+ $table
361
+ .find('> tbody > tr:not(.' + cls.detail + ',.' + cls.disabled + ') > td:first-child')
362
+ .not('.' + cls.detailCell)
363
+ .prepend($('<span />').addClass(cls.toggle));
364
+ }
365
+ };
287
366
 
288
- ft.raise('footable_initialized');
367
+ ft.setColumnClasses = function () {
368
+ $table = $(ft.table);
369
+ for (var c in ft.columns) {
370
+ var col = ft.columns[c];
371
+ if (col.className !== null) {
372
+ var selector = '', first = true;
373
+ $.each(col.matches, function (m, match) { //support for colspans
374
+ if (!first) selector += ', ';
375
+ selector += '> tbody > tr:not(.' + cls.detail + ') > td:nth-child(' + (parseInt(match, 10) + 1) + ')';
376
+ first = false;
377
+ });
378
+ //add the className to the cells specified by data-class="blah"
379
+ $table.find(selector).not('.' + cls.detailCell).addClass(col.className);
380
+ }
381
+ }
289
382
  };
290
383
 
291
384
  //moved this out into it's own function so that it can be called from other add-ons
292
385
  ft.bindToggleSelectors = function () {
293
386
  var $table = $(ft.table);
387
+
388
+ if (!ft.hasAnyBreakpointColumn()) return;
389
+
390
+ $table.find(opt.toggleSelector).unbind(trg.toggleRow).bind(trg.toggleRow, function (e) {
391
+ var $row = $(this).is('tr') ? $(this) : $(this).parents('tr:first');
392
+ ft.toggleDetail($row.get(0));
393
+ });
394
+
294
395
  $table.find(opt.toggleSelector).unbind('click.footable').bind('click.footable', function (e) {
295
- if ($table.is('.breakpoint') && $(e.target).is('td')) {
296
- var $row = $(this).is('tr') ? $(this) : $(this).parents('tr:first');
297
- ft.toggleDetail($row.get(0));
396
+ if ($table.is('.breakpoint') && $(e.target).is('td,.footable-toggle')) {
397
+ $(this).trigger(trg.toggleRow);
298
398
  }
299
399
  });
300
400
  };
@@ -307,13 +407,16 @@
307
407
  ft.getColumnData = function (th) {
308
408
  var $th = $(th), hide = $th.data('hide'), index = $th.index();
309
409
  hide = hide || '';
310
- hide = hide.split(',');
410
+ hide = jQuery.map(hide.split(','), function (a) {
411
+ return jQuery.trim(a);
412
+ });
311
413
  var data = {
312
414
  'index': index,
313
415
  'hide': { },
314
416
  'type': $th.data('type') || 'alpha',
315
417
  'name': $th.data('name') || $.trim($th.text()),
316
418
  'ignore': $th.data('ignore') || false,
419
+ 'toggle': $th.data('toggle') || false,
317
420
  'className': $th.data('class') || null,
318
421
  'matches': [],
319
422
  'names': { },
@@ -321,14 +424,14 @@
321
424
  'groupName': null
322
425
  };
323
426
 
324
- if (data.group != null) {
427
+ if (data.group !== null) {
325
428
  var $group = $(ft.table).find('> thead > tr.footable-group-row > th[data-group="' + data.group + '"], > thead > tr.footable-group-row > td[data-group="' + data.group + '"]').first();
326
429
  data.groupName = ft.parse($group, { 'type': 'alpha' });
327
430
  }
328
431
 
329
- var pcolspan = parseInt($th.prev().attr('colspan') || 0);
432
+ var pcolspan = parseInt($th.prev().attr('colspan') || 0, 10);
330
433
  indexOffset += pcolspan > 1 ? pcolspan - 1 : 0;
331
- var colspan = parseInt($th.attr('colspan') || 0), curindex = data.index + indexOffset;
434
+ var colspan = parseInt($th.attr('colspan') || 0, 10), curindex = data.index + indexOffset;
332
435
  if (colspan > 1) {
333
436
  var names = $th.data('names');
334
437
  names = names || '';
@@ -343,10 +446,13 @@
343
446
 
344
447
  data.hide['default'] = ($th.data('hide') === "all") || ($.inArray('default', hide) >= 0);
345
448
 
449
+ var hasBreakpoint = false;
346
450
  for (var name in opt.breakpoints) {
347
451
  data.hide[name] = ($th.data('hide') === "all") || ($.inArray(name, hide) >= 0);
452
+ hasBreakpoint = hasBreakpoint || data.hide[name];
348
453
  }
349
- var e = ft.raise('footable_column_data', { 'column': { 'data': data, 'th': th } });
454
+ data.hasBreakpoint = hasBreakpoint;
455
+ var e = ft.raise(evt.columnData, { 'column': { 'data': data, 'th': th } });
350
456
  return e.column.data;
351
457
  };
352
458
 
@@ -371,6 +477,18 @@
371
477
  ft.hasBreakpointColumn = function (breakpoint) {
372
478
  for (var c in ft.columns) {
373
479
  if (ft.columns[c].hide[breakpoint]) {
480
+ if (ft.columns[c].ignore) {
481
+ continue;
482
+ }
483
+ return true;
484
+ }
485
+ }
486
+ return false;
487
+ };
488
+
489
+ ft.hasAnyBreakpointColumn = function () {
490
+ for (var c in ft.columns) {
491
+ if (ft.columns[c].hasBreakpoint) {
374
492
  return true;
375
493
  }
376
494
  }
@@ -379,6 +497,15 @@
379
497
 
380
498
  ft.resize = function () {
381
499
  var $table = $(ft.table);
500
+
501
+ if (!$table.is(':visible')) {
502
+ return;
503
+ } //we only care about FooTables that are visible
504
+
505
+ if (!ft.hasAnyBreakpointColumn()) {
506
+ return;
507
+ } //we only care about FooTables that have breakpoints
508
+
382
509
  var info = {
383
510
  'width': $table.width(), //the table width
384
511
  'height': $table.height(), //the table height
@@ -386,16 +513,18 @@
386
513
  'viewportHeight': ft.getViewportHeight(), //the width of the viewport
387
514
  'orientation': null
388
515
  };
516
+
389
517
  info.orientation = info.viewportWidth > info.viewportHeight ? 'landscape' : 'portrait';
390
518
 
391
519
  info = ft.calculateWidthAndHeight($table, info);
392
520
 
393
521
  var pinfo = $table.data('footable_info');
394
522
  $table.data('footable_info', info);
395
- ft.raise('footable_resizing', { 'old': pinfo, 'info': info });
523
+ ft.raise(evt.resizing, { 'old': pinfo, 'info': info });
396
524
 
397
525
  // This (if) statement is here purely to make sure events aren't raised twice as mobile safari seems to do
398
- if (!pinfo || ((pinfo && pinfo.width && pinfo.width != info.width) || (pinfo && pinfo.height && pinfo.height != info.height))) {
526
+ if (!pinfo || ((pinfo && pinfo.width && pinfo.width !== info.width) || (pinfo && pinfo.height && pinfo.height !== info.height))) {
527
+
399
528
  var current = null, breakpoint;
400
529
  for (var i = 0; i < ft.breakpoints.length; i++) {
401
530
  breakpoint = ft.breakpoints[i];
@@ -405,90 +534,148 @@
405
534
  }
406
535
  }
407
536
 
408
- var breakpointName = (current == null ? 'default' : current['name']);
409
-
410
- var hasBreakpointFired = ft.hasBreakpointColumn(breakpointName);
537
+ var breakpointName = (current === null ? 'default' : current['name']),
538
+ hasBreakpointFired = ft.hasBreakpointColumn(breakpointName),
539
+ previousBreakpoint = $table.data('breakpoint');
411
540
 
412
541
  $table
542
+ .data('breakpoint', breakpointName)
413
543
  .removeClass('default breakpoint').removeClass(ft.breakpointNames)
414
- .addClass(breakpointName + (hasBreakpointFired ? ' breakpoint' : ''))
415
- .find('> thead > tr:last-child > th')
416
- .each(function () {
417
- var data = ft.columns[$(this).index()], selector = '', first = true;
418
- $.each(data.matches, function (m, match) {
419
- if (!first) {
420
- selector += ', ';
421
- }
422
- var count = match + 1;
423
- selector += '> tbody > tr:not(.footable-row-detail) > td:nth-child(' + count + ')';
424
- selector += ', > tfoot > tr:not(.footable-row-detail) > td:nth-child(' + count + ')';
425
- selector += ', > colgroup > col:nth-child(' + count + ')';
426
- first = false;
427
- });
544
+ .addClass(breakpointName + (hasBreakpointFired ? ' breakpoint' : ''));
545
+
546
+ //only do something if the breakpoint has changed
547
+ if (breakpointName !== previousBreakpoint) {
548
+ //trigger a redraw
549
+ $table.trigger(trg.redraw);
550
+ //raise a breakpoint event
551
+ ft.raise(evt.breakpoint, { 'breakpoint': breakpointName, 'info': info });
552
+ }
553
+ }
428
554
 
429
- selector += ', > thead > tr[data-group-row="true"] > th[data-group="' + data.group + '"]';
430
- var $column = $table.find(selector).add(this);
431
- if (data.hide[breakpointName] == false) $column.show();
432
- else $column.hide();
555
+ ft.raise(evt.resized, { 'old': pinfo, 'info': info });
556
+ };
433
557
 
434
- if ($table.find('> thead > tr.footable-group-row').length == 1) {
435
- var $groupcols = $table.find('> thead > tr:last-child > th[data-group="' + data.group + '"]:visible, > thead > tr:last-child > th[data-group="' + data.group + '"]:visible'),
436
- $group = $table.find('> thead > tr.footable-group-row > th[data-group="' + data.group + '"], > thead > tr.footable-group-row > td[data-group="' + data.group + '"]'),
437
- groupspan = 0;
558
+ ft.redraw = function () {
559
+ //add the toggler to each row
560
+ ft.addRowToggle();
438
561
 
439
- $.each($groupcols, function () {
440
- groupspan += parseInt($(this).attr('colspan') || 1);
441
- });
562
+ //bind the toggle selector click events
563
+ ft.bindToggleSelectors();
564
+
565
+ //set any cell classes defined for the columns
566
+ ft.setColumnClasses();
442
567
 
443
- if (groupspan > 0) $group.attr('colspan', groupspan).show();
444
- else $group.hide();
568
+ var $table = $(ft.table),
569
+ breakpointName = $table.data('breakpoint'),
570
+ hasBreakpointFired = ft.hasBreakpointColumn(breakpointName);
571
+
572
+ $table
573
+ .find('> tbody > tr:not(.' + cls.detail + ')').data('detail_created', false).end()
574
+ .find('> thead > tr:last-child > th')
575
+ .each(function () {
576
+ var data = ft.columns[$(this).index()], selector = '', first = true;
577
+ $.each(data.matches, function (m, match) {
578
+ if (!first) {
579
+ selector += ', ';
445
580
  }
446
- })
447
- .end()
448
- .find('> tbody > tr.footable-detail-show').each(function () {
449
- ft.createOrUpdateDetailRow(this);
581
+ var count = match + 1;
582
+ selector += '> tbody > tr:not(.' + cls.detail + ') > td:nth-child(' + count + ')';
583
+ selector += ', > tfoot > tr:not(.' + cls.detail + ') > td:nth-child(' + count + ')';
584
+ selector += ', > colgroup > col:nth-child(' + count + ')';
585
+ first = false;
450
586
  });
451
587
 
452
- $table.find('> tbody > tr.footable-detail-show:visible').each(function () {
453
- var $next = $(this).next();
454
- if ($next.hasClass('footable-row-detail')) {
455
- if (breakpointName == 'default' && !hasBreakpointFired) $next.hide();
456
- else $next.show();
588
+ selector += ', > thead > tr[data-group-row="true"] > th[data-group="' + data.group + '"]';
589
+ var $column = $table.find(selector).add(this);
590
+ if (data.hide[breakpointName] === false) $column.show();
591
+ else $column.hide();
592
+
593
+ if ($table.find('> thead > tr.footable-group-row').length === 1) {
594
+ var $groupcols = $table.find('> thead > tr:last-child > th[data-group="' + data.group + '"]:visible, > thead > tr:last-child > th[data-group="' + data.group + '"]:visible'),
595
+ $group = $table.find('> thead > tr.footable-group-row > th[data-group="' + data.group + '"], > thead > tr.footable-group-row > td[data-group="' + data.group + '"]'),
596
+ groupspan = 0;
597
+
598
+ $.each($groupcols, function () {
599
+ groupspan += parseInt($(this).attr('colspan') || 1, 10);
600
+ });
601
+
602
+ if (groupspan > 0) $group.attr('colspan', groupspan).show();
603
+ else $group.hide();
457
604
  }
605
+ })
606
+ .end()
607
+ .find('> tbody > tr.' + cls.detailShow).each(function () {
608
+ ft.createOrUpdateDetailRow(this);
458
609
  });
459
610
 
460
- // adding .footable-first-column and .footable-last-column to the first and last th and td of each row in order to allow
461
- // for styling if the first or last column is hidden (which won't work using :first-child or :last-child)
462
- $table.find('> thead > tr > th.footable-last-column, > tbody > tr > td.footable-last-column').removeClass('footable-last-column');
463
- $table.find('> thead > tr > th.footable-first-column, > tbody > tr > td.footable-first-column').removeClass('footable-first-column');
464
- $table.find('> thead > tr, > tbody > tr')
465
- .find('> th:visible:last, > td:visible:last')
466
- .addClass('footable-last-column')
467
- .end()
468
- .find('> th:visible:first, > td:visible:first')
469
- .addClass('footable-first-column');
470
-
471
- ft.raise('footable_breakpoint_' + breakpointName, { 'info': info });
472
- }
611
+ $table.find('> tbody > tr.' + cls.detailShow + ':visible').each(function () {
612
+ var $next = $(this).next();
613
+ if ($next.hasClass(cls.detail)) {
614
+ if (!hasBreakpointFired) $next.hide();
615
+ else $next.show();
616
+ }
617
+ });
473
618
 
474
- ft.raise('footable_resized', { 'old': pinfo, 'info': info });
619
+ // adding .footable-first-column and .footable-last-column to the first and last th and td of each row in order to allow
620
+ // for styling if the first or last column is hidden (which won't work using :first-child or :last-child)
621
+ $table.find('> thead > tr > th.footable-last-column, > tbody > tr > td.footable-last-column').removeClass('footable-last-column');
622
+ $table.find('> thead > tr > th.footable-first-column, > tbody > tr > td.footable-first-column').removeClass('footable-first-column');
623
+ $table.find('> thead > tr, > tbody > tr')
624
+ .find('> th:visible:last, > td:visible:last')
625
+ .addClass('footable-last-column')
626
+ .end()
627
+ .find('> th:visible:first, > td:visible:first')
628
+ .addClass('footable-first-column');
629
+
630
+ ft.raise(evt.redrawn);
475
631
  };
476
632
 
477
- ft.toggleDetail = function (actualRow) {
478
- var $row = $(actualRow),
479
- created = ft.createOrUpdateDetailRow($row.get(0)),
633
+ ft.toggleDetail = function (row) {
634
+ var $row = (row.jquery) ? row : $(row),
480
635
  $next = $row.next();
481
636
 
482
- if (!created && $next.is(':visible')) {
483
- $row.removeClass('footable-detail-show');
637
+ //check if the row is already expanded
638
+ if ($row.hasClass(cls.detailShow)) {
639
+ $row.removeClass(cls.detailShow);
640
+
484
641
  //only hide the next row if it's a detail row
485
- if ($next.hasClass('footable-row-detail')) $next.hide();
642
+ if ($next.hasClass(cls.detail)) $next.hide();
643
+
644
+ ft.raise(evt.rowCollapsed, { 'row': $row[0] });
645
+
486
646
  } else {
487
- $row.addClass('footable-detail-show');
488
- $next.show();
647
+ ft.createOrUpdateDetailRow($row[0]);
648
+ $row.addClass(cls.detailShow);
649
+ $row.next().show();
650
+
651
+ ft.raise(evt.rowExpanded, { 'row': $row[0] });
489
652
  }
490
653
  };
491
654
 
655
+ ft.removeRow = function (row) {
656
+ var $row = (row.jquery) ? row : $(row);
657
+ if ($row.hasClass(cls.detail)) {
658
+ $row = $row.prev();
659
+ }
660
+ var $next = $row.next();
661
+ if ($row.data('detail_created') === true) {
662
+ //remove the detail row
663
+ $next.remove();
664
+ }
665
+ $row.remove();
666
+
667
+ //raise event
668
+ ft.raise(evt.rowRemoved);
669
+ };
670
+
671
+ ft.appendRow = function (row) {
672
+ var $row = (row.jquery) ? row : $(row);
673
+ $(ft.table).find('tbody').append($row);
674
+
675
+ //redraw the table
676
+ ft.redraw();
677
+ };
678
+
492
679
  ft.getColumnFromTdIndex = function (index) {
493
680
  /// <summary>Returns the correct column data for the supplied index taking into account colspans.</summary>
494
681
  /// <param name="index">The index to retrieve the column data for.</param>
@@ -505,30 +692,37 @@
505
692
 
506
693
  ft.createOrUpdateDetailRow = function (actualRow) {
507
694
  var $row = $(actualRow), $next = $row.next(), $detail, values = [];
508
- if ($row.is(':hidden')) return false; //if the row is hidden for some readon (perhaps filtered) then get out of here
509
- ft.raise('footable_rowdetailupdated', { 'row': $row, 'detail': $next });
695
+ if ($row.data('detail_created') === true) return true;
696
+
697
+ if ($row.is(':hidden')) return false; //if the row is hidden for some reason (perhaps filtered) then get out of here
698
+ ft.raise(evt.rowDetailUpdating, { 'row': $row, 'detail': $next });
510
699
  $row.find('> td:hidden').each(function () {
511
700
  var index = $(this).index(), column = ft.getColumnFromTdIndex(index), name = column.name;
512
- if (column.ignore == true) return true;
701
+ if (column.ignore === true) return true;
513
702
 
514
703
  if (index in column.names) name = column.names[index];
515
704
  values.push({ 'name': name, 'value': ft.parse(this, column), 'display': $.trim($(this).html()), 'group': column.group, 'groupName': column.groupName });
516
705
  return true;
517
706
  });
518
- if (values.length == 0) return false; //return if we don't have any data to show
707
+ if (values.length === 0) return false; //return if we don't have any data to show
519
708
  var colspan = $row.find('> td:visible').length;
520
- var exists = $next.hasClass('footable-row-detail');
709
+ var exists = $next.hasClass(cls.detail);
521
710
  if (!exists) { // Create
522
- $next = $('<tr class="footable-row-detail"><td class="footable-cell-detail"><div class="footable-row-detail-inner"></div></td></tr>');
711
+ $next = $('<tr class="' + cls.detail + '"><td class="' + cls.detailCell + '"><div class="' + cls.detailInner + '"></div></td></tr>');
523
712
  $row.after($next);
524
713
  }
525
714
  $next.find('> td:first').attr('colspan', colspan);
526
- $detail = $next.find('.footable-row-detail-inner').empty();
527
- opt.createDetail($detail, values);
715
+ $detail = $next.find('.' + cls.detailInner).empty();
716
+ opt.createDetail($detail, values, opt.createGroupedDetail, opt.detailSeparator, cls);
717
+ $row.data('detail_created', true);
718
+ ft.raise(evt.rowDetailUpdated, { 'row': $row, 'detail': $next });
528
719
  return !exists;
529
720
  };
530
721
 
531
722
  ft.raise = function (eventName, args) {
723
+
724
+ if (ft.options.debug === true && $.isFunction(ft.options.log)) ft.options.log(eventName, 'event');
725
+
532
726
  args = args || { };
533
727
  var def = { 'ft': ft };
534
728
  $.extend(true, def, args);
@@ -542,5 +736,5 @@
542
736
 
543
737
  ft.init();
544
738
  return ft;
545
- };
739
+ }
546
740
  })(jQuery, window);