/**
 * Backbone-relational.js 0.5.0
 * (c) 2011 Paul Uithol
 *
 * Backbone-relational may be freely distributed under the MIT license.
 * For details and documentation: https://github.com/PaulUithol/Backbone-relational.
 * Depends on Backbone: https://github.com/documentcloud/backbone.
 */
(function(undefined){"use strict";var _,Backbone,exports;if(typeof window==='undefined'){_=require('underscore');Backbone=require('backbone');exports=module.exports=Backbone}else{var _=window._;Backbone=window.Backbone;exports=window}Backbone.Relational={showWarnings:true};Backbone.Semaphore={_permitsAvailable:null,_permitsUsed:0,acquire:function(){if(this._permitsAvailable&&this._permitsUsed>=this._permitsAvailable){throw new Error('Max permits acquired');}else{this._permitsUsed++}},release:function(){if(this._permitsUsed===0){throw new Error('All permits released');}else{this._permitsUsed--}},isLocked:function(){return this._permitsUsed>0},setAvailablePermits:function(amount){if(this._permitsUsed>amount){throw new Error('Available permits cannot be less than used permits');}this._permitsAvailable=amount}};Backbone.BlockingQueue=function(){this._queue=[]};_.extend(Backbone.BlockingQueue.prototype,Backbone.Semaphore,{_queue:null,add:function(func){if(this.isBlocked()){this._queue.push(func)}else{func()}},process:function(){while(this._queue&&this._queue.length){this._queue.shift()()}},block:function(){this.acquire()},unblock:function(){this.release();if(!this.isBlocked()){this.process()}},isBlocked:function(){return this.isLocked()}});Backbone.Relational.eventQueue=new Backbone.BlockingQueue();Backbone.Store=function(){this._collections=[];this._reverseRelations=[];this._subModels=[]};_.extend(Backbone.Store.prototype,Backbone.Events,{addSubModels:function(subModelTypes,superModelType){this._subModels.push({'superModelType':superModelType,'subModels':subModelTypes})},setupSuperModel:function(modelType){_.find(this._subModels,function(subModelDef){return _.find(subModelDef.subModels,function(subModelTypeName,typeValue){var subModelType=this.getObjectByName(subModelTypeName);if(modelType===subModelType){subModelDef.superModelType._subModels[typeValue]=modelType;modelType._superModel=subModelDef.superModelType;modelType._subModelTypeValue=typeValue;modelType._subModelTypeAttribute=subModelDef.superModelType.prototype.subModelTypeAttribute;return true}},this)},this)},addReverseRelation:function(relation){var exists=_.any(this._reverseRelations,function(rel){return _.all(relation,function(val,key){return val===rel[key]})});if(!exists&&relation.model&&relation.type){this._reverseRelations.push(relation);var addRelation=function(model,relation){if(!model.prototype.relations){model.prototype.relations=[]}model.prototype.relations.push(relation);_.each(model._subModels,function(subModel){addRelation(subModel,relation)},this)};addRelation(relation.model,relation);this.retroFitRelation(relation)}},retroFitRelation:function(relation){var coll=this.getCollection(relation.model);coll.each(function(model){if(!(model instanceof relation.model)){return}new relation.type(model,relation)},this)},getCollection:function(model){if(model instanceof Backbone.RelationalModel){model=model.constructor}var rootModel=model;while(rootModel._superModel){rootModel=rootModel._superModel}var coll=_.detect(this._collections,function(c){return c.model===rootModel});if(!coll){coll=this._createCollection(model)}return coll},getObjectByName:function(name){var type=_.reduce(name.split('.'),function(memo,val){return memo[val]},exports);return type!==exports?type:null},_createCollection:function(type){var coll;if(type instanceof Backbone.RelationalModel){type=type.constructor}if(type.prototype instanceof Backbone.RelationalModel){coll=new Backbone.Collection();coll.model=type;this._collections.push(coll)}return coll},resolveIdForItem:function(type,item){var id=_.isString(item)||_.isNumber(item)?item:null;if(id==null){if(item instanceof Backbone.RelationalModel){id=item.id}else if(_.isObject(item)){id=item[type.prototype.idAttribute]}}return id},find:function(type,item){var id=this.resolveIdForItem(type,item);var coll=this.getCollection(type);if(coll){var obj=coll.get(id);if(obj instanceof type){return obj}}return null},register:function(model){var modelColl=model.collection;var coll=this.getCollection(model);coll&&coll.add(model);model.bind('destroy',this.unregister,this);model.collection=modelColl},update:function(model){var coll=this.getCollection(model);coll._onModelEvent('change:'+model.idAttribute,model,coll)},unregister:function(model){model.unbind('destroy',this.unregister);var coll=this.getCollection(model);coll&&coll.remove(model)}});Backbone.Relational.store=new Backbone.Store();Backbone.Relation=function(instance,options){this.instance=instance;options=_.isObject(options)?options:{};this.reverseRelation=_.defaults(options.reverseRelation||{},this.options.reverseRelation);this.reverseRelation.type=!_.isString(this.reverseRelation.type)?this.reverseRelation.type:Backbone[this.reverseRelation.type]||Backbone.Relational.store.getObjectByName(this.reverseRelation.type);this.model=options.model||this.instance.constructor;this.options=_.defaults(options,this.options,Backbone.Relation.prototype.options);this.key=this.options.key;this.keySource=this.options.keySource||this.key;this.keyDestination=this.options.keyDestination||this.options.keySource||this.key;this.relatedModel=this.options.relatedModel;if(_.isString(this.relatedModel)){this.relatedModel=Backbone.Relational.store.getObjectByName(this.relatedModel)}if(!this.checkPreconditions()){return false}if(instance){this.keyContents=this.instance.get(this.keySource);if(this.key!==this.keySource){this.instance.unset(this.keySource,{silent:true})}this.instance._relations.push(this)}if(!this.options.isAutoRelation&&this.reverseRelation.type&&this.reverseRelation.key){Backbone.Relational.store.addReverseRelation(_.defaults({isAutoRelation:true,model:this.relatedModel,relatedModel:this.model,reverseRelation:this.options},this.reverseRelation))}_.bindAll(this,'_modelRemovedFromCollection','_relatedModelAdded','_relatedModelRemoved');if(instance){this.initialize();Backbone.Relational.store.getCollection(this.instance).bind('relational:remove',this._modelRemovedFromCollection);Backbone.Relational.store.getCollection(this.relatedModel).bind('relational:add',this._relatedModelAdded).bind('relational:remove',this._relatedModelRemoved)}};_.extend(Backbone.Relation.prototype,Backbone.Events,Backbone.Semaphore,{options:{createModels:true,includeInJSON:true,isAutoRelation:false},instance:null,key:null,keyContents:null,relatedModel:null,reverseRelation:null,related:null,_relatedModelAdded:function(model,coll,options){var dit=this;model.queue(function(){dit.tryAddRelated(model,options)})},_relatedModelRemoved:function(model,coll,options){this.removeRelated(model,options)},_modelRemovedFromCollection:function(model){if(model===this.instance){this.destroy()}},checkPreconditions:function(){var i=this.instance,k=this.key,m=this.model,rm=this.relatedModel,warn=Backbone.Relational.showWarnings&&typeof console!=='undefined';if(!m||!k||!rm){warn&&console.warn('Relation=%o; no model, key or relatedModel (%o, %o, %o)',this,m,k,rm);return false}if(!(m.prototype instanceof Backbone.RelationalModel)){warn&&console.warn('Relation=%o; model does not inherit from Backbone.RelationalModel (%o)',this,i);return false}if(!(rm.prototype instanceof Backbone.RelationalModel)){warn&&console.warn('Relation=%o; relatedModel does not inherit from Backbone.RelationalModel (%o)',this,rm);return false}if(this instanceof Backbone.HasMany&&this.reverseRelation.type===Backbone.HasMany){warn&&console.warn('Relation=%o; relation is a HasMany, and the reverseRelation is HasMany as well.',this);return false}if(i&&i._relations.length){var exists=_.any(i._relations,function(rel){var hasReverseRelation=this.reverseRelation.key&&rel.reverseRelation.key;return rel.relatedModel===rm&&rel.key===k&&(!hasReverseRelation||this.reverseRelation.key===rel.reverseRelation.key)},this);if(exists){warn&&console.warn('Relation=%o between instance=%o.%s and relatedModel=%o.%s already exists',this,i,k,rm,this.reverseRelation.key);return false}}return true},setRelated:function(related,options){this.related=related;this.instance.acquire();this.instance.set(this.key,related,_.defaults(options||{},{silent:true}));this.instance.release()},_isReverseRelation:function(relation){if(relation.instance instanceof this.relatedModel&&this.reverseRelation.key===relation.key&&this.key===relation.reverseRelation.key){return true}return false},getReverseRelations:function(model){var reverseRelations=[];var models=!_.isUndefined(model)?[model]:this.related&&(this.related.models||[this.related]);_.each(models,function(related){_.each(related.getRelations(),function(relation){if(this._isReverseRelation(relation)){reverseRelations.push(relation)}},this)},this);return reverseRelations},sanitizeOptions:function(options){options=options?_.clone(options):{};if(options.silent){options=_.extend({},options,{silentChange:true});delete options.silent}return options},unsanitizeOptions:function(options){options=options?_.clone(options):{};if(options.silentChange){options=_.extend({},options,{silent:true});delete options.silentChange}return options},destroy:function(){Backbone.Relational.store.getCollection(this.instance).unbind('relational:remove',this._modelRemovedFromCollection);Backbone.Relational.store.getCollection(this.relatedModel).unbind('relational:add',this._relatedModelAdded).unbind('relational:remove',this._relatedModelRemoved);_.each(this.getReverseRelations(),function(relation){relation.removeRelated(this.instance)},this)}});Backbone.HasOne=Backbone.Relation.extend({options:{reverseRelation:{type:'HasMany'}},initialize:function(){_.bindAll(this,'onChange');this.instance.bind('relational:change:'+this.key,this.onChange);var model=this.findRelated({silent:true});this.setRelated(model);_.each(this.getReverseRelations(),function(relation){relation.addRelated(this.instance)},this)},findRelated:function(options){var item=this.keyContents;var model=null;if(item instanceof this.relatedModel){model=item}else if(item){model=this.relatedModel.findOrCreate(item,{create:this.options.createModels})}return model},onChange:function(model,attr,options){if(this.isLocked()){return}this.acquire();options=this.sanitizeOptions(options);var changed=_.isUndefined(options._related);var oldRelated=changed?this.related:options._related;if(changed){this.keyContents=attr;if(attr instanceof this.relatedModel){this.related=attr}else if(attr){var related=this.findRelated(options);this.setRelated(related)}else{this.setRelated(null)}}if(oldRelated&&this.related!==oldRelated){_.each(this.getReverseRelations(oldRelated),function(relation){relation.removeRelated(this.instance,options)},this)}_.each(this.getReverseRelations(),function(relation){relation.addRelated(this.instance,options)},this);if(!options.silentChange&&this.related!==oldRelated){var dit=this;Backbone.Relational.eventQueue.add(function(){dit.instance.trigger('update:'+dit.key,dit.instance,dit.related,options)})}this.release()},tryAddRelated:function(model,options){if(this.related){return}options=this.sanitizeOptions(options);var item=this.keyContents;if(item){var id=Backbone.Relational.store.resolveIdForItem(this.relatedModel,item);if(model.id===id){this.addRelated(model,options)}}},addRelated:function(model,options){if(model!==this.related){var oldRelated=this.related||null;this.setRelated(model);this.onChange(this.instance,model,{_related:oldRelated})}},removeRelated:function(model,options){if(!this.related){return}if(model===this.related){var oldRelated=this.related||null;this.setRelated(null);this.onChange(this.instance,model,{_related:oldRelated})}}});Backbone.HasMany=Backbone.Relation.extend({collectionType:null,options:{reverseRelation:{type:'HasOne'},collectionType:Backbone.Collection,collectionKey:true,collectionOptions:{}},initialize:function(){_.bindAll(this,'onChange','handleAddition','handleRemoval','handleReset');this.instance.bind('relational:change:'+this.key,this.onChange);this.collectionType=this.options.collectionType;if(_.isString(this.collectionType)){this.collectionType=Backbone.Relational.store.getObjectByName(this.collectionType)}if(!this.collectionType.prototype instanceof Backbone.Collection){throw new Error('collectionType must inherit from Backbone.Collection');}if(this.keyContents instanceof Backbone.Collection){this.setRelated(this._prepareCollection(this.keyContents))}else{this.setRelated(this._prepareCollection())}this.findRelated({silent:true})},_getCollectionOptions:function(){return _.isFunction(this.options.collectionOptions)?this.options.collectionOptions(this.instance):this.options.collectionOptions},_prepareCollection:function(collection){if(this.related){this.related.unbind('relational:add',this.handleAddition).unbind('relational:remove',this.handleRemoval).unbind('relational:reset',this.handleReset)}if(!collection||!(collection instanceof Backbone.Collection)){collection=new this.collectionType([],this._getCollectionOptions())}collection.model=this.relatedModel;if(this.options.collectionKey){var key=this.options.collectionKey===true?this.options.reverseRelation.key:this.options.collectionKey;if(collection[key]&&collection[key]!==this.instance){if(Backbone.Relational.showWarnings&&typeof console!=='undefined'){console.warn('Relation=%o; collectionKey=%s already exists on collection=%o',this,key,this.options.collectionKey)}}else if(key){collection[key]=this.instance}}collection.bind('relational:add',this.handleAddition).bind('relational:remove',this.handleRemoval).bind('relational:reset',this.handleReset);return collection},findRelated:function(options){if(this.keyContents){var models=[];if(this.keyContents instanceof Backbone.Collection){models=this.keyContents.models}else{this.keyContents=_.isArray(this.keyContents)?this.keyContents:[this.keyContents];_.each(this.keyContents,function(item){var model=null;if(item instanceof this.relatedModel){model=item}else{model=this.relatedModel.findOrCreate(item,{create:this.options.createModels})}if(model&&!this.related.getByCid(model)&&!this.related.get(model)){models.push(model)}},this)}if(models.length){options=this.unsanitizeOptions(options);this.related.add(models,options)}}},onChange:function(model,attr,options){options=this.sanitizeOptions(options);this.keyContents=attr;_.each(this.getReverseRelations(),function(relation){relation.removeRelated(this.instance,options)},this);if(attr instanceof Backbone.Collection){this._prepareCollection(attr);this.related=attr}else{var coll;if(this.related instanceof Backbone.Collection){coll=this.related;coll.remove(coll.models)}else{coll=this._prepareCollection()}this.setRelated(coll);this.findRelated(options)}_.each(this.getReverseRelations(),function(relation){relation.addRelated(this.instance,options)},this);var dit=this;Backbone.Relational.eventQueue.add(function(){!options.silentChange&&dit.instance.trigger('update:'+dit.key,dit.instance,dit.related,options)})},tryAddRelated:function(model,options){options=this.sanitizeOptions(options);if(!this.related.getByCid(model)&&!this.related.get(model)){var item=_.any(this.keyContents,function(item){var id=Backbone.Relational.store.resolveIdForItem(this.relatedModel,item);return id&&id===model.id},this);if(item){this.related.add(model,options)}}},handleAddition:function(model,coll,options){if(!(model instanceof Backbone.Model)){return}options=this.sanitizeOptions(options);_.each(this.getReverseRelations(model),function(relation){relation.addRelated(this.instance,options)},this);var dit=this;Backbone.Relational.eventQueue.add(function(){!options.silentChange&&dit.instance.trigger('add:'+dit.key,model,dit.related,options)})},handleRemoval:function(model,coll,options){if(!(model instanceof Backbone.Model)){return}options=this.sanitizeOptions(options);_.each(this.getReverseRelations(model),function(relation){relation.removeRelated(this.instance,options)},this);var dit=this;Backbone.Relational.eventQueue.add(function(){!options.silentChange&&dit.instance.trigger('remove:'+dit.key,model,dit.related,options)})},handleReset:function(coll,options){options=this.sanitizeOptions(options);var dit=this;Backbone.Relational.eventQueue.add(function(){!options.silentChange&&dit.instance.trigger('reset:'+dit.key,dit.related,options)})},addRelated:function(model,options){var dit=this;options=this.unsanitizeOptions(options);model.queue(function(){if(dit.related&&!dit.related.getByCid(model)&&!dit.related.get(model)){dit.related.add(model,options)}})},removeRelated:function(model,options){options=this.unsanitizeOptions(options);if(this.related.getByCid(model)||this.related.get(model)){this.related.remove(model,options)}}});Backbone.RelationalModel=Backbone.Model.extend({relations:null,_relations:null,_isInitialized:false,_deferProcessing:false,_queue:null,subModelTypeAttribute:'type',subModelTypes:null,constructor:function(attributes,options){var dit=this;if(options&&options.collection){this._deferProcessing=true;var processQueue=function(model){if(model===dit){dit._deferProcessing=false;dit.processQueue();options.collection.unbind('relational:add',processQueue)}};options.collection.bind('relational:add',processQueue);_.defer(function(){processQueue(dit)})}this._queue=new Backbone.BlockingQueue();this._queue.block();Backbone.Relational.eventQueue.block();Backbone.Model.apply(this,arguments);Backbone.Relational.eventQueue.unblock()},trigger:function(eventName){if(eventName.length>5&&'change'===eventName.substr(0,6)){var dit=this,args=arguments;Backbone.Relational.eventQueue.add(function(){Backbone.Model.prototype.trigger.apply(dit,args)})}else{Backbone.Model.prototype.trigger.apply(this,arguments)}return this},initializeRelations:function(){this.acquire();this._relations=[];_.each(this.relations,function(rel){var type=!_.isString(rel.type)?rel.type:Backbone[rel.type]||Backbone.Relational.store.getObjectByName(rel.type);if(type&&type.prototype instanceof Backbone.Relation){new type(this,rel)}else{Backbone.Relational.showWarnings&&typeof console!=='undefined'&&console.warn('Relation=%o; missing or invalid type!',rel)}},this);this._isInitialized=true;this.release();this.processQueue()},updateRelations:function(options){if(this._isInitialized&&!this.isLocked()){_.each(this._relations,function(rel){var val=this.attributes[rel.key];if(rel.related!==val){this.trigger('relational:change:'+rel.key,this,val,options||{})}},this)}},queue:function(func){this._queue.add(func)},processQueue:function(){if(this._isInitialized&&!this._deferProcessing&&this._queue.isBlocked()){this._queue.unblock()}},getRelation:function(key){return _.detect(this._relations,function(rel){if(rel.key===key){return true}},this)},getRelations:function(){return this._relations},fetchRelated:function(key,options,update){options||(options={});var setUrl,requests=[],rel=this.getRelation(key),keyContents=rel&&rel.keyContents,toFetch=keyContents&&_.select(_.isArray(keyContents)?keyContents:[keyContents],function(item){var id=Backbone.Relational.store.resolveIdForItem(rel.relatedModel,item);return id&&(update||!Backbone.Relational.store.find(rel.relatedModel,id))},this);if(toFetch&&toFetch.length){var models=_.map(toFetch,function(item){var model;if(_.isObject(item)){model=rel.relatedModel.build(item)}else{var attrs={};attrs[rel.relatedModel.prototype.idAttribute]=item;model=rel.relatedModel.build(attrs)}return model},this);if(rel.related instanceof Backbone.Collection&&_.isFunction(rel.related.url)){setUrl=rel.related.url(models)}if(setUrl&&setUrl!==rel.related.url()){var opts=_.defaults({error:function(){var args=arguments;_.each(models,function(model){model.trigger('destroy',model,model.collection,options);options.error&&options.error.apply(model,args)})},url:setUrl},options,{add:true});requests=[rel.related.fetch(opts)]}else{requests=_.map(models,function(model){var opts=_.defaults({error:function(){model.trigger('destroy',model,model.collection,options);options.error&&options.error.apply(model,arguments)}},options);return model.fetch(opts)},this)}}return requests},set:function(key,value,options){Backbone.Relational.eventQueue.block();var attributes;if(_.isObject(key)||key==null){attributes=key;options=value}else{attributes={};attributes[key]=value}var result=Backbone.Model.prototype.set.apply(this,arguments);if(!this._isInitialized&&!this.isLocked()){this.constructor.initializeModelHierarchy();Backbone.Relational.store.register(this);this.initializeRelations()}else if(attributes&&this.idAttribute in attributes){Backbone.Relational.store.update(this)}if(attributes){this.updateRelations(options)}Backbone.Relational.eventQueue.unblock();return result},unset:function(attribute,options){Backbone.Relational.eventQueue.block();var result=Backbone.Model.prototype.unset.apply(this,arguments);this.updateRelations(options);Backbone.Relational.eventQueue.unblock();return result},clear:function(options){Backbone.Relational.eventQueue.block();var result=Backbone.Model.prototype.clear.apply(this,arguments);this.updateRelations(options);Backbone.Relational.eventQueue.unblock();return result},change:function(options){var dit=this,args=arguments;Backbone.Relational.eventQueue.add(function(){Backbone.Model.prototype.change.apply(dit,args)})},clone:function(){var attributes=_.clone(this.attributes);if(!_.isUndefined(attributes[this.idAttribute])){attributes[this.idAttribute]=null}_.each(this.getRelations(),function(rel){delete attributes[rel.key]});return new this.constructor(attributes)},toJSON:function(){if(this.isLocked()){return this.id}this.acquire();var json=Backbone.Model.prototype.toJSON.call(this);if(this.constructor._superModel&&!(this.constructor._subModelTypeAttribute in json)){json[this.constructor._subModelTypeAttribute]=this.constructor._subModelTypeValue}_.each(this._relations,function(rel){var value=json[rel.key];if(rel.options.includeInJSON===true){if(value&&_.isFunction(value.toJSON)){json[rel.keyDestination]=value.toJSON()}else{json[rel.keyDestination]=null}}else if(_.isString(rel.options.includeInJSON)){if(value instanceof Backbone.Collection){json[rel.keyDestination]=value.pluck(rel.options.includeInJSON)}else if(value instanceof Backbone.Model){json[rel.keyDestination]=value.get(rel.options.includeInJSON)}else{json[rel.keyDestination]=null}}else if(_.isArray(rel.options.includeInJSON)){if(value instanceof Backbone.Collection){var valueSub=[];value.each(function(model){var curJson={};_.each(rel.options.includeInJSON,function(key){curJson[key]=model.get(key)});valueSub.push(curJson)});json[rel.keyDestination]=valueSub}else if(value instanceof Backbone.Model){var valueSub={};_.each(rel.options.includeInJSON,function(key){valueSub[key]=value.get(key)});json[rel.keyDestination]=valueSub}else{json[rel.keyDestination]=null}}else{delete json[rel.key]}if(rel.keyDestination!==rel.key){delete json[rel.key]}});this.release();return json}},{setup:function(superModel){this.prototype.relations=(this.prototype.relations||[]).slice(0);this._subModels={};this._superModel=null;if(this.prototype.hasOwnProperty('subModelTypes')){Backbone.Relational.store.addSubModels(this.prototype.subModelTypes,this)}else{this.prototype.subModelTypes=null}_.each(this.prototype.relations,function(rel){if(!rel.model){rel.model=this}if(rel.reverseRelation&&rel.model===this){var preInitialize=true;if(_.isString(rel.relatedModel)){var relatedModel=Backbone.Relational.store.getObjectByName(rel.relatedModel);preInitialize=relatedModel&&(relatedModel.prototype instanceof Backbone.RelationalModel)}var type=!_.isString(rel.type)?rel.type:Backbone[rel.type]||Backbone.Relational.store.getObjectByName(rel.type);if(preInitialize&&type&&type.prototype instanceof Backbone.Relation){new type(null,rel)}}},this)},build:function(attributes,options){var model=this;this.initializeModelHierarchy();if(this._subModels&&this.prototype.subModelTypeAttribute in attributes){var subModelTypeAttribute=attributes[this.prototype.subModelTypeAttribute];var subModelType=this._subModels[subModelTypeAttribute];if(subModelType){model=subModelType}}return new model(attributes,options)},initializeModelHierarchy:function(){if(_.isUndefined(this._superModel)||_.isNull(this._superModel)){Backbone.Relational.store.setupSuperModel(this);if(this._superModel){if(this._superModel.prototype.relations){var supermodelRelationsExist=_.any(this.prototype.relations,function(rel){return rel.model&&rel.model!==this},this);if(!supermodelRelationsExist){this.prototype.relations=this._superModel.prototype.relations.concat(this.prototype.relations)}}}else{this._superModel=false}}if(this.prototype.subModelTypes&&_.keys(this.prototype.subModelTypes).length!==_.keys(this._subModels).length){_.each(this.prototype.subModelTypes,function(subModelTypeName){var subModelType=Backbone.Relational.store.getObjectByName(subModelTypeName);subModelType&&subModelType.initializeModelHierarchy()})}},findOrCreate:function(attributes,options){var model=Backbone.Relational.store.find(this,attributes);if(_.isObject(attributes)){if(model){model.set(attributes,options)}else if(!options||(options&&options.create!==false)){model=this.build(attributes,options)}}return model}});_.extend(Backbone.RelationalModel.prototype,Backbone.Semaphore);Backbone.Collection.prototype.__prepareModel=Backbone.Collection.prototype._prepareModel;Backbone.Collection.prototype._prepareModel=function(model,options){options||(options={});if(!(model instanceof Backbone.Model)){var attrs=model;options.collection=this;if(typeof this.model.build!=='undefined'){model=this.model.build(attrs,options)}else{model=new this.model(attrs,options)}if(!model._validate(model.attributes,options)){model=false}}else if(!model.collection){model.collection=this}return model}var add=Backbone.Collection.prototype.__add=Backbone.Collection.prototype.add;Backbone.Collection.prototype.add=function(models,options){options||(options={});if(!_.isArray(models)){models=[models]}var modelsToAdd=[];_.each(models,function(model){if(!(model instanceof Backbone.Model)){var existingModel=Backbone.Relational.store.find(this.model,model[this.model.prototype.idAttribute]);if(existingModel){existingModel.set(existingModel.parse?existingModel.parse(model):model,options);model=existingModel}else{model=Backbone.Collection.prototype._prepareModel.call(this,model,options)}}if(model instanceof Backbone.Model&&!this.get(model)&&!this.getByCid(model)){modelsToAdd.push(model)}},this);if(modelsToAdd.length){add.call(this,modelsToAdd,options);_.each(modelsToAdd,function(model){this.trigger('relational:add',model,this,options)},this)}return this};var remove=Backbone.Collection.prototype.__remove=Backbone.Collection.prototype.remove;Backbone.Collection.prototype.remove=function(models,options){options||(options={});if(!_.isArray(models)){models=[models]}else{models=models.slice(0)}_.each(models,function(model){model=this.getByCid(model)||this.get(model);if(model instanceof Backbone.Model){remove.call(this,model,options);this.trigger('relational:remove',model,this,options)}},this);return this};var reset=Backbone.Collection.prototype.__reset=Backbone.Collection.prototype.reset;Backbone.Collection.prototype.reset=function(models,options){reset.call(this,models,options);this.trigger('relational:reset',this,options);return this};var sort=Backbone.Collection.prototype.__sort=Backbone.Collection.prototype.sort;Backbone.Collection.prototype.sort=function(options){sort.call(this,options);this.trigger('relational:reset',this,options);return this};var trigger=Backbone.Collection.prototype.__trigger=Backbone.Collection.prototype.trigger;Backbone.Collection.prototype.trigger=function(eventName){if(eventName==='add'||eventName==='remove'||eventName==='reset'){var dit=this,args=arguments;Backbone.Relational.eventQueue.add(function(){trigger.apply(dit,args)})}else{trigger.apply(this,arguments)}return this};Backbone.RelationalModel.extend=function(protoProps,classProps){var child=Backbone.Model.extend.apply(this,arguments);child.setup(this);return child}})();