tableling-rails 0.0.3 → 0.0.4

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