footable-on-rails 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +17 -0
  3. data/FooTable-On-Rails.gemspec +23 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +34 -0
  7. data/Rakefile +1 -0
  8. data/lib/footable-on-rails.rb +12 -0
  9. data/lib/footable-on-rails/engine.rb +6 -0
  10. data/lib/footable-on-rails/railtie.rb +5 -0
  11. data/lib/footable-on-rails/version.rb +5 -0
  12. data/vendor/assets/images/footable-on-rails/minus.png +0 -0
  13. data/vendor/assets/images/footable-on-rails/plus.png +0 -0
  14. data/vendor/assets/javascripts/footable-on-rails/footable.js +784 -0
  15. data/vendor/assets/javascripts/footable-on-rails/index.js +2 -0
  16. data/vendor/assets/javascripts/footable-on-rails/plugins/footable.filter.js +144 -0
  17. data/vendor/assets/javascripts/footable-on-rails/plugins/footable.paginate.js +241 -0
  18. data/vendor/assets/javascripts/footable-on-rails/plugins/footable.sort.js +190 -0
  19. data/vendor/assets/javascripts/footable-on-rails/plugins/footable.striping.js +57 -0
  20. data/vendor/assets/stylesheets/footable-on-rails/fonts/footable.eot +0 -0
  21. data/vendor/assets/stylesheets/footable-on-rails/fonts/footable.svg +78 -0
  22. data/vendor/assets/stylesheets/footable-on-rails/fonts/footable.ttf +0 -0
  23. data/vendor/assets/stylesheets/footable-on-rails/fonts/footable.woff +0 -0
  24. data/vendor/assets/stylesheets/footable-on-rails/footable.core.css +178 -0
  25. data/vendor/assets/stylesheets/footable-on-rails/footable.core.min.css +1 -0
  26. data/vendor/assets/stylesheets/footable-on-rails/footable.metro.css +152 -0
  27. data/vendor/assets/stylesheets/footable-on-rails/footable.metro.min.css +1 -0
  28. data/vendor/assets/stylesheets/footable-on-rails/footable.standalone.css +181 -0
  29. data/vendor/assets/stylesheets/footable-on-rails/footable.standalone.min.css +1 -0
  30. data/vendor/assets/stylesheets/footable-on-rails/index.css +9 -0
  31. metadata +101 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NTFiNTZjYTNhMTFiYzNmMDA0MTBlZmRlYzk0NzYzZTIzYWRjNTIwOA==
