Skip to content

Commit

Permalink
Switched to browserify / grunt built process
Browse files Browse the repository at this point in the history
Now bringing in `backbone-collection-proxy` to deal with the tricky
parts of proxy-ing the paginated collection through the returned
object.
  • Loading branch information
jmorrell committed Sep 4, 2013
1 parent cfe69c4 commit da6c620
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 61 deletions.
37 changes: 37 additions & 0 deletions Gruntfile.js
@@ -0,0 +1,37 @@
module.exports = function (grunt) {
grunt.initConfig({

browserify: {
basic: {
src: [],
dest: './backbone-paginated-collection.js',
options: {
external: [ 'underscore', 'backbone' ],
alias: ['./index.js:backbone-paginated-collection']
}
}
},

umd: {
default: {
src: './backbone-paginated-collection.js',
template: './templates/umd.hbs',
objectToExport: "require('backbone-paginated-collection')",
globalAlias: 'PaginatedCollection',
deps: {
'default': ['_', 'Backbone'],
amd: ['underscore', 'backbone'],
cjs: ['underscore', 'backbone'],
global: ['_', 'Backbone']
},
browserifyMapping: '{"backbone":Backbone,"underscore":_}'
}
}

});

grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-umd');

grunt.registerTask('default', ['browserify', 'umd']);
};
144 changes: 89 additions & 55 deletions backbone-paginated-collection.js
@@ -1,5 +1,27 @@
/*! backbone-paginated-collection */
;(function (root) { function moduleDefinition(Backbone, _) {
(function(root, factory) {
if(typeof exports === 'object') {
module.exports = factory(require('underscore'), require('backbone'));
}
else if(typeof define === 'function' && define.amd) {
define(['underscore', 'backbone'], factory);
}
else {
root.PaginatedCollection = factory(root._, root.Backbone);
}
}(this, function(_, Backbone) {
var require=function(name){return {"backbone":Backbone,"underscore":_}[name];};
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({"PxKHrf":[function(require,module,exports){

var _ = require('underscore');
var Backbone = require('backbone');
var proxyCollection = require('backbone-collection-proxy');

function updatePagination() {
var start = this.getPage() * this.getPerPage();
var end = start + this.getPerPage();

this._collection.reset(this.superset().toArray().slice(start, end));
}

function recalculatePagination() {
var length = this.superset().length;
Expand All @@ -22,29 +44,6 @@ function recalculatePagination() {
}
}

function updatePagination() {
var start = this.getPage() * this.getPerPage();
var end = start + this.getPerPage();

this._collection.reset(this.superset().toArray().slice(start, end));
this.length = this._collection.length;
}

function pipeEvents() {
var args = _.toArray(arguments);

// replace any references to `this._collection` with `this`
for (var i = 1; i < args.length; i++) {
// Is there a better way to check for this?
// List all of the possible events?
if (args[i].models && args[i].models.length === this._collection.models.length) {
args[i] = this;
}
}

this.trigger.apply(this, args);
}

function Paginated(superset, options) {
// Save a reference to the original collection
this._superset = superset;
Expand All @@ -54,10 +53,11 @@ function Paginated(superset, options) {
this._collection = new Backbone.Collection(superset.toArray());
this.setPerPage(options ? options.perPage: this._defaultPerPage);

proxyCollection(this._collection, this);

this.listenTo(this._superset, 'add', recalculatePagination);
this.listenTo(this._superset, 'remove', recalculatePagination);
this.listenTo(this._superset, 'reset', recalculatePagination);
this.listenTo(this._collection, 'all', pipeEvents);
}

var methods = {
Expand Down Expand Up @@ -130,36 +130,70 @@ var methods = {

};

// Methods on `this._collection` we will expose to the outside world
var collectionMethods = [
'toJSON', 'forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
'max', 'min', 'groupBy', 'sortedIndex', 'shuffle', 'toArray', 'size',
'first', 'head', 'take', 'initial', 'rest', 'tail', 'drop', 'last',
'without', 'indexOf', 'lastIndexOf', 'isEmpty', 'chain', 'pluck',
'findWhere', 'get', 'at', 'slice', 'where', 'findWhere'
// Build up the prototype
_.extend(Paginated.prototype, methods, Backbone.Events);

module.exports = Paginated;


},{"backbone":false,"backbone-collection-proxy":2,"underscore":false}],2:[function(require,module,exports){

var _ = require('underscore');
var Backbone = require('backbone');

// Methods in the collection prototype that we won't expose
var blacklistedMethods = [
"_onModelEvent", "_prepareModel", "_removeReference", "_reset", "add",
"initialize", "sync", "remove", "reset", "set", "push", "pop", "unshift",
"shift", "sort", "parse", "fetch", "create", "model", "off", "on",
"listenTo", "listenToOnce", "bind", "trigger", "once", "stopListening"
];

_.each(collectionMethods, function(method) {
methods[method] = function() {
return Backbone.Collection.prototype[method].apply(this._collection, arguments);
};
});
function proxyCollection(from, target) {

// Build up the prototype
_.extend(Paginated.prototype, methods, Backbone.Events);
function updateLength() {
target.length = from.length;
}

function pipeEvents() {
var args = _.toArray(arguments);

// replace any references to `from` with `this`
for (var i = 1; i < args.length; i++) {
if (args[i] && args[i].length && args[i].length === from.length) {
args[i] = this;
}
}

this.trigger.apply(this, args);
}

var methods = {};

_.each(_.functions(Backbone.Collection.prototype), function(method) {
if (!_.contains(blacklistedMethods, method)) {
methods[method] = function() {
return from[method].apply(from, arguments);
};
}
});

_.extend(target, Backbone.Events, methods);

target.listenTo(from, 'all', updateLength);
target.listenTo(from, 'all', pipeEvents);

updateLength();
return target;
}

module.exports = proxyCollection;


},{"backbone":false,"underscore":false}],"backbone-paginated-collection":[function(require,module,exports){
module.exports=require('PxKHrf');
},{}]},{},[])
;
return require('backbone-paginated-collection');

return Paginated;

// ---------------------------------------------------------------------------
} if (typeof exports === 'object') {
// node export
module.exports = moduleDefinition(require('backbone'), require('underscore'));
} else if (typeof define === 'function' && define.amd) {
// amd anonymous module registration
define(['backbone', 'underscore'], moduleDefinition);
} else {
// browser global
root.PaginatedCollection = moduleDefinition(root.Backbone, root._);
}}(this));
}));
124 changes: 124 additions & 0 deletions index.js
@@ -0,0 +1,124 @@

var _ = require('underscore');
var Backbone = require('backbone');
var proxyCollection = require('backbone-collection-proxy');

function updatePagination() {
var start = this.getPage() * this.getPerPage();
var end = start + this.getPerPage();

this._collection.reset(this.superset().toArray().slice(start, end));
}

function recalculatePagination() {
var length = this.superset().length;
var perPage = this.getPerPage();

// If the # of objects can be exactly divided by the number
// of pages, it would leave an empty last page if we took
// the floor.
var totalPages = length % perPage === 0 ?
(length / perPage) : Math.floor(length / perPage) + 1;

this._totalPages = totalPages;

// If the current page no longer exists, switch to the last
// existing page in the set.
if (this.getPage() >= totalPages) {
this.setPage(totalPages - 1);
} else {
updatePagination.call(this);
}
}

function Paginated(superset, options) {
// Save a reference to the original collection
this._superset = superset;

// The idea is to keep an internal backbone collection with the paginated
// set, and expose limited functionality.
this._collection = new Backbone.Collection(superset.toArray());
this.setPerPage(options ? options.perPage: this._defaultPerPage);

proxyCollection(this._collection, this);

this.listenTo(this._superset, 'add', recalculatePagination);
this.listenTo(this._superset, 'remove', recalculatePagination);
this.listenTo(this._superset, 'reset', recalculatePagination);
}

var methods = {

_defaultPerPage: 20,

setPerPage: function(perPage) {
this._perPage = perPage;
recalculatePagination.call(this);
this.setPage(0);

this.trigger('paginated:change:perPage', {
perPage: perPage,
numPages: this.getNumPages()
});
},

setPage: function(page) {
// The lowest page we could set
var lowerLimit = 0;
// The highest page we could set
var upperLimit = this.getNumPages() - 1;

// If the page is higher or lower than these limits,
// set it to the limit.
page = page > lowerLimit ? page : lowerLimit;
page = page < upperLimit ? page : upperLimit;

this._page = page;
updatePagination.call(this);

this.trigger('paginated:change:page', { page: page });
},

getPerPage: function() {
return this._perPage;
},

getNumPages: function() {
return this._totalPages;
},

getPage: function() {
return this._page;
},

hasNextPage: function() {
return this.getPage() < this.getNumPages() - 1;
},

hasPrevPage: function() {
return this.getPage() > 0;
},

nextPage: function() {
this.movePage(1);
},

prevPage: function() {
this.movePage(-1);
},

movePage: function(delta) {
this.setPage(this.getPage() + delta);
},

superset: function() {
return this._superset;
}

};

// Build up the prototype
_.extend(Paginated.prototype, methods, Backbone.Events);

module.exports = Paginated;

17 changes: 11 additions & 6 deletions package.json
Expand Up @@ -3,7 +3,7 @@
"description": "Create a paginated version of a backbone collection that stays in sync.",
"version": "0.1.0",
"homepage": "http://github.com/jmorrell/backbone-paginated-collection",
"main": "backbone-paginated-collection.js",
"main": "index.js",
"license": "MIT",
"author": {
"name": "Jeremy Morrell",
Expand All @@ -26,13 +26,18 @@
"karma": "~0.10.2",
"mocha": "~1.12.0",
"karma-mocha": "~0.1.0",
"karma-osx-reporter": "0.0.3"
},
"peerDependencies": {
"backbone": "~1.0.0",
"underscore": "~1.5.1"
"karma-osx-reporter": "0.0.3",
"grunt-umd": "~1.3.0",
"grunt": "~0.4.1",
"grunt-browserify": "~1.2.4"
},
"scripts": {
"test": "./node_modules/.bin/karma start --browsers Firefox --single-run"
},
"dependencies": {
"underscore": "~1.5.1",
"backbone": "~1.0.0",
"underscore": "~1.5.1",
"backbone-collection-proxy": "~0.1.0"
}
}
16 changes: 16 additions & 0 deletions templates/umd.hbs
@@ -0,0 +1,16 @@
(function(root, factory) {
if(typeof exports === 'object') {
module.exports = factory({{{cjsDependencies}}});
}
else if(typeof define === 'function' && define.amd) {
define({{#if amdModuleId}}'{{amdModuleId}}', {{/if}}[{{{amdDependencies}}}], factory);
}
else {
root.{{globalAlias}} = factory({{{globalDependencies}}});
}
}(this, function({{dependencies}}) {
var require=function(name){return {{{ browserifyMapping }}}[name];};
{{{code}}}
return {{{objectToExport}}};

}));

0 comments on commit da6c620

Please sign in to comment.