footable_rails 0.0.1

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.
@@ -0,0 +1,429 @@
1
+ /**
2
+ * Footable Memory
3
+ *
4
+ * Version 1.1.0
5
+ *
6
+ * Requires browser support for localStorage. Fallback to cookies using
7
+ * jQuery Cookie (https://github.com/carhartl/jquery-cookie)
8
+ *
9
+ * Stores table state in a cookie and reloads state when page is refreshed.
10
+ *
11
+ * Supports common FooTable features:
12
+ * - Pagination
13
+ * - Sorting
14
+ * - Filtering
15
+ * - Expansion
16
+ *
17
+ * Written to be compatible with multiple FooTables per page and with
18
+ * JavaScript libraries like AngularJS and Ember that use hash based URLs.
19
+ *
20
+ * Disabled by default, to enable add the following section to the footable
21
+ * options:
22
+ *
23
+ * $('#table').footable({
24
+ * memory: {
25
+ * enabled: true
26
+ * }
27
+ * });
28
+ *
29
+ * Based on FooTable Plugin Bookmarkable by Amy Farrell (https://github.com/akf)
30
+ *
31
+ * Created by Chris Laskey (https://github.com/chrislaskey)
32
+ */
33
+
34
+ (function ($, w, undefined) {
35
+
36
+ if (w.footable === undefined || w.foobox === null) {
37
+ throw new Error('Please check and make sure footable.js is included in the page and is loaded prior to this script.');
38
+ }
39
+
40
+ var defaults = {
41
+ memory: {
42
+ enabled: false
43
+ }
44
+ };
45
+
46
+ var storage;
47
+
48
+ var storage_engines = {};
49
+
50
+ storage_engines.local_storage = (function($){
51
+
52
+ 'use strict';
53
+
54
+ var path_page = function(){
55
+ return location.pathname;
56
+ };
57
+
58
+ var path_subpage = function(){
59
+ return location.hash || 'root';
60
+ };
61
+
62
+ var storage_key = function(index){
63
+ return path_page() + '/' + path_subpage() + '/index-' + index;
64
+ };
65
+
66
+ var get = function(index){
67
+ var key = storage_key(index),
68
+ as_string = localStorage.getItem(key);
69
+
70
+ return as_string ? JSON.parse(as_string) : {};
71
+ };
72
+
73
+ var set = function(index, item){
74
+ var key = storage_key(index),
75
+ as_string = JSON.stringify(item);
76
+
77
+ localStorage.setItem(key, as_string);
78
+ };
79
+
80
+ return {
81
+ get: function(index){
82
+ return get(index);
83
+ },
84
+ set: function(index, item){
85
+ set(index, item);
86
+ }
87
+ };
88
+
89
+ })($);
90
+
91
+ storage_engines.cookie = (function($){
92
+
93
+ 'use strict';
94
+
95
+ /**
96
+ * Stores footable bookmarkable data in a cookie
97
+ *
98
+ * By default will store each page in its own cookie.
99
+ * Supports multiple FooTables per page.
100
+ * Supports JS frameworks that use hashmap URLs (AngularJS, Ember, etc).
101
+ *
102
+ * For example take an example application:
103
+ *
104
+ * http://example.com/application-data (2 FooTables on this page)
105
+ * http://example.com/application-data/#/details (1 FooTable on this page)
106
+ * http://example.com/other-data (1 FooTable on this page)
107
+ *
108
+ * Would be stored like this:
109
+ *
110
+ * cookie['/application-data'] = {
111
+ * '/': {
112
+ * 1: {
113
+ * key1: value1,
114
+ * key2: value2
115
+ * },
116
+ * 2: {
117
+ * key1: value1,
118
+ * key2: value2
119
+ * }
120
+ * },
121
+ * '#/details': {
122
+ * 1: {
123
+ * key1: value1,
124
+ * key2: value2
125
+ * }
126
+ * }
127
+ * };
128
+ *
129
+ * cookie['/other-data'] = {
130
+ * '/': {
131
+ * 1: {
132
+ * key1: value1,
133
+ * key2: value2
134
+ * },
135
+ * }
136
+ * }
137
+ *
138
+ */
139
+
140
+ if( $.cookie ){
141
+ $.cookie.json = true;
142
+ }
143
+
144
+ var days_to_keep_data = 7;
145
+
146
+ var path_page = function(){
147
+ return location.pathname;
148
+ };
149
+
150
+ var path_subpage = function(){
151
+ return location.hash || '/';
152
+ };
153
+
154
+ var get_data = function(){
155
+ var page = path_page(),
156
+ data = $.cookie(page);
157
+
158
+ return data || {};
159
+ };
160
+
161
+ var get_table = function(index){
162
+ var subpage = path_subpage(),
163
+ data = get_data();
164
+
165
+ if( data[subpage] && data[subpage][index] ){
166
+ return data[subpage][index];
167
+ } else {
168
+ return {};
169
+ }
170
+ };
171
+
172
+ var set = function(index, item){
173
+ var page = path_page(),
174
+ subpage = path_subpage(),
175
+ data = get_data(),
176
+ options;
177
+
178
+ if( !data[subpage] ){
179
+ data[subpage] = {};
180
+ }
181
+
182
+ data[subpage][index] = item;
183
+
184
+ options = {
185
+ path: page,
186
+ expires: days_to_keep_data
187
+ };
188
+
189
+ $.cookie(page, data, options);
190
+ };
191
+
192
+ return {
193
+ get: function(index){
194
+ return get_table(index);
195
+ },
196
+ set: function(index, item){
197
+ set(index, item);
198
+ }
199
+ };
200
+
201
+ })($);
202
+
203
+ var set_storage_engine = (function(){
204
+ var test = 'footable-memory-plugin-storage-test';
205
+
206
+ try {
207
+ localStorage.setItem(test, test);
208
+ localStorage.removeItem(test);
209
+ storage = storage_engines.local_storage;
210
+ } catch(e) {
211
+ try {
212
+ $.cookie(test, test);
213
+ storage = storage_engines.cookie;
214
+ } catch(e) {
215
+ throw new Error('FooTable Memory requires either localStorage or cookie support via jQuery $.cookie plugin (https://github.com/carhartl/jquery-cookie)');
216
+ }
217
+ }
218
+ })($);
219
+
220
+ var state = (function($){
221
+
222
+ 'use strict';
223
+
224
+ /**
225
+ * Gets and sets current table state
226
+ */
227
+
228
+ var vars = {};
229
+
230
+ var get = {};
231
+
232
+ var set = {};
233
+
234
+ set.vars = function(ft){
235
+ vars.ft = ft;
236
+ vars.table = $(ft.table);
237
+ };
238
+
239
+ get.descending = function(){
240
+ var descending = false;
241
+ $.each(vars.table.find('th'), function(index){
242
+ if( $(this).hasClass('footable-sorted-desc') ){
243
+ descending = true;
244
+ }
245
+ });
246
+ return descending;
247
+ };
248
+
249
+ get.expanded = function(){
250
+ var indexes = [];
251
+ $.each(vars.ft.table.rows, function(index, value){
252
+ if( $(this).hasClass('footable-detail-show') ){
253
+ indexes.push(index);
254
+ }
255
+ });
256
+ return indexes;
257
+ };
258
+
259
+ set.expanded = function(data){
260
+ if( data.expanded ){
261
+ $.each(data.expanded, function(index, value){
262
+ // var row = $(vars.ft.table.rows[value]);
263
+ // row.find('> td:first').trigger('footable_toggle_row');
264
+
265
+ // Trying to execute the lines above, but the expanded row
266
+ // shows raw AngularJS template (with {{ values }}) instead
267
+ // of the fully rendered result.
268
+ //
269
+ // Best guess is some things happen after
270
+ // 'footable_initialized' event and row expanding can not
271
+ // occur until after those fire.
272
+ //
273
+ // A hack to get around this is to wait an interval before
274
+ // executing the intended commands. Wrapped in an
275
+ // immediately executing function to ensure ft is the
276
+ // current value.
277
+
278
+ (function(ft){
279
+ setTimeout(function(){
280
+ var row = $(ft.table.rows[value]);
281
+ row.find('> td:first').trigger('footable_toggle_row');
282
+ }, 150);
283
+ })(vars.ft);
284
+ });
285
+ }
286
+ };
287
+
288
+ get.filter = function(){
289
+ return vars.table.data('filter') ? $(vars.table.data('filter')).val() : '';
290
+ };
291
+
292
+ set.filter = function(data){
293
+ if( data.filter ){
294
+ $(vars.table.data('filter'))
295
+ .val(data.filter)
296
+ .trigger('keyup');
297
+ }
298
+ };
299
+
300
+ get.page = function(){
301
+ return vars.ft.pageInfo && vars.ft.pageInfo.currentPage !== undefined ? vars.ft.pageInfo.currentPage : 0;
302
+ };
303
+
304
+ set.page = function(data){
305
+ if( data.page ){
306
+ vars.table.data('currentPage', data.page);
307
+ // Delay triggering table until sort is updated, since both effect
308
+ // pagination.
309
+ }
310
+ };
311
+
312
+ get.shown = function(){
313
+ return vars.table
314
+ .find('tbody')
315
+ .find('tr:not(.footable-row-detail)')
316
+ .filter(':visible').length;
317
+ };
318
+
319
+ get.sorted = function(){
320
+ if( vars.table.data('sorted') !== undefined ){
321
+ return vars.table.data('sorted');
322
+ } else {
323
+ return -1;
324
+ }
325
+ };
326
+
327
+ set.sorted = function(data){
328
+ if( data.sorted >= 0 ) {
329
+ // vars.table.data('footable-sort').doSort(data.sorted, !data.descending);
330
+
331
+ // Trying to execute the line above, but only sort icon on the
332
+ // <th> element gets set. The rows themselves do not get sorted.
333
+ //
334
+ // Best guess is some things happen after 'footable_initialized' event
335
+ // and sorting can not occur until after those fire.
336
+ //
337
+ // A hack to get around this is to wait an interval before executing
338
+ // the intended commands. Wrapped in an immediately executing
339
+ // function to ensure ft is the current value.
340
+
341
+ (function(ft){
342
+ setTimeout(function(){
343
+ $(ft.table).data('footable-sort').doSort(data.sorted, !data.descending);
344
+ }, 150);
345
+ })(vars.ft);
346
+ } else {
347
+ vars.table.trigger('footable_setup_paging');
348
+ }
349
+ };
350
+
351
+ get.total = function(){
352
+ return vars.table
353
+ .find('tbody')
354
+ .find('tr:not(.footable-row-detail, .footable-filtered)').length;
355
+ };
356
+
357
+ var get_state = function(){
358
+ return {
359
+ descending: get.descending(),
360
+ expanded: get.expanded(),
361
+ filter: get.filter(),
362
+ page: get.page(),
363
+ shown: get.shown(),
364
+ sorted: get.sorted(),
365
+ total: get.total()
366
+ };
367
+ };
368
+
369
+ var set_state = function(data){
370
+ set.filter(data);
371
+ set.page(data);
372
+ set.sorted(data);
373
+ set.expanded(data);
374
+ };
375
+
376
+ return {
377
+ get: function(ft){
378
+ set.vars(ft);
379
+ return get_state();
380
+ },
381
+ set: function(ft, data){
382
+ set.vars(ft);
383
+ return set_state(data);
384
+ }
385
+ };
386
+
387
+ })($);
388
+
389
+ var is_enabled = function(ft){
390
+ return ft.options.memory.enabled;
391
+ };
392
+
393
+ var update = function(ft, event) {
394
+ var index = ft.id,
395
+ data = state.get(ft);
396
+
397
+ storage.set(index, data);
398
+ };
399
+
400
+ var load = function(ft){
401
+ var index = ft.id,
402
+ data = storage.get(index);
403
+
404
+ state.set(ft, data);
405
+ ft.memory_plugin_loaded = true;
406
+ };
407
+
408
+ function Memory() {
409
+ var p = this;
410
+ p.name = 'Footable Memory';
411
+ p.init = function(ft) {
412
+ if (is_enabled(ft)) {
413
+ $(ft.table).bind({
414
+ 'footable_initialized': function(){
415
+ load(ft);
416
+ },
417
+ 'footable_page_filled footable_redrawn footable_filtered footable_sorted footable_row_expanded footable_row_collapsed': function(e) {
418
+ if (ft.memory_plugin_loaded) {
419
+ update(ft, e);
420
+ }
421
+ }
422
+ });
423
+ }
424
+ };
425
+ }
426
+
427
+ w.footable.plugins.register(Memory, defaults);
428
+
429
+ })(jQuery, window);
@@ -0,0 +1,242 @@
1
+ (function ($, w, undefined) {
2
+ if (w.footable === undefined || w.footable === null)
3
+ throw new Error('Please check and make sure footable.js is included in the page and is loaded prior to this script.');
4
+
5
+ var defaults = {
6
+ paginate: true,
7
+ pageSize: 10,
8
+ pageNavigation: '.pagination',
9
+ firstText: '&laquo;',
10
+ previousText: '&lsaquo;',
11
+ nextText: '&rsaquo;',
12
+ lastText: '&raquo;',
13
+ limitNavigation: 0,
14
+ limitPreviousText: '...',
15
+ limitNextText: '...'
16
+ };
17
+
18
+ function pageInfo(ft) {
19
+ var $table = $(ft.table), data = $table.data();
20
+ this.pageNavigation = data.pageNavigation || ft.options.pageNavigation;
21
+ this.pageSize = data.pageSize || ft.options.pageSize;
22
+ this.firstText = data.firstText || ft.options.firstText;
23
+ this.previousText = data.previousText || ft.options.previousText;
24
+ this.nextText = data.nextText || ft.options.nextText;
25
+ this.lastText = data.lastText || ft.options.lastText;
26
+ this.limitNavigation = parseInt(data.limitNavigation || ft.options.limitNavigation || defaults.limitNavigation, 10);
27
+ this.limitPreviousText = data.limitPreviousText || ft.options.limitPreviousText;
28
+ this.limitNextText = data.limitNextText || ft.options.limitNextText;
29
+ this.limit = this.limitNavigation > 0;
30
+ this.currentPage = data.currentPage || 0;
31
+ this.pages = [];
32
+ this.control = false;
33
+ }
34
+
35
+ function Paginate() {
36
+ var p = this;
37
+ p.name = 'Footable Paginate';
38
+
39
+ p.init = function (ft) {
40
+ if (ft.options.paginate === true) {
41
+ if ($(ft.table).data('page') === false) return;
42
+ p.footable = ft;
43
+ $(ft.table)
44
+ .unbind('.paging')
45
+ .bind({
46
+ 'footable_initialized.paging footable_row_removed.paging footable_redrawn.paging footable_sorted.paging footable_filtered.paging': function () {
47
+ p.setupPaging();
48
+ }
49
+ })
50
+ //save the filter object onto the table so we can access it later
51
+ .data('footable-paging', p);
52
+ }
53
+ };
54
+
55
+ p.setupPaging = function () {
56
+ var ft = p.footable,
57
+ $tbody = $(ft.table).find('> tbody');
58
+
59
+ ft.pageInfo = new pageInfo(ft);
60
+
61
+ p.createPages(ft, $tbody);
62
+ p.createNavigation(ft, $tbody);
63
+ p.fillPage(ft, $tbody, ft.pageInfo.currentPage);
64
+ };
65
+
66
+ p.createPages = function (ft, tbody) {
67
+ var pages = 1;
68
+ var info = ft.pageInfo;
69
+ var pageCount = pages * info.pageSize;
70
+ var page = [];
71
+ var lastPage = [];
72
+ info.pages = [];
73
+ var rows = tbody.find('> tr:not(.footable-filtered,.footable-row-detail)');
74
+ rows.each(function (i, row) {
75
+ page.push(row);
76
+ if (i === pageCount - 1) {
77
+ info.pages.push(page);
78
+ pages++;
79
+ pageCount = pages * info.pageSize;
80
+ page = [];
81
+ } else if (i >= rows.length - (rows.length % info.pageSize)) {
82
+ lastPage.push(row);
83
+ }
84
+ });
85
+ if (lastPage.length > 0) info.pages.push(lastPage);
86
+ if (info.currentPage >= info.pages.length) info.currentPage = info.pages.length - 1;
87
+ if (info.currentPage < 0) info.currentPage = 0;
88
+ if (info.pages.length === 1) {
89
+ //we only have a single page
90
+ $(ft.table).addClass('no-paging');
91
+ } else {
92
+ $(ft.table).removeClass('no-paging');
93
+ }
94
+ };
95
+
96
+ p.createNavigation = function (ft, tbody) {
97
+ var $nav = $(ft.table).find(ft.pageInfo.pageNavigation);
98
+ //if we cannot find the navigation control within the table, then try find it outside
99
+ if ($nav.length === 0) {
100
+ $nav = $(ft.pageInfo.pageNavigation);
101
+ //if the navigation control is inside another table, then get out
102
+ if ($nav.parents('table:first').length > 0 && $nav.parents('table:first') !== $(ft.table)) return;
103
+ //if we found more than one navigation control, write error to console
104
+ if ($nav.length > 1 && ft.options.debug === true) console.error('More than one pagination control was found!');
105
+ }
106
+ //if we still cannot find the control, then don't do anything
107
+ if ($nav.length === 0) return;
108
+ //if the nav is not a UL, then find or create a UL
109
+ if (!$nav.is('ul')) {
110
+ if ($nav.find('ul:first').length === 0) {
111
+ $nav.append('<ul />');
112
+ }
113
+ $nav = $nav.find('ul');
114
+ }
115
+ $nav.find('li').remove();
116
+ var info = ft.pageInfo;
117
+ info.control = $nav;
118
+ if (info.pages.length > 0) {
119
+ $nav.append('<li class="footable-page-arrow"><a data-page="first" href="#first">' + ft.pageInfo.firstText + '</a>');
120
+ $nav.append('<li class="footable-page-arrow"><a data-page="prev" href="#prev">' + ft.pageInfo.previousText + '</a></li>');
121
+ if (info.limit){
122
+ $nav.append('<li class="footable-page-arrow"><a data-page="limit-prev" href="#limit-prev">' + ft.pageInfo.limitPreviousText + '</a></li>');
123
+ }
124
+ if (!info.limit){
125
+ $.each(info.pages, function (i, page) {
126
+ if (page.length > 0) {
127
+ $nav.append('<li class="footable-page"><a data-page="' + i + '" href="#">' + (i + 1) + '</a></li>');
128
+ }
129
+ });
130
+ }
131
+ if (info.limit){
132
+ $nav.append('<li class="footable-page-arrow"><a data-page="limit-next" href="#limit-next">' + ft.pageInfo.limitNextText + '</a></li>');
133
+ p.createLimited($nav, info, 0);
134
+ }
135
+ $nav.append('<li class="footable-page-arrow"><a data-page="next" href="#next">' + ft.pageInfo.nextText + '</a></li>');
136
+ $nav.append('<li class="footable-page-arrow"><a data-page="last" href="#last">' + ft.pageInfo.lastText + '</a></li>');
137
+ }
138
+ $nav.off('click', 'a[data-page]').on('click', 'a[data-page]', function (e) {
139
+ e.preventDefault();
140
+ var page = $(this).data('page');
141
+ var newPage = info.currentPage;
142
+ if (page === 'first') {
143
+ newPage = 0;
144
+ } else if (page === 'prev') {
145
+ if (newPage > 0) newPage--;
146
+ } else if (page === 'next') {
147
+ if (newPage < info.pages.length - 1) newPage++;
148
+ } else if (page === 'last') {
149
+ newPage = info.pages.length - 1;
150
+ } else if (page === 'limit-prev') {
151
+ newPage = -1;
152
+ var first = $nav.find('.footable-page:first a').data('page');
153
+ p.createLimited($nav, info, first - info.limitNavigation);
154
+ p.setPagingClasses($nav, info.currentPage, info.pages.length);
155
+ } else if (page === 'limit-next') {
156
+ newPage = -1;
157
+ var last = $nav.find('.footable-page:last a').data('page');
158
+ p.createLimited($nav, info, last + 1);
159
+ p.setPagingClasses($nav, info.currentPage, info.pages.length);
160
+ } else {
161
+ newPage = page;
162
+ }
163
+ if (newPage >= 0){
164
+ if (info.limit && info.currentPage != newPage){
165
+ var start = newPage;
166
+ while (start % info.limitNavigation !== 0){ start -= 1; }
167
+ p.createLimited($nav, info, start);
168
+ }
169
+ p.paginate(ft, newPage);
170
+ }
171
+ });
172
+ p.setPagingClasses($nav, info.currentPage, info.pages.length);
173
+ };
174
+
175
+ p.createLimited = function(nav, info, start){
176
+ start = start || 0;
177
+ nav.find('li.footable-page').remove();
178
+ var i, page,
179
+ $prev = nav.find('li.footable-page-arrow > a[data-page="limit-prev"]').parent(),
180
+ $next = nav.find('li.footable-page-arrow > a[data-page="limit-next"]').parent();
181
+ for (i = info.pages.length - 1; i >=0 ; i--){
182
+ page = info.pages[i];
183
+ if (i >= start && i < start + info.limitNavigation && page.length > 0) {
184
+ $prev.after('<li class="footable-page"><a data-page="' + i + '" href="#">' + (i + 1) + '</a></li>');
185
+ }
186
+ }
187
+ if (start === 0){ $prev.hide(); }
188
+ else { $prev.show(); }
189
+ if (start + info.limitNavigation >= info.pages.length){ $next.hide(); }
190
+ else { $next.show(); }
191
+ };
192
+
193
+ p.paginate = function (ft, newPage) {
194
+ var info = ft.pageInfo;
195
+ if (info.currentPage !== newPage) {
196
+ var $tbody = $(ft.table).find('> tbody');
197
+
198
+ //raise a pre-pagin event so that we can cancel the paging if needed
199
+ var event = ft.raise('footable_paging', { page: newPage, size: info.pageSize });
200
+ if (event && event.result === false) return;
201
+
202
+ p.fillPage(ft, $tbody, newPage);
203
+ info.control.find('li').removeClass('active disabled');
204
+ p.setPagingClasses(info.control, info.currentPage, info.pages.length);
205
+ }
206
+ };
207
+
208
+ p.setPagingClasses = function (nav, currentPage, pageCount) {
209
+ nav.find('li.footable-page > a[data-page=' + currentPage + ']').parent().addClass('active');
210
+ if (currentPage >= pageCount - 1) {
211
+ nav.find('li.footable-page-arrow > a[data-page="next"]').parent().addClass('disabled');
212
+ nav.find('li.footable-page-arrow > a[data-page="last"]').parent().addClass('disabled');
213
+ }
214
+ if (currentPage < 1) {
215
+ nav.find('li.footable-page-arrow > a[data-page="first"]').parent().addClass('disabled');
216
+ nav.find('li.footable-page-arrow > a[data-page="prev"]').parent().addClass('disabled');
217
+ }
218
+ };
219
+
220
+ p.fillPage = function (ft, tbody, pageNumber) {
221
+ ft.pageInfo.currentPage = pageNumber;
222
+ $(ft.table).data('currentPage', pageNumber);
223
+ tbody.find('> tr').hide();
224
+ $(ft.pageInfo.pages[pageNumber]).each(function () {
225
+ p.showRow(this, ft);
226
+ });
227
+ ft.raise('footable_page_filled');
228
+ };
229
+
230
+ p.showRow = function (row, ft) {
231
+ var $row = $(row), $next = $row.next(), $table = $(ft.table);
232
+ if ($table.hasClass('breakpoint') && $row.hasClass('footable-detail-show') && $next.hasClass('footable-row-detail')) {
233
+ $row.add($next).show();
234
+ ft.createOrUpdateDetailRow(row);
235
+ }
236
+ else $row.show();
237
+ };
238
+ }
239
+
240
+ w.footable.plugins.register(Paginate, defaults);
241
+
242
+ })(jQuery, window);