tableling-rails 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. data/Rakefile +2 -2
  2. data/lib/tableling-rails/version.rb +1 -1
  3. data/spec/dummy/app/assets/javascripts/application.js +6 -1
  4. data/spec/dummy/app/assets/javascripts/books.js +6 -4
  5. data/spec/dummy/db/development.sqlite3 +0 -0
  6. data/spec/dummy/log/development.log +5136 -4946
  7. data/spec/dummy/tmp/cache/assets/BF1/770/sprockets%2F8228a53c539c582499241527570aa216 +0 -0
  8. data/spec/dummy/tmp/cache/assets/C3F/0C0/sprockets%2F4643701467ac62c314510c8dd021916f +0 -0
  9. data/spec/dummy/tmp/cache/assets/C53/AD0/sprockets%2F712078516a81502575c831cb0a8e898e +0 -0
  10. data/spec/dummy/tmp/cache/assets/C59/0F0/sprockets%2F099f4e84a443568245ff0674030f6e05 +0 -0
  11. data/spec/dummy/tmp/cache/assets/C60/CF0/sprockets%2F32a45292ee67867d8715952f3072ad84 +0 -0
  12. data/spec/dummy/tmp/cache/assets/C6F/0E0/sprockets%2F82372776487d948ef7666d304d6fb345 +0 -0
  13. data/spec/dummy/tmp/cache/assets/C80/840/sprockets%2F562c2d168da585f80579347d10790a0a +0 -0
  14. data/spec/dummy/tmp/cache/assets/C82/F20/sprockets%2F117015d65237e3682f5b21c87d58ab95 +0 -0
  15. data/spec/dummy/tmp/cache/assets/C86/280/sprockets%2F1345c9947753f018f45371ec5083fa2d +0 -0
  16. data/spec/dummy/tmp/cache/assets/C91/350/sprockets%2F6864c49a10954375948d9f306db308fb +0 -0
  17. data/spec/dummy/tmp/cache/assets/C9A/900/sprockets%2Fc1b5b7611d7c10e124512724f29f241d +0 -0
  18. data/spec/dummy/tmp/cache/assets/CAF/540/sprockets%2F8f42e689b84f0b25c0180046467adf31 +0 -0
  19. data/spec/dummy/tmp/cache/assets/CB1/9A0/sprockets%2F57805dfa396248a9665cb4a0c85116d0 +0 -0
  20. data/spec/dummy/tmp/cache/assets/CC1/4E0/sprockets%2F6ef77414c793c84a437244d7d965ba90 +0 -0
  21. data/spec/dummy/tmp/cache/assets/CC3/B10/sprockets%2F0a2c59284c7a5d99581883a9763b5ad8 +0 -0
  22. data/spec/dummy/tmp/cache/assets/CC9/940/sprockets%2F8128c279185cf97dba7914278ce838b4 +0 -0
  23. data/spec/dummy/tmp/cache/assets/CCA/0C0/sprockets%2F6c3e053393afe303432ca73f1b41490b +0 -0
  24. data/spec/dummy/tmp/cache/assets/CD5/2C0/sprockets%2F166c056119ebdfb8b7104c97b424b423 +0 -0
  25. data/spec/dummy/tmp/cache/assets/CDB/610/sprockets%2F7adf20860b018477a95cdb8321b49d16 +0 -0
  26. data/spec/dummy/tmp/cache/assets/CDD/F50/sprockets%2F7353f31f80f1139aade47728a62af336 +0 -0
  27. data/spec/dummy/tmp/cache/assets/CE9/760/sprockets%2F5ae7fab601683996918936cad21fc244 +0 -0
  28. data/spec/dummy/tmp/cache/assets/CE9/B90/sprockets%2Fea2eb066933b1d8b5949972c3c79353a +0 -0
  29. data/spec/dummy/tmp/cache/assets/CF1/AF0/sprockets%2F14dc9633b61024231ebec0c1a4a0259f +0 -0
  30. data/spec/dummy/tmp/cache/assets/D02/340/sprockets%2Fae58ed66f72137a594b0912bab600e03 +0 -0
  31. data/spec/dummy/tmp/cache/assets/D0A/AC0/sprockets%2Fc154991d0a7564e15c0fb4a555c5c73d +0 -0
  32. data/spec/dummy/tmp/cache/assets/D0B/190/sprockets%2F68cb0d1054ca546fc473274c4ac8737c +0 -0
  33. data/spec/dummy/tmp/cache/assets/D11/530/sprockets%2F13bc8c66140adef2e97648630866aae3 +0 -0
  34. data/spec/dummy/tmp/cache/assets/D16/AA0/sprockets%2Ff181e659a47e2cf9c257f32d80452bd3 +0 -0
  35. data/spec/dummy/tmp/cache/assets/D18/840/sprockets%2F50c1885539f61359c48af74fcae1df31 +0 -0
  36. data/spec/dummy/tmp/cache/assets/D1B/940/sprockets%2F7c4819d1dd9b64a22941d8c7bd1e6954 +0 -0
  37. data/spec/dummy/tmp/cache/assets/D2B/1F0/sprockets%2F6a4c116e8316d082bb0ffae02e94b107 +0 -0
  38. data/spec/dummy/tmp/cache/assets/D3C/E20/sprockets%2Ffc6c44ec250bc532c55649e5292b5c1f +0 -0
  39. data/spec/dummy/tmp/cache/assets/D4B/C30/sprockets%2Fe8aa746e637d69ff0ac1e71e528c5292 +0 -0
  40. data/spec/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  41. data/spec/dummy/tmp/cache/assets/D5A/E20/sprockets%2Febf23d670481ba70b341aa2eb6e37b08 +0 -0
  42. data/spec/dummy/tmp/cache/assets/D5C/1F0/sprockets%2F2d3f2cfd98bf787168fb59f84ab24784 +0 -0
  43. data/spec/dummy/tmp/cache/assets/D5D/C60/sprockets%2Ffa0336b42d01aa9173d9d4c12fc4e82d +0 -0
  44. data/spec/dummy/tmp/cache/assets/D65/DD0/sprockets%2Fed4503679ef66f0e597bfc738bc9e698 +0 -0
  45. data/spec/dummy/tmp/cache/assets/D68/930/sprockets%2F5a15e52e84502f9fceb7ea04020f65fc +0 -0
  46. data/spec/dummy/tmp/cache/assets/D8C/140/sprockets%2F2afdbb3902a1be5d2f045cfc837903a3 +0 -0
  47. data/spec/dummy/tmp/cache/assets/D8E/660/sprockets%2F3a3eed926bdf64000ccacb36022f78d4 +0 -0
  48. data/spec/dummy/tmp/cache/assets/D95/690/sprockets%2Fe4d19a04705ee5d72c7fa64dc2070ddb +0 -0
  49. data/spec/dummy/tmp/cache/assets/DA4/EC0/sprockets%2F8da2289e16c1d0cac94aa825ee18d99d +0 -0
  50. data/spec/dummy/tmp/cache/assets/DA6/120/sprockets%2Fc5880aca76ccbb51f9388362e8afc1e6 +0 -0
  51. data/spec/dummy/tmp/cache/assets/DA7/390/sprockets%2F25e744cd1b6f8f7ce1d52c1e86a8f19a +0 -0
  52. data/spec/dummy/tmp/cache/assets/DAB/3E0/sprockets%2F63d2fa621beec4fe878f9eb2884426ab +0 -0
  53. data/spec/dummy/tmp/cache/assets/DAB/660/sprockets%2F9a8c36769afcc7027b4b6ee0c3e77ce5 +0 -0
  54. data/spec/dummy/tmp/cache/assets/DBF/EB0/sprockets%2F2afcec323c8ba12a2b7c852f23aa589e +0 -0
  55. data/spec/dummy/tmp/cache/assets/DCE/9F0/sprockets%2F8fa8c08ea8da97c06cd6c32fe3e613b0 +0 -0
  56. data/spec/dummy/tmp/cache/assets/DCF/E50/sprockets%2Fc39ff2ed4e1fb4269c39d415acf0d90b +0 -0
  57. data/spec/dummy/tmp/cache/assets/DD8/0A0/sprockets%2Fb2c3097effcd6084ec75e1f745d1d7dd +0 -0
  58. data/spec/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  59. data/spec/dummy/tmp/cache/assets/DF0/040/sprockets%2F04bbbe1ee5ca914173cd90cf6e8a4e0b +0 -0
  60. data/spec/dummy/tmp/cache/assets/DF7/C80/sprockets%2Ff503b3666caf04beb7cfe54cbdb0575e +0 -0
  61. data/spec/dummy/tmp/cache/assets/DFA/B70/sprockets%2F262fb95d3f7cfc16febb0f0128ac38ce +0 -0
  62. data/spec/dummy/tmp/cache/assets/E03/260/sprockets%2Fd1fcfea59ff53c32557ffcd93016cb2e +0 -0
  63. data/spec/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  64. data/spec/dummy/tmp/cache/assets/E0A/510/sprockets%2F8efee416ba436e75bea85d4d367faea7 +0 -0
  65. data/spec/dummy/tmp/cache/assets/E43/810/sprockets%2F79b0ef75ceb26a9c8fca797db3efba96 +0 -0
  66. data/spec/dummy/tmp/cache/assets/E96/1B0/sprockets%2F6d5fe51f9c1a3d2f898becd0bcddef3e +0 -0
  67. data/spec/dummy/tmp/cache/assets/ED4/410/sprockets%2F082adbb8e2abd8a21c8b34aebbbfbfad +0 -0
  68. data/spec/dummy/tmp/pids/server.pid +1 -0
  69. data/vendor/assets/javascripts/tableling.js +3 -543
  70. data/vendor/assets/javascripts/tableling/bootstrap.js +230 -0
  71. data/vendor/assets/javascripts/tableling/core.js +321 -0
  72. metadata +11 -99
  73. data/spec/dummy/db/test.sqlite3 +0 -0
  74. data/spec/dummy/log/test.log +0 -78
  75. data/spec/dummy/tmp/cache/assets/C20/C20/sprockets%2F932e86665765e61017be1015b249d270 +0 -0
  76. data/spec/dummy/tmp/cache/assets/C98/B70/sprockets%2F64db420a3a172209bf0b734312a4888a +0 -0
  77. data/spec/dummy/tmp/cache/assets/CC1/B50/sprockets%2F13559b77aaaf524896e427719ef5d354 +0 -0
  78. data/spec/dummy/tmp/cache/assets/CD4/420/sprockets%2F19b3d492932dc6a59093dfb1aa852101 +0 -0
  79. data/spec/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  80. data/spec/dummy/tmp/cache/assets/CD8/520/sprockets%2F89128ce416ae74c4148c5c1f281e111c +0 -0
  81. data/spec/dummy/tmp/cache/assets/CDA/600/sprockets%2F98ab7e75433ba5d503533fb00b488c80 +0 -0
  82. data/spec/dummy/tmp/cache/assets/CE1/0F0/sprockets%2F92d30845fe3923e231cfe7e056a654b4 +0 -0
  83. data/spec/dummy/tmp/cache/assets/CE2/E70/sprockets%2Fd569808c7b411447dec0137e4d1ee707 +0 -0
  84. data/spec/dummy/tmp/cache/assets/D0A/780/sprockets%2F3d727e58c55c30009bbaf1462e466d4d +0 -0
  85. data/spec/dummy/tmp/cache/assets/D21/890/sprockets%2F73f9cd97cac4382d565e7b835709a2e3 +0 -0
  86. data/spec/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  87. data/spec/dummy/tmp/cache/assets/D3A/720/sprockets%2F0e2516c1cac22716289bfc87d5ea9a26 +0 -0
  88. data/spec/dummy/tmp/cache/assets/D4B/BF0/sprockets%2Fdf124de0072e308f8e157eae77b85d88 +0 -0
  89. data/spec/dummy/tmp/cache/assets/D54/910/sprockets%2F787b6a4c2386b5edf98f90508b85cc3d +0 -0
  90. data/spec/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  91. data/spec/dummy/tmp/cache/assets/D63/170/sprockets%2Fb5b73c3b0862ee59b701a6819b2aa4cd +0 -0
  92. data/spec/dummy/tmp/cache/assets/D66/580/sprockets%2Ffe6bb9bb5ef9c54f501650b1365c1b05 +0 -0
  93. data/spec/dummy/tmp/cache/assets/D6A/F40/sprockets%2F53fdbea64f1803eebb72f426f74110d8 +0 -0
  94. data/spec/dummy/tmp/cache/assets/D6B/D30/sprockets%2Faeb25572f539ffdbb122e9de1218572a +0 -0
  95. data/spec/dummy/tmp/cache/assets/D6F/110/sprockets%2F2c8c9177efb7e5be1a7055bb0d44792a +0 -0
  96. data/spec/dummy/tmp/cache/assets/D76/3F0/sprockets%2Fbb569c97f9cc01af670906b40c6be9e2 +0 -0
  97. data/spec/dummy/tmp/cache/assets/D81/510/sprockets%2F3e6bd5a2d6d0d88f61f5b616f7d879b5 +0 -0
  98. data/spec/dummy/tmp/cache/assets/D82/940/sprockets%2F29cd0ae31066baa6f2cb92b5c6305aa2 +0 -0
  99. data/spec/dummy/tmp/cache/assets/D85/D20/sprockets%2F7f7f398c298ca8a1e812b6c9832bcf6e +0 -0
  100. data/spec/dummy/tmp/cache/assets/D97/230/sprockets%2F0f46cb65e018de57d2c8b480a0ec5dd3 +0 -0
  101. data/spec/dummy/tmp/cache/assets/DA1/F00/sprockets%2F7b23e35dd61e9d734a59179e2d2aebea +0 -0
  102. data/spec/dummy/tmp/cache/assets/DA3/950/sprockets%2F38737f6fa54dbda0dfe3ce4c51710e79 +0 -0
  103. data/spec/dummy/tmp/cache/assets/DB4/7A0/sprockets%2F900105aaabba7938ce23f1e0aade71e5 +0 -0
  104. data/spec/dummy/tmp/cache/assets/DB4/F90/sprockets%2Fcf5e68e329cedb58b84f445f2b8e37b5 +0 -0
  105. data/spec/dummy/tmp/cache/assets/DE2/6D0/sprockets%2Fdcaa7d2b2814413aa75d3ba6ada3c6b5 +0 -0
  106. data/spec/dummy/tmp/cache/assets/DF0/D60/sprockets%2F5a54a1ecc1db271edfc0727bfcb8d912 +0 -0
  107. data/spec/dummy/tmp/cache/assets/DFE/E10/sprockets%2F9a19dfdd7eb0bf5262919a0207ecfdbf +0 -0
  108. data/spec/dummy/tmp/cache/assets/E33/8F0/sprockets%2F4933e07f8ef2da7eb5bee9a6fcac3d41 +0 -0
  109. data/spec/dummy/tmp/cache/assets/E67/730/sprockets%2Fadd4efdfa7661cc60e8ea63cef1f0f98 +0 -0
  110. data/vendor/assets/javascripts/tableling.backbone.js +0 -3638
  111. data/vendor/assets/javascripts/tableling.world.js +0 -14768
