|
var _ = require('underscore'); |
|
var Collection = require('ampersand-rest-collection'); |
|
// Streaming JSON parser |
|
var oboe = require('oboe'); |
|
var qs = require('qs'); |
|
// requestAnimationFrame wrapper built with async iteration in mind |
|
// helps reduce UI thread congestion when initializing large amounts |
|
// of client-side models |
|
var raf = require('rifraf'); |
|
|
|
module.exports = Collection.extend({ |
|
initialize: function (models, opts) { |
|
var options = opts || {}; |
|
this.page = options.page; |
|
this.pageSize = options.pageSize; |
|
}, |
|
sync: function (method, self, opts) { |
|
if (method === 'read') { |
|
var options = opts || {}; |
|
var url = options.url || _.result(this, 'url'); |
|
if (options.data) { |
|
url += (url.indexOf('?') === -1) ? '?' : '&'; |
|
url += qs.stringify(options.data); |
|
} |
|
_.extend(options, {url: url}, _.result(this, 'ajaxConfig')); |
|
var pageSize = (this.pageSize || 50); |
|
var readLength = (this.page || 1) * pageSize; |
|
return oboe(options) |
|
// The pattern below means emit array members as they are completed |
|
.node('![*]', raf.delayed(function _handleModel(model) { |
|
// Don't let the view know that we've gotten models until… |
|
self.add(model, {silent: true}); |
|
// we reach the end of the requested page. |
|
if (self.length >= readLength && (self.length % pageSize === 0)) { |
|
raf.request(self.trigger.bind(self, 'reset', self, options)); |
|
} |
|
}, 33)) |
|
// The delay amount was tweaked for best tradeoff between UI blocking / parsing performance |
|
// across our user base, which included iPhones from 4 up and iPads gen 1 up in addition to |
|
// desktop and laptop computers up to several years old. |
|
.done(function _handleDone(resp) { |
|
self.trigger('reset', self, options); |
|
self.trigger('sync', self, resp, options); |
|
}); |
|
} else { |
|
return Collection.prototype.sync.apply(this, arguments); |
|
} |
|
} |
|
}); |