5
+ data.tar.gz: !binary |-
6
+ ZWQ0M2ViYjRkMzFmYzE2MDliMTlhZTU1NDEyYmY5M2FjYWExYTY0Yw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ N2NhMzY0OWM2MDU1MjNkOTgyNGU5NGI4NTBlMTZmMDljY2NkODhmYjU2ZDY0
10
+ NTg5NDc2NDNiMWM1MjlkNGNmYTgyNmI5MzgwNmUxNjcwNGU2NjQyMjVhOTUz
11
+ YTIzZWVlOGZjOTI5ZDM5YmZkMjQ1ODQyOTUxZGRiNTYxMDI4MTc=
12
+ data.tar.gz: !binary |-
13
+ YWNkNjhkZTBlMDVkYzA0Njk0NWQ3OWY0NWM2MzdhNzcxYWRkMjQwM2FlODVm
14
+ NTYzM2E2Y2JjYjEwM2MyYjEzYjIyNmUxZmEzYzA4MmQ0NTAxODRiOGQ4NmUz
15
+ MzJmMjgxNTIzZWVkM2IwMWU4N2I4Nzk3MjljMDYzNTY4YjFiODY=
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'footable-on-rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "footable-on-rails"
8
+ spec.version = FootableOnRails::Rails::VERSION
9
+ spec.authors = ["Rian Andrea"]
10
+ spec.email = ["rian.andrea@gmail.com"]
11
+ spec.description = %q{Integrates FooTable On Rails}
12
+ spec.summary = %q{Integrates FooTable On Rails}
13
+ spec.homepage = "https://github.com/riandrea/FooTable-On-Rails"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in FooTable-On-Rails.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Rian Andrea
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,34 @@
1
+ # FooTable::On::Rails
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ gem 'footable-on-rails'
8
+
9
+ And then execute:
10
+
11
+ $ bundle install
12
+
13
+
14
+ ## Usage
15
+
16
+ 1. Add this line below before require_tree on 'application.css' file
17
+
18
+ *= require footable-on-rails
19
+
20
+ 2. Add this line after the above line to use the metro style
21
+
22
+ *= require footable-on-rails/footable.metro.css
23
+
24
+ 2. Add this line below before require_tree on 'application.js'
25
+
26
+ //= require footable-on-rails
27
+
28
+ ## Contributing
29
+
30
+ 1. Fork it
31
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
32
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
33
+ 4. Push to the branch (`git push origin my-new-feature`)
34
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,12 @@
1
+ require "rails"
2
+ require "footable-on-rails/version"
3
+
4
+ module FootableOnRails
5
+ module Rails
6
+ if ::Rails.version < "3.1"
7
+ require "footable-on-rails/railtie"
8
+ else
9
+ require "footable-on-rails/engine"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ module FootableOnRails
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module FootableOnRails
2
+ module Rails
3
+ class Railtie < ::Rails::Railtie; end
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module FootableOnRails
2
+ module Rails
3
+ VERSION = "0.0.4"
4
+ end
5
+ end
@@ -0,0 +1,784 @@
1
+ /*!
2
+ * FooTable - Awesome Responsive Tables
3
+ * Version : 2.0.1.4
4
+ * http://fooplugins.com/plugins/footable-jquery/
5
+ *
6
+ * Requires jQuery - http://jquery.com/
7
+ *
8
+ * Copyright 2014 Steven Usher & Brad Vincent
9
+ * Released under the MIT license
10
+ * You are free to use FooTable in commercial projects as long as this copyright header is left intact.
11
+ *
12
+ * Date: 16 Feb 2014
13
+ */
14
+ (function ($, w, undefined) {
15
+ w.footable = {
16
+ options: {
17
+ delay: 100, // The number of millseconds to wait before triggering the react event
18
+ breakpoints: { // The different screen resolution breakpoints
19
+ phone: 480,
20
+ tablet: 1024
21
+ },
22
+ parsers: { // The default parser to parse the value out of a cell (values are used in building up row detail)
23
+ alpha: function (cell) {
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;
31
+ }
32
+ },
33
+ addRowToggle: true,
34
+ calculateWidthOverride: null,
35
+ toggleSelector: ' > tbody > tr:not(.footable-row-detail)', //the selector to show/hide the detail row
36
+ columnDataSelector: '> thead > tr:last-child > th, > thead > tr:last-child > td', //the selector used to find the column data in the thead
37
+ detailSeparator: ':', //the separator character used when building up the detail row
38
+ toggleHTMLElement: '<span />', // override this if you want to insert a click target rather than use a background image.
39
+ createGroupedDetail: function (data) {
40
+ var groups = { '_none': { 'name': null, 'data': [] } };
41
+ for (var i = 0; i < data.length; i++) {
42
+ var groupid = data[i].group;
43
+ if (groupid !== null) {
44
+ if (!(groupid in groups))
45
+ groups[groupid] = { 'name': data[i].groupName || data[i].group, 'data': [] };
46
+
47
+ groups[groupid].data.push(data[i]);
48
+ } else {
49
+ groups._none.data.push(data[i]);
50
+ }
51
+ }
52
+ return groups;
53
+ },
54
+ createDetail: function (element, data, createGroupedDetail, separatorChar, classes) {
55
+ /// <summary>This function is used by FooTable to generate the detail view seen when expanding a collapsed row.</summary>
56
+ /// <param name="element">This is the div that contains all the detail row information, anything could be added to it.</param>
57
+ /// <param name="data">
58
+ /// This is an array of objects containing the cell information for the current row.
59
+ /// These objects look like the below:
60
+ /// obj = {
61
+ /// 'name': String, // The name of the column
62
+ /// 'value': Object, // The value parsed from the cell using the parsers. This could be a string, a number or whatever the parser outputs.
63
+ /// 'display': String, // This is the actual HTML from the cell, so if you have images etc you want moved this is the one to use and is the default value used.
64
+ /// 'group': String, // This is the identifier used in the data-group attribute of the column.
65
+ /// 'groupName': String // This is the actual name of the group the column belongs to.
66
+ /// }
67
+ /// </param>
68
+ /// <param name="createGroupedDetail">The grouping function to group the data</param>
69
+ /// <param name="separatorChar">The separator charactor used</param>
70
+ /// <param name="classes">The array of class names used to build up the detail row</param>
71
+
72
+ var groups = createGroupedDetail(data);
73
+ for (var group in groups) {
74
+ if (groups[group].data.length === 0) continue;
75
+ if (group !== '_none') element.append('<div class="' + classes.detailInnerGroup + '">' + groups[group].name + '</div>');
76
+
77
+ for (var j = 0; j < groups[group].data.length; j++) {
78
+ var separator = (groups[group].data[j].name) ? separatorChar : '';
79
+ 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>');
80
+ }
81
+ }
82
+ },
83
+ classes: {
84
+ main: 'footable',
85
+ loading: 'footable-loading',
86
+ loaded: 'footable-loaded',
87
+ toggle: 'footable-toggle',
88
+ disabled: 'footable-disabled',
89
+ detail: 'footable-row-detail',
90
+ detailCell: 'footable-row-detail-cell',
91
+ detailInner: 'footable-row-detail-inner',
92
+ detailInnerRow: 'footable-row-detail-row',
93
+ detailInnerGroup: 'footable-row-detail-group',
94
+ detailInnerName: 'footable-row-detail-name',
95
+ detailInnerValue: 'footable-row-detail-value',
96
+ detailShow: 'footable-detail-show'
97
+ },
98
+ triggers: {
99
+ initialize: 'footable_initialize', //trigger this event to force FooTable to reinitialize
100
+ resize: 'footable_resize', //trigger this event to force FooTable to resize
101
+ redraw: 'footable_redraw', //trigger this event to force FooTable to redraw
102
+ toggleRow: 'footable_toggle_row', //trigger this event to force FooTable to toggle a row
103
+ expandFirstRow: 'footable_expand_first_row', //trigger this event to force FooTable to expand the first row
104
+ expandAll: 'footable_expand_all', //trigger this event to force FooTable to expand all rows
105
+ collapseAll: 'footable_collapse_all' //trigger this event to force FooTable to collapse all rows
106
+ },
107
+ events: {
108
+ alreadyInitialized: 'footable_already_initialized', //fires when the FooTable has already been initialized
109
+ initializing: 'footable_initializing', //fires before FooTable starts initializing
110
+ initialized: 'footable_initialized', //fires after FooTable has finished initializing
111
+ resizing: 'footable_resizing', //fires before FooTable resizes
112
+ resized: 'footable_resized', //fires after FooTable has resized
113
+ redrawn: 'footable_redrawn', //fires after FooTable has redrawn
114
+ breakpoint: 'footable_breakpoint', //fires inside the resize function, when a breakpoint is hit
115
+ columnData: 'footable_column_data', //fires when setting up column data. Plugins should use this event to capture their own info about a column
116
+ rowDetailUpdating: 'footable_row_detail_updating', //fires before a detail row is updated
117
+ rowDetailUpdated: 'footable_row_detail_updated', //fires when a detail row is being updated
118
+ rowCollapsed: 'footable_row_collapsed', //fires when a row is collapsed
119
+ rowExpanded: 'footable_row_expanded', //fires when a row is expanded
120
+ rowRemoved: 'footable_row_removed', //fires when a row is removed
121
+ reset: 'footable_reset' //fires when FooTable is reset
122
+ },
123
+ debug: false, // Whether or not to log information to the console.
124
+ log: null
125
+ },
126
+
127
+ version: {
128
+ major: 0, minor: 5,
129
+ toString: function () {
130
+ return w.footable.version.major + '.' + w.footable.version.minor;
131
+ },
132
+ parse: function (str) {
133
+ version = /(\d+)\.?(\d+)?\.?(\d+)?/.exec(str);
134
+ return {
135
+ major: parseInt(version[1], 10) || 0,
136
+ minor: parseInt(version[2], 10) || 0,
137
+ patch: parseInt(version[3], 10) || 0
138
+ };
139
+ }
140
+ },
141
+
142
+ plugins: {
143
+ _validate: function (plugin) {
144
+ ///<summary>Simple validation of the <paramref name="plugin"/> to make sure any members called by FooTable actually exist.</summary>
145
+ ///<param name="plugin">The object defining the plugin, this should implement a string property called "name" and a function called "init".</param>
146
+
147
+ if (!$.isFunction(plugin)) {
148
+ if (w.footable.options.debug === true) console.error('Validation failed, expected type "function", received type "{0}".', typeof plugin);
149
+ return false;
150
+ }
151
+ var p = new plugin();
152
+ if (typeof p['name'] !== 'string') {
153
+ if (w.footable.options.debug === true) console.error('Validation failed, plugin does not implement a string property called "name".', p);
154
+ return false;
155
+ }
156
+ if (!$.isFunction(p['init'])) {
157
+ if (w.footable.options.debug === true) console.error('Validation failed, plugin "' + p['name'] + '" does not implement a function called "init".', p);
158
+ return false;
159
+ }
160
+ if (w.footable.options.debug === true) console.log('Validation succeeded for plugin "' + p['name'] + '".', p);
161
+ return true;
162
+ },
163
+ registered: [], // An array containing all registered plugins.
164
+ register: function (plugin, options) {
165
+ ///<summary>Registers a <paramref name="plugin"/> and its default <paramref name="options"/> with FooTable.</summary>
166
+ ///<param name="plugin">The plugin that should implement a string property called "name" and a function called "init".</param>
167
+ ///<param name="options">The default options to merge with the FooTable's base options.</param>
168
+
169
+ if (w.footable.plugins._validate(plugin)) {
170
+ w.footable.plugins.registered.push(plugin);
171
+ if (typeof options === 'object') $.extend(true, w.footable.options, options);
172
+ }
173
+ },
174
+ load: function(instance){
175
+ var loaded = [], registered, i;
176
+ for(i = 0; i < w.footable.plugins.registered.length; i++){
177
+ try {
178
+ registered = w.footable.plugins.registered[i];
179
+ loaded.push(new registered(instance));
180
+ } catch (err) {
181
+ if (w.footable.options.debug === true) console.error(err);
182
+ }
183
+ }
184
+ return loaded;
185
+ },
186
+ init: function (instance) {
187
+ ///<summary>Loops through all registered plugins and calls the "init" method supplying the current <paramref name="instance"/> of the FooTable as the first parameter.</summary>
188
+ ///<param name="instance">The current instance of the FooTable that the plugin is being initialized for.</param>
189
+
190
+ for (var i = 0; i < instance.plugins.length; i++) {
191
+ try {
192
+ instance.plugins[i]['init'](instance);
193
+ } catch (err) {
194
+ if (w.footable.options.debug === true) console.error(err);
195
+ }
196
+ }
197
+ }
198
+ }
199
+ };
200
+
201
+ var instanceCount = 0;
202
+
203
+ $.fn.footable = function (options) {
204
+ ///<summary>The main constructor call to initialize the plugin using the supplied <paramref name="options"/>.</summary>
205
+ ///<param name="options">
206
+ ///<para>A JSON object containing user defined options for the plugin to use. Any options not supplied will have a default value assigned.</para>
207
+ ///<para>Check the documentation or the default options object above for more information on available options.</para>
208
+ ///</param>
209
+
210
+ options = options || {};
211
+ var o = $.extend(true, {}, w.footable.options, options); //merge user and default options
212
+ return this.each(function () {
213
+ instanceCount++;
214
+ var footable = new Footable(this, o, instanceCount);
215
+ $(this).data('footable', footable);
216
+ });
217
+ };
218
+
219
+ //helper for using timeouts
220
+ function Timer() {
221
+ ///<summary>Simple timer object created around a timeout.</summary>
222
+ var t = this;
223
+ t.id = null;
224
+ t.busy = false;
225
+ t.start = function (code, milliseconds) {
226
+ ///<summary>Starts the timer and waits the specified amount of <paramref name="milliseconds"/> before executing the supplied <paramref name="code"/>.</summary>
227
+ ///<param name="code">The code to execute once the timer runs out.</param>
228
+ ///<param name="milliseconds">The time in milliseconds to wait before executing the supplied <paramref name="code"/>.</param>
229
+
230
+ if (t.busy) {
231
+ return;
232
+ }
233
+ t.stop();
234
+ t.id = setTimeout(function () {
235
+ code();
236
+ t.id = null;
237
+ t.busy = false;
238
+ }, milliseconds);
239
+ t.busy = true;
240
+ };
241
+ t.stop = function () {
242
+ ///<summary>Stops the timer if its runnning and resets it back to its starting state.</summary>
243
+
244
+ if (t.id !== null) {
245
+ clearTimeout(t.id);
246
+ t.id = null;
247
+ t.busy = false;
248
+ }
249
+ };
250
+ }
251
+
252
+ function Footable(t, o, id) {
253
+ ///<summary>Inits a new instance of the plugin.</summary>
254
+ ///<param name="t">The main table element to apply this plugin to.</param>
255
+ ///<param name="o">The options supplied to the plugin. Check the defaults object to see all available options.</param>
256
+ ///<param name="id">The id to assign to this instance of the plugin.</param>
257
+
258
+ var ft = this;
259
+ ft.id = id;
260
+ ft.table = t;
261
+ ft.options = o;
262
+ ft.breakpoints = [];
263
+ ft.breakpointNames = '';
264
+ ft.columns = {};
265
+ ft.plugins = w.footable.plugins.load(ft);
266
+
267
+ var opt = ft.options,
268
+ cls = opt.classes,
269
+ evt = opt.events,
270
+ trg = opt.triggers,
271
+ indexOffset = 0;
272
+
273
+ // This object simply houses all the timers used in the FooTable.
274
+ ft.timers = {
275
+ resize: new Timer(),
276
+ register: function (name) {
277
+ ft.timers[name] = new Timer();
278
+ return ft.timers[name];
279
+ }
280
+ };
281
+
282
+ ft.init = function () {
283
+ var $window = $(w), $table = $(ft.table);
284
+
285
+ w.footable.plugins.init(ft);
286
+
287
+ if ($table.hasClass(cls.loaded)) {
288
+ //already loaded FooTable for the table, so don't init again
289
+ ft.raise(evt.alreadyInitialized);
290
+ return;
291
+ }
292
+
293
+ //raise the initializing event
294
+ ft.raise(evt.initializing);
295
+
296
+ $table.addClass(cls.loading);
297
+
298
+ // Get the column data once for the life time of the plugin
299
+ $table.find(opt.columnDataSelector).each(function () {
300
+ var data = ft.getColumnData(this);
301
+ ft.columns[data.index] = data;
302
+ });
303
+
304
+ // Create a nice friendly array to work with out of the breakpoints object.
305
+ for (var name in opt.breakpoints) {
306
+ ft.breakpoints.push({ 'name': name, 'width': opt.breakpoints[name] });
307
+ ft.breakpointNames += (name + ' ');
308
+ }
309
+
310
+ // Sort the breakpoints so the smallest is checked first
311
+ ft.breakpoints.sort(function (a, b) {
312
+ return a['width'] - b['width'];
313
+ });
314
+
315
+ $table
316
+ .unbind(trg.initialize)
317
+ //bind to FooTable initialize trigger
318
+ .bind(trg.initialize, function () {
319
+ //remove previous "state" (to "force" a resize)
320
+ $table.removeData('footable_info');
321
+ $table.data('breakpoint', '');
322
+
323
+ //trigger the FooTable resize
324
+ $table.trigger(trg.resize);
325
+
326
+ //remove the loading class
327
+ $table.removeClass(cls.loading);
328
+
329
+ //add the FooTable and loaded class
330
+ $table.addClass(cls.loaded).addClass(cls.main);
331
+
332
+ //raise the initialized event
333
+ ft.raise(evt.initialized);
334
+ })
335
+ .unbind(trg.redraw)
336
+ //bind to FooTable redraw trigger
337
+ .bind(trg.redraw, function () {
338
+ ft.redraw();
339
+ })
340
+ .unbind(trg.resize)
341
+ //bind to FooTable resize trigger
342
+ .bind(trg.resize, function () {
343
+ ft.resize();
344
+ })
345
+ .unbind(trg.expandFirstRow)
346
+ //bind to FooTable expandFirstRow trigger
347
+ .bind(trg.expandFirstRow, function () {
348
+ $table.find(opt.toggleSelector).first().not('.' + cls.detailShow).trigger(trg.toggleRow);
349
+ })
350
+ .unbind(trg.expandAll)
351
+ //bind to FooTable expandFirstRow trigger
352
+ .bind(trg.expandAll, function () {
353
+ $table.find(opt.toggleSelector).not('.' + cls.detailShow).trigger(trg.toggleRow);
354
+ })
355
+ .unbind(trg.collapseAll)
356
+ //bind to FooTable expandFirstRow trigger
357
+ .bind(trg.collapseAll, function () {
358
+ $table.find('.' + cls.detailShow).trigger(trg.toggleRow);
359
+ });
360
+
361
+ //trigger a FooTable initialize
362
+ $table.trigger(trg.initialize);
363
+
364
+ //bind to window resize
365
+ $window
366
+ .bind('resize.footable', function () {
367
+ ft.timers.resize.stop();
368
+ ft.timers.resize.start(function () {
369
+ ft.raise(trg.resize);
370
+ }, opt.delay);
371
+ });
372
+ };
373
+
374
+ ft.addRowToggle = function () {
375
+ if (!opt.addRowToggle) return;
376
+
377
+ var $table = $(ft.table),
378
+ hasToggleColumn = false;
379
+
380
+ //first remove all toggle spans
381
+ $table.find('span.' + cls.toggle).remove();
382
+
383
+ for (var c in ft.columns) {
384
+ var col = ft.columns[c];
385
+ if (col.toggle) {
386
+ hasToggleColumn = true;
387
+ var selector = '> tbody > tr:not(.' + cls.detail + ',.' + cls.disabled + ') > td:nth-child(' + (parseInt(col.index, 10) + 1) + ')';
388
+ $table.find(selector).not('.' + cls.detailCell).prepend($(opt.toggleHTMLElement).addClass(cls.toggle));
389
+ return;
390
+ }
391
+ }
392
+ //check if we have an toggle column. If not then add it to the first column just to be safe
393
+ if (!hasToggleColumn) {
394
+ $table
395
+ .find('> tbody > tr:not(.' + cls.detail + ',.' + cls.disabled + ') > td:first-child')
396
+ .not('.' + cls.detailCell)
397
+ .prepend($(opt.toggleHTMLElement).addClass(cls.toggle));
398
+ }
399
+ };
400
+
401
+ ft.setColumnClasses = function () {
402
+ $table = $(ft.table);
403
+ for (var c in ft.columns) {
404
+ var col = ft.columns[c];
405
+ if (col.className !== null) {
406
+ var selector = '', first = true;
407
+ $.each(col.matches, function (m, match) { //support for colspans
408
+ if (!first) selector += ', ';
409
+ selector += '> tbody > tr:not(.' + cls.detail + ') > td:nth-child(' + (parseInt(match, 10) + 1) + ')';
410
+ first = false;
411
+ });
412
+ //add the className to the cells specified by data-class="blah"
413
+ $table.find(selector).not('.' + cls.detailCell).addClass(col.className);
414
+ }
415
+ }
416
+ };
417
+
418
+ //moved this out into it's own function so that it can be called from other add-ons
419
+ ft.bindToggleSelectors = function () {
420
+ var $table = $(ft.table);
421
+
422
+ if (!ft.hasAnyBreakpointColumn()) return;
423
+
424
+ $table.find(opt.toggleSelector).unbind(trg.toggleRow).bind(trg.toggleRow, function (e) {
425
+ var $row = $(this).is('tr') ? $(this) : $(this).parents('tr:first');
426
+ ft.toggleDetail($row);
427
+ });
428
+
429
+ $table.find(opt.toggleSelector).unbind('click.footable').bind('click.footable', function (e) {
430
+ if ($table.is('.breakpoint') && $(e.target).is('td,.'+ cls.toggle)) {
431
+ $(this).trigger(trg.toggleRow);
432
+ }
433
+ });
434
+ };
435
+
436
+ ft.parse = function (cell, column) {
437
+ var parser = opt.parsers[column.type] || opt.parsers.alpha;
438
+ return parser(cell);
439
+ };
440
+
441
+ ft.getColumnData = function (th) {
442
+ var $th = $(th), hide = $th.data('hide'), index = $th.index();
443
+ hide = hide || '';
444
+ hide = jQuery.map(hide.split(','), function (a) {
445
+ return jQuery.trim(a);
446
+ });
447
+ var data = {
448
+ 'index': index,
449
+ 'hide': { },
450
+ 'type': $th.data('type') || 'alpha',
451
+ 'name': $th.data('name') || $.trim($th.text()),
452
+ 'ignore': $th.data('ignore') || false,
453
+ 'toggle': $th.data('toggle') || false,
454
+ 'className': $th.data('class') || null,
455
+ 'matches': [],
456
+ 'names': { },
457
+ 'group': $th.data('group') || null,
458
+ 'groupName': null
459
+ };
460
+
461
+ if (data.group !== null) {
462
+ 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();
463
+ data.groupName = ft.parse($group, { 'type': 'alpha' });
464
+ }
465
+
466
+ var pcolspan = parseInt($th.prev().attr('colspan') || 0, 10);
467
+ indexOffset += pcolspan > 1 ? pcolspan - 1 : 0;
468
+ var colspan = parseInt($th.attr('colspan') || 0, 10), curindex = data.index + indexOffset;
469
+ if (colspan > 1) {
470
+ var names = $th.data('names');
471
+ names = names || '';
472
+ names = names.split(',');
473
+ for (var i = 0; i < colspan; i++) {
474
+ data.matches.push(i + curindex);
475
+ if (i < names.length) data.names[i + curindex] = names[i];
476
+ }
477
+ } else {
478
+ data.matches.push(curindex);
479
+ }
480
+
481
+ data.hide['default'] = ($th.data('hide') === "all") || ($.inArray('default', hide) >= 0);
482
+
483
+ var hasBreakpoint = false;
484
+ for (var name in opt.breakpoints) {
485
+ data.hide[name] = ($th.data('hide') === "all") || ($.inArray(name, hide) >= 0);
486
+ hasBreakpoint = hasBreakpoint || data.hide[name];
487
+ }
488
+ data.hasBreakpoint = hasBreakpoint;
489
+ var e = ft.raise(evt.columnData, { 'column': { 'data': data, 'th': th } });
490
+ return e.column.data;
491
+ };
492
+
493
+ ft.getViewportWidth = function () {
494
+ return window.innerWidth || (document.body ? document.body.offsetWidth : 0);
495
+ };
496
+
497
+ ft.calculateWidth = function ($table, info) {
498
+ if (jQuery.isFunction(opt.calculateWidthOverride)) {
499
+ return opt.calculateWidthOverride($table, info);
500
+ }
501
+ if (info.viewportWidth < info.width) info.width = info.viewportWidth;
502
+ if (info.parentWidth < info.width) info.width = info.parentWidth;
503
+ return info;
504
+ };
505
+
506
+ ft.hasBreakpointColumn = function (breakpoint) {
507
+ for (var c in ft.columns) {
508
+ if (ft.columns[c].hide[breakpoint]) {
509
+ if (ft.columns[c].ignore) {
510
+ continue;
511
+ }
512
+ return true;
513
+ }
514
+ }
515
+ return false;
516
+ };
517
+
518
+ ft.hasAnyBreakpointColumn = function () {
519
+ for (var c in ft.columns) {
520
+ if (ft.columns[c].hasBreakpoint) {
521
+ return true;
522
+ }
523
+ }
524
+ return false;
525
+ };
526
+
527
+ ft.resize = function () {
528
+ var $table = $(ft.table);
529
+
530
+ if (!$table.is(':visible')) {
531
+ return;
532
+ } //we only care about FooTables that are visible
533
+
534
+ if (!ft.hasAnyBreakpointColumn()) {
535
+ return;
536
+ } //we only care about FooTables that have breakpoints
537
+
538
+ var info = {
539
+ 'width': $table.width(), //the table width
540
+ 'viewportWidth': ft.getViewportWidth(), //the width of the viewport
541
+ 'parentWidth': $table.parent().width() //the width of the parent
542
+ };
543
+
544
+ info = ft.calculateWidth($table, info);
545
+
546
+ var pinfo = $table.data('footable_info');
547
+ $table.data('footable_info', info);
548
+ ft.raise(evt.resizing, { 'old': pinfo, 'info': info });
549
+
550
+ // This (if) statement is here purely to make sure events aren't raised twice as mobile safari seems to do
551
+ if (!pinfo || (pinfo && pinfo.width && pinfo.width !== info.width)) {
552
+
553
+ var current = null, breakpoint;
554
+ for (var i = 0; i < ft.breakpoints.length; i++) {
555
+ breakpoint = ft.breakpoints[i];
556
+ if (breakpoint && breakpoint.width && info.width <= breakpoint.width) {
557
+ current = breakpoint;
558
+ break;
559
+ }
560
+ }
561
+
562
+ var breakpointName = (current === null ? 'default' : current['name']),
563
+ hasBreakpointFired = ft.hasBreakpointColumn(breakpointName),
564
+ previousBreakpoint = $table.data('breakpoint');
565
+
566
+ $table
567
+ .data('breakpoint', breakpointName)
568
+ .removeClass('default breakpoint').removeClass(ft.breakpointNames)
569
+ .addClass(breakpointName + (hasBreakpointFired ? ' breakpoint' : ''));
570
+
571
+ //only do something if the breakpoint has changed
572
+ if (breakpointName !== previousBreakpoint) {
573
+ //trigger a redraw
574
+ $table.trigger(trg.redraw);
575
+ //raise a breakpoint event
576
+ ft.raise(evt.breakpoint, { 'breakpoint': breakpointName, 'info': info });
577
+ }
578
+ }
579
+
580
+ ft.raise(evt.resized, { 'old': pinfo, 'info': info });
581
+ };
582
+
583
+ ft.redraw = function () {
584
+ //add the toggler to each row
585
+ ft.addRowToggle();
586
+
587
+ //bind the toggle selector click events
588
+ ft.bindToggleSelectors();
589
+
590
+ //set any cell classes defined for the columns
591
+ ft.setColumnClasses();
592
+
593
+ var $table = $(ft.table),
594
+ breakpointName = $table.data('breakpoint'),
595
+ hasBreakpointFired = ft.hasBreakpointColumn(breakpointName);
596
+
597
+ $table
598
+ .find('> tbody > tr:not(.' + cls.detail + ')').data('detail_created', false).end()
599
+ .find('> thead > tr:last-child > th')
600
+ .each(function () {
601
+ var data = ft.columns[$(this).index()], selector = '', first = true;
602
+ $.each(data.matches, function (m, match) {
603
+ if (!first) {
604
+ selector += ', ';
605
+ }
606
+ var count = match + 1;
607
+ selector += '> tbody > tr:not(.' + cls.detail + ') > td:nth-child(' + count + ')';
608
+ selector += ', > tfoot > tr:not(.' + cls.detail + ') > td:nth-child(' + count + ')';
609
+ selector += ', > colgroup > col:nth-child(' + count + ')';
610
+ first = false;
611
+ });
612
+
613
+ selector += ', > thead > tr[data-group-row="true"] > th[data-group="' + data.group + '"]';
614
+ var $column = $table.find(selector).add(this);
615
+ if (breakpointName !== '') {
616
+ if (data.hide[breakpointName] === false) $column.addClass('footable-visible').show();
617
+ else $column.removeClass('footable-visible').hide();
618
+ }
619
+
620
+ if ($table.find('> thead > tr.footable-group-row').length === 1) {
621
+ var $groupcols = $table.find('> thead > tr:last-child > th[data-group="' + data.group + '"]:visible, > thead > tr:last-child > th[data-group="' + data.group + '"]:visible'),
622
+ $group = $table.find('> thead > tr.footable-group-row > th[data-group="' + data.group + '"], > thead > tr.footable-group-row > td[data-group="' + data.group + '"]'),
623
+ groupspan = 0;
624
+
625
+ $.each($groupcols, function () {
626
+ groupspan += parseInt($(this).attr('colspan') || 1, 10);
627
+ });
628
+
629
+ if (groupspan > 0) $group.attr('colspan', groupspan).show();
630
+ else $group.hide();
631
+ }
632
+ })
633
+ .end()
634
+ .find('> tbody > tr.' + cls.detailShow).each(function () {
635
+ ft.createOrUpdateDetailRow(this);
636
+ });
637
+
638
+ $table.find('> tbody > tr.' + cls.detailShow + ':visible').each(function () {
639
+ var $next = $(this).next();
640
+ if ($next.hasClass(cls.detail)) {
641
+ if (!hasBreakpointFired) $next.hide();
642
+ else $next.show();
643
+ }
644
+ });
645
+
646
+ // adding .footable-first-column and .footable-last-column to the first and last th and td of each row in order to allow
647
+ // for styling if the first or last column is hidden (which won't work using :first-child or :last-child)
648
+ $table.find('> thead > tr > th.footable-last-column, > tbody > tr > td.footable-last-column').removeClass('footable-last-column');
649
+ $table.find('> thead > tr > th.footable-first-column, > tbody > tr > td.footable-first-column').removeClass('footable-first-column');
650
+ $table.find('> thead > tr, > tbody > tr')
651
+ .find('> th.footable-visible:last, > td.footable-visible:last')
652
+ .addClass('footable-last-column')
653
+ .end()
654
+ .find('> th.footable-visible:first, > td.footable-visible:first')
655
+ .addClass('footable-first-column');
656
+
657
+ ft.raise(evt.redrawn);
658
+ };
659
+
660
+ ft.toggleDetail = function (row) {
661
+ var $row = (row.jquery) ? row : $(row),
662
+ $next = $row.next();
663
+
664
+ //check if the row is already expanded
665
+ if ($row.hasClass(cls.detailShow)) {
666
+ $row.removeClass(cls.detailShow);
667
+
668
+ //only hide the next row if it's a detail row
669
+ if ($next.hasClass(cls.detail)) $next.hide();
670
+
671
+ ft.raise(evt.rowCollapsed, { 'row': $row[0] });
672
+
673
+ } else {
674
+ ft.createOrUpdateDetailRow($row[0]);
675
+ $row.addClass(cls.detailShow)
676
+ .next().show();
677
+
678
+ ft.raise(evt.rowExpanded, { 'row': $row[0] });
679
+ }
680
+ };
681
+
682
+ ft.removeRow = function (row) {
683
+ var $row = (row.jquery) ? row : $(row);
684
+ if ($row.hasClass(cls.detail)) {
685
+ $row = $row.prev();
686
+ }
687
+ var $next = $row.next();
688
+ if ($row.data('detail_created') === true) {
689
+ //remove the detail row
690
+ $next.remove();
691
+ }
692
+ $row.remove();
693
+
694
+ //raise event
695
+ ft.raise(evt.rowRemoved);
696
+ };
697
+
698
+ ft.appendRow = function (row) {
699
+ var $row = (row.jquery) ? row : $(row);
700
+ $(ft.table).find('tbody').append($row);
701
+
702
+ //redraw the table
703
+ ft.redraw();
704
+ };
705
+
706
+ ft.getColumnFromTdIndex = function (index) {
707
+ /// <summary>Returns the correct column data for the supplied index taking into account colspans.</summary>
708
+ /// <param name="index">The index to retrieve the column data for.</param>
709
+ /// <returns type="json">A JSON object containing the column data for the supplied index.</returns>
710
+ var result = null;
711
+ for (var column in ft.columns) {
712
+ if ($.inArray(index, ft.columns[column].matches) >= 0) {
713
+ result = ft.columns[column];
714
+ break;
715
+ }
716
+ }
717
+ return result;
718
+ };
719
+
720
+ ft.createOrUpdateDetailRow = function (actualRow) {
721
+ var $row = $(actualRow), $next = $row.next(), $detail, values = [];
722
+ if ($row.data('detail_created') === true) return true;
723
+
724
+ if ($row.is(':hidden')) return false; //if the row is hidden for some reason (perhaps filtered) then get out of here
725
+ ft.raise(evt.rowDetailUpdating, { 'row': $row, 'detail': $next });
726
+ $row.find('> td:hidden').each(function () {
727
+ var index = $(this).index(), column = ft.getColumnFromTdIndex(index), name = column.name;
728
+ if (column.ignore === true) return true;
729
+
730
+ if (index in column.names) name = column.names[index];
731
+ values.push({ 'name': name, 'value': ft.parse(this, column), 'display': $.trim($(this).html()), 'group': column.group, 'groupName': column.groupName });
732
+ return true;
733
+ });
734
+ if (values.length === 0) return false; //return if we don't have any data to show
735
+ var colspan = $row.find('> td:visible').length;
736
+ var exists = $next.hasClass(cls.detail);
737
+ if (!exists) { // Create
738
+ $next = $('<tr class="' + cls.detail + '"><td class="' + cls.detailCell + '"><div class="' + cls.detailInner + '"></div></td></tr>');
739
+ $row.after($next);
740
+ }
741
+ $next.find('> td:first').attr('colspan', colspan);
742
+ $detail = $next.find('.' + cls.detailInner).empty();
743
+ opt.createDetail($detail, values, opt.createGroupedDetail, opt.detailSeparator, cls);
744
+ $row.data('detail_created', true);
745
+ ft.raise(evt.rowDetailUpdated, { 'row': $row, 'detail': $next });
746
+ return !exists;
747
+ };
748
+
749
+ ft.raise = function (eventName, args) {
750
+
751
+ if (ft.options.debug === true && $.isFunction(ft.options.log)) ft.options.log(eventName, 'event');
752
+
753
+ args = args || { };
754
+ var def = { 'ft': ft };
755
+ $.extend(true, def, args);
756
+ var e = $.Event(eventName, def);
757
+ if (!e.ft) {
758
+ $.extend(true, e, def);
759
+ } //pre jQuery 1.6 which did not allow data to be passed to event object constructor
760
+ $(ft.table).trigger(e);
761
+ return e;
762
+ };
763
+
764
+ //reset the state of FooTable
765
+ ft.reset = function() {
766
+ var $table = $(ft.table);
767
+ $table.removeData('footable_info')
768
+ .data('breakpoint', '')
769
+ .removeClass(cls.loading)
770
+ .removeClass(cls.loaded);
771
+
772
+ $table.find(opt.toggleSelector).unbind(trg.toggleRow).unbind('click.footable');
773
+
774
+ $table.find('> tbody > tr').removeClass(cls.detailShow);
775
+
776
+ $table.find('> tbody > tr.' + cls.detail).remove();
777
+
778
+ ft.raise(evt.reset);
779
+ };
780
+
781
+ ft.init();
782
+ return ft;
783
+ }
784
+ })(jQuery, window);