!function(e,t){"function"==typeof define&&define.amd?define(["exports","backbone","underscore"],t):"undefined"!=typeof exports?t(exports,require("backbone"),require("underscore")):t(e,e.Backbone,e._)}(this,function(e,t,i){"use strict";t.Relational={showWarnings:!0},t.Semaphore={_permitsAvailable:null,_permitsUsed:0,acquire:function(){if(this._permitsAvailable&&this._permitsUsed>=this._permitsAvailable)throw new Error("Max permits acquired");this._permitsUsed++},release:function(){if(0===this._permitsUsed)throw new Error("All permits released");this._permitsUsed--},isLocked:function(){return this._permitsUsed>0},setAvailablePermits:function(e){if(this._permitsUsed>e)throw new Error("Available permits cannot be less than used permits");this._permitsAvailable=e}},t.BlockingQueue=function(){this._queue=[]},i.extend(t.BlockingQueue.prototype,t.Semaphore,{_queue:null,add:function(e){this.isBlocked()?this._queue.push(e):e()},process:function(){var e=this._queue;for(this._queue=[];e&&e.length;)e.shift()()},block:function(){this.acquire()},unblock:function(){this.release(),this.isBlocked()||this.process()},isBlocked:function(){return this.isLocked()}}),t.Relational.eventQueue=new t.BlockingQueue,t.Store=function(){this._collections=[],this._reverseRelations=[],this._orphanRelations=[],this._subModels=[],this._modelScopes=[e]},i.extend(t.Store.prototype,t.Events,{initializeRelation:function(e,o,n){var s=i.isString(o.type)?t[o.type]||this.getObjectByName(o.type):o.type;if(s&&s.prototype instanceof t.Relation){new s(e,o,n)}else t.Relational.showWarnings&&"undefined"!=typeof console&&console.warn("Relation=%o; missing or invalid relation type!",o)},addModelScope:function(e){this._modelScopes.push(e)},removeModelScope:function(e){this._modelScopes=i.without(this._modelScopes,e)},addSubModels:function(e,t){this._subModels.push({superModelType:t,subModels:e})},setupSuperModel:function(e){i.find(this._subModels,function(t){return i.filter(t.subModels||[],function(i,o){var n=this.getObjectByName(i);return e===n?(t.superModelType._subModels[o]=e,e._superModel=t.superModelType,e._subModelTypeValue=o,e._subModelTypeAttribute=t.superModelType.prototype.subModelTypeAttribute,!0):void 0},this).length},this)},addReverseRelation:function(e){var t=i.any(this._reverseRelations,function(t){return i.all(e||[],function(e,i){return e===t[i]})});!t&&e.model&&e.type&&(this._reverseRelations.push(e),this._addRelation(e.model,e),this.retroFitRelation(e))},addOrphanRelation:function(e){var t=i.any(this._orphanRelations,function(t){return i.all(e||[],function(e,i){return e===t[i]})});!t&&e.model&&e.type&&this._orphanRelations.push(e)},processOrphanRelations:function(){i.each(this._orphanRelations.slice(0),function(e){var o=t.Relational.store.getObjectByName(e.relatedModel);o&&(this.initializeRelation(null,e),this._orphanRelations=i.without(this._orphanRelations,e))},this)},_addRelation:function(e,t){e.prototype.relations||(e.prototype.relations=[]),e.prototype.relations.push(t),i.each(e._subModels||[],function(e){this._addRelation(e,t)},this)},retroFitRelation:function(e){var t=this.getCollection(e.model,!1);t&&t.each(function(t){if(t instanceof e.model){new e.type(t,e)}},this)},getCollection:function(e,o){e instanceof t.RelationalModel&&(e=e.constructor);for(var n=e;n._superModel;)n=n._superModel;var s=i.find(this._collections,function(e){return e.model===n});return s||o===!1||(s=this._createCollection(n)),s},getObjectByName:function(e){var t=e.split("."),o=null;return i.find(this._modelScopes,function(e){return o=i.reduce(t||[],function(e,t){return e?e[t]:void 0},e),o&&o!==e?!0:void 0},this),o},_createCollection:function(e){var i;return e instanceof t.RelationalModel&&(e=e.constructor),e.prototype instanceof t.RelationalModel&&(i=new t.Collection,i.model=e,this._collections.push(i)),i},resolveIdForItem:function(e,o){var n=i.isString(o)||i.isNumber(o)?o:null;return null===n&&(o instanceof t.RelationalModel?n=o.id:i.isObject(o)&&(n=o[e.prototype.idAttribute])),n||0===n||(n=null),n},find:function(e,t){var i=this.resolveIdForItem(e,t),o=this.getCollection(e);if(o){var n=o.get(i);if(n instanceof e)return n}return null},register:function(e){var t=this.getCollection(e);if(t){var i=e.collection;t.add(e),e.collection=i}},checkId:function(e,i){var o=this.getCollection(e),n=o&&o.get(i);if(n&&e!==n)throw t.Relational.showWarnings&&"undefined"!=typeof console&&console.warn("Duplicate id! Old RelationalModel=%o, new RelationalModel=%o",n,e),new Error("Cannot instantiate more than one Backbone.RelationalModel with the same id per type!")},update:function(e){var t=this.getCollection(e);t.contains(e)||this.register(e),t._onModelEvent("change:"+e.idAttribute,e,t),e.trigger("relational:change:id",e,t)},unregister:function(e){var o,n;e instanceof t.Model?(o=this.getCollection(e),n=[e]):e instanceof t.Collection?(o=this.getCollection(e.model),n=i.clone(e.models)):(o=this.getCollection(e),n=i.clone(o.models)),i.each(n,function(e){this.stopListening(e),i.invoke(e.getRelations(),"stopListening")},this),i.contains(this._collections,e)?o.reset([]):i.each(n,function(e){o.get(e)?o.remove(e):o.trigger("relational:remove",e,o)},this)},reset:function(){this.stopListening(),i.each(this._collections,function(e){this.unregister(e)},this),this._collections=[],this._subModels=[],this._modelScopes=[e]}}),t.Relational.store=new t.Store,t.Relation=function(e,o,n){if(this.instance=e,o=i.isObject(o)?o:{},this.reverseRelation=i.defaults(o.reverseRelation||{},this.options.reverseRelation),this.options=i.defaults(o,this.options,t.Relation.prototype.options),this.reverseRelation.type=i.isString(this.reverseRelation.type)?t[this.reverseRelation.type]||t.Relational.store.getObjectByName(this.reverseRelation.type):this.reverseRelation.type,this.key=this.options.key,this.keySource=this.options.keySource||this.key,this.keyDestination=this.options.keyDestination||this.keySource||this.key,this.model=this.options.model||this.instance.constructor,this.relatedModel=this.options.relatedModel,i.isUndefined(this.relatedModel)&&(this.relatedModel=this.model),!i.isFunction(this.relatedModel)||this.relatedModel.prototype instanceof t.RelationalModel||(this.relatedModel=i.result(this,"relatedModel")),i.isString(this.relatedModel)&&(this.relatedModel=t.Relational.store.getObjectByName(this.relatedModel)),this.checkPreconditions()&&(!this.options.isAutoRelation&&this.reverseRelation.type&&this.reverseRelation.key&&t.Relational.store.addReverseRelation(i.defaults({isAutoRelation:!0,model:this.relatedModel,relatedModel:this.model,reverseRelation:this.options},this.reverseRelation)),e)){var s=this.keySource;s!==this.key&&i.isObject(this.instance.get(this.key))&&(s=this.key),this.setKeyContents(this.instance.get(s)),this.relatedCollection=t.Relational.store.getCollection(this.relatedModel),this.keySource!==this.key&&delete this.instance.attributes[this.keySource],this.instance._relations[this.key]=this,this.initialize(n),this.options.autoFetch&&this.instance.getAsync(this.key,i.isObject(this.options.autoFetch)?this.options.autoFetch:{}),this.listenTo(this.instance,"destroy",this.destroy).listenTo(this.relatedCollection,"relational:add relational:change:id",this.tryAddRelated).listenTo(this.relatedCollection,"relational:remove",this.removeRelated)}},t.Relation.extend=t.Model.extend,i.extend(t.Relation.prototype,t.Events,t.Semaphore,{options:{createModels:!0,includeInJSON:!0,isAutoRelation:!1,autoFetch:!1,parse:!1},instance:null,key:null,keyContents:null,relatedModel:null,relatedCollection:null,reverseRelation:null,related:null,checkPreconditions:function(){var e=this.instance,o=this.key,n=this.model,s=this.relatedModel,l=t.Relational.showWarnings&&"undefined"!=typeof console;if(!n||!o||!s)return l&&console.warn("Relation=%o: missing model, key or relatedModel (%o, %o, %o).",this,n,o,s),!1;if(!(n.prototype instanceof t.RelationalModel))return l&&console.warn("Relation=%o: model does not inherit from Backbone.RelationalModel (%o).",this,e),!1;if(!(s.prototype instanceof t.RelationalModel))return l&&console.warn("Relation=%o: relatedModel does not inherit from Backbone.RelationalModel (%o).",this,s),!1;if(this instanceof t.HasMany&&this.reverseRelation.type===t.HasMany)return l&&console.warn("Relation=%o: relation is a HasMany, and the reverseRelation is HasMany as well.",this),!1;if(e&&i.keys(e._relations).length){var r=i.find(e._relations,function(e){return e.key===o},this);if(r)return l&&console.warn("Cannot create relation=%o on %o for model=%o: already taken by relation=%o.",this,o,e,r),!1}return!0},setRelated:function(e){this.related=e,this.instance.attributes[this.key]=e},_isReverseRelation:function(e){return e.instance instanceof this.relatedModel&&this.reverseRelation.key===e.key&&this.key===e.reverseRelation.key},getReverseRelations:function(e){for(var t=[],o=i.isUndefined(e)?this.related&&(this.related.models||[this.related]):[e],n=null,s=null,l=0;l<(o||[]).length;l++){n=o[l].getRelations()||[];for(var r=0;r5&&0===e.indexOf("change")){var i=this,o=arguments;t.Relational.eventQueue.isLocked()?t.Relational.eventQueue.add(function(){var n=!0;if("change"===e)n=i.hasChanged()||i._attributeChangeFired,i._attributeChangeFired=!1;else{var s=e.slice(7),l=i.getRelation(s);l?(n=o[4]===!0,n?i.changed[s]=o[2]:l.changed||delete i.changed[s]):n&&(i._attributeChangeFired=!0)}n&&t.Model.prototype.trigger.apply(i,o)}):t.Model.prototype.trigger.apply(i,o)}else"destroy"===e?(t.Model.prototype.trigger.apply(this,arguments),t.Relational.store.unregister(this)):t.Model.prototype.trigger.apply(this,arguments);return this},initializeRelations:function(e){this.acquire(),this._relations={},i.each(this.relations||[],function(i){t.Relational.store.initializeRelation(this,i,e)},this),this._isInitialized=!0,this.release(),this.processQueue()},updateRelations:function(e,t){this._isInitialized&&!this.isLocked()&&i.each(this._relations,function(i){if(!e||i.keySource in e||i.key in e){var o=this.attributes[i.keySource]||this.attributes[i.key],n=e&&(e[i.keySource]||e[i.key]);(i.related!==o||null===o&&null===n)&&this.trigger("relational:change:"+i.key,this,o,t||{})}i.keySource!==i.key&&delete this.attributes[i.keySource]},this)},queue:function(e){this._queue.add(e)},processQueue:function(){this._isInitialized&&!this._deferProcessing&&this._queue.isBlocked()&&this._queue.unblock()},getRelation:function(e){return this._relations[e]},getRelations:function(){return i.values(this._relations)},getIdsToFetch:function(e,o){var n=e instanceof t.Relation?e:this.getRelation(e),s=n?n.keyIds&&n.keyIds.slice(0)||(n.keyId||0===n.keyId?[n.keyId]:[]):[];if(o){var l=n.related&&(n.related.models||[n.related]);i.each(l,function(e){(e.id||0===e.id)&&s.push(e.id)})}return s},getAsync:function(e,o){o=i.extend({add:!0,remove:!1,refresh:!1},o);var n=this,s=[],l=this.getRelation(e),r=l&&this.getIdsToFetch(l,o.refresh),a=l.related instanceof t.Collection?l.related:l.relatedCollection;if(r&&r.length){var h,c=[],d=[],u=function(){c=i.map(r,function(e){var t=l.relatedModel.findModel(e);if(!t){var i={};i[l.relatedModel.prototype.idAttribute]=e,t=l.relatedModel.findOrCreate(i,o),d.push(t)}return t},this)};if(a instanceof t.Collection&&i.isFunction(a.url)){var p=a.url();h=a.url(r),h===p&&(u(),h=a.url(c),h===p&&(h=null))}if(h){var f=i.defaults({error:function(){i.each(d,function(e){e.trigger("destroy",e,e.collection,o)}),o.error&&o.error.apply(c,arguments)},url:h},o);s=[a.fetch(f)]}else c.length||u(),s=i.map(c,function(e){var t=i.defaults({error:function(){i.contains(d,e)&&e.trigger("destroy",e,e.collection,o),o.error&&o.error.apply(c,arguments)}},o);return e.fetch(t)},this)}return $.when.apply(null,s).then(function(){return t.Model.prototype.get.call(n,e)})},set:function(e,o,n){t.Relational.eventQueue.block();var s,l;i.isObject(e)||null==e?(s=e,n=o):(s={},s[e]=o);try{var r=this.id,a=s&&this.idAttribute in s&&s[this.idAttribute];t.Relational.store.checkId(this,a),l=t.Model.prototype.set.apply(this,arguments),this._isInitialized||this.isLocked()?a&&a!==r&&t.Relational.store.update(this):(this.constructor.initializeModelHierarchy(),(a||0===a)&&t.Relational.store.register(this),this.initializeRelations(n)),s&&this.updateRelations(s,n)}finally{t.Relational.eventQueue.unblock()}return l},clone:function(){var e=i.clone(this.attributes);return i.isUndefined(e[this.idAttribute])||(e[this.idAttribute]=null),i.each(this.getRelations(),function(t){delete e[t.key]}),new this.constructor(e)},toJSON:function(e){if(this.isLocked())return this.id;this.acquire();var o=t.Model.prototype.toJSON.call(this,e);return!this.constructor._superModel||this.constructor._subModelTypeAttribute in o||(o[this.constructor._subModelTypeAttribute]=this.constructor._subModelTypeValue),i.each(this._relations,function(n){var s=o[n.key],l=n.options.includeInJSON,r=null;l===!0?s&&i.isFunction(s.toJSON)&&(r=s.toJSON(e)):i.isString(l)?(s instanceof t.Collection?r=s.pluck(l):s instanceof t.Model&&(r=s.get(l)),l===n.relatedModel.prototype.idAttribute&&(n instanceof t.HasMany?r=r.concat(n.keyIds):n instanceof t.HasOne&&(r=r||n.keyId,r||i.isObject(n.keyContents)||(r=n.keyContents||null)))):i.isArray(l)?s instanceof t.Collection?(r=[],s.each(function(e){var t={};i.each(l,function(i){t[i]=e.get(i)}),r.push(t)})):s instanceof t.Model&&(r={},i.each(l,function(e){r[e]=s.get(e)})):delete o[n.key],null===r&&e&&e.wait&&(r=s),l&&(o[n.keyDestination]=r),n.keyDestination!==n.key&&delete o[n.key]}),this.release(),o}},{setup:function(){return this.prototype.relations=(this.prototype.relations||[]).slice(0),this._subModels={},this._superModel=null,this.prototype.hasOwnProperty("subModelTypes")?t.Relational.store.addSubModels(this.prototype.subModelTypes,this):this.prototype.subModelTypes=null,i.each(this.prototype.relations||[],function(e){if(e.model||(e.model=this),e.reverseRelation&&e.model===this){var o=!0;if(i.isString(e.relatedModel)){var n=t.Relational.store.getObjectByName(e.relatedModel);o=n&&n.prototype instanceof t.RelationalModel}o?t.Relational.store.initializeRelation(null,e):i.isString(e.relatedModel)&&t.Relational.store.addOrphanRelation(e)}},this),this},build:function(e,t){this.initializeModelHierarchy();var i=this._findSubModelType(this,e)||this;return new i(e,t)},_findSubModelType:function(e,t){if(e._subModels&&e.prototype.subModelTypeAttribute in t){var i=t[e.prototype.subModelTypeAttribute],o=e._subModels[i];if(o)return o;for(i in e._subModels)if(o=this._findSubModelType(e._subModels[i],t))return o}return null},initializeModelHierarchy:function(){if(this.inheritRelations(),this.prototype.subModelTypes){var e=i.keys(this._subModels),o=i.omit(this.prototype.subModelTypes,e);i.each(o,function(e){var i=t.Relational.store.getObjectByName(e);i&&i.initializeModelHierarchy()})}},inheritRelations:function(){if(i.isUndefined(this._superModel)||i.isNull(this._superModel))if(t.Relational.store.setupSuperModel(this),this._superModel){if(this._superModel.inheritRelations(),this._superModel.prototype.relations){var e=i.filter(this._superModel.prototype.relations||[],function(e){return!i.any(this.prototype.relations||[],function(t){return e.relatedModel===t.relatedModel&&e.key===t.key},this)},this);this.prototype.relations=e.concat(this.prototype.relations)}}else this._superModel=!1},findOrCreate:function(e,t){t||(t={});var o=i.isObject(e)&&t.parse&&this.prototype.parse?this.prototype.parse(i.clone(e)):e,n=this.findModel(o);return i.isObject(e)&&(n&&t.merge!==!1?(delete t.collection,delete t.url,n.set(o,t)):n||t.create===!1||(n=this.build(o,i.defaults({parse:!1},t)))),n},find:function(e,t){return t||(t={}),t.create=!1,this.findOrCreate(e,t)},findModel:function(e){return t.Relational.store.find(this,e)}}),i.extend(t.RelationalModel.prototype,t.Semaphore),t.Collection.prototype.__prepareModel=t.Collection.prototype._prepareModel,t.Collection.prototype._prepareModel=function(e,o){var n;return e instanceof t.Model?(e.collection||(e.collection=this),n=e):(o=o?i.clone(o):{},o.collection=this,n="undefined"!=typeof this.model.findOrCreate?this.model.findOrCreate(e,o):new this.model(e,o),n&&n.validationError&&(this.trigger("invalid",this,e,o),n=!1)),n};var o=t.Collection.prototype.__set=t.Collection.prototype.set;t.Collection.prototype.set=function(e,n){if(!(this.model.prototype instanceof t.RelationalModel))return o.call(this,e,n);n&&n.parse&&(e=this.parse(e,n));var s=!i.isArray(e),l=[],r=[],a=null;e=s?e?[e]:[]:i.clone(e);for(var h=0;h