tableling-rails 0.0.21 → 0.0.22
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.
- checksums.yaml +8 -8
- data/VERSION +1 -1
- data/lib/tableling-rails/version.rb +1 -1
- data/vendor/assets/javascripts/tableling.backbone.js +4762 -0
- data/vendor/assets/javascripts/tableling.js +713 -0
- data/vendor/assets/javascripts/tableling.world.js +15356 -0
- metadata +4 -1
@@ -0,0 +1,713 @@
|
|
1
|
+
/*!
|
2
|
+
* Tableling v0.0.22
|
3
|
+
* Copyright (c) 2012-2013 Simon Oulevay (Alpha Hydrae) <hydrae.alpha@gmail.com>
|
4
|
+
* Distributed under MIT license
|
5
|
+
* https://github.com/AlphaHydrae/tableling
|
6
|
+
*/
|
7
|
+
Backbone.Tableling = Tableling = (function(Backbone, _, $){
|
8
|
+
|
9
|
+
var Tableling = {
|
10
|
+
version : "0.0.22"
|
11
|
+
};
|
12
|
+
|
13
|
+
// Tableling
|
14
|
+
// ---------
|
15
|
+
//
|
16
|
+
// A tableling table is a Marionette layout which fetches data
|
17
|
+
// from a Backbone collection. It is controlled with an EventAggregator.
|
18
|
+
Tableling.Table = Backbone.Marionette.Layout.extend({
|
19
|
+
|
20
|
+
className: 'tableling',
|
21
|
+
|
22
|
+
// Default table options can be overriden by subclasses.
|
23
|
+
config : {
|
24
|
+
page : 1
|
25
|
+
},
|
26
|
+
|
27
|
+
initialize : function(options) {
|
28
|
+
options = options || {};
|
29
|
+
|
30
|
+
this.collection = options.collection;
|
31
|
+
|
32
|
+
// Table options can also be overriden for each instance at construction.
|
33
|
+
this.config = _.extend(_.clone(this.config || {}), _.result(options, 'config') || {});
|
34
|
+
|
35
|
+
// We use an event aggregator to manage the layout and its components.
|
36
|
+
// You can use your own by passing a `vent` option.
|
37
|
+
this.vent = options.vent || new Backbone.Wreqr.EventAggregator();
|
38
|
+
|
39
|
+
this.fetchOptions = _.extend(_.clone(this.fetchOptions || {}), _.result(options, 'fetchOptions') || {});
|
40
|
+
this.autoUpdate = typeof(options.autoUpdate) != 'undefined' ? options.autoUpdate : true;
|
41
|
+
|
42
|
+
// Components should trigger the `table:update` event to update
|
43
|
+
// the table (e.g. change page size, sort) and fetch the new data.
|
44
|
+
this.vent.on('table:update', this.onUpdate, this);
|
45
|
+
|
46
|
+
this.on('item:rendered', this.setup, this);
|
47
|
+
},
|
48
|
+
|
49
|
+
// Called once rendering is complete. By default, it updates the table.
|
50
|
+
setup : function() {
|
51
|
+
this.ventTrigger('table:setup', this.config);
|
52
|
+
if (this.autoUpdate) {
|
53
|
+
this.ventTrigger('table:update');
|
54
|
+
}
|
55
|
+
},
|
56
|
+
|
57
|
+
// Subclasses must return the Backbone.Collection used to fetch data.
|
58
|
+
getCollection : function() {
|
59
|
+
return this.collection;
|
60
|
+
},
|
61
|
+
|
62
|
+
// ### Refreshing the table
|
63
|
+
update : function(config, options) {
|
64
|
+
this.ventTrigger('table:update', config, options);
|
65
|
+
},
|
66
|
+
|
67
|
+
onUpdate : function(config, options) {
|
68
|
+
|
69
|
+
_.each(config || {}, _.bind(this.updateValue, this));
|
70
|
+
|
71
|
+
// Set the `refresh` option to false to update the table configuration
|
72
|
+
// without refreshing.
|
73
|
+
if (!options || typeof(options.refresh) == 'undefined' || options.refresh) {
|
74
|
+
this.refresh();
|
75
|
+
}
|
76
|
+
},
|
77
|
+
|
78
|
+
updateValue : function(value, key) {
|
79
|
+
if (value && value.toString().length) {
|
80
|
+
this.config[key] = value;
|
81
|
+
} else {
|
82
|
+
// Blank values are deleted to avoid sending them in ajax requests.
|
83
|
+
delete this.config[key];
|
84
|
+
}
|
85
|
+
},
|
86
|
+
|
87
|
+
refresh : function() {
|
88
|
+
|
89
|
+
// You can provide `fetchOptions` to add properties to the
|
90
|
+
// fetch request.
|
91
|
+
//
|
92
|
+
// var MyTable = Tableling.Table.extend({
|
93
|
+
// fetchOptions : {
|
94
|
+
// type : 'POST' // fetch data with POST
|
95
|
+
// }
|
96
|
+
// });
|
97
|
+
//
|
98
|
+
// // You can also override for each instance.
|
99
|
+
// new MyTable({
|
100
|
+
// fetchOptions : {
|
101
|
+
// type : 'GET'
|
102
|
+
// }
|
103
|
+
// });
|
104
|
+
var options = _.clone(this.fetchOptions);
|
105
|
+
options.data = this.requestData();
|
106
|
+
options.success = _.bind(this.processResponse, this);
|
107
|
+
|
108
|
+
// `table:refreshing` is triggered every time new data is being fetched.
|
109
|
+
// The first argument is the request data.
|
110
|
+
this.ventTrigger('table:refreshing', options.data);
|
111
|
+
|
112
|
+
this.getCollection().fetch(options);
|
113
|
+
},
|
114
|
+
|
115
|
+
// ### Request
|
116
|
+
requestData : function() {
|
117
|
+
return this.config;
|
118
|
+
},
|
119
|
+
|
120
|
+
// ### Response
|
121
|
+
processResponse : function(collection, response) {
|
122
|
+
|
123
|
+
this.config.length = collection.length;
|
124
|
+
|
125
|
+
// Tableling expects the response from a fetch to have a `total` property
|
126
|
+
// which is the total number of items (not just in the current page).
|
127
|
+
this.config.total = response.total;
|
128
|
+
|
129
|
+
// The server may override the `page` property, for example if the
|
130
|
+
// requested page was outside the range of available pages.
|
131
|
+
if (response.page) {
|
132
|
+
this.config.page = response.page;
|
133
|
+
}
|
134
|
+
|
135
|
+
// `tableling:refreshed` is triggered after every refresh. The first argument
|
136
|
+
// is the current table configuration with the following additional meta data:
|
137
|
+
//
|
138
|
+
// * `total` - the total number of items
|
139
|
+
// * `length` - the number of items in the current page
|
140
|
+
this.ventTrigger('table:refreshed', this.config);
|
141
|
+
},
|
142
|
+
|
143
|
+
// Triggers an event in the event aggregator. If `Tableling.debug` is set, it also
|
144
|
+
// logs the event and its arguments.
|
145
|
+
ventTrigger : function() {
|
146
|
+
|
147
|
+
var args = Array.prototype.slice.call(arguments);
|
148
|
+
if (Tableling.debug) {
|
149
|
+
console.log(_.first(args) + ' - ' + JSON.stringify(args.slice(1)));
|
150
|
+
}
|
151
|
+
|
152
|
+
this.vent.trigger.apply(this.vent, args);
|
153
|
+
}
|
154
|
+
});
|
155
|
+
|
156
|
+
// Tableling.Collection
|
157
|
+
// --------------------
|
158
|
+
//
|
159
|
+
// Tableling expects fetch responses to have a `total` property in addition
|
160
|
+
// to the model data. You can extend this Backbone.Collection subclass which
|
161
|
+
// expects the following response format:
|
162
|
+
//
|
163
|
+
// {
|
164
|
+
// "total": 12,
|
165
|
+
// "data": [
|
166
|
+
// { /* ... model data ... */ },
|
167
|
+
// { /* ... model data ... */ }
|
168
|
+
// ]
|
169
|
+
// }
|
170
|
+
Tableling.Collection = Backbone.Collection.extend({
|
171
|
+
|
172
|
+
parse : function(response) {
|
173
|
+
return response.data;
|
174
|
+
}
|
175
|
+
});
|
176
|
+
|
177
|
+
// Implementations
|
178
|
+
// ---------------
|
179
|
+
//
|
180
|
+
// <a href="tableling.bootstrap.html">tableling.bootstrap</a> provides views styled
|
181
|
+
// with [Twitter Bootstrap](http://twitter.github.com/bootstrap/) classes.
|
182
|
+
|
183
|
+
// Tableling.Modular
|
184
|
+
// -----------------
|
185
|
+
//
|
186
|
+
// Tableling subclass which splits functionality into *modules*
|
187
|
+
// and handles rendering.
|
188
|
+
Tableling.Modular = Tableling.Table.extend({
|
189
|
+
|
190
|
+
// The list of module names must be specified by subclasses.
|
191
|
+
modules : [],
|
192
|
+
|
193
|
+
// Modules are set up after rendering, before refreshing.
|
194
|
+
setup : function() {
|
195
|
+
|
196
|
+
this.moduleViews = {};
|
197
|
+
_.each(this.modules, _.bind(this.setupModule, this));
|
198
|
+
|
199
|
+
Tableling.Table.prototype.setup.call(this);
|
200
|
+
},
|
201
|
+
|
202
|
+
// ### Modules
|
203
|
+
// Each module is identified by a name, for example `pageSize`.
|
204
|
+
setupModule : function(name) {
|
205
|
+
|
206
|
+
// The layout must have a region named after the module, e.g. `pageSizeRegion`.
|
207
|
+
var region = name + 'Region';
|
208
|
+
|
209
|
+
// It must have a view class, e.g. `pageSizeView`, which will be shown into
|
210
|
+
// the region.
|
211
|
+
var viewClass = this[name + 'View'];
|
212
|
+
|
213
|
+
// When instantiated, the view class will be passed the event
|
214
|
+
// aggregator as the `vent` option. Additional options can be
|
215
|
+
// given named after the view class, e.g. `pageSizeViewOptions`.
|
216
|
+
var options = _.extend(this.getModuleOptions(name), { vent: this.vent });
|
217
|
+
|
218
|
+
var view = new viewClass(options);
|
219
|
+
|
220
|
+
// Module view instances are stored by name in the `moduleViews` property
|
221
|
+
// for future reference.
|
222
|
+
this.moduleViews[name] = view;
|
223
|
+
|
224
|
+
this[region].show(view);
|
225
|
+
return view;
|
226
|
+
},
|
227
|
+
|
228
|
+
// By default the collection is the one given at construction.
|
229
|
+
// Otherwise, a modular table expects a `table` module which
|
230
|
+
// should have a collection (e.g. a Marionette CompositeView or
|
231
|
+
// CollectionView). If your subclass does not have either, it
|
232
|
+
// should override this method to return the Backbone.Collection
|
233
|
+
// used to fetch table data.
|
234
|
+
getCollection : function() {
|
235
|
+
return this.collection || (this.moduleViews && this.moduleViews.table ? this.moduleViews.table.collection : undefined);
|
236
|
+
},
|
237
|
+
|
238
|
+
getModuleOptions : function(name) {
|
239
|
+
var options = this[name + 'ViewOptions'] || {};
|
240
|
+
options = typeof(options) == 'function' ? options.call(this) : options;
|
241
|
+
return name == 'table' ? _.defaults(options, { collection : this.collection }) : options;
|
242
|
+
}
|
243
|
+
});
|
244
|
+
|
245
|
+
// ### Example
|
246
|
+
// This is how a `PageSizeView` module might be registered in a subclass:
|
247
|
+
//
|
248
|
+
// var MyTable = Tableling.Modular.extend({
|
249
|
+
//
|
250
|
+
// modules : [ 'pageSize' ],
|
251
|
+
//
|
252
|
+
// pageSizeView : PageSizeView,
|
253
|
+
// pageSizeViewOptions : {
|
254
|
+
// itemView : PageSizeItem
|
255
|
+
// },
|
256
|
+
//
|
257
|
+
// regions : {
|
258
|
+
// pageSizeRegion : '.pageSize'
|
259
|
+
// }
|
260
|
+
// });
|
261
|
+
|
262
|
+
// Tableling.Module
|
263
|
+
// ----------------
|
264
|
+
//
|
265
|
+
// A module is an item view that is automatically bound to the table's
|
266
|
+
// event aggregator.
|
267
|
+
Tableling.Module = Backbone.Marionette.ItemView.extend({
|
268
|
+
|
269
|
+
i18n : {},
|
270
|
+
templateHelpers : function() {
|
271
|
+
return this.i18n;
|
272
|
+
},
|
273
|
+
|
274
|
+
initialize : function(options) {
|
275
|
+
|
276
|
+
this.vent = options.vent;
|
277
|
+
|
278
|
+
// The `setup` method of the view is called when the table
|
279
|
+
// is first set up.
|
280
|
+
this.vent.on('table:setup', this.setup, this);
|
281
|
+
|
282
|
+
// The `refresh` method of the view is called every time the table
|
283
|
+
// is refreshed.
|
284
|
+
this.vent.on('table:refreshed', this.refresh, this);
|
285
|
+
|
286
|
+
this.i18n = _.clone(options.i18n || this.i18n);
|
287
|
+
},
|
288
|
+
|
289
|
+
// Call `update` to trigger an update of the table.
|
290
|
+
update : function() {
|
291
|
+
this.vent.trigger('table:update', this.config());
|
292
|
+
},
|
293
|
+
|
294
|
+
// Implementations should override this to set initial values.
|
295
|
+
setup : function(config) {
|
296
|
+
},
|
297
|
+
|
298
|
+
// Implementations should override this to stay up to date with
|
299
|
+
// the table state.
|
300
|
+
refresh : function(config) {
|
301
|
+
},
|
302
|
+
|
303
|
+
// New table configuration to be sent on updates. For example,
|
304
|
+
// a page size view might update the `pageSize` property.
|
305
|
+
config : function() {
|
306
|
+
return {};
|
307
|
+
}
|
308
|
+
});
|
309
|
+
|
310
|
+
// Tableling.FieldModule
|
311
|
+
// ---------------------
|
312
|
+
//
|
313
|
+
// A basic module with a single form field. It comes with sensible
|
314
|
+
// defaults and only requires a `name` and a `template` parameter.
|
315
|
+
Tableling.FieldModule = Tableling.Module.extend({
|
316
|
+
|
317
|
+
// TODO: check name
|
318
|
+
|
319
|
+
initialize : function(options) {
|
320
|
+
|
321
|
+
Tableling.Module.prototype.initialize.call(this, options);
|
322
|
+
|
323
|
+
if (!this.ui) {
|
324
|
+
this.ui = {};
|
325
|
+
}
|
326
|
+
// The name attribute of the form field is the same as the
|
327
|
+
// module's, e.g. `pageSize`.
|
328
|
+
this.ui.field = '[name="' + this.name + '"]';
|
329
|
+
|
330
|
+
if (!this.events) {
|
331
|
+
this.events = {};
|
332
|
+
}
|
333
|
+
this.events['change [name="' + this.name + '"]'] = 'update';
|
334
|
+
},
|
335
|
+
|
336
|
+
setup : function(config) {
|
337
|
+
this.setupValue(config[this.name]);
|
338
|
+
this.vent.trigger('table:update', this.config(), { refresh : false });
|
339
|
+
},
|
340
|
+
|
341
|
+
setupValue : function(value) {
|
342
|
+
this.ui.field.val(value);
|
343
|
+
},
|
344
|
+
|
345
|
+
// The table property updated is the one with the same name as the module.
|
346
|
+
config : function() {
|
347
|
+
var config = {};
|
348
|
+
config[this.name] = this.ui.field.val();
|
349
|
+
return config;
|
350
|
+
}
|
351
|
+
});
|
352
|
+
|
353
|
+
// This is how a `PageSizeView` module might be implemented:
|
354
|
+
//
|
355
|
+
// var html = '<input type="text" name="pageSize" />';
|
356
|
+
//
|
357
|
+
// var PageSizeView = Tableling.FieldModule.extend({
|
358
|
+
// name : 'pageSize'
|
359
|
+
// template : _.template(html)
|
360
|
+
// });
|
361
|
+
//
|
362
|
+
// When the value of the input field changes, the event aggregator will
|
363
|
+
// receive a `tableling:update` event with the `pageSize` property set
|
364
|
+
// to that value.
|
365
|
+
|
366
|
+
Tableling.Plain = {};
|
367
|
+
|
368
|
+
Tableling.Plain.Table = Tableling.Modular.extend({
|
369
|
+
|
370
|
+
className: 'tableling',
|
371
|
+
modules : [ 'table', 'pageSize', 'quickSearch', 'info', 'page' ],
|
372
|
+
template : _.template('<div class="header"><div class="pageSize" /><div class="quickSearch" /></div><div class="table" /><div class="footer"><div class="info" /><div class="page" /></div>'),
|
373
|
+
|
374
|
+
regions : {
|
375
|
+
tableRegion : '.table',
|
376
|
+
pageSizeRegion : '.pageSize',
|
377
|
+
quickSearchRegion : '.quickSearch',
|
378
|
+
infoRegion : '.info',
|
379
|
+
pageRegion : '.page'
|
380
|
+
}
|
381
|
+
});
|
382
|
+
|
383
|
+
Tableling.Plain.TableView = Backbone.Marionette.CompositeView.extend({
|
384
|
+
|
385
|
+
events : {
|
386
|
+
'click thead th.sorting' : 'updateSort',
|
387
|
+
'click thead th.sorting-asc' : 'updateSort',
|
388
|
+
'click thead th.sorting-desc' : 'updateSort'
|
389
|
+
},
|
390
|
+
|
391
|
+
initialize : function(options) {
|
392
|
+
// TODO: add auto-sort
|
393
|
+
this.vent = options.vent;
|
394
|
+
this.sort = [];
|
395
|
+
this.vent.on('table:setup', this.setSort, this);
|
396
|
+
this.vent.on('table:refreshed', this.setSort, this);
|
397
|
+
},
|
398
|
+
|
399
|
+
updateSort : function(ev) {
|
400
|
+
|
401
|
+
var el = $(ev.currentTarget);
|
402
|
+
if (!(el.hasClass('sorting') || el.hasClass('sorting-asc') || el.hasClass('sorting-desc'))) {
|
403
|
+
return;
|
404
|
+
}
|
405
|
+
|
406
|
+
var field = this.fieldName(el);
|
407
|
+
|
408
|
+
if (ev.shiftKey || this.sort.length == 1) {
|
409
|
+
|
410
|
+
var index = -1;
|
411
|
+
_.find(this.sort, function(item, i) {
|
412
|
+
if (item.split(' ')[0] == field) {
|
413
|
+
index = i;
|
414
|
+
}
|
415
|
+
});
|
416
|
+
|
417
|
+
if (index >= 0) {
|
418
|
+
|
419
|
+
var parts = this.sort[index].split(' ');
|
420
|
+
this.sort[index] = parts[0] + ' ' + (parts[1] == 'asc' ? 'desc' : 'asc');
|
421
|
+
this.showSort();
|
422
|
+
return this.vent.trigger('table:update', this.config());
|
423
|
+
}
|
424
|
+
}
|
425
|
+
|
426
|
+
if (!ev.shiftKey) {
|
427
|
+
this.sort.length = 0;
|
428
|
+
}
|
429
|
+
|
430
|
+
this.sort.push(field + ' asc');
|
431
|
+
|
432
|
+
this.showSort();
|
433
|
+
|
434
|
+
this.vent.trigger('table:update', this.config());
|
435
|
+
},
|
436
|
+
|
437
|
+
setSort : function(config) {
|
438
|
+
if (config && config.sort) {
|
439
|
+
this.sort = config.sort.slice(0);
|
440
|
+
this.showSort();
|
441
|
+
}
|
442
|
+
},
|
443
|
+
|
444
|
+
showSort : function() {
|
445
|
+
|
446
|
+
this.$el.find('thead th.sorting, thead th.sorting-asc, thead th.sorting-desc').removeClass('sorting sorting-asc sorting-desc').addClass('sorting');
|
447
|
+
|
448
|
+
for (var i = 0; i < this.sort.length; i++) {
|
449
|
+
|
450
|
+
var parts = this.sort[i].split(' ');
|
451
|
+
var name = parts[0];
|
452
|
+
var direction = parts[1];
|
453
|
+
|
454
|
+
field = this.$el.find('thead [data-field="' + name + '"]');
|
455
|
+
if (!field.length) {
|
456
|
+
field = this.$el.find('thead th:contains("' + name + '")');
|
457
|
+
}
|
458
|
+
|
459
|
+
if (field.length) {
|
460
|
+
field.removeClass('sorting').addClass(direction == 'desc' ? 'sorting-desc' : 'sorting-asc');
|
461
|
+
}
|
462
|
+
}
|
463
|
+
},
|
464
|
+
|
465
|
+
config : function() {
|
466
|
+
return {
|
467
|
+
page : 1,
|
468
|
+
sort : this.sortConfig()
|
469
|
+
};
|
470
|
+
},
|
471
|
+
|
472
|
+
sortConfig : function() {
|
473
|
+
return this.sort.length ? this.sort : null;
|
474
|
+
},
|
475
|
+
|
476
|
+
fieldName : function(el) {
|
477
|
+
return el.data('field') || el.text();
|
478
|
+
}
|
479
|
+
});
|
480
|
+
|
481
|
+
Tableling.Plain.PageSizeView = Tableling.Plain.Table.prototype.pageSizeView = Tableling.FieldModule.extend({
|
482
|
+
|
483
|
+
// TODO: update current page intelligently
|
484
|
+
name : 'pageSize',
|
485
|
+
template : function(data) {
|
486
|
+
return _.template('<select name="pageSize" /> <%- entries %>', data);
|
487
|
+
},
|
488
|
+
|
489
|
+
i18n : {
|
490
|
+
entries : 'entries per page'
|
491
|
+
},
|
492
|
+
sizes : [ 10, 15, 20, 25, 50 ],
|
493
|
+
|
494
|
+
ui : {
|
495
|
+
field : 'select'
|
496
|
+
},
|
497
|
+
|
498
|
+
initialize : function(options) {
|
499
|
+
Tableling.FieldModule.prototype.initialize.call(this, options);
|
500
|
+
this.sizes = _.clone(options.sizes || this.sizes);
|
501
|
+
},
|
502
|
+
|
503
|
+
onRender : function() {
|
504
|
+
this.ui.field.empty();
|
505
|
+
_.each(this.sizes, _.bind(this.addSize, this));
|
506
|
+
},
|
507
|
+
|
508
|
+
addSize : function(size) {
|
509
|
+
$('<option />').text(size).appendTo(this.ui.field);
|
510
|
+
},
|
511
|
+
|
512
|
+
setupValue : function(value) {
|
513
|
+
if (value) {
|
514
|
+
Tableling.FieldModule.prototype.setupValue.apply(this, Array.prototype.slice.call(arguments));
|
515
|
+
}
|
516
|
+
},
|
517
|
+
|
518
|
+
config : function() {
|
519
|
+
var config = Tableling.FieldModule.prototype.config.call(this);
|
520
|
+
config.page = 1;
|
521
|
+
return config;
|
522
|
+
}
|
523
|
+
});
|
524
|
+
|
525
|
+
Tableling.Plain.QuickSearchView = Tableling.Plain.Table.prototype.quickSearchView = Tableling.FieldModule.extend({
|
526
|
+
|
527
|
+
name : 'quickSearch',
|
528
|
+
template : function(data) {
|
529
|
+
return _.template('<input type="text" name="quickSearch" placeholder="<%- quickSearch %>" />', data);
|
530
|
+
},
|
531
|
+
|
532
|
+
i18n : {
|
533
|
+
quickSearch : 'Quick search...'
|
534
|
+
},
|
535
|
+
|
536
|
+
config : function() {
|
537
|
+
var config = Tableling.FieldModule.prototype.config.call(this);
|
538
|
+
config.page = 1;
|
539
|
+
return config;
|
540
|
+
}
|
541
|
+
});
|
542
|
+
|
543
|
+
Tableling.Plain.InfoView = Tableling.Plain.Table.prototype.infoView = Tableling.Module.extend({
|
544
|
+
|
545
|
+
template : function(data) {
|
546
|
+
return _.template(data.template, {
|
547
|
+
first : '<span class="first">0</span>',
|
548
|
+
last : '<span class="last">0</span>',
|
549
|
+
total : '<span class="total">0</span>'
|
550
|
+
});
|
551
|
+
},
|
552
|
+
|
553
|
+
i18n : {
|
554
|
+
template : 'Showing <%= first %> to <%= last %> of <%= total %> entries'
|
555
|
+
},
|
556
|
+
|
557
|
+
ui : {
|
558
|
+
first: '.first',
|
559
|
+
last: '.last',
|
560
|
+
total: '.total'
|
561
|
+
},
|
562
|
+
|
563
|
+
refresh : function(data) {
|
564
|
+
if (data) {
|
565
|
+
this.ui.first.text(this.firstRecord(data));
|
566
|
+
this.ui.last.text(this.lastRecord(data));
|
567
|
+
this.ui.total.text(data.total);
|
568
|
+
}
|
569
|
+
},
|
570
|
+
|
571
|
+
firstRecord : function(data) {
|
572
|
+
return data.length ? ((data.page || 1) - 1) * data.pageSize + 1 : 0;
|
573
|
+
},
|
574
|
+
|
575
|
+
lastRecord : function(data) {
|
576
|
+
return data.length ? this.firstRecord(data) + data.length - 1 : 0;
|
577
|
+
}
|
578
|
+
});
|
579
|
+
|
580
|
+
Tableling.Plain.PageView = Tableling.Plain.Table.prototype.pageView = Tableling.Module.extend({
|
581
|
+
|
582
|
+
template : _.template('<div class="pagination"><ul><li class="first"><a href="#"><<</a></li><li class="previous"><a href="#"><</a></li><li class="next"><a href="#">></a></li><li class="last"><a href="#">>></a></li></ul></div>'),
|
583
|
+
pageTemplate : _.template('<li class="page"><a href="#"><%- number %></a></li>'),
|
584
|
+
|
585
|
+
ui : {
|
586
|
+
first : '.first',
|
587
|
+
previous : '.previous',
|
588
|
+
next : '.next',
|
589
|
+
last : '.last'
|
590
|
+
},
|
591
|
+
|
592
|
+
events : {
|
593
|
+
'click .first:not(.disabled)' : 'goToFirstPage',
|
594
|
+
'click .previous:not(.disabled)' : 'goToPreviousPage',
|
595
|
+
'click .page:not(.disabled)' : 'goToPage',
|
596
|
+
'click .next:not(.disabled)' : 'goToNextPage',
|
597
|
+
'click .last:not(.disabled)' : 'goToLastPage'
|
598
|
+
},
|
599
|
+
|
600
|
+
refresh : function(data) {
|
601
|
+
this.$el.find('.page').remove();
|
602
|
+
if (!data || !data.length) {
|
603
|
+
this.ui.first.addClass('disabled');
|
604
|
+
this.ui.previous.addClass('disabled');
|
605
|
+
this.ui.next.addClass('disabled');
|
606
|
+
this.ui.last.addClass('disabled');
|
607
|
+
} else {
|
608
|
+
this.data = data;
|
609
|
+
this.enable(this.ui.first, this.getPage(data) > 1);
|
610
|
+
this.enable(this.ui.previous, this.getPage(data) > 1);
|
611
|
+
this.setupPages();
|
612
|
+
this.enable(this.ui.next, this.getPage(data) < this.numberOfPages(data));
|
613
|
+
this.enable(this.ui.last, this.getPage(data) < this.numberOfPages(data));
|
614
|
+
}
|
615
|
+
},
|
616
|
+
|
617
|
+
setupPages : function() {
|
618
|
+
|
619
|
+
var page = this.getPage(this.data);
|
620
|
+
var total = this.numberOfPages();
|
621
|
+
|
622
|
+
var first = page - 2;
|
623
|
+
if (total - first < 4) {
|
624
|
+
first = total - 4;
|
625
|
+
}
|
626
|
+
|
627
|
+
if (first < 1) {
|
628
|
+
first = 1;
|
629
|
+
}
|
630
|
+
|
631
|
+
var n = 5;
|
632
|
+
if (first + n - 1 > total) {
|
633
|
+
n = total - first + 1;
|
634
|
+
}
|
635
|
+
|
636
|
+
_.times(n, function(i) {
|
637
|
+
$(this.pageTemplate({ number : first + i })).insertBefore(this.ui.next);
|
638
|
+
}, this);
|
639
|
+
|
640
|
+
var i = page - first;
|
641
|
+
this.$el.find('.page').slice(i, i + 1).addClass('disabled');
|
642
|
+
},
|
643
|
+
|
644
|
+
enable : function(el, enabled) {
|
645
|
+
el.removeClass('disabled');
|
646
|
+
if (!enabled) {
|
647
|
+
el.addClass('disabled');
|
648
|
+
}
|
649
|
+
},
|
650
|
+
|
651
|
+
numberOfPages : function() {
|
652
|
+
return Math.ceil(this.data.total / this.data.pageSize);
|
653
|
+
},
|
654
|
+
|
655
|
+
goToFirstPage : function(e) {
|
656
|
+
e.preventDefault();
|
657
|
+
this.goToPageNumber(1);
|
658
|
+
},
|
659
|
+
|
660
|
+
goToPreviousPage : function(e) {
|
661
|
+
e.preventDefault();
|
662
|
+
this.goToPageNumber(this.getPage(this.data) - 1);
|
663
|
+
},
|
664
|
+
|
665
|
+
goToPage : function(e) {
|
666
|
+
e.preventDefault();
|
667
|
+
this.goToPageNumber(parseInt($(e.target).text(), 10));
|
668
|
+
},
|
669
|
+
|
670
|
+
goToNextPage : function(e) {
|
671
|
+
e.preventDefault();
|
672
|
+
this.goToPageNumber(this.getPage(this.data) + 1);
|
673
|
+
},
|
674
|
+
|
675
|
+
goToLastPage : function(e) {
|
676
|
+
e.preventDefault();
|
677
|
+
this.goToPageNumber(this.numberOfPages());
|
678
|
+
},
|
679
|
+
|
680
|
+
goToPageNumber : function(n) {
|
681
|
+
this.vent.trigger('table:update', { page : n });
|
682
|
+
},
|
683
|
+
|
684
|
+
getPage : function(data) {
|
685
|
+
return data.page || 1;
|
686
|
+
}
|
687
|
+
});
|
688
|
+
|
689
|
+
Tableling.Bootstrap = {};
|
690
|
+
|
691
|
+
Tableling.Bootstrap.Table = Tableling.Plain.Table.extend({
|
692
|
+
template : _.template('<div class="header"><div class="pageSize pull-left" /><div class="quickSearch pull-right" /></div><div class="table" /><div class="footer"><div class="info pull-left" /><div class="page pull-right" /></div>')
|
693
|
+
});
|
694
|
+
|
695
|
+
Tableling.Bootstrap.TableView = Tableling.Plain.TableView.extend({});
|
696
|
+
|
697
|
+
Tableling.Bootstrap.PageSizeView = Tableling.Bootstrap.Table.prototype.pageSizeView = Tableling.Plain.PageSizeView.extend({
|
698
|
+
|
699
|
+
template : function(data) {
|
700
|
+
return _.template('<select name="pageSize" class="input-mini"><option>5</option><option>10</option><option>15</option></select> <%- entries %>', data);
|
701
|
+
}
|
702
|
+
});
|
703
|
+
|
704
|
+
Tableling.Bootstrap.QuickSearchView = Tableling.Bootstrap.Table.prototype.quickSearchView = Tableling.Plain.QuickSearchView.extend({});
|
705
|
+
|
706
|
+
Tableling.Bootstrap.InfoView = Tableling.Bootstrap.Table.prototype.infoView = Tableling.Plain.InfoView.extend({});
|
707
|
+
|
708
|
+
Tableling.Bootstrap.PageView = Tableling.Bootstrap.Table.prototype.pageView = Tableling.Plain.PageView.extend({});
|
709
|
+
|
710
|
+
|
711
|
+
return Tableling;
|
712
|
+
|
713
|
+
})(Backbone, _, $ || window.jQuery || window.Zepto || window.ender);
|