footable_rails 0.0.1

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