diff --git a/ajax/libs/backbone-associations/0.5.2/backbone-associations-min.js b/ajax/libs/backbone-associations/0.5.2/backbone-associations-min.js new file mode 100644 index 000000000..25e789993 --- /dev/null +++ b/ajax/libs/backbone-associations/0.5.2/backbone-associations-min.js @@ -0,0 +1,12 @@ +(function(){var y=this,h,f,v,m,n,t,A,s,B,C;"undefined"===typeof window?(h=require("underscore"),f=require("backbone"),"undefined"!==typeof exports&&(exports=module.exports=f)):(h=y._,f=y.Backbone);v=f.Model;m=f.Collection;n=v.prototype;t=m.prototype;B=/[\.\[\]]+/g;A="change add remove reset sort destroy".split(" ");C=["reset","sort"];f.Associations={VERSION:"0.5.2"};f.Associations.Many=f.Many="Many";f.Associations.One=f.One="One";f.Associations.Self=f.Self="Self";s=f.AssociatedModel=f.Associations.AssociatedModel= +v.extend({relations:void 0,_proxyCalls:void 0,get:function(a){var c=n.get.call(this,a);return c?c:this._getAttr.apply(this,arguments)},set:function(a,c,d){var b;h.isObject(a)||null==a?(b=a,d=c):(b={},b[a]=c);a=this._set(b,d);this._processPendingEvents();return a},_set:function(a,c){var d,b,D,g,l=this;if(!a)return this;for(d in a)if(b||(b={}),d.match(B)){var f=z(d);g=h.initial(f);f=f[f.length-1];g=this.get(g);g instanceof s&&(g=b[g.cid]||(b[g.cid]={model:g,data:{}}),g.data[f]=a[d])}else g=b[this.cid]|| +(b[this.cid]={model:this,data:{}}),g.data[d]=a[d];if(b)for(D in b)g=b[D],this._setAttr.call(g.model,g.data,c)||(l=!1);else l=this._setAttr.call(this,a,c);return l},_setAttr:function(a,c){var d;c||(c={});if(c.unset)for(d in a)a[d]=void 0;this.parents=this.parents||[];this.relations&&h.each(this.relations,function(b){var d=b.key,g=b.relatedModel,l=b.collectionType,p=b.map,k=this.attributes[d],q=k&&k.idAttribute,e,u,r,n=!1;!g||g.prototype instanceof v||(g=h.isFunction(g)?g.call(this,b,a):g);g&&h.isString(g)&& +(g=g===f.Self?this.constructor:w(g));l&&h.isString(l)&&(l=w(l));p&&h.isString(p)&&(p=w(p));u=b.options?h.extend({},b.options,c):c;if(!g&&!l)throw Error("specify either a relatedModel or collectionType");if(a[d]){e=h.result(a,d);e=p?p.call(this,e,l?l:g):e;if(b.type===f.Many){if(l&&!l.prototype instanceof m)throw Error("collectionType must inherit from Backbone.Collection");e instanceof m&&!k?(b=e,n=!0):(k?(b=k,b._deferEvents=!0):b=l?new l:this._createCollection(g),b[u.reset?"reset":"set"](e instanceof +m?e.models:e,u))}else if(b.type===f.One){if(!g)throw Error("specify a relatedModel for Backbone.One type");if(!(g.prototype instanceof f.AssociatedModel))throw Error("specify an AssociatedModel for Backbone.One type");e instanceof s?(b=e,n=k!==e):k?k&&e[q]&&k.get(q)===e[q]?(k._deferEvents=!0,k._set(e,u),b=k):b=new g(e,u):b=new g(e,u)}else throw Error("type attribute must be specified and have the values Backbone.One or Backbone.Many");r=a[d]=b;if(n||r&&!r._proxyCallback)r._proxyCallback=function(){return this._bubbleEvent.call(this, +d,r,arguments)},r.on("all",r._proxyCallback,this)}a.hasOwnProperty(d)&&(k=a[d],e=this.attributes[d],k?(k.parents=k.parents||[],-1==h.indexOf(k.parents,this)&&k.parents.push(this)):e&&0h.size(a))){for(b=0;b 0) { // New value is undefined + original.parents = _.difference(original.parents, [this]); + // Don't bubble to this parent anymore + original._proxyCallback && original.off("all", original._proxyCallback, this); + } + } + }, this); + } + // Return results for `BackboneModel.set`. + return ModelProto.set.call(this, attributes, options); + }, + // Bubble-up event to `parent` Model + _bubbleEvent:function (relationKey, relationValue, eventArguments) { + var args = eventArguments, + opt = args[0].split(":"), + eventType = opt[0], + catch_all = args[0] == "nested-change", + eventObject = args[1], + colObject = args[2], + indexEventObject = -1, + _proxyCalls = relationValue._proxyCalls, + cargs, + eventPath, + basecolEventPath, + isDefaultEvent = _.indexOf(defaultEvents, eventType) !== -1; + + //Short circuit the listen in to the nested-graph event + if (catch_all) return; + + // Change the event name to a fully qualified path. + _.size(opt) > 1 && (eventPath = opt[1]); + + if (_.indexOf(collectionEvents, eventType) !== -1) { + colObject = eventObject; + } + + // Find the specific object in the collection which has changed. + if (relationValue instanceof BackboneCollection && isDefaultEvent && eventObject) { + var pathTokens = getPathArray(eventPath), + initialTokens = _.initial(pathTokens), colModel; + + colModel = relationValue.find(function (model) { + if (eventObject === model) return true; + if (!model) return false; + var changedModel = model.get(initialTokens); + + if ((changedModel instanceof AssociatedModel || changedModel instanceof BackboneCollection) + && eventObject === changedModel) + return true; + + changedModel = model.get(pathTokens); + + if ((changedModel instanceof AssociatedModel || changedModel instanceof BackboneCollection) + && eventObject === changedModel) + return true; + + if (changedModel instanceof BackboneCollection && colObject + && colObject === changedModel) + return true; + }); + colModel && (indexEventObject = relationValue.indexOf(colModel)); + } + + // Manipulate `eventPath`. + eventPath = relationKey + ((indexEventObject !== -1 && (eventType === "change" || eventPath)) ? + "[" + indexEventObject + "]" : "") + (eventPath ? "." + eventPath : ""); + + // Short circuit collection * events + if (/\[\*\]/g.test(eventPath)) return this; + basecolEventPath = eventPath.replace(/\[\d+\]/g, '[*]'); + + cargs = []; + cargs.push.apply(cargs, args); + cargs[0] = eventType + ":" + eventPath; + + // If event has been already triggered as result of same source `eventPath`, + // no need to re-trigger event to prevent cycle. + _proxyCalls = relationValue._proxyCalls = (_proxyCalls || {}); + if (this._isEventAvailable.call(this, _proxyCalls, eventPath)) return this; + + // Add `eventPath` in `_proxyCalls` to keep track of already triggered `event`. + _proxyCalls[eventPath] = true; + + // Set up previous attributes correctly. + if ("change" === eventType) { + this._previousAttributes[relationKey] = relationValue._previousAttributes; + this.changed[relationKey] = relationValue; + } + + // Bubble up event to parent `model` with new changed arguments. + this.trigger.apply(this, cargs); + + //Only fire for change. Not change:attribute + if ("change" === eventType && this.get(eventPath) != args[2]) { + var ncargs = ["nested-change", eventPath, args[1]]; + args[2] && ncargs.push(args[2]); //args[2] will be options if present + this.trigger.apply(this, ncargs); + } + + // Remove `eventPath` from `_proxyCalls`, + // if `eventPath` and `_proxyCalls` are available, + // which allow event to be triggered on for next operation of `set`. + if (_proxyCalls && eventPath) delete _proxyCalls[eventPath]; + + // Create a collection modified event with wild-card + if (eventPath !== basecolEventPath) { + cargs[0] = eventType + ":" + basecolEventPath; + this.trigger.apply(this, cargs); + } + + return this; + }, + + // Has event been fired from this source. Used to prevent event recursion in cyclic graphs + _isEventAvailable:function (_proxyCalls, path) { + return _.find(_proxyCalls, function (value, eventKey) { + return path.indexOf(eventKey, path.length - eventKey.length) !== -1; + }); + }, + + // Returns New `collection` of type `relation.relatedModel`. + _createCollection:function (type) { + var collection, relatedModel = type; + _.isString(relatedModel) && (relatedModel = map2Scope(relatedModel)); + // Creates new `Backbone.Collection` and defines model class. + if (relatedModel && relatedModel.prototype instanceof AssociatedModel) { + collection = new BackboneCollection(); + collection.model = relatedModel; + } else { + throw new Error('type must inherit from Backbone.AssociatedModel'); + } + return collection; + }, + + // Process all pending events after the entire object graph has been updated + _processPendingEvents:function () { + if (!this._processedEvents) { + this._processedEvents = true; + + this._deferEvents = false; + + // Trigger all pending events + _.each(this._pendingEvents, function (e) { + e.c.trigger.apply(e.c, e.a); + }); + + this._pendingEvents = []; + + // Traverse down the object graph and call process pending events on sub-trees + _.each(this.relations, function (relation) { + var val = this.attributes[relation.key]; + val && val._processPendingEvents(); + }, this); + + delete this._processedEvents; + } + }, + + // Override trigger to defer events in the object graph. + trigger:function (name) { + // Defer event processing + if (this._deferEvents) { + this._pendingEvents = this._pendingEvents || []; + // Maintain a queue of pending events to trigger after the entire object graph is updated. + this._pendingEvents.push({c:this, a:arguments}); + } else { + ModelProto.trigger.apply(this, arguments); + } + }, + + // The JSON representation of the model. + toJSON:function (options) { + var json = {}, aJson; + json[this.idAttribute] = this.id; + if (!this.visited) { + this.visited = true; + // Get json representation from `BackboneModel.toJSON`. + json = ModelProto.toJSON.apply(this, arguments); + // If `this.relations` is defined, iterate through each `relation` + // and added it's json representation to parents' json representation. + if (this.relations) { + _.each(this.relations, function (relation) { + var attr = this.attributes[relation.key]; + if (attr) { + aJson = attr.toJSON(options); + json[relation.key] = _.isArray(aJson) ? _.compact(aJson) : aJson; + } + }, this); + } + delete this.visited; + } + return json; + }, + + // Create a new model with identical attributes to this one. + clone:function () { + return new this.constructor(this.toJSON()); + }, + + // Call this if you want to set an `AssociatedModel` to a falsy value like undefined/null directly. + // Not calling this will leak memory and have wrong parents. + // See test case "parent relations" + cleanup:function () { + _.each(this.relations, function (relation) { + var val = this.attributes[relation.key]; + val && (val.parents = _.difference(val.parents, [this])); + }, this); + this.off(); + }, + + // Navigate the path to the leaf object in the path to query for the attribute value + _getAttr:function (path) { + + var result = this, + //Tokenize the path + attrs = getPathArray(path), + key, + i; + if (_.size(attrs) < 1) return; + for (i = 0; i < attrs.length; i++) { + key = attrs[i]; + if (!result) break; + //Navigate the path to get to the result + result = result instanceof BackboneCollection + ? (isNaN(key) ? undefined : result.at(key)) + : result.attributes[key]; + } + return result; + } + }); + + var delimiters = /[^\.\[\]]+/g; + + // Tokenize the fully qualified event path + var getPathArray = function (path) { + if (path === '') return ['']; + return _.isString(path) ? (path.match(delimiters)) : path || []; + }; + + var map2Scope = function (path) { + return _.reduce(path.split('.'), function (memo, elem) { + return memo[elem] + }, root); + }; + + //Infer the relation from the collection's parents and find the appropriate map for the passed in `models` + var map2models = function (parents, target, models) { + var relation, surrogate; + //Iterate over collection's parents + _.find(parents, function (parent) { + //Iterate over relations + relation = _.find(parent.relations, function (rel) { + return parent.get(rel.key) === target; + }, this); + if (relation) { + surrogate = parent;//surrogate for transformation + return true;//break; + } + }, this); + + //If we found a relation and it has a mapping function + if (relation && relation.map) { + return relation.map.call(surrogate, models, target); + } + return models; + }; + + var proxies = {}; + // Proxy Backbone collection methods + _.each(['set', 'remove', 'reset'], function (method) { + proxies[method] = BackboneCollection.prototype[method]; + + CollectionProto[method] = function (models, options) { + //Short-circuit if this collection doesn't hold `AssociatedModels` + if (this.model.prototype instanceof AssociatedModel && this.parents) { + //Find a map function if available and perform a transformation + arguments[0] = map2models(this.parents, this, models); + } + return proxies[method].apply(this, arguments); + } + }); + + // Override trigger to defer events in the object graph. + proxies['trigger'] = CollectionProto['trigger']; + CollectionProto['trigger'] = function (name) { + if (this._deferEvents) { + this._pendingEvents = this._pendingEvents || []; + // Maintain a queue of pending events to trigger after the entire object graph is updated. + this._pendingEvents.push({c:this, a:arguments}); + } else { + proxies['trigger'].apply(this, arguments); + } + }; + + // Attach process pending event functionality on collections as well. Re-use from `AssociatedModel` + CollectionProto._processPendingEvents = AssociatedModel.prototype._processPendingEvents; + + +}).call(this); diff --git a/ajax/libs/backbone-associations/package.json b/ajax/libs/backbone-associations/package.json index 7da83837c..ae48fc840 100644 --- a/ajax/libs/backbone-associations/package.json +++ b/ajax/libs/backbone-associations/package.json @@ -8,7 +8,7 @@ "author":["Dhruva Ray", "Jaynti Kanani"], "dependencies":{ "underscore":">=1.4.3", - "backbone":">=0.9.10" + "backbone":">=1.0.0" }, "devDependencies":{ "phantomjs":"0.2.2" @@ -16,7 +16,7 @@ "jam": { "dependencies": { "underscore":">=1.4.3", - "backbone":">=0.9.10" + "backbone":">=1.0.0" }, "main":"backbone-associations.js", "include": [ @@ -30,7 +30,7 @@ "test":"phantomjs test/lib/runner.js test/test-suite.html?noglobals=true" }, "main":"backbone-associations.js", - "version":"0.5.1", + "version":"0.5.2", "repository":"git://github.com/dhruvaray/backbone-associations.git", "licenses": [ {