rack-push-notification 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +12 -50
  4. data/README.md +0 -15
  5. data/lib/rack/push-notification.rb +15 -9
  6. data/lib/rack/push-notification/migrations/001_base_schema.rb +4 -4
  7. data/lib/rack/push-notification/migrations/002_add_full_text_search.rb +9 -16
  8. data/lib/rack/push-notification/{device.rb → models/device.rb} +7 -10
  9. data/rack-push-notification-0.3.0.gem +0 -0
  10. data/rack-push-notification-0.3.1.gem +0 -0
  11. data/rack-push-notification.gemspec +3 -11
  12. metadata +47 -144
  13. data/lib/rack/push-notification/admin.rb +0 -140
  14. data/lib/rack/push-notification/assets/images/wallpaper-clown-fish.jpg +0 -0
  15. data/lib/rack/push-notification/assets/javascripts/application.coffee +0 -28
  16. data/lib/rack/push-notification/assets/javascripts/collections/devices.coffee +0 -29
  17. data/lib/rack/push-notification/assets/javascripts/models/device.coffee +0 -2
  18. data/lib/rack/push-notification/assets/javascripts/routers/root.coffee +0 -30
  19. data/lib/rack/push-notification/assets/javascripts/rpn.coffee +0 -14
  20. data/lib/rack/push-notification/assets/javascripts/templates/_devices.jst.eco +0 -23
  21. data/lib/rack/push-notification/assets/javascripts/templates/_preview.jst.eco +0 -24
  22. data/lib/rack/push-notification/assets/javascripts/templates/compose.jst.eco +0 -46
  23. data/lib/rack/push-notification/assets/javascripts/templates/devices.jst.eco +0 -12
  24. data/lib/rack/push-notification/assets/javascripts/templates/pagination.jst.eco +0 -12
  25. data/lib/rack/push-notification/assets/javascripts/vendor/backbone.js +0 -1431
  26. data/lib/rack/push-notification/assets/javascripts/vendor/backbone.paginator.js +0 -833
  27. data/lib/rack/push-notification/assets/javascripts/vendor/codemirror.javascript.js +0 -411
  28. data/lib/rack/push-notification/assets/javascripts/vendor/codemirror.js +0 -3047
  29. data/lib/rack/push-notification/assets/javascripts/vendor/date.js +0 -104
  30. data/lib/rack/push-notification/assets/javascripts/vendor/jquery.js +0 -9404
  31. data/lib/rack/push-notification/assets/javascripts/vendor/underscore.js +0 -1059
  32. data/lib/rack/push-notification/assets/javascripts/views/compose.coffee +0 -120
  33. data/lib/rack/push-notification/assets/javascripts/views/devices.coffee +0 -23
  34. data/lib/rack/push-notification/assets/javascripts/views/pagination.coffee +0 -29
  35. data/lib/rack/push-notification/assets/stylesheets/_codemirror.sass +0 -219
  36. data/lib/rack/push-notification/assets/stylesheets/_preview.sass +0 -148
  37. data/lib/rack/push-notification/assets/stylesheets/screen.sass +0 -108
  38. data/lib/rack/push-notification/assets/views/index.haml +0 -26
  39. data/lib/rack/push-notification/version.rb +0 -5
