htdocs/assets/js/collection.js

Summary

Maintainability
A
1 hr
Test Coverage
"use strict";
define(function(require) {
    var _ = require('underscore'),
        Backbone = require('backbone');


    // Retrieve a field from the model, accounting for objects.
    var getField = function(m, k) {
        return m instanceof Backbone.Model ?
            m.get(k):m[k];
    };
    // Some custom update logic.
    var update = function(models, options) {
        if(!_.isArray(models) && models) {
            models = [models];
        }

        if(!models || models.length === 0) {
            return;
        }

        // Update the lastTimestamp field.
        for(var i in models) {
            var create = getField(models[i], 'create_date');
            var update = getField(models[i], 'update_date');
            if(create > this.lastTimestamp) {
                this.lastTimestamp = create;
            }
            if(update > this.lastTimestamp) {
                this.lastTimestamp = update;
            }
        }

        // Automatically destroy any models that have been archived.
        _.each(this.where({archived: true}), function(model) {
            model.destroy({soft: true});
        });
    };

    // Base collection class. Extended with some useful partial-update functions
    var Collection = Backbone.Collection.extend({
        // The most recent timestamp within the models. All models are expected to have create_date and update_date fields.
        lastTimestamp: 0,
        query: null,

        // Fetch models that have changed since lastTimestamp.
        update: function(options) {
            options = options ? _.clone(options) : {};
            // Don't remove existing models.
            options.remove = false;
            options.data = options.data || {};
            // Add the timestamp so the server knows what models to send.
            options.data.time = this.lastTimestamp;
            // If an id was passed, only retrieve that model.
            var old_timestamp = this.lastTimestamp;
            if(!_.isNull(this.query)) {
                _.extend(options.data, this.query);
            }

            var success = options.success;
            options.success = function(collection, resp, options) {
                // If a specific id was fetched, don't want to update lastTimestamp. However, set will already have done so. Set lastTimestamp back to the previous value.
                if('id' in options.data) {
                    collection.lastTimestamp = old_timestamp;
                }

                // Execute the callback, if it exists.
                if(success) success(collection, resp, options);
            };

            return this.fetch(options);
        },
        // Accepts a list of changed attributes for each model and pushes them to the server.
        save: function(data, options) {
            if(_.isUndefined(data)) {
                data = [];
                for(var k in this.models) {
                    var attrs = this.models[k].toJSON();
                    if(_.isUndefined(this.models[k]['id'])) {
                        attrs.cid = this.models[k].cid;
                    }
                    data.push(attrs);
                }
            }

            var collection = this;

            options = options || {};
            var success = options.success;
            options.success = function(response, status, xhr) {
                // Update the collection with the response, but don't delete anything!
                collection.set(response, {remove: false});

                collection.trigger('sync');
                if(success) success(response, status, xhr);
            };

            var url = _.isFunction(this.url) ? this.url():this.url;
            var model = {
                url: url,
                toJSON: function() {
                    return {models: data};
                },
                trigger: function() {}
            };

            return this.sync('update', model, options);
        },
        // Reset, extended to perform some extra bookkeeping.
        reset: function(models, options) {
            this.lastTimestamp = 0;
            Backbone.Collection.prototype.reset.call(this, models, options);
            update.call(this, models, options);
        },
        // Extended set to update any local-only models with the id from the server.
        set: function(models, options) {
            if(_.isArray(models)) {
                for(var i = 0; i < models.length; ++i) {
                    var attrs = models[i];
                    if('cid' in attrs && !_.isUndefined(attrs['id'])) {
                        var model = this.get(attrs.cid);
                        if(model.isNew()) {
                            model.set('id', attrs.id, {silent: true});
                        }
                    }
                }
            }
            Backbone.Collection.prototype.set.call(this, models, options);
            update.call(this, models, options);
        },

        // Return the url for this collection.
        getUrl: function() {
            return _.isFunction(this.url) ? this.url():this.url;
        }
    });

    return Collection;
});