foo_table-rails 0.5.0 → 2.0.0

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