(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['backbone'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('backbone'));
} else {
root.Torso = root.Torso || {};
root.Torso.Mixins = root.Torso.Mixins || {};
root.Torso.Mixins.loading = factory(root.Backbone);
}
}(this, function(Backbone) {
var $ = Backbone.$;
/**
* Loading logic.
*
* @mixin loadingMixin
*
* @author kent.willis@vecna.com
*
* @see <a href="../annotated/modules/mixins/loadingMixin.html">loadingMixin Annotated Source</a>
*/
var loadingMixin = function(base) {
return /** @lends loadingMixin */ {
/**
* Adds the loading mixin
* @param {Object} args the arguments to the base constructor method
*/
constructor: function(args) {
base.call(this, args);
this.loadedOnceDeferred = new $.Deferred();
this.loadedOnce = false;
this.loadingCount = 0;
// Loading is a convenience flag that is the equivalent of loadingCount > 0
this.loading = false;
},
/**
* @returns {boolean} true if this model/collection has ever loaded from a fetch call
*/
hasLoadedOnce: function() {
return this.loadedOnce;
},
/**
* @returns {boolean} true if this model/collection is currently loading new values from the server
*/
isLoading: function() {
return this.loading;
},
/**
* @returns {Promise} a promise that will resolve when the model/collection has loaded for the first time
*/
getLoadedOncePromise: function() {
return this.loadedOnceDeferred.promise();
},
/**
* Wraps the base fetch in a wrapper that manages loaded states
* @param {Object} options - the object to hold the options needed by the base fetch method
* @returns {Promise} The loadWrapper promise
*/
fetch: function(options) {
return this.__loadWrapper(base.prototype.fetch, options);
},
/**
* Base load function that will trigger a "load-begin" and a "load-complete" as
* the fetch happens. Use this method to wrap any method that returns a promise in loading events
*
* @private
* @param {Function} fetchMethod - the method to invoke a fetch
* @param {Object} options - the object to hold the options needed by the fetchMethod
* @returns {Promise} a promise when the fetch method has completed and the events have been triggered
*/
__loadWrapper: function(fetchMethod, options) {
var object = this;
this.loadingCount++;
this.loading = true;
this.trigger('load-begin');
return $.when(fetchMethod.call(object, options)).always(function() {
if (!object.loadedOnce) {
object.loadedOnce = true;
object.loadedOnceDeferred.resolve();
}
object.loadingCount--;
if (object.loadingCount <= 0) {
object.loadingCount = 0; // prevent going negative.
object.loading = false;
}
}).done(function(data, textStatus, jqXHR) {
object.trigger('load-complete', {success: true, data: data, textStatus: textStatus, jqXHR: jqXHR});
}).fail(function(jqXHR, textStatus, errorThrown) {
object.trigger('load-complete', {success: false, jqXHR: jqXHR, textStatus: textStatus, errorThrown: errorThrown});
});
}
};
};
return loadingMixin;
}));