@@ -1,833 +0,0 @@
1
- /*! backbone.paginator - v0.1.54 - 8/18/2012
2
- * http://github.com/addyosmani/backbone.paginator
3
- * Copyright (c) 2012 Addy Osmani; Licensed MIT */
4
-
5
- Backbone.Paginator = (function ( Backbone, _, $ ) {
6
- "use strict";
7
-
8
- var Paginator = {};
9
- Paginator.version = "0.15";
10
-
11
- // @name: clientPager
12
- //
13
- // @tagline: Paginator for client-side data
14
- //
15
- // @description:
16
- // This paginator is responsible for providing pagination
17
- // and sort capabilities for a single payload of data
18
- // we wish to paginate by the UI for easier browsering.
19
- //
20
- Paginator.clientPager = Backbone.Collection.extend({
21
-
22
- // Default values used when sorting and/or filtering.
23
- initialize: function(){
24
- this.useDiacriticsPlugin = true; // use diacritics plugin if available
25
- this.useLevenshteinPlugin = true; // use levenshtein plugin if available
26
-
27
- this.sortColumn = "";
28
- this.sortDirection = "desc";
29
- this.lastSortColumn = "";
30
-
31
- this.fieldFilterRules = [];
32
- this.lastFieldFilterRules = [];
33
-
34
- this.filterFields = "";
35
- this.filterExpression = "";
36
- this.lastFilterExpression = "";
37
- },
38
-
39
- sync: function ( method, model, options ) {
40
-
41
- var self = this;
42
-
43
- // Create default values if no others are specified
44
- _.defaults(self.paginator_ui, {
45
- firstPage: 0,
46
- currentPage: 1,
47
- perPage: 5,
48
- totalPages: 10
49
- });
50
-
51
- // Change scope of 'paginator_ui' object values
52
- _.each(self.paginator_ui, function(value, key) {
53
- if( _.isUndefined(self[key]) ) {
54
- self[key] = self.paginator_ui[key];
55
- }
56
- });
57
-
58
- // Some values could be functions, let's make sure
59
- // to change their scope too and run them
60
- var queryAttributes = {};
61
- _.each(self.server_api, function(value, key){
62
- if( _.isFunction(value) ) {
63
- value = _.bind(value, self);
64
- value = value();
65
- }
66
- queryAttributes[key] = value;
67
- });
68
-
69
- var queryOptions = _.clone(self.paginator_core);
70
- _.each(queryOptions, function(value, key){
71
- if( _.isFunction(value) ) {
72
- value = _.bind(value, self);
73
- value = value();
74
- }
75
- queryOptions[key] = value;
76
- });
77
-
78
- // Create default values if no others are specified
79
- queryOptions = _.defaults(queryOptions, {
80
- timeout: 25000,
81
- cache: false,
82
- type: 'GET',
83
- dataType: 'jsonp'
84
- });
85
-
86
- queryOptions = _.extend(queryOptions, {
87
- jsonpCallback: 'callback',
88
- data: decodeURIComponent($.param(queryAttributes)),
89
- processData: false,
90
- url: _.result(queryOptions, 'url')
91
- }, options);
92
-
93
- return $.ajax( queryOptions );
94
-
95
- },
96
-
97
- nextPage: function () {
98
- this.currentPage = ++this.currentPage;
99
- this.pager();
100
- },
101
-
102
- previousPage: function () {
103
- this.currentPage = --this.currentPage || 1;
104
- this.pager();
105
- },
106
-
107
- goTo: function ( page ) {
108
- if(page !== undefined){
109
- this.currentPage = parseInt(page, 10);
110
- this.pager();
111
- }
112
- },
113
-
114
- howManyPer: function ( perPage ) {
115
- if(perPage !== undefined){
116
- var lastPerPage = this.perPage;
117
- this.perPage = parseInt(perPage, 10);
118
- this.currentPage = Math.ceil( ( lastPerPage * ( this.currentPage - 1 ) + 1 ) / perPage);
119
- this.pager();
120
- }
121
- },
122
-
123
-
124
- // setSort is used to sort the current model. After
125
- // passing 'column', which is the model's field you want
126
- // to filter and 'direction', which is the direction
127
- // desired for the ordering ('asc' or 'desc'), pager()
128
- // and info() will be called automatically.
129
- setSort: function ( column, direction ) {
130
- if(column !== undefined && direction !== undefined){
131
- this.lastSortColumn = this.sortColumn;
132
- this.sortColumn = column;
133
- this.sortDirection = direction;
134
- this.pager();
135
- this.info();
136
- }
137
- },
138
-
139
- // setFieldFilter is used to filter each value of each model
140
- // according to `rules` that you pass as argument.
141
- // Example: You have a collection of books with 'release year' and 'author'.
142
- // You can filter only the books that were released between 1999 and 2003
143
- // And then you can add another `rule` that will filter those books only to
144
- // authors who's name start with 'A'.
145
- setFieldFilter: function ( fieldFilterRules ) {
146
- if( !_.isEmpty( fieldFilterRules ) ) {
147
- this.lastFieldFilterRules = this.fieldFilterRules;
148
- this.fieldFilterRules = fieldFilterRules;
149
- this.pager();
150
- this.info();
151
- }
152
- },
153
-
154
- // doFakeFieldFilter can be used to get the number of models that will remain
155
- // after calling setFieldFilter with a filter rule(s)
156
- doFakeFieldFilter: function ( fieldFilterRules ) {
157
- if( !_.isEmpty( fieldFilterRules ) ) {
158
- var bkp_lastFieldFilterRules = this.lastFieldFilterRules;
159
- var bkp_fieldFilterRules = this.fieldFilterRules;
160
-
161
- this.lastFieldFilterRules = this.fieldFilterRules;
162
- this.fieldFilterRules = fieldFilterRules;
163
- this.pager();
164
- this.info();
165
-
166
- var cmodels = this.models.length;
167
-
168
- this.lastFieldFilterRules = bkp_lastFieldFilterRules;
169
- this.fieldFilterRules = bkp_fieldFilterRules;
170
- this.pager();
171
- this.info();
172
-
173
- // Return size
174
- return cmodels;
175
- }
176
- },
177
-
178
- // setFilter is used to filter the current model. After
179
- // passing 'fields', which can be a string referring to
180
- // the model's field, an array of strings representing
181
- // each of the model's fields or an object with the name
182
- // of the model's field(s) and comparing options (see docs)
183
- // you wish to filter by and
184
- // 'filter', which is the word or words you wish to
185
- // filter by, pager() and info() will be called automatically.
186
- setFilter: function ( fields, filter ) {
187
- if( fields !== undefined && filter !== undefined ){
188
- this.filterFields = fields;
189
- this.lastFilterExpression = this.filterExpression;
190
- this.filterExpression = filter;
191
- this.pager();
192
- this.info();
193
- }
194
- },
195
-
196
- // doFakeFilter can be used to get the number of models that will
197
- // remain after calling setFilter with a `fields` and `filter` args.
198
- doFakeFilter: function ( fields, filter ) {
199
- if( fields !== undefined && filter !== undefined ){
200
- var bkp_filterFields = this.filterFields;
201
- var bkp_lastFilterExpression = this.lastFilterExpression;
202
- var bkp_filterExpression = this.filterExpression;
203
-
204
- this.filterFields = fields;
205
- this.lastFilterExpression = this.filterExpression;
206
- this.filterExpression = filter;
207
- this.pager();
208
- this.info();
209
-
210
- var cmodels = this.models.length;
211
-
212
- this.filterFields = bkp_filterFields;
213
- this.lastFilterExpression = bkp_lastFilterExpression;
214
- this.filterExpression = bkp_filterExpression;
215
- this.pager();
216
- this.info();
217
-
218
- // Return size
219
- return cmodels;
220
- }
221
- },
222
-
223
- // pager is used to sort, filter and show the data
224
- // you expect the library to display.
225
- pager: function () {
226
- var self = this,
227
- disp = this.perPage,
228
- start = (self.currentPage - 1) * disp,
229
- stop = start + disp;
230
-
231
- // Saving the original models collection is important
232
- // as we could need to sort or filter, and we don't want
233
- // to loose the data we fetched from the server.
234
- if (self.origModels === undefined) {
235
- self.origModels = self.models;
236
- }
237
-
238
- self.models = self.origModels;
239
-
240
- // Check if sorting was set using setSort.
241
- if ( this.sortColumn !== "" ) {
242
- self.models = self._sort(self.models, this.sortColumn, this.sortDirection);
243
- }
244
-
245
- // Check if field-filtering was set using setFieldFilter
246
- if ( !_.isEmpty( this.fieldFilterRules ) ) {
247
- self.models = self._fieldFilter(self.models, this.fieldFilterRules);
248
- }
249
-
250
- // Check if filtering was set using setFilter.
251
- if ( this.filterExpression !== "" ) {
252
- self.models = self._filter(self.models, this.filterFields, this.filterExpression);
253
- }
254
-
255
- // If the sorting or the filtering was changed go to the first page
256
- if ( this.lastSortColumn !== this.sortColumn || this.lastFilterExpression !== this.filterExpression || !_.isEqual(this.fieldFilterRules, this.lastFieldFilterRules) ) {
257
- start = 0;
258
- stop = start + disp;
259
- self.currentPage = 1;
260
-
261
- this.lastSortColumn = this.sortColumn;
262
- this.lastFieldFilterRules = this.fieldFilterRules;
263
- this.lastFilterExpression = this.filterExpression;
264
- }
265
-
266
- // We need to save the sorted and filtered models collection
267
- // because we'll use that sorted and filtered collection in info().
268
- self.sortedAndFilteredModels = self.models;
269
-
270
- self.reset(self.models.slice(start, stop));
271
- },
272
-
273
- // The actual place where the collection is sorted.
274
- // Check setSort for arguments explicacion.
275
- _sort: function ( models, sort, direction ) {
276
- models = models.sort(function (a, b) {
277
- var ac = a.get(sort),
278
- bc = b.get(sort);
279
-
280
- if ( !ac || !bc ) {
281
- return 0;
282
- } else {
283
- /* Make sure that both ac and bc are lowercase strings.
284
- * .toString() first so we don't have to worry if ac or bc
285
- * have other String-only methods.
286
- */
287
- ac = ac.toString().toLowerCase();
288
- bc = bc.toString().toLowerCase();
289
- }
290
-
291
- if (direction === 'desc') {
292
-
293
- // We need to know if there aren't any non-number characters
294
- // and that there are numbers-only characters and maybe a dot
295
- // if we have a float.
296
- if((!ac.match(/[^\d\.]/) && ac.match(/[\d\.]*/)) &&
297
- (!bc.match(/[^\d\.]/) && bc.match(/[\d\.]*/))
298
- ){
299
-
300
- if( (ac - 0) < (bc - 0) ) {
301
- return 1;
302
- }
303
- if( (ac - 0) > (bc - 0) ) {
304
- return -1;
305
- }
306
- } else {
307
- if (ac < bc) {
308
- return 1;
309
- }
310
- if (ac > bc) {
311
- return -1;
312
- }
313
- }
314
-
315
- } else {
316
-
317
- //Same as the regexp check in the 'if' part.
318
- if((!ac.match(/[^\d\.]/) && ac.match(/[\d\.]*/)) &&
319
- (!bc.match(/[^\d\.]/) && bc.match(/[\d\.]*/))
320
- ){
321
- if( (ac - 0) < (bc - 0) ) {
322
- return -1;
323
- }
324
- if( (ac - 0) > (bc - 0) ) {
325
- return 1;
326
- }
327
- } else {
328
- if (ac < bc) {
329
- return -1;
330
- }
331
- if (ac > bc) {
332
- return 1;
333
- }
334
- }
335
-
336
- }
337
-
338
- return 0;
339
- });
340
-
341
- return models;
342
- },
343
-
344
- // The actual place where the collection is field-filtered.
345
- // Check setFieldFilter for arguments explicacion.
346
- _fieldFilter: function( models, rules ) {
347
-
348
- // Check if there are any rules
349
- if ( _.isEmpty(rules) ) {
350
- return models;
351
- }
352
-
353
- var filteredModels = [];
354
-
355
- // Iterate over each rule
356
- _.each(models, function(model){
357
-
358
- var should_push = true;
359
-
360
- // Apply each rule to each model in the collection
361
- _.each(rules, function(rule){
362
-
363
- // Don't go inside the switch if we're already sure that the model won't be included in the results
364
- if( !should_push ){
365
- return false;
366
- }
367
-
368
- should_push = false;
369
-
370
- // The field's value will be passed to a custom function, which should
371
- // return true (if model should be included) or false (model should be ignored)
372
- if(rule.type === "function"){
373
- var f = _.wrap(rule.value, function(func){
374
- return func( model.get(rule.field) );
375
- });
376
- if( f() ){
377
- should_push = true;
378
- }
379
-
380
- // The field's value is required to be non-empty
381
- }else if(rule.type === "required"){
382
- if( !_.isEmpty( model.get(rule.field).toString() ) ) {
383
- should_push = true;
384
- }
385
-
386
- // The field's value is required to be greater tan N (numbers only)
387
- }else if(rule.type === "min"){
388
- if( !_.isNaN( Number( model.get(rule.field) ) ) &&
389
- !_.isNaN( Number( rule.value ) ) &&
390
- Number( model.get(rule.field) ) >= Number( rule.value ) ) {
391
- should_push = true;
392
- }
393
-
394
- // The field's value is required to be smaller tan N (numbers only)
395
- }else if(rule.type === "max"){
396
- if( !_.isNaN( Number( model.get(rule.field) ) ) &&
397
- !_.isNaN( Number( rule.value ) ) &&
398
- Number( model.get(rule.field) ) <= Number( rule.value ) ) {
399
- should_push = true;
400
- }
401
-
402
- // The field's value is required to be between N and M (numbers only)
403
- }else if(rule.type === "range"){
404
- if( !_.isNaN( Number( model.get(rule.field) ) ) &&
405
- _.isObject( rule.value ) &&
406
- !_.isNaN( Number( rule.value.min ) ) &&
407
- !_.isNaN( Number( rule.value.max ) ) &&
408
- Number( model.get(rule.field) ) >= Number( rule.value.min ) &&
409
- Number( model.get(rule.field) ) <= Number( rule.value.max ) ) {
410
- should_push = true;
411
- }
412
-
413
- // The field's value is required to be more than N chars long
414
- }else if(rule.type === "minLength"){
415
- if( model.get(rule.field).toString().length >= rule.value ) {
416
- should_push = true;
417
- }
418
-
419
- // The field's value is required to be no more than N chars long
420
- }else if(rule.type === "maxLength"){
421
- if( model.get(rule.field).toString().length <= rule.value ) {
422
- should_push = true;
423
- }
424
-
425
- // The field's value is required to be more than N chars long and no more than M chars long
426
- }else if(rule.type === "rangeLength"){
427
- if( _.isObject( rule.value ) &&
428
- !_.isNaN( Number( rule.value.min ) ) &&
429
- !_.isNaN( Number( rule.value.max ) ) &&
430
- model.get(rule.field).toString().length >= rule.value.min &&
431
- model.get(rule.field).toString().length <= rule.value.max ) {
432
- should_push = true;
433
- }
434
-
435
- // The field's value is required to be equal to one of the values in rules.value
436
- }else if(rule.type === "oneOf"){
437
- if( _.isArray( rule.value ) &&
438
- _.include( rule.value, model.get(rule.field) ) ) {
439
- should_push = true;
440
- }
441
-
442
- // The field's value is required to be equal to the value in rules.value
443
- }else if(rule.type === "equalTo"){
444
- if( rule.value === model.get(rule.field) ) {
445
- should_push = true;
446
- }
447
-
448
- // The field's value is required to match the regular expression
449
- }else if(rule.type === "pattern"){
450
- if( model.get(rule.field).toString().match(rule.value) ) {
451
- should_push = true;
452
- }
453
-
454
- //Unknown type
455
- }else{
456
- should_push = false;
457
- }
458
-
459
- });
460
-
461
- if( should_push ){
462
- filteredModels.push(model);
463
- }
464
-
465
- });
466
-
467
- return filteredModels;
468
- },
469
-
470
- // The actual place where the collection is filtered.
471
- // Check setFilter for arguments explicacion.
472
- _filter: function ( models, fields, filter ) {
473
-
474
- // For example, if you had a data model containing cars like { color: '', description: '', hp: '' },
475
- // your fields was set to ['color', 'description', 'hp'] and your filter was set
476
- // to "Black Mustang 300", the word "Black" will match all the cars that have black color, then
477
- // "Mustang" in the description and then the HP in the 'hp' field.
478
- // NOTE: "Black Musta 300" will return the same as "Black Mustang 300"
479
-
480
- // We accept fields to be a string, an array or an object
481
- // but if string or array is passed we need to convert it
482
- // to an object.
483
-
484
- var self = this;
485
-
486
- var obj_fields = {};
487
-
488
- if( _.isString( fields ) ) {
489
- obj_fields[fields] = {cmp_method: 'regexp'};
490
- }else if( _.isArray( fields ) ) {
491
- _.each(fields, function(field){
492
- obj_fields[field] = {cmp_method: 'regexp'};
493
- });
494
- }else{
495
- _.each(fields, function( cmp_opts, field ) {
496
- obj_fields[field] = _.defaults(cmp_opts, { cmp_method: 'regexp' });
497
- });
498
- }
499
-
500
- fields = obj_fields;
501
-
502
- //Remove diacritic characters if diacritic plugin is loaded
503
- if( _.has(Backbone.Paginator, 'removeDiacritics') && self.useDiacriticsPlugin ){
504
- filter = Backbone.Paginator.removeDiacritics(filter);
505
- }
506
-
507
- // 'filter' can be only a string.
508
- // If 'filter' is string we need to convert it to
509
- // a regular expression.
510
- // For example, if 'filter' is 'black dog' we need
511
- // to find every single word, remove duplicated ones (if any)
512
- // and transform the result to '(black|dog)'
513
- if( filter === '' || !_.isString(filter) ) {
514
- return models;
515
- } else {
516
- var words = filter.match(/\w+/ig);
517
- var pattern = "(" + _.uniq(words).join("|") + ")";
518
- var regexp = new RegExp(pattern, "igm");
519
- }
520
-
521
- var filteredModels = [];
522
-
523
- // We need to iterate over each model
524
- _.each( models, function( model ) {
525
-
526
- var matchesPerModel = [];
527
-
528
- // and over each field of each model
529
- _.each( fields, function( cmp_opts, field ) {
530
-
531
- var value = model.get( field );
532
-
533
- if( value ) {
534
-
535
- // The regular expression we created earlier let's us detect if a
536
- // given string contains each and all of the words in the regular expression
537
- // or not, but in both cases match() will return an array containing all
538
- // the words it matched.
539
- var matchesPerField = [];
540
-
541
- if( _.has(Backbone.Paginator, 'removeDiacritics') && self.useDiacriticsPlugin ){
542
- value = Backbone.Paginator.removeDiacritics(value.toString());
543
- }else{
544
- value = value.toString();
545
- }
546
-
547
- // Levenshtein cmp
548
- if( cmp_opts.cmp_method === 'levenshtein' && _.has(Backbone.Paginator, 'levenshtein') && self.useLevenshteinPlugin ) {
549
- var distance = Backbone.Paginator.levenshtein(value, filter);
550
-
551
- _.defaults(cmp_opts, { max_distance: 0 });
552
-
553
- if( distance <= cmp_opts.max_distance ) {
554
- matchesPerField = _.uniq(words);
555
- }
556
-
557
- // Default (RegExp) cmp
558
- }else{
559
- matchesPerField = value.match( regexp );
560
- }
561
-
562
- matchesPerField = _.map(matchesPerField, function(match) {
563
- return match.toString().toLowerCase();
564
- });
565
-
566
- _.each(matchesPerField, function(match){
567
- matchesPerModel.push(match);
568
- });
569
-
570
- }
571
-
572
- });
573
-
574
- // We just need to check if the returned array contains all the words in our
575
- // regex, and if it does, it means that we have a match, so we should save it.
576
- matchesPerModel = _.uniq( _.without(matchesPerModel, "") );
577
-
578
- if( _.isEmpty( _.difference(words, matchesPerModel) ) ) {
579
- filteredModels.push(model);
580
- }
581
-
582
- });
583
-
584
- return filteredModels;
585
- },
586
-
587
- // You shouldn't need to call info() as this method is used to
588
- // calculate internal data as first/prev/next/last page...
589
- info: function () {
590
- var self = this,
591
- info = {},
592
- totalRecords = (self.sortedAndFilteredModels) ? self.sortedAndFilteredModels.length : self.length,
593
- totalPages = Math.ceil(totalRecords / self.perPage);
594
-
595
- info = {
596
- totalUnfilteredRecords: self.origModels.length,
597
- totalRecords: totalRecords,
598
- currentPage: self.currentPage,
599
- perPage: this.perPage,
600
- totalPages: totalPages,
601
- lastPage: totalPages,
602
- previous: false,
603
- next: false,
604
- startRecord: totalRecords === 0 ? 0 : (self.currentPage - 1) * this.perPage + 1,
605
- endRecord: Math.min(totalRecords, self.currentPage * this.perPage)
606
- };
607
-
608
- if (self.currentPage > 1) {
609
- info.previous = self.currentPage - 1;
610
- }
611
-
612
- if (self.currentPage < info.totalPages) {
613
- info.next = self.currentPage + 1;
614
- }
615
-
616
- info.pageSet = self.setPagination(info);
617
-
618
- self.information = info;
619
- return info;
620
- },
621
-
622
-
623
- // setPagination also is an internal function that shouldn't be called directly.
624
- // It will create an array containing the pages right before and right after the
625
- // actual page.
626
- setPagination: function ( info ) {
627
- var pages = [], i = 0, l = 0;
628
-
629
-
630
- // How many adjacent pages should be shown on each side?
631
- var ADJACENT = 3,
632
- ADJACENTx2 = ADJACENT * 2,
633
- LASTPAGE = Math.ceil(info.totalRecords / info.perPage),
634
- LPM1 = -1;
635
-
636
- if (LASTPAGE > 1) {
637
- // not enough pages to bother breaking it up
638
- if (LASTPAGE < (7 + ADJACENTx2)) {
639
- for (i = 1, l = LASTPAGE; i <= l; i++) {
640
- pages.push(i);
641
- }
642
- }
643
- // enough pages to hide some
644
- else if (LASTPAGE > (5 + ADJACENTx2)) {
645
-
646
- //close to beginning; only hide later pages
647
- if (info.currentPage < (1 + ADJACENTx2)) {
648
- for (i = 1, l = 4 + ADJACENTx2; i < l; i++) {
649
- pages.push(i);
650
- }
651
- }
652
-
653
- // in middle; hide some front and some back
654
- else if (LASTPAGE - ADJACENTx2 > info.currentPage && info.currentPage > ADJACENTx2) {
655
- for (i = info.currentPage - ADJACENT; i <= info.currentPage + ADJACENT; i++) {
656
- pages.push(i);
657
- }
658
- }
659
- // close to end; only hide early pages
660
- else {
661
- for (i = LASTPAGE - (2 + ADJACENTx2); i <= LASTPAGE; i++) {
662
- pages.push(i);
663
- }
664
- }
665
- }
666
- }
667
-
668
- return pages;
669
- }
670
-
671
- });
672
-
673
-
674
- // @name: requestPager
675
- //
676
- // Paginator for server-side data being requested from a backend/API
677
- //
678
- // @description:
679
- // This paginator is responsible for providing pagination
680
- // and sort capabilities for requests to a server-side
681
- // data service (e.g an API)
682
- //
683
- Paginator.requestPager = Backbone.Collection.extend({
684
-
685
- sync: function ( method, model, options ) {
686
-
687
- var self = this;
688
-
689
- // Create default values if no others are specified
690
- _.defaults(self.paginator_ui, {
691
- firstPage: 0,
692
- currentPage: 1,
693
- perPage: 5,
694
- totalPages: 10
695
- });
696
-
697
- // Change scope of 'paginator_ui' object values
698
- _.each(self.paginator_ui, function(value, key) {
699
- if( _.isUndefined(self[key]) ) {
700
- self[key] = self.paginator_ui[key];
701
- }
702
- });
703
-
704
- // Some values could be functions, let's make sure
705
- // to change their scope too and run them
706
- var queryAttributes = {};
707
- _.each(self.server_api, function(value, key){
708
- if( _.isFunction(value) ) {
709
- value = _.bind(value, self);
710
- value = value();
711
- }
712
- queryAttributes[key] = value;
713
- });
714
-
715
- var queryOptions = _.clone(self.paginator_core);
716
- _.each(queryOptions, function(value, key){
717
- if( _.isFunction(value) ) {
718
- value = _.bind(value, self);
719
- value = value();
720
- }
721
- queryOptions[key] = value;
722
- });
723
-
724
- // Create default values if no others are specified
725
- queryOptions = _.defaults(queryOptions, {
726
- timeout: 25000,
727
- cache: false,
728
- type: 'GET',
729
- dataType: 'jsonp'
730
- });
731
-
732
- // Allows the passing in of {data: {foo: 'bar'}} at request time to overwrite server_api defaults
733
- if( options.data ){
734
- options.data = decodeURIComponent($.param(_.extend(queryAttributes,options.data)));
735
- }else{
736
- options.data = decodeURIComponent($.param(queryAttributes));
737
- }
738
-
739
- queryOptions = _.extend(queryOptions, {
740
- jsonpCallback: 'callback',
741
- processData: false,
742
- url: _.result(queryOptions, 'url')
743
- }, options);
744
-
745
- return $.ajax( queryOptions );
746
-
747
- },
748
-
749
- requestNextPage: function ( options ) {
750
- if ( this.currentPage !== undefined ) {
751
- this.currentPage += 1;
752
- return this.pager( options );
753
- } else {
754
- var response = new $.Deferred();
755
- response.reject();
756
- return response.promise();
757
- }
758
- },
759
-
760
- requestPreviousPage: function ( options ) {
761
- if ( this.currentPage !== undefined ) {
762
- this.currentPage -= 1;
763
- return this.pager( options );
764
- } else {
765
- var response = new $.Deferred();
766
- response.reject();
767
- return response.promise();
768
- }
769
- },
770
-
771
- updateOrder: function ( column ) {
772
- if (column !== undefined) {
773
- this.sortField = column;
774
- this.pager();
775
- }
776
-
777
- },
778
-
779
- goTo: function ( page, options ) {
780
- if ( page !== undefined ) {
781
- this.currentPage = parseInt(page, 10);
782
- return this.pager( options );
783
- } else {
784
- var response = new $.Deferred();
785
- response.reject();
786
- return response.promise();
787
- }
788
- },
789
-
790
- howManyPer: function ( count ) {
791
- if( count !== undefined ){
792
- this.currentPage = this.firstPage;
793
- this.perPage = count;
794
- this.pager();
795
- }
796
- },
797
-
798
- sort: function () {
799
- //assign to as needed.
800
- },
801
-
802
- info: function () {
803
-
804
- var info = {
805
- // If parse() method is implemented and totalRecords is set to the length
806
- // of the records returned, make it available. Else, default it to 0
807
- totalRecords: this.totalRecords || 0,
808
-
809
- currentPage: this.currentPage,
810
- firstPage: this.firstPage,
811
- totalPages: this.totalPages,
812
- lastPage: this.totalPages,
813
- perPage: this.perPage
814
- };
815
-
816
- this.information = info;
817
- return info;
818
- },
819
-
820
- // fetches the latest results from the server
821
- pager: function ( options ) {
822
- if ( !_.isObject(options) ) {
823
- options = {};
824
- }
825
- return this.fetch( options );
826
- }
827
-
828
-
829
- });
830
-
831
- return Paginator;
832
-
833
- }( Backbone, _, jQuery ));