@@ -0,0 +1,230 @@
1
+ /*!
2
+ * Tableling v0.0.4 - Bootstrap
3
+ * Copyright (c) 2012 Simon Oulevay (Alpha Hydrae)
4
+ * Distributed under MIT license
5
+ * https://github.com/AlphaHydrae/tableling
6
+ */
7
+
8
+ (function(module) {
9
+
10
+ var Tableling = module.Tableling;
11
+
12
+ Tableling.Bootstrap = Tableling.Modular.extend({
13
+
14
+ className: 'tableling',
15
+ modules : [ 'table', 'pageSize', 'quickSearch', 'info', 'pagination' ],
16
+ template : _.template('<div class="tableling-headers"><div class="tableling-page-size pull-left" /><div class="tableling-quick-search pull-right" /></div><div class="tableling-table" /><div class="tableling-footers"><div class="tableling-info pull-left" /><div class="tableling-pagination pull-right" /></div>'),
17
+
18
+ regions : {
19
+ tableRegion : '.tableling-table',
20
+ pageSizeRegion : '.tableling-page-size',
21
+ quickSearchRegion : '.tableling-quick-search',
22
+ infoRegion : '.tableling-info',
23
+ paginationRegion : '.tableling-pagination'
24
+ }
25
+ });
26
+
27
+ _.extend(Tableling.Bootstrap, {
28
+
29
+ TableView : Backbone.Marionette.CompositeView.extend({
30
+
31
+ events : {
32
+ 'click thead th' : 'updateSort'
33
+ },
34
+
35
+ initialize : function(options) {
36
+ // TODO: add auto-sort
37
+ this.vent = options.vent;
38
+ this.sort = [];
39
+ },
40
+
41
+ updateSort : function(ev) {
42
+
43
+ var el = $(ev.currentTarget);
44
+ if (!(el.hasClass('sorting') || el.hasClass('sorting-asc') || el.hasClass('sorting-desc'))) {
45
+ return;
46
+ }
47
+
48
+ var field = this.fieldName(el);
49
+
50
+ if (ev.shiftKey || this.sort.length == 1) {
51
+
52
+ var existing = _.find(this.sort, function(item) {
53
+ return item.field == field;
54
+ });
55
+
56
+ if (existing) {
57
+ existing.direction = existing.direction == 'asc' ? 'desc' : 'asc';
58
+ el.removeClass('sorting sorting-asc sorting-desc');
59
+ el.addClass('sorting-' + existing.direction);
60
+ return this.vent.trigger('tableling:update', this.config());
61
+ }
62
+ }
63
+
64
+ if (!ev.shiftKey) {
65
+ this.sort.length = 0;
66
+ this.$el.find('thead th').removeClass('sorting sorting-asc sorting-desc').addClass('sorting');
67
+ }
68
+
69
+ this.sort.push({
70
+ field: field,
71
+ direction: 'asc'
72
+ });
73
+
74
+ el.removeClass('sorting sorting-asc sorting-desc').addClass('sorting-asc');
75
+
76
+ this.vent.trigger('tableling:update', this.config());
77
+ },
78
+
79
+ config : function() {
80
+ return {
81
+ page : 1,
82
+ sort : this.sortConfig()
83
+ };
84
+ },
85
+
86
+ sortConfig : function() {
87
+ if (!this.sort.length) {
88
+ return null;
89
+ }
90
+ return _.map(this.sort, function(item) {
91
+ return item.field + ' ' + item.direction;
92
+ });
93
+ },
94
+
95
+ fieldName : function(el) {
96
+ return el.data('field') || el.text().toLowerCase();
97
+ }
98
+ })
99
+ });
100
+
101
+ _.extend(Tableling.Bootstrap, {
102
+
103
+ PageSizeView : Tableling.Modular.createFieldModule('pageSize', {
104
+ // TODO: update current page intelligently
105
+ template : _.template('<select name="pageSize" class="input-mini"><option>5</option><option>10</option><option>15</option></select> entries per page')
106
+ })
107
+ });
108
+
109
+ _.extend(Tableling.Bootstrap.prototype, {
110
+ pageSizeView : Tableling.Bootstrap.PageSizeView
111
+ });
112
+
113
+ _.extend(Tableling.Bootstrap, {
114
+
115
+ QuickSearchView : Tableling.Modular.createFieldModule('quickSearch', {
116
+ template : _.template('<input type="text" name="quickSearch" placeholder="Quick search..." />')
117
+ })
118
+ });
119
+
120
+ _.extend(Tableling.Bootstrap.prototype, {
121
+ quickSearchView : Tableling.Bootstrap.QuickSearchView
122
+ });
123
+
124
+ _.extend(Tableling.Bootstrap, {
125
+
126
+ InfoView : Tableling.Modular.createModule({
127
+
128
+ template : _.template('Showing <span class="first">0</span> to <span class="last">0</span> of <span class="total">0</span> entries'),
129
+
130
+ ui : {
131
+ first: '.first',
132
+ last: '.last',
133
+ total: '.total'
134
+ },
135
+
136
+ refresh : function(data) {
137
+ if (data) {
138
+ this.ui.first.text(this.firstRecord(data));
139
+ this.ui.last.text(this.lastRecord(data));
140
+ this.ui.total.text(data.total);
141
+ }
142
+ },
143
+
144
+ firstRecord : function(data) {
145
+ return data.length ? (data.page - 1) * data.pageSize + 1 : 0;
146
+ },
147
+
148
+ lastRecord : function(data) {
149
+ return data.length ? this.firstRecord(data) + data.length - 1 : 0;
150
+ }
151
+ })
152
+ });
153
+
154
+ _.extend(Tableling.Bootstrap.prototype, {
155
+ infoView : Tableling.Bootstrap.InfoView
156
+ });
157
+
158
+ _.extend(Tableling.Bootstrap, {
159
+
160
+ PaginationView : Tableling.Modular.createModule({
161
+
162
+ template : _.template('<div class="pagination"><ul><li class="first"><a href="#">&lt;&lt;</a></li><li class="previous"><a href="#">&lt;</a></li><li class="next"><a href="#">&gt;</a></li><li class="last"><a href="#">&gt;&gt;</a></li></ul></div>'),
163
+
164
+ ui : {
165
+ first : '.first',
166
+ previous : '.previous',
167
+ next : '.next',
168
+ last : '.last'
169
+ },
170
+
171
+ events : {
172
+ 'click .first:not(.disabled)' : 'goToFirstPage',
173
+ 'click .previous:not(.disabled)' : 'goToPreviousPage',
174
+ 'click .next:not(.disabled)' : 'goToNextPage',
175
+ 'click .last:not(.disabled)' : 'goToLastPage'
176
+ },
177
+
178
+ refresh : function(data) {
179
+ if (!data) {
180
+ this.ui.first.addClass('disabled');
181
+ this.ui.previous.addClass('disabled');
182
+ this.ui.next.addClass('disabled');
183
+ this.ui.last.addClass('disabled');
184
+ } else {
185
+ this.data = data;
186
+ this.enable(this.ui.first, data.page > 1);
187
+ this.enable(this.ui.previous, data.page > 1);
188
+ this.enable(this.ui.next, data.page < this.numberOfPages(data));
189
+ this.enable(this.ui.last, data.page < this.numberOfPages(data));
190
+ }
191
+ },
192
+
193
+ enable : function(el, enabled) {
194
+ el.removeClass('disabled');
195
+ if (!enabled) {
196
+ el.addClass('disabled');
197
+ }
198
+ },
199
+
200
+ numberOfPages : function() {
201
+ return Math.ceil(this.data.total / this.data.pageSize);
202
+ },
203
+
204
+ goToFirstPage : function() {
205
+ this.goToPage(1);
206
+ },
207
+
208
+ goToPreviousPage : function() {
209
+ this.goToPage(this.data.page - 1);
210
+ },
211
+
212
+ goToNextPage : function() {
213
+ this.goToPage(this.data.page + 1);
214
+ },
215
+
216
+ goToLastPage : function() {
217
+ this.goToPage(this.numberOfPages());
218
+ },
219
+
220
+ goToPage : function(n) {
221
+ this.vent.trigger('tableling:update', { page : n });
222
+ }
223
+ })
224
+ });
225
+
226
+ _.extend(Tableling.Bootstrap.prototype, {
227
+ paginationView : Tableling.Bootstrap.PaginationView
228
+ });
229
+
230
+ })(this);
@@ -0,0 +1,321 @@
1
+ /*!
2
+ * Tableling v0.0.4
3
+ * Copyright (c) 2012 Simon Oulevay (Alpha Hydrae)
4
+ * Distributed under MIT license
5
+ * https://github.com/AlphaHydrae/tableling
6
+ */
7
+
8
+ (function(module) {
9
+
10
+ // Tableling
11
+ // ---------
12
+ //
13
+ // A tableling table is a Marionette layout which fetches data
14
+ // from a Backbone collection. It is controlled with an EventAggregator.
15
+ module.Tableling = Tableling = Backbone.Marionette.Layout.extend({
16
+
17
+ className: 'tableling',
18
+
19
+ // Default table options can be overriden by subclasses.
20
+ tableling : {
21
+ page : 1,
22
+ pageSize : 15
23
+ },
24
+
25
+ initialize : function(options) {
26
+ options = options || {};
27
+
28
+ // Table options can also be overriden for each instance by passing
29
+ // a `tableling` object in the options.
30
+ this.tableling = _.extend(_.clone(this.tableling), options.tableling || {});
31
+
32
+ // We use an event aggregator to manage the layout and its components.
33
+ // You can use your own by passing a `vent` option.
34
+ this.vent = options.vent || new Backbone.Marionette.EventAggregator();
35
+
36
+ // Components should trigger the `tableling:update` event to update
37
+ // the table (e.g. change page size, sort) and fetch the new data.
38
+ this.vent.on('tableling:update', this.update, this);
39
+
40
+ this.on('render', this.setup, this);
41
+ },
42
+
43
+ // Called once rendering is complete. By default, it updates the table.
44
+ setup : function() {
45
+ this.vent.trigger('tableling:update');
46
+ },
47
+
48
+ // Subclasses must return the Backbone.Collection used to fetch data.
49
+ getCollection : function() {
50
+ throw new Error('#getCollection not implemented. It should return the Backbone.Collection instance used to fetch data.');
51
+ },
52
+
53
+ // ### Refreshing the table
54
+ update : function(config, options) {
55
+
56
+ _.each(this.filterConfig(config || {}), _.bind(this.updateValue, this));
57
+
58
+ // Set the `refresh` option to false to update the table configuration
59
+ // without refreshing.
60
+ if (!options || typeof(options.refresh) == 'undefined' || options.refresh) {
61
+ this.refresh();
62
+ }
63
+ },
64
+
65
+ updateValue : function(value, key) {
66
+ if (value && value.toString().length) {
67
+ this.tableling[key] = value;
68
+ } else {
69
+ // Blank values are deleted to avoid sending them in ajax requests.
70
+ delete this.tableling[key];
71
+ }
72
+ },
73
+
74
+ refresh : function() {
75
+
76
+ // `tableling:refreshing` is triggered every time new data is being fetched.
77
+ // The first argument is the request data.
78
+ var data = this.requestData();
79
+ this.ventTrigger('tableling:refreshing', data);
80
+
81
+ // You can provide a `tableling.request` option to add properties to the
82
+ // fetch request.
83
+ //
84
+ // var MyTable = Tableling.extend({
85
+ // tableling : {
86
+ // type : 'POST' // fetch data with POST
87
+ // }
88
+ // });
89
+ this.getCollection().fetch(_.extend(this.tableling.request || {}, {
90
+ data: data,
91
+ success: _.bind(this.processResponse, this)
92
+ }));
93
+ },
94
+
95
+ // ### Response
96
+ processResponse : function(collection, response) {
97
+
98
+ this.tableling.length = collection.length;
99
+
100
+ // Tableling expects the response from a fetch to have a `total` property
101
+ // which is the total number of items (not just in the current page).
102
+ this.tableling.total = response.total;
103
+
104
+ // `tableling:refreshed` is triggered after every refresh. The first argument
105
+ // is the current table configuration with the following additional meta data:
106
+ //
107
+ // * `total` - the total number of items
108
+ // * `length` - the number of items in the current page
109
+ this.ventTrigger('tableling:refreshed', this.filterConfig(this.tableling, true));
110
+ },
111
+
112
+ // ### Request
113
+ // Builds the request data. Subclasses may override this if they need to
114
+ // send additional data.
115
+ requestData : function() {
116
+ return this.filterConfig(this.tableling);
117
+ },
118
+
119
+ // ### Utilities
120
+ // Whitelists the given configuration to contain only table configuration properties.
121
+ // Pass `true` as the second argument to include meta data (i.e. total & length).
122
+ filterConfig : function(config, all) {
123
+ if (all) {
124
+ return _.pick(config, 'page', 'pageSize', 'quickSearch', 'sort', 'length', 'total');
125
+ } else {
126
+ return _.pick(config, 'page', 'pageSize', 'quickSearch', 'sort');
127
+ }
128
+ },
129
+
130
+ // Triggers an event in the event aggregator. If `Tableling.debug` is set, it also
131
+ // logs the event and its arguments.
132
+ ventTrigger : function() {
133
+
134
+ var args = Array.prototype.slice.call(arguments);
135
+ this.vent.trigger.apply(this.vent, args);
136
+
137
+ if (Tableling.debug) {
138
+ console.log(args.shift() + ' - ' + JSON.stringify(args));
139
+ }
140
+ }
141
+ });
142
+
143
+ // Tableling.Collection
144
+ // --------------------
145
+ //
146
+ // Tableling expects fetch responses to have a `total` property in addition
147
+ // to the model data. You can extend this Backbone.Collection subclass which
148
+ // expects the following response format:
149
+ //
150
+ // {
151
+ // "total": 12,
152
+ // "data": [
153
+ // { /* ... model data ... */ },
154
+ // { /* ... model data ... */ }
155
+ // ]
156
+ // }
157
+ Tableling.Collection = Backbone.Collection.extend({
158
+
159
+ parse : function(response) {
160
+ return response.data;
161
+ }
162
+ });
163
+
164
+ // Tableling.Modular
165
+ // -----------------
166
+ //
167
+ // Tableling subclass which splits functionality into *modules*
168
+ // and handles rendering.
169
+ Tableling.Modular = Tableling.extend({
170
+
171
+ // The list of module names must be specified by subclasses.
172
+ modules : [],
173
+
174
+ // Modules are set up after rendering, before refreshing.
175
+ setup : function() {
176
+
177
+ this.moduleViews = {};
178
+ _.each(this.modules, _.bind(this.setupModule, this));
179
+
180
+ Tableling.prototype.setup.call(this);
181
+ },
182
+
183
+ // ### Modules
184
+ // Each module is identified by a name, for example `pageSize`.
185
+ setupModule : function(name) {
186
+
187
+ // The layout must have a region named after the module, e.g. `pageSizeRegion`.
188
+ var region = name + 'Region';
189
+
190
+ // It must have a view class, e.g. `pageSizeView`, which will be shown into
191
+ // the region.
192
+ var viewClass = this[name + 'View'];
193
+
194
+ // When instantiated, the view class will be passed the event
195
+ // aggregator as the `vent` option. Additional options can be
196
+ // given named after the view class, e.g. `pageSizeViewOptions`.
197
+ var options = _.extend(this[name + 'ViewOptions'] || {}, { vent: this.vent });
198
+
199
+ var view = new viewClass(options);
200
+
201
+ // Module view instances are stored by name in the `moduleViews` property
202
+ // for future reference.
203
+ this.moduleViews[name] = view;
204
+
205
+ this[region].show(view);
206
+ return view;
207
+ },
208
+
209
+ // By default, a modular table expects a `table` module which
210
+ // should have a collection (e.g. a Marionette CompositeView or
211
+ // CollectionView). If your subclass does not have that, it
212
+ // should override this method to return the Backbone.Collection
213
+ // used to fetch table data.
214
+ getCollection : function() {
215
+ return this.moduleViews.table.collection;
216
+ }
217
+ });
218
+
219
+ // ### Example
220
+ // This is how a `PageSizeView` module might be registered in a subclass:
221
+ //
222
+ // var MyTable = Tableling.Modular.extend({
223
+ //
224
+ // modules : [ 'pageSize' ],
225
+ //
226
+ // pageSizeView : PageSizeView,
227
+ // pageSizeViewOptions : {
228
+ // itemView : PageSizeItem
229
+ // },
230
+ //
231
+ // regions : {
232
+ // pageSizeRegion : '.pageSize'
233
+ // }
234
+ // });
235
+
236
+ // ### Utilities
237
+ _.extend(Tableling.Modular, {
238
+
239
+ // **createModule** creates an item view that is automatically bound
240
+ // to the layout's event aggregator.
241
+ createModule : function(options) {
242
+ return Backbone.Marionette.ItemView.extend(_.extend({
243
+
244
+ initialize : function(options) {
245
+ this.vent = options.vent;
246
+
247
+ // The `refresh` method of the view is called once the view is rendered
248
+ // and every time the table is refreshed.
249
+ this.vent.on('tableling:refreshed', this.refresh, this);
250
+ this.on('render', this.refresh, this);
251
+ },
252
+
253
+ // Call `update` to trigger an update of the table.
254
+ update : function() {
255
+ this.vent.trigger('tableling:update', this.config());
256
+ },
257
+
258
+ // Implementations should override this to stay up to date with
259
+ // the table state. Note that the data parameter will be undefined
260
+ // on the first refresh when the view is rendered.
261
+ refresh : function(data) {
262
+ },
263
+
264
+ // New table configuration to be sent on updates. For example,
265
+ // a page size view might update the `pageSize` property.
266
+ config : function() {
267
+ return {};
268
+ }
269
+ }, options));
270
+ },
271
+
272
+ // **createFieldModule** creates a basic module with a single form field.
273
+ // It comes with sensible defaults and only requires a `template` parameter.
274
+ createFieldModule : function(name, options) {
275
+
276
+ var definition = {
277
+
278
+ // The name attribute of the form field is expected to be the name of
279
+ // the module, e.g. `pageSize`.
280
+ ui : {
281
+ field : '[name="' + name + '"]'
282
+ },
283
+
284
+ events : {},
285
+
286
+ // The table property updated is the one with the same name as the module.
287
+ config : function() {
288
+ var config = {};
289
+ config[name] = this.ui.field.val();
290
+ return config;
291
+ }
292
+ };
293
+
294
+ // The `change` even triggers an update.
295
+ definition.events['change [name="' + name + '"]'] = 'update';
296
+
297
+ return Tableling.Modular.createModule(_.extend(definition, options));
298
+ }
299
+
300
+ // This is how a `PageSizeView` module might be implemented:
301
+ //
302
+ // var html = '<input type="text" name="pageSize" />';
303
+ //
304
+ // var PageSizeView =
305
+ // Tableling.Modular.createFieldModule('pageSize', {
306
+ //
307
+ // template : _.template(html)
308
+ // });
309
+ //
310
+ // When the value of the input field changes, the event aggregator will
311
+ // receive a `tableling:update` event with the `pageSize` property set
312
+ // to that value.
313
+ });
314
+
315
+ })(this);
316
+
317
+ // Implementations
318
+ // ---------------
319
+ //
320
+ // <a href="tableling-bootstrap.html">tableling-bootstrap</a> provides views styled
321
+ // with [Twitter Bootstrap](http://twitter.github.com/bootstrap/) classes.