From a0adad45ac32f4f50e6b134b94a6ffb1c4507122 Mon Sep 17 00:00:00 2001 From: Philip Diffenderfer Date: Wed, 28 Mar 2018 01:48:56 -0400 Subject: [PATCH] Expose All Classes (resolves #381) --- build/rekord.js | 26 ++++++++++++++++++++++++++ build/rekord.min.js | 2 +- build/rekord.min.js.map | 2 +- src/footer.js | 26 ++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/build/rekord.js b/build/rekord.js index 0c6129e..1c9c624 100644 --- a/build/rekord.js +++ b/build/rekord.js @@ -18968,6 +18968,8 @@ addPlugin(function(model, db, options) Rekord.Search = Search; Rekord.SearchPaged = SearchPaged; Rekord.Promise = Promise; + Rekord.Dependents = Dependents; + Rekord.Shard = Shard; /* Keys */ Rekord.KeyHandler = KeyHandler; @@ -18982,6 +18984,7 @@ addPlugin(function(model, db, options) Rekord.Store = Store; Rekord.Save = Save; Rekord.Load = Load; + Rekord.RestStatus = RestStatus; /* Collections */ Rekord.Map = Map; @@ -18989,6 +18992,7 @@ addPlugin(function(model, db, options) Rekord.FilteredCollection = FilteredCollection; Rekord.ModelCollection = ModelCollection; Rekord.FilteredModelCollection = FilteredModelCollection; + Rekord.RelationCollection = RelationCollection; Rekord.Page = Page; Rekord.Context = Context; @@ -18999,6 +19003,20 @@ addPlugin(function(model, db, options) Rekord.HasManyThrough = HasManyThrough; Rekord.HasRemote = HasRemote; Rekord.HasList = HasList; + Rekord.HasReference = HasReference; + Rekord.RelationMultiple = RelationMultiple; + Rekord.RelationSingle = RelationSingle; + + /* Operations */ + Rekord.GetLocal = GetLocal; + Rekord.GetRemote = GetRemote; + Rekord.RemoveCache = RemoveCache; + Rekord.RemoveLocal = RemoveLocal; + Rekord.RemoveNow = RemoveNow; + Rekord.RemoveRemote = RemoveRemote; + Rekord.SaveLocal = SaveLocal; + Rekord.SaveNow = SaveNow; + Rekord.SaveRemote = SaveRemote; /* Projections */ Rekord.Filters = {}; @@ -19026,6 +19044,14 @@ addPlugin(function(model, db, options) Rekord.now = now; Rekord.merge = merge; + /* Morphing Functions */ + Rekord.Gate = Gate; + Rekord.DiscriminateCollection = DiscriminateCollection; + + /* Morphing Objects */ + Rekord.Filtering = Filtering; + Rekord.Polymorphic = Polymorphic; + /* Array Functions */ Rekord.toArray = toArray; Rekord.indexOf = indexOf; diff --git a/build/rekord.min.js b/build/rekord.min.js index d44da23..7fd5fc1 100644 --- a/build/rekord.min.js +++ b/build/rekord.min.js @@ -3,5 +3,5 @@ },setSummarize:function(e){c(e)?this.summarize=e:f(e)?i(this.fields,e)!==!1?this.summarize=function(t){return R(t)?t[e]:t}:this.summarize=ae(e):this.summarize=function(e){return e.$key()}},sort:function(){this.models.sort()},isSorted:function(){return this.models.isSorted()},clean:function(){var e=this,t=e.models.keys,n=e.models;e.clearAll();for(var i=0;it.max;){var a=n.minModel("$touched");a&&s(a)}t.keepAlive&&n.eachWhere(s,r)}},destroyLocalUncachedModel:function(e,t){var n=this;return e?e.$hasChanges()?(delete e.$saved,n.keyHandler.removeKey(e),e.$trigger(Ye.Events.Detach),!1):(n.destroyModel(e,t),!0):!1},destroyLocalCachedModel:function(e,t){var n=this;return e?e.$hasChanges()?(delete e.$saved,n.keyHandler.removeKey(e),e.$local&&(delete e.$local.$saved,n.keyHandler.removeKey(e.$local)),e.$trigger(Ye.Events.Detach),e.$addOperation(pt),!1):(e.$addOperation(ft),n.destroyModel(e,t),!0):(n.store.remove(t,function(e){e&&Se.debug(Se.Debugs.REMOTE_REMOVE,n,e)}),!1)},destroyLocalModel:function(e){var t=this,n=t.all[e];return t.cache===sn.All?t.destroyLocalCachedModel(n,e):t.destroyLocalUncachedModel(n,e)},loadFinish:function(){var e=this;Ce(function(){for(var t in e.loaded){var n=e.loaded[t];n.$status===Ye.Status.RemovePending?(Se.debug(Se.Debugs.LOCAL_RESUME_DELETE,e,n),n.$addOperation(vt)):(n.$status===Ye.Status.SavePending?(Se.debug(Se.Debugs.LOCAL_RESUME_SAVE,e,n),n.$addOperation(mt)):Se.debug(Se.Debugs.LOCAL_LOAD_SAVED,e,n),e.saveReference(n,t,!0))}}),e.loaded={},e.updated(),e.hasLoad(an.All)&&(0===e.pendingOperations?e.refresh():e.firstRefresh=!0)},hasLoad:function(e){return 0!==(this.load&e)},loadBegin:function(e){function t(t,n){Se.debug(Se.Debugs.LOCAL_LOAD,i,t);for(var s=0;s=Ye.Status.RemovePending},$isSaved:function(){return!!this.$saved},$isSavedLocally:function(){return!!this.$local},$isNew:function(){return!(this.$saved||this.$local)},$touch:function(){this.$db.hasPruning()&&(this.$touched=Jt())},$project:function(e){var t=Qe.parse(this.$db,e);return t.project(this)},$getChanges:function(e){var t=this.$db,n=t.decode(this.$saved,{}),i=e||this,s=t.saveFields;return n?ee(i,n,s,k):i},$hasChanges:function(e){var t=e?this.$local:this.$saved;if(!t)return!0;for(var n=this.$db,i=n.ignoredFields,s=n.decode(t,{}),r=n.saveFields,a=0;aa;a++){var o=s[a],u=r[a];e.has(u)||n.put(u,o)}return n},filter:function(e,t){for(var n=t||new ze,i=this.size(),s=this.values,r=this.keys,a=0;i>a;a++){var o=s[a],u=r[a];e(o,u)&&n.put(u,o)}return n},reverse:function(){return o(this.values),o(this.keys),this.rebuildIndex(),this},isSorted:function(e){return u(e,this.values)},sort:function(e){function t(t,n){for(var s=i.values[Math.floor((n+t)/2)],r=t,o=n;o>=r;){for(;e(i.values[r],s)<0;)r++;for(;e(i.values[o],s)>0;)o--;o>=r&&(a(i.values,r,o),a(i.keys,r,o),r++,o--)}return r}function n(e,i){var s=t(e,i);s-1>e&&n(e,s-1),i>s&&n(s,i)}var i=this,s=this.size()-1;return s>0&&(n(0,s),this.rebuildIndex()),this},rebuildIndex:function(){this.indices={};for(var e=0,t=this.keys.length;t>e;e++)this.indices[this.keys[e]]=e;return this},toObject:function(e){for(var t=e||{},n=this.keys,i=this.values,s=0;s0&&(n=e.substring(0,i),e=e.substring(i+1));for(var s="",r=[],a=["property"],o=[this.database],u=0,h=[],l=function(e){if(e){var n=a[0],i=Qe.TOKEN_HANDLER[n];r.unshift(e),i&&i.post&&h.push(i.post(r,a,o,t))}},c=function(e){var n=Qe.TOKEN_HANDLER[e];a.unshift(e),n&&n.pre&&h.push(n.pre(r,a,o,t))},u=0;u=0;u--)v=h[u](v);this.projections[n]=v},project:function(e){var t={};for(var n in this.projections)t[n]=this.projections[n](e);return t}}),Qe.TOKENS={".":"property","?":"where","|":"filter","#":"resolve","(":"subStart",")":"subEnd","[":"pluckValueStart","]":"pluckValueEnd","{":"pluckObjectStart",":":"pluckObjectDelimiter","}":"pluckObjectEnd","@":"aggregateStart","=":"aggregateProperty"},Qe.TOKEN_HANDLER={property:{post:function(e,t,n,s){var r=e[0],a=n[0];if(!(a instanceof Fe))throw"The property "+r+" can only be taken from a Model";var o=a.relations[r];o&&(o instanceof yt?n.unshift(o.model.Database):n.unshift(o));var u=i(a.fields,r);if(u===!1&&!o)throw"The property "+r+" does not exist as a field or relation on the Model "+a.name;return function(e){return function(t){return R(t)?e(t.$get(r)):null}}}},filter:{post:function(e,t,n,i){var s=e[0],r=Se.Filters[s];if(!r)throw s+" is not a valid filter function";return function(e){return function(t){return R(t)?e(r(t)):null}}}},resolve:{post:function(e,t,n,i){var s=e[0];return function(e){return function(t){if(!R(t))return null;var n=t[s];return c(n)&&(n=n.apply(t)),e(n)}}}},where:{post:function(e,t,n,i){var s=e[0],r=n[0],a=Se.Wheres[s];if(!a)throw s+" is not a valid where expression";if(!(r instanceof Rt))throw s+" where expressions can only be used on relations";return function(e){return function(t){return R(t)?e(t.where(a)):null}}}},aggregateProperty:{post:function(e,t,n,i){var s=e[0],r=e[1],a=n[0];if("aggregateStart"!==t[1])throw"Aggregate function syntax error, a = must follow a @";if(!(a instanceof Et))throw"Aggregate functions like "+r+" from "+a+" can only be used on relations";return function(e){return function(t){return R(t)?e(t[r](s)):null}}}},subEnd:{pre:function(e,t,n,i){var s=e[0],r=n[0];if("subStart"!==t[1])throw"Sub projection syntax error, an ending ) requires a starting (";if(!(r instanceof Et))throw"Sub projections like "+s+" from "+e[1]+" can only be used on relations";if(!r.model.Database.projections[s])throw"The projection "+s+" does not exist on "+r.model.Database.name;return r instanceof yt?function(e){return function(t){return R(t)?e(t.$project(s)):null}}:function(e){return function(t){return R(t)?e(t.project(s)):null}}}},pluckValueEnd:{pre:function(e,t,n,i){var s=e[0],r=n[0];if("pluckValueStart"!==t[1])throw"Pluck value syntax error, an ending ] requires a starting [";if(!(r instanceof Rt))throw"Pluck values like "+s+" from "+e[1]+" can only be used on relations";return function(e){return function(t){return R(t)?e(t.pluck(s)):null}}}},pluckObjectEnd:{pre:function(e,t,n,i){var s=e[0],r=e[1],a=n[0];if("pluckObjectDelimiter"!==t[1]||"pluckObjectStart"!==t[2])throw"Pluck object syntax error, must be {key: value}";if(!(a instanceof Rt))throw"Pluck values like "+s+" from "+e[1]+" can only be used on relations";return function(e){return function(t){return R(t)?e(t.pluck(s,r)):null}}}}},Qe.ALIAS_DELIMITER=":",Qe.parse=function(e,t){var n=t;if(f(t)&&(t=e.projections[t]),E(t)&&(t=new Qe(e,t)),!(t instanceof Qe))throw n+" is not a valid projection";return t},qe.start=function(e){var t=new qe(e);return t.apply(),t},Bt.create(qe,{add:function(e){f(e)&&(e=Se.classes[e]),d(e)&&(e=e.Database),e instanceof Fe&&(this.databases.push(e),this.alls.push({}),this.models.push(new tt(e)))},getApplied:function(){var e=0;return this.each(function(t){t.context===this&&e++}),e/this.databases.length},apply:function(){this.each(this.applyDatabase)},applyDatabase:function(e,t,n,i){e.all=t,e.models=n,e.context=this,e.contextIndex=i},discard:function(){this.each(this.discardDatabase)},discardDatabase:function(e){e.context===this&&(e.all=e.allCached,e.models=e.modelsCached,e.context=null,e.contextIndex=-1)},destroy:function(){this.each(this.destroyDatabase),this.databases.length=0,this.alls.length=0,this.models.length=0},destroyDatabase:function(e,t,n,i){this.discardDatabase(e),this.databases[i]=null,this.alls[i]=null,this.models[i].clear(),this.models[i]=null},clear:function(e){this.alls[e.contextIndex]={}},each:function(e){for(var t=this.databases,n=this.alls,i=this.models,s=0;s=0;n--)i(e,t[n])===!1&&e.unshift(t[n])},isValid:function(e){return R(e)},copyFields:function(e,t,n,i){for(var s=0;s=0&&e=0;a--)qt.splice.call(this,r[a],1);this.trigger(Xe.Events.Removes,[this,s,r]),n||this.sort(t,t,!0)}return s},removeWhere:function(e,n,i,s,r){for(var a=pe(e,n,i),o=s||this.cloneEmpty(),u=[],h=0;h=0;h--)qt.splice.call(this,u[h],1);return this.trigger(Xe.Events.Removes,[this,o,u]),r||this.sort(t,t,!0),o},splice:function(e,n){var i=qt.slice.call(arguments,2),s=qt.splice.apply(this,arguments);return n&&this.trigger(Xe.Events.Removes,[this,s,e,n]),i.length&&this.trigger(Xe.Events.Adds,[this,i,e]),this.sort(t,t,!0),s},reverse:function(){return qt.reverse?qt.reverse.apply(this):o(this),this.trigger(Xe.Events.Updates,[this]),this},indexOf:function(e,t){for(var n=t||C,i=0;i0&&(i=this[s]);return i},maxModel:function(e,t){for(var n=N(e||this.comparator,!0),i=t,s=0;s0&&(r=o)}return r},max:function(e,t,n){for(var i=n||U,s=ce(e),r=t,a=0;a=0;s--){var r=this[s];if(i(r))return r}return null},last:function(e){for(var t=ce(e),n=this.length-1;n>=0;n--){var i=t(this[n]);if(R(i))return i}},aggregate:function(e,t,n,i){for(var s=0;s=e&&(r=0,i++,s.length=e,s=n[i]=n[i]||[]);return 0!==r&&i++,s.length=r,n.length=i,n},contains:function(e,t,n){for(var i=pe(e,t,n),s=0;s=0&&e0},canNext:function(){return this.total()&&this.pageIndexr;r++)this.push(e[n++])},more:function(e){for(var t=this.collection,n=t.length,i=e||1,s=this.pageIndex*this.pageSize,r=s+this.length,a=this.pageSize*i,o=r+a,u=Math.min(n,o);u>r;)this.push(t[r++])},toArray:function(){return this.slice()}}),w(Ze),x(Ze,"change",Ze.Events.Changes),Bt.extend(Xe,et,{bind:En.bind,init:En.init,setFilter:En.setFilter,connect:En.connect,disconnect:En.disconnect,sync:En.sync,clone:En.clone,cloneEmpty:En.cloneEmpty}),Bt.extend(Xe,tt,{init:function(e,t,n){return Bt.props(this,{database:e,map:new ze}),this.map.values=this,this.reset(t,n),this},sort:function(e,t){var n=e?N(e,t):this.comparator;return u(n,this)||(this.map.sort(n),this.trigger(Xe.Events.Sort,[this])),this},buildKeyFromInput:function(e){return this.database.keyHandler.buildKeyFromInput(e)},parseModel:function(e,t){return this.database.parseModel(e,t)},filtered:function(e,t,n){var i=pe(e,t,n);return nt.create(this,i)},subtract:function(e,t){for(var n=t||this.cloneEmpty(),i=0;i=0&&e=0;r--)n.removeAt(s[r]);return this.trigger(Xe.Events.Removes,[this,i,s]),t||this.sort(),i},indexOf:function(e){var n=this.buildKeyFromInput(e),i=this.map.indices[n];return i===t?-1:i},rebuild:function(){this.map.rebuildIndex()},keys:function(){return this.map.keys},reverse:function(){return this.map.reverse(),this.trigger(Xe.Events.Updates,[this]),this},splice:function(e,t){for(var n=qt.slice.call(arguments,2),i=[e,t],s=0;s=0&&e0},$canNext:function(){return this.$getTotal()&&this.$getPageIndex()=0;e--)this.removeModel(i,r[e],n)})}},isRelated:function(e,t){var n=e.$relations[this.name],i=n.related;if(this.isModelArray(t)){for(var s=0;s0}if(R(t)){var r=this.parseModel(t);return r&&i.has(r.$key())}return!1},canRemoveRelated:function(e,t){return!t||!e.$isSaving()},checkSave:function(e,t){e.delaySaving||t||!e.parent.$exists()||this.store!==hn.Model&&this.save!==un.Model||(Se.debug(this.debugAutoSave,this,e),e.parent.$save(this.saveParentCascade,this.saveParentOptions))},handleModel:function(e,t,n){return function(i){var s=e.pending,r=i.$key();(r in s||n)&&(Se.debug(this.debugInitialGrabbed,this,e,i),this.addModel(e,i,t),delete s[r])}},sort:function(e){var t=e.related;e.delaySorting||(Se.debug(this.debugSort,this,e),t.sort(this.comparator),e.parent.$trigger(Ye.Events.RelationUpdate,[this,e]))}}),Se.Relations.belongsTo=$t,$t.Defaults={model:null,lazy:!1,query:!1,store:hn.None,save:un.None,auto:!0,autoCascade:rn.All,autoOptions:null,property:!0,preserve:!0,clearKey:!0,dynamic:!1,local:null,cascade:rn.Local,cascadeRemoveOptions:null,discriminator:"discriminator",discriminators:{},discriminatorToModel:{}},Bt.extend(yt,$t,{type:"belongsTo",debugInit:Se.Debugs.BELONGSTO_INIT,debugClearModel:Se.Debugs.BELONGSTO_CLEAR_MODEL,debugSetModel:Se.Debugs.BELONGSTO_SET_MODEL,debugLoaded:Se.Debugs.BELONGSTO_LOADED,debugClearKey:Se.Debugs.BELONGSTO_CLEAR_KEY,debugUpdateKey:Se.Debugs.BELONGSTO_UPDATE_KEY,debugQuery:Se.Debugs.BELONGSTO_QUERY,debugQueryResults:Se.Debugs.BELONGSTO_QUERY_RESULTS,getDefaults:function(e,t,n){return $t.Defaults},load:Ie(function(e,t,n){var i=e.$relations[this.name]={parent:e,isRelated:this.isRelatedFactory(e),related:null,loaded:!1,onRemoved:function(){Se.debug(Se.Debugs.BELONGSTO_NINJA_REMOVE,this,e,i),e.$remove(this.cascade,this.cascadeRemoveOptions),this.clearRelated(i,!1,!0)},onSaved:function(){Se.debug(Se.Debugs.BELONGSTO_NINJA_SAVE,this,e,i),i.isRelated(i.related)||this.clearRelated(i,!1,!0)}};e.$on(Ye.Events.PostRemove,this.postRemove,this),e.$on(Ye.Events.KeyUpdate,this.onKeyUpdate,this),M(t)&&(t=this.grabInitial(e,this.local),t&&Se.debug(Se.Debugs.BELONGSTO_INITIAL_PULLED,this,e,t)),M(t)?this.query&&(i.query=this.executeQuery(e)):(Se.debug(Se.Debugs.BELONGSTO_INITIAL,this,e,t),this.grabModel(t,this.handleModel(i,n),n))}),sync:function(e,t){var n=e.$relations[this.name],i=this.grabInitial(e,this.local),s=!0,r=!0,a=!0;n&&(M(i)?t&&this.clearRelated(n,s,a):this.grabModel(i,this.handleModel(n,s,r),s))},postRemove:function(e){var t=e.$relations[this.name];t&&(Se.debug(Se.Debugs.BELONGSTO_POSTREMOVE,this,e,t),this.clearModel(t),this.setProperty(t))},onKeyUpdate:function(e,t,n,i){if(this.local===n){var s=e.$relations[this.name];s&&t!==s.related&&(this.clearModel(s,!1,!0),this.setModel(s,t),this.setProperty(s))}}}),Se.Relations.hasOne=bt,bt.Defaults={model:null,lazy:!1,query:!1,store:hn.None,save:un.None,saveCascade:rn.All,saveOptions:null,auto:!0,autoCascade:rn.All,autoOptions:null,property:!0,preserve:!0,clearKey:!0,dynamic:!1,local:null,cascade:rn.All,cascadeRemoveOptions:null,discriminator:"discriminator",discriminators:{},discriminatorToModel:{}},Bt.extend(yt,bt,{type:"hasOne",debugInit:Se.Debugs.HASONE_INIT,debugClearModel:Se.Debugs.HASONE_CLEAR_MODEL,debugSetModel:Se.Debugs.HASONE_SET_MODEL,debugLoaded:Se.Debugs.HASONE_LOADED,debugClearKey:Se.Debugs.HASONE_CLEAR_KEY,debugUpdateKey:Se.Debugs.HASONE_UPDATE_KEY,debugQuery:Se.Debugs.HASONE_QUERY,debugQueryResults:Se.Debugs.HASONE_QUERY_RESULTS,getDefaults:function(e,t,n){return bt.Defaults},load:Ie(function(e,t,n){var i=e.$relations[this.name]={parent:e,isRelated:this.isRelatedFactory(e),related:null,loaded:!1,dirty:!1,saving:!1,child:k(this.local,e.$db.key),onRemoved:function(){Se.debug(Se.Debugs.HASONE_NINJA_REMOVE,this,e,i),this.clearRelated(i,!1,!0)}};e.$on(Ye.Events.PreSave,this.preSave,this),e.$on(Ye.Events.PostRemove,this.postRemove,this),M(t)&&(t=this.grabInitial(e,this.local),t&&Se.debug(Se.Debugs.HASONE_INITIAL_PULLED,this,e,t)),M(t)?this.query&&(i.query=this.executeQuery(e)):(Se.debug(Se.Debugs.HASONE_INITIAL,this,e,t),this.populateInitial(t,i,e),this.grabModel(t,this.handleModel(i,n),n))}),populateInitial:function(e,t,i){if(y(e)&&t.child)for(var s=n(this.local),r=n(this.model.Database.key),a=0;a=0;n--){var i=e[n];i.$remove(this.cascadeRemove,this.cascadeRemoveOptions)}})},this))},handleModelAdded:function(e){return function(t,n){e.isRelated(t)&&(Se.debug(Se.Debugs.HASMANY_NINJA_ADD,this,e,t),this.addModel(e,t,n))}},handleLazyLoad:function(e,t){return function(n){var i=n.filter(e.isRelated);Se.debug(Se.Debugs.HASMANY_LAZY_LOAD,this,e,i),t&&t.call(this,i),i.length?this.bulk(e,function(){for(var t=0;ti)&&(l=i),o()}var l,c=!0,d=!1,f=0;if(E(e)&&0!==e.length)for(var g=0;g1||!E(e)?qt.slice.call(arguments):e;return tt["native"](t,n)}}),D(function(e,t,n){e.at=function(e){return t.models[e]}}),D(function(e,t,n){e.boot=function(e){return E(e)?tt.create(t,e,!0):y(e)?t.putRemoteData(e):e}}),D(function(e,t,n){ -e.clear=function(e){return t.clear(e)}}),D(function(e,t,n){e.collect=function(e){var n=arguments.length>1||!E(e)?qt.slice.call(arguments):e;return tt.create(t,n)}}),D(function(e,t,n){e.count=function(e,n,i){return t.models.countWhere(e,n,i)}}),D(function(e,t,n){e.create=function(e,n,i){var s=y(e)?t.createModel(e):t.instantiate();return s.$save(n,i),s}}),D(function(e,t,n){var i=J(n.dynamic,mn.dynamic);if(!M(i))for(var s in i)Tt(e.prototype,s,i[s])}),D(function(e,t,n){var i=J(n.events,mn.events);if(!M(i)){var s=[],r=[];for(var a in i){var o=i[a],u=fe(a),h=Fe.Events[u],l=Ye.Events[u];h&&Lt(h,o,!1,r),l&&Lt(l,o,!0,s)}Nt(t,r),s.length&&Bt.replace(e,"$init",function(e){return function(){e.apply(this,arguments),Nt(this,s)}})}}),D(function(e,t,n){function s(e){n[e]||(t[e]=u[e])}function r(e){var n=t[e],i=u[e];for(var s in i)s in n||(n[s]=i[s])}function a(e,n){for(var s=u[n||e],r=t[e],a=s.length-1;a>=0;a--){var o=i(r,s[a]);o!==!1&&r.splice(o,1),r.unshift(s[a])}}var o=n.extend||mn.extend;if(d(o)){var u=o.Database,h=u.options;s("keySeparator"),r("defaults"),r("ignoredFields"),s("loadRelations"),s("load"),s("autoRefresh"),s("cache"),s("fullSave"),s("fullPublish"),r("encodings"),r("decodings"),s("summarize"),a("fields"),a("saveFields","fields"),n.comparator||t.setComparator(h.comparator,h.comparatorNullsFirst),n.revision||t.setRevision(h.revision),n.summarize||t.setSummarize(h.summarize);for(var l in u.relations)if(!(l in t.relations)){var c=u.relations[l],f=new c.constructor;f.init(t,l,c.options),f.save&&t.saveFields.push(l),t.relations[l]=f,t.relationNames.push(l)}t.rest=Se.rest(t),t.store=Se.store(t),t.live=Se.live(t)}}),D(function(e,t,n){e.fetch=function(e,n,i,s){var r=t.keyHandler.buildKeyFromInput(e),a=t.get(r);if(a||(a=t.keyHandler.buildObjectFromKey(r),y(e)&&a.$set(e)),c(i)){var o=s||this;a.$once(Ye.Events.RemoteGets,function(){i.call(o,a)})}return a.$refresh(rn.Rest,n),a}}),D(function(e,t,n){e.fetchAll=function(e,n){return t.refresh(e,n),t.models}}),D(function(e,t,n){var i=n.files||mn.files;if(y(i)){if(!Ct())return void Se.trigger(Se.Events.FilesNotSupported);for(var s in i){var r=i[s];f(r)&&(r={type:r}),t.decodings[s]=Rn[r.type](t,r),t.encodings[s]=wt}}}),Se.fileProcessors={},Se.Events.FilesNotSupported="files-not-supported",Se.Events.FileTooLarge="file-too-large",Se.Events.FileWrongType="file-wrong-type",Se.Events.FileOffline="file-offline",Se.addFileProcessor=function(e,t){Se.fileProcessors[e]=t},Se.fileProperties=["lastModifiedDate","name","size","type"];var Rn={text:function(e,t){return Pt("readAsText",Ft,t)},dataURL:function(e,t){return Pt("readAsDataURL",Ft,t)},base64:function(e,t){return Pt("readAsDataURL",kt,t)},resource:function(e,t){return function(e,n,s){var r=It(e),a=Se.fileProcessors[t.processor];if(!a)throw"Processor required for resource files.";if(r!==!1){if(v(t.capacity)&&v(r.size)&&r.size>t.capacity)return void Se.trigger(Se.Events.FileTooLarge,[r,n,s]);if(E(t.types)&&f(r.type)&&i(t.types,r.type)===!1)return void Se.trigger(Se.Events.FileWrongType,[r,n,s]);var o,u=!1;return a.fileToValue(r,n,s,function(e){Ut(n,s,e,r,t),o=xt(a,e,n,s,t),u&&(n[s]=o,Ht(n,t))}),u=!0,o}return y(e)&&e.FILE?void Se.trigger(Se.Events.FileOffline,[e,n,s]):(Ut(n,s,e,null,t),xt(a,e,n,s,t))}}};D(function(e,t,n){e.filtered=function(e,n,i){return t.models.filtered(e,n,i)}}),D(function(e,t,n){e.first=e.find=function(e,n,i){return t.models.firstWhere(e,n,i)}}),D(function(e,t,n){e.findOrCreate=function(n,i,s,r,a){var o=a||this,u=t.get(n),h=!1;return u?(u.$set(n),r&&r.call(o,u,h)):t.grabModel(n,function(t){t?(u=t,u.$set(n),u.$isSaved()||u.$save(i,s)):(u=e.create(n,i,s),h=!0),r&&r.call(o,u,h)}),u}}),D(function(e,t,n){e.get=function(e,n,i){return c(n)?void t.grabModel(e,n,i):t.get(e)}}),D(function(e,t,n){e.grab=function(n,i,s,r){var a=r||this,o=t.get(n);return o?s.call(a,o):t.grabModel(n,function(t){t?s.call(a,t):e.fetch(n,i,s,r)}),o}}),D(function(e,t,n){e.grabAll=function(e,n){var i=n||this,s=t.models;return s.length?e.call(i,s):t.ready(function(){s.length?e.call(i,s):t.refresh(function(){e.call(i,s)})}),s}}),D(function(e,t,n){e.hasTrait=function(e,n){return t.hasTrait(e,n)},e.hasTraits=function(e,n){return t.hasTraits(e,n)}}),D(function(e,t,n){n.keyChanges&&Gt()});var $n=ze.prototype.put,bn=ze.prototype.remove;D(function(e,t,n){var i=J(n.methods,mn.methods);M(i)||Bt.methods(e,i)}),D(function(e,t,n){e.persist=function(t,n,i,s,r){var a=r||this;return e.findOrCreate(t,n,i,function(e,t){t||e.$save(n,i),s&&s.call(a,e)})}}),D(function(e,t,n){e.projection=function(e){return Qe.parse(t,e)}}),D(function(e,t,n){e.ready=function(e,n,i){t.ready(e,n,i)}}),D(function(e,t,n){e.refresh=function(e,n){return t.refresh(e,n)}}),D(function(e,t,n){e.reset=function(e,n){return t.reset(e,n)}}),D(function(e,t,n){e.results=function(e,n,i){return new rt(t,e,i,n,!0).$results}}),D(function(e,t,n){e.search=function(e,n,i,s){return new rt(t,e,n,i,s)}}),D(function(e,t,n){e.searchAt=function(e,n,i,s,r,a,o){var u={page_index:e,page_size:1},h=i?new at(t,n,J(s,u),r):new rt(t,n,s,r),l=new ot;return l.success(a),l.failure(o),h.$run().then(function(t,n,s){l.resolve(s[i?0:e])},function(){l.reject()},function(){l.noline()}),l}}),D(function(e,t,n){e.searchPaged=function(e,n,i,s){return new at(t,e,n,i,s)}}),D(function(e){var t=e.shard||mn.shard;y(t)&&(e.createRest=Se.shard(t))},!0),D(function(e,t,n){var i=J(n.staticMethods,mn.staticMethods);M(i)||Bt.props(e,i)}),D(function(e,t,n){function s(e,t){return y(e)&&y(t)?J(e,t):e||t}function r(e){return $===!0||i($,e)!==!1}function a(e,t){return y(t)?t[e]:t}function o(e){var t=a(e,m);return function(){return jt(new Date,t)}}function u(e,t,n,i){var s=a(n,p),r=jt(e,s);return r||e}function h(e,t,n){var i=a(n,m),s=a(n,R),r=jt(e,i,s);return r||e}function l(e){var n=i(t.fields,e);n===!1&&(t.fields.push(e),t.saveFields.push(e)),!r(e)||e in t.defaults||(t.defaults[e]=o(e)),!p||e in t.encodings||(t.encodings[e]=u),!m||e in t.decodings||(t.decodings[e]=h)}function c(e){l(e),t.ignoredFields[e]=!0}function d(n){l(n),t.ignoredFields[n]=!0,Bt.replace(e,"$save",function(e){return function(){return this[n]=_(t.defaults[n]),e.apply(this,arguments)}})}function v(e,t){switch(e){case"created_at":return c(t);case"updated_at":return d(t);default:return l(t)}}var g=n.timestamps||mn.timestamps,p=s(n.timestampFormat,mn.timestampFormat),m=s(n.timestampType,mn.timestampType),R=s(n.timestampUTC,mn.timestampUTC),$=n.timestampCurrent||mn.timestampCurrent;if(g)if(f(g))v(g,g);else if(E(g))for(var b=0;b1||!E(e)?qt.slice.call(arguments):e;return tt.create(t,n)}}),D(function(e,t,n){e.count=function(e,n,i){return t.models.countWhere(e,n,i)}}),D(function(e,t,n){e.create=function(e,n,i){var s=y(e)?t.createModel(e):t.instantiate();return s.$save(n,i),s}}),D(function(e,t,n){var i=J(n.dynamic,mn.dynamic);if(!M(i))for(var s in i)Tt(e.prototype,s,i[s])}),D(function(e,t,n){var i=J(n.events,mn.events);if(!M(i)){var s=[],r=[];for(var a in i){var o=i[a],u=fe(a),h=Fe.Events[u],l=Ye.Events[u];h&&Lt(h,o,!1,r),l&&Lt(l,o,!0,s)}Nt(t,r),s.length&&Bt.replace(e,"$init",function(e){return function(){e.apply(this,arguments),Nt(this,s)}})}}),D(function(e,t,n){function s(e){n[e]||(t[e]=u[e])}function r(e){var n=t[e],i=u[e];for(var s in i)s in n||(n[s]=i[s])}function a(e,n){for(var s=u[n||e],r=t[e],a=s.length-1;a>=0;a--){var o=i(r,s[a]);o!==!1&&r.splice(o,1),r.unshift(s[a])}}var o=n.extend||mn.extend;if(d(o)){var u=o.Database,h=u.options;s("keySeparator"),r("defaults"),r("ignoredFields"),s("loadRelations"),s("load"),s("autoRefresh"),s("cache"),s("fullSave"),s("fullPublish"),r("encodings"),r("decodings"),s("summarize"),a("fields"),a("saveFields","fields"),n.comparator||t.setComparator(h.comparator,h.comparatorNullsFirst),n.revision||t.setRevision(h.revision),n.summarize||t.setSummarize(h.summarize);for(var l in u.relations)if(!(l in t.relations)){var c=u.relations[l],f=new c.constructor;f.init(t,l,c.options),f.save&&t.saveFields.push(l),t.relations[l]=f,t.relationNames.push(l)}t.rest=Se.rest(t),t.store=Se.store(t),t.live=Se.live(t)}}),D(function(e,t,n){e.fetch=function(e,n,i,s){var r=t.keyHandler.buildKeyFromInput(e),a=t.get(r);if(a||(a=t.keyHandler.buildObjectFromKey(r),y(e)&&a.$set(e)),c(i)){var o=s||this;a.$once(Ye.Events.RemoteGets,function(){i.call(o,a)})}return a.$refresh(rn.Rest,n),a}}),D(function(e,t,n){e.fetchAll=function(e,n){return t.refresh(e,n),t.models}}),D(function(e,t,n){var i=n.files||mn.files;if(y(i)){if(!Ct())return void Se.trigger(Se.Events.FilesNotSupported);for(var s in i){var r=i[s];f(r)&&(r={type:r}),t.decodings[s]=Rn[r.type](t,r),t.encodings[s]=wt}}}),Se.fileProcessors={},Se.Events.FilesNotSupported="files-not-supported",Se.Events.FileTooLarge="file-too-large",Se.Events.FileWrongType="file-wrong-type",Se.Events.FileOffline="file-offline",Se.addFileProcessor=function(e,t){Se.fileProcessors[e]=t},Se.fileProperties=["lastModifiedDate","name","size","type"];var Rn={text:function(e,t){return Pt("readAsText",Ft,t)},dataURL:function(e,t){return Pt("readAsDataURL",Ft,t)},base64:function(e,t){return Pt("readAsDataURL",kt,t)},resource:function(e,t){return function(e,n,s){var r=It(e),a=Se.fileProcessors[t.processor];if(!a)throw"Processor required for resource files.";if(r!==!1){if(v(t.capacity)&&v(r.size)&&r.size>t.capacity)return void Se.trigger(Se.Events.FileTooLarge,[r,n,s]);if(E(t.types)&&f(r.type)&&i(t.types,r.type)===!1)return void Se.trigger(Se.Events.FileWrongType,[r,n,s]);var o,u=!1;return a.fileToValue(r,n,s,function(e){Ut(n,s,e,r,t),o=xt(a,e,n,s,t),u&&(n[s]=o,Ht(n,t))}),u=!0,o}return y(e)&&e.FILE?void Se.trigger(Se.Events.FileOffline,[e,n,s]):(Ut(n,s,e,null,t),xt(a,e,n,s,t))}}};D(function(e,t,n){e.filtered=function(e,n,i){return t.models.filtered(e,n,i)}}),D(function(e,t,n){e.first=e.find=function(e,n,i){return t.models.firstWhere(e,n,i)}}),D(function(e,t,n){e.findOrCreate=function(n,i,s,r,a){var o=a||this,u=t.get(n),h=!1;return u?(u.$set(n),r&&r.call(o,u,h)):t.grabModel(n,function(t){t?(u=t,u.$set(n),u.$isSaved()||u.$save(i,s)):(u=e.create(n,i,s),h=!0),r&&r.call(o,u,h)}),u}}),D(function(e,t,n){e.get=function(e,n,i){return c(n)?void t.grabModel(e,n,i):t.get(e)}}),D(function(e,t,n){e.grab=function(n,i,s,r){var a=r||this,o=t.get(n);return o?s.call(a,o):t.grabModel(n,function(t){t?s.call(a,t):e.fetch(n,i,s,r)}),o}}),D(function(e,t,n){e.grabAll=function(e,n){var i=n||this,s=t.models;return s.length?e.call(i,s):t.ready(function(){s.length?e.call(i,s):t.refresh(function(){e.call(i,s)})}),s}}),D(function(e,t,n){e.hasTrait=function(e,n){return t.hasTrait(e,n)},e.hasTraits=function(e,n){return t.hasTraits(e,n)}}),D(function(e,t,n){n.keyChanges&&Gt()});var $n=ze.prototype.put,bn=ze.prototype.remove;D(function(e,t,n){var i=J(n.methods,mn.methods);M(i)||Bt.methods(e,i)}),D(function(e,t,n){e.persist=function(t,n,i,s,r){var a=r||this;return e.findOrCreate(t,n,i,function(e,t){t||e.$save(n,i),s&&s.call(a,e)})}}),D(function(e,t,n){e.projection=function(e){return Qe.parse(t,e)}}),D(function(e,t,n){e.ready=function(e,n,i){t.ready(e,n,i)}}),D(function(e,t,n){e.refresh=function(e,n){return t.refresh(e,n)}}),D(function(e,t,n){e.reset=function(e,n){return t.reset(e,n)}}),D(function(e,t,n){e.results=function(e,n,i){return new rt(t,e,i,n,!0).$results}}),D(function(e,t,n){e.search=function(e,n,i,s){return new rt(t,e,n,i,s)}}),D(function(e,t,n){e.searchAt=function(e,n,i,s,r,a,o){var u={page_index:e,page_size:1},h=i?new at(t,n,J(s,u),r):new rt(t,n,s,r),l=new ot;return l.success(a),l.failure(o),h.$run().then(function(t,n,s){l.resolve(s[i?0:e])},function(){l.reject()},function(){l.noline()}),l}}),D(function(e,t,n){e.searchPaged=function(e,n,i,s){return new at(t,e,n,i,s)}}),D(function(e){var t=e.shard||mn.shard;y(t)&&(e.createRest=Se.shard(t))},!0),D(function(e,t,n){var i=J(n.staticMethods,mn.staticMethods);M(i)||Bt.props(e,i)}),D(function(e,t,n){function s(e,t){return y(e)&&y(t)?J(e,t):e||t}function r(e){return $===!0||i($,e)!==!1}function a(e,t){return y(t)?t[e]:t}function o(e){var t=a(e,m);return function(){return jt(new Date,t)}}function u(e,t,n,i){var s=a(n,p),r=jt(e,s);return r||e}function h(e,t,n){var i=a(n,m),s=a(n,R),r=jt(e,i,s);return r||e}function l(e){var n=i(t.fields,e);n===!1&&(t.fields.push(e),t.saveFields.push(e)),!r(e)||e in t.defaults||(t.defaults[e]=o(e)),!p||e in t.encodings||(t.encodings[e]=u),!m||e in t.decodings||(t.decodings[e]=h)}function c(e){l(e),t.ignoredFields[e]=!0}function d(n){l(n),t.ignoredFields[n]=!0,Bt.replace(e,"$save",function(e){return function(){return this[n]=_(t.defaults[n]),e.apply(this,arguments)}})}function v(e,t){switch(e){case"created_at":return c(t);case"updated_at":return d(t);default:return l(t)}}var g=n.timestamps||mn.timestamps,p=s(n.timestampFormat,mn.timestampFormat),m=s(n.timestampType,mn.timestampType),R=s(n.timestampUTC,mn.timestampUTC),$=n.timestampCurrent||mn.timestampCurrent;if(g)if(f(g))v(g,g);else if(E(g))for(var b=0;b 1 || !isArray(a) ? Array.prototype.slice.call( arguments ) : a;\n\n return Collection.create( values );\n}\n\n/**\n * Returns an instance of {@link Rekord.Collection} with the initial values\n * passed as arguments to this function.\n *\n * ```javascript\n * Rekord.collectArray(1, 2, 3, 4);\n * Rekord.collectArray([1, 2, 3, 4]); // same as above\n * Rekord.collectArray();\n * Rekord.collectArray([]); // same as above\n * ```\n *\n * @memberof Rekord\n * @param {Any[]|...Any} a\n * The initial values in the collection. You can pass an array of values\n * or any number of arguments.\n * @return {Rekord.Collection} -\n * A newly created instance containing the given values.\n */\nfunction collectArray(a)\n{\n var values = arguments.length > 1 || !isArray(a) ? Array.prototype.slice.call( arguments ) : a;\n\n return Collection.native( values );\n}\n\nfunction swap(a, i, k)\n{\n var t = a[ i ];\n a[ i ] = a[ k ];\n a[ k ] = t;\n}\n\nfunction reverse(arr)\n{\n var n = arr.length;\n var half = Math.floor( n / 2 );\n\n for (var i = 0; i < half; i++)\n {\n swap( arr, n - i - 1, i );\n }\n\n return arr;\n}\n\nfunction isSorted(comparator, array)\n{\n if ( !comparator )\n {\n return true;\n }\n\n for (var i = 0, n = array.length - 1; i < n; i++)\n {\n if ( comparator( array[ i ], array[ i + 1 ] ) > 0 )\n {\n return false;\n }\n }\n\n return true;\n}\n\nfunction isPrimitiveArray(array)\n{\n for (var i = 0; i < array.length; i++)\n {\n var item = array[i];\n\n if ( isValue( item ) )\n {\n return !isObject( item );\n }\n }\n\n return true;\n}\n\n\n// Class.create( construct, methods )\n// Class.extend( parent, construct, override )\n// Class.prop( target, name, value )\n// Class.props( target, properties )\n// Class.method( construct, methodName, method )\n// Class.method( construct, methods )\n// Class.replace( construct, methodName, methodFactory(super) )\n\n// constructor.create( ... )\n// constructor.native( ... ) // for arrays\n// constructor.$constuctor\n// constructor.prototype.$super\n// constructor.$methods\n// constructor.$prop( name, value ) // add to prototype\n// constructor.$method( methodName, method ) // add to prototype\n// constructor.$replace( methodName, methodFactory(super) )\n\nvar Class =\n{\n\n create: function( construct, methods )\n {\n Class.prop( construct, 'create', Class.factory( construct ) );\n Class.build( construct, methods, noop );\n },\n\n extend: function( parent, construct, override )\n {\n var methods = collapse( override, parent.$methods );\n var parentCopy = Class.copyConstructor( parent );\n\n construct.prototype = new parentCopy();\n\n var instanceFactory = Class.factory( construct );\n\n if ( Class.isArray( parent ) )\n {\n var nativeArray = function()\n {\n var arr = [];\n Class.props( arr, methods );\n construct.apply( arr, arguments );\n return arr;\n };\n\n Class.prop( construct, 'native', nativeArray );\n Class.prop( construct, 'create', Settings.nativeArray ? nativeArray : instanceFactory );\n }\n else\n {\n Class.prop( construct, 'create', instanceFactory );\n }\n\n Class.build( construct, methods, parent );\n },\n\n dynamic: function(parent, parentInstance, className, code)\n {\n var DynamicClass = new Function('return function ' + className + code)(); // jshint ignore:line\n\n DynamicClass.prototype = parentInstance;\n\n Class.build( DynamicClass, {}, parent );\n\n return DynamicClass;\n },\n\n build: function(construct, methods, parent)\n {\n Class.prop( construct, '$methods', methods );\n Class.prop( construct, '$prop', Class.propThis );\n Class.prop( construct, '$method', Class.methodThis );\n Class.prop( construct, '$replace', Class.replaceThis );\n Class.prop( construct.prototype, '$super', parent );\n Class.prop( construct.prototype, 'constructor', construct );\n Class.props( construct.prototype, methods );\n },\n\n isArray: function( construct )\n {\n return Array === construct || construct.prototype instanceof Array;\n },\n\n method: function( construct, methodName, method )\n {\n if (construct.$methods)\n {\n construct.$methods[ methodName ] = method;\n }\n\n Class.prop( construct.prototype, methodName, method );\n },\n\n methodThis: function( methodName, method )\n {\n Class.method( this, methodName, method );\n },\n\n methods: function( construct, methods )\n {\n for (var methodName in methods)\n {\n Class.method( construct, methodName, methods[ methodName ] );\n }\n },\n\n prop: (function()\n {\n if (Object.defineProperty)\n {\n return function( target, property, value )\n {\n Object.defineProperty( target, property, {\n configurable: true,\n enumerable: false,\n writable: true,\n value: value\n });\n };\n }\n else\n {\n return function( target, property, value )\n {\n target[ property ] = value;\n };\n }\n })(),\n\n propThis: function( property, value )\n {\n Class.prop( this.prototype, property, value );\n },\n\n props: function( target, properties )\n {\n for (var propertyName in properties)\n {\n Class.prop( target, propertyName, properties[ propertyName ] );\n }\n },\n\n replace: function( target, methodName, methodFactory )\n {\n var existingMethod = target.prototype[ methodName ] || target[ methodName ] || noop;\n\n Class.method( target, methodName, methodFactory( existingMethod ) );\n },\n\n replaceThis: function( methodName, methodFactory )\n {\n Class.replace( this, methodName, methodFactory );\n },\n\n copyConstructor: function(construct)\n {\n function F()\n {\n\n }\n\n F.prototype = construct.prototype;\n\n return F;\n },\n\n factory: function(construct)\n {\n function F(args)\n {\n construct.apply( this, args );\n }\n\n F.prototype = construct.prototype;\n\n return function()\n {\n return new F( arguments );\n };\n }\n\n};\n\n\n/**\n * Determines whether the given variable is defined.\n *\n * ```javascript\n * Rekord.isDefined(); // false\n * Rekord.isDefined(0); // true\n * Rekord.isDefined(true); // true\n * Rekord.isDefined(void 0); // false\n * Rekord.isDefined(undefined); // false\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is defined, otherwise false.\n */\nfunction isDefined(x)\n{\n return x !== undefined;\n}\n\n/**\n * Determines whether the given variable is a function.\n *\n * ```javascript\n * Rekord.isFunction(); // false\n * Rekord.isFunction(parseInt); // true\n * Rekord.isFunction(2); // false\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is a function, otherwise false.\n */\nfunction isFunction(x)\n{\n return !!(x && x.constructor && x.call && x.apply);\n}\n\n/**\n * Determines whether the given variable is a Rekord object. A Rekord object is a\n * constructor for a model and also has a Database variable. A Rekord object is\n * strictly created by the Rekord function.\n *\n * ```javascript\n * var Task = Rekord({\n * name: 'task',\n * fields: ['name', 'done', 'finished_at', 'created_at', 'assigned_to']\n * });\n * Rekord.isRekord( Task ); // true\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is a Rekord object, otherwise false.\n */\nfunction isRekord(x)\n{\n return !!(x && x.Database && isFunction( x ) && x.prototype instanceof Model);\n}\n\n/**\n * Determines whether the given variable is a string.\n *\n * ```javascript\n * Rekord.isString(); // false\n * Rekord.isString('x'): // true\n * Rekord.isString(1); // false\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is a string, otherwise false.\n */\nfunction isString(x)\n{\n return typeof x === 'string';\n}\n\n/**\n * Determines whether the given variable is a valid number. NaN and Infinity are\n * not valid numbers.\n *\n * ```javascript\n * Rekord.isNumber(); // false\n * Rekord.isNumber('x'): // false\n * Rekord.isNumber(1); // true\n * Rekord.isNumber(NaN); // false\n * Rekord.isNumber(Infinity); // true\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is a valid number, otherwise false.\n */\nfunction isNumber(x)\n{\n return typeof x === 'number' && !isNaN(x);\n}\n\n/**\n * Determines whether the given variable is a boolean value.\n *\n * ```javascript\n * Rekord.isBoolean(); // false\n * Rekord.isBoolean('x'): // false\n * Rekord.isBoolean(1); // false\n * Rekord.isBoolean(true); // true\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is a boolean value, otherwise false.\n */\nfunction isBoolean(x)\n{\n return typeof x === 'boolean';\n}\n\n/**\n * Determines whether the given variable is an instance of Date.\n *\n * ```javascript\n * Rekord.isDate(); // false\n * Rekord.isDate('x'): // false\n * Rekord.isDate(1); // false\n * Rekord.isDate(true); // false\n * Rekord.isDate(new Date()); // true\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is an instance of Date, otherwise false.\n */\nfunction isDate(x)\n{\n return x instanceof Date;\n}\n\n/**\n * Determines whether the given variable is an instance of RegExp.\n *\n * ```javascript\n * Rekord.isRegExp(); // false\n * Rekord.isRegExp('x'): // false\n * Rekord.isRegExp(1); // false\n * Rekord.isRegExp(true); // false\n * Rekord.isRegExp(/[xyz]/); // true\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is an instance of RegExp, otherwise false.\n */\nfunction isRegExp(x)\n{\n return x instanceof RegExp;\n}\n\n/**\n * Determines whether the given variable is an instance of Array.\n *\n * ```javascript\n * Rekord.isArray(); // false\n * Rekord.isArray('x'): // false\n * Rekord.isArray(1); // false\n * Rekord.isArray([]); // true\n * Rekord.isArray(Rekord.collect(1, 2, 3)); // true\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is an instance of Array, otherwise false.\n */\nfunction isArray(x)\n{\n return x instanceof Array;\n}\n\n/**\n * Determines whether the given variable is a non-null object. As a note,\n * Arrays are considered objects.\n *\n * ```javascript\n * Rekord.isObject(); // false\n * Rekord.isObject('x'): // false\n * Rekord.isObject(1); // false\n * Rekord.isObject([]); // true\n * Rekord.isObject({}); // true\n * Rekord.isObject(null); // false\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is a non-null object, otherwise false.\n */\nfunction isObject(x)\n{\n return x !== null && typeof x === 'object';\n}\n\n/**\n * Determines whether the given variable is not null and is not undefined.\n *\n * ```javascript\n * Rekord.isValue(); // false\n * Rekord.isValue('x'): // true\n * Rekord.isValue(1); // true\n * Rekord.isValue([]); // true\n * Rekord.isValue({}); // true\n * Rekord.isValue(null); // false\n * Rekord.isValue(void 0); // false\n * Rekord.isValue(undefined); // false\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is non-null and not undefined.\n */\nfunction isValue(x)\n{\n return !!(x !== undefined && x !== null);\n}\n\n/**\n * A function that doesn't perform any operations.\n *\n * @memberof Rekord\n */\nfunction noop()\n{\n\n}\n\n/**\n * Returns the given function with the given context (`this`). This also has the\n * benefits of returning a \"copy\" of the function which makes it ideal for use\n * in listening on/once events and off events.\n *\n * ```javascript\n * var context = {};\n * var func = Rekord.bind( context, function(x) {\n * this.y = x * 2;\n * });\n * func( 4 );\n * context.y; // 8\n * ```\n *\n * @memberof Rekord\n * @param {Object} context\n * The value of `this` for the given function.\n * @param {Function}\n * The function to invoke with the given context.\n * @return {Function} -\n * A new function which is a copy of the given function with a new context.\n */\nfunction bind(context, func)\n{\n return function bindedFunction()\n {\n return func.apply( context, arguments );\n };\n}\n\n/**\n * Generates a UUID using the random number method.\n *\n * @memberof Rekord\n * @return {String} -\n * The generated UUID.\n */\nfunction uuid()\n{\n return (S4()+S4()+\"-\"+S4()+\"-\"+S4()+\"-\"+S4()+\"-\"+S4()+S4()+S4());\n}\n\nfunction S4()\n{\n return (((1+Math.random())*0x10000)|0).toString(16).substring(1);\n}\n\nvar now = Date.now || function()\n{\n return new Date().getTime();\n};\n\nfunction sizeof(x)\n{\n if ( isArray(x) || isString(x) )\n {\n return x.length;\n }\n else if ( isObject(x) )\n {\n var properties = 0;\n\n for (var prop in x) // jshint ignore:line\n {\n properties++;\n }\n\n return properties;\n }\n else if ( isNumber( x ) )\n {\n return x;\n }\n\n return 0;\n}\n\nfunction isEmpty(x)\n{\n if (x === null || x === undefined || x === 0)\n {\n return true;\n }\n if (isArray(x) || isString(x))\n {\n return x.length === 0;\n }\n if (isDate(x))\n {\n return x.getTime() === 0 || isNaN( x.getTime() );\n }\n if (isObject(x))\n {\n for (var prop in x) // jshint ignore:line\n {\n return false;\n }\n\n return true;\n }\n\n return false;\n}\n\nfunction evaluate(x, avoidCopy, context)\n{\n if ( !isValue( x ) )\n {\n return x;\n }\n\n if ( isRekord( x ) )\n {\n return new x();\n }\n if ( isFunction( x ) )\n {\n return context ? x.apply( context ) : x();\n }\n\n return avoidCopy ? x : copy( x );\n}\n\nfunction addPlugin( callback, beforeCreation )\n{\n if ( beforeCreation )\n {\n return Rekord.on( Rekord.Events.Options, callback ); // (options)\n }\n else\n {\n return Rekord.on( Rekord.Events.Plugins, callback ); // (model, db, options)\n }\n}\n\n\n /**\n * A string, a function, or an array of mixed values.\n *\n * ```javascript\n * 'age' // age property of an object\n * '-age' // age property of an object, ordering reversed\n * function(a, b) {} // a function which compares two values\n * ['age', 'done'] // age property of an object, and when equal, the done value\n * 'creator.name' // name sub-property of creator property\n * '{creator.name}, {age}' // formatted string\n * ```\n *\n * @typedef {String|comparisonCallback|Array} comparatorInput\n */\n\n\nvar Comparators = {};\n\nfunction saveComparator(name, comparatorInput, nullsFirst)\n{\n var comparator = createComparator( comparatorInput, nullsFirst );\n\n Comparators[ name ] = comparator;\n\n return comparator;\n}\n\nfunction addComparator(second, comparatorInput, nullsFirst)\n{\n var first = createComparator( comparatorInput, nullsFirst );\n\n if ( !isFunction( second ) )\n {\n return first;\n }\n\n return function compareCascading(a, b)\n {\n var d = first( a, b );\n\n return d !== 0 ? d : second( a, b );\n };\n}\n\n/**\n * Creates a function which compares two values.\n *\n * @memberof Rekord\n * @param {comparatorInput} comparator\n * The input which creates a comparison function.\n * @param {Boolean} [nullsFirst=false] -\n * True if null values should be sorted first.\n * @return {comparisonCallback}\n */\nfunction createComparator(comparator, nullsFirst)\n{\n if ( isFunction( comparator ) )\n {\n return comparator;\n }\n else if ( isString( comparator ) )\n {\n if ( comparator in Comparators )\n {\n return Comparators[ comparator ];\n }\n\n if ( comparator.charAt(0) === '-' )\n {\n var parsed = createComparator( comparator.substring( 1 ), !nullsFirst );\n\n return function compareObjectsReversed(a, b)\n {\n return -parsed( a, b );\n };\n }\n else if ( isFormatInput( comparator ) )\n {\n var formatter = createFormatter( comparator );\n\n return function compareFormatted(a, b)\n {\n var af = formatter( a );\n var bf = formatter( b );\n\n return af.localeCompare( bf );\n };\n }\n else if ( isParseInput( comparator ) )\n {\n var parser = createParser( comparator );\n\n return function compareExpression(a, b)\n {\n var ap = parser( a );\n var bp = parser( b );\n\n return compare( ap, bp, nullsFirst );\n };\n }\n else\n {\n return function compareObjects(a, b)\n {\n var av = isValue( a ) ? a[ comparator ] : a;\n var bv = isValue( b ) ? b[ comparator ] : b;\n\n return compare( av, bv, nullsFirst );\n };\n }\n }\n else if ( isArray( comparator ) )\n {\n var parsedChain = [];\n\n for (var i = 0; i < comparator.length; i++)\n {\n parsedChain[ i ] = createComparator( comparator[ i ], nullsFirst );\n }\n\n return function compareObjectsCascade(a, b)\n {\n var d = 0;\n\n for (var i = 0; i < parsedChain.length && d === 0; i++)\n {\n d = parsedChain[ i ]( a, b );\n }\n\n return d;\n };\n }\n\n return null;\n}\n\n\n/**\n * A function for comparing two values and determine whether they're considered\n * equal.\n *\n * @callback equalityCallback\n * @param {Any} a -\n * The first value to test.\n * @param {Any} b -\n * The second value to test.\n * @return {Boolean} -\n * Whether or not the two values are considered equivalent.\n * @see Rekord.equals\n * @see Rekord.equalsStrict\n * @see Rekord.equalsCompare\n */\n\n /**\n * A function for comparing two values to determine if one is greater or lesser\n * than the other or if they're equal.\n *\n * ```javascript\n * comparisonCallback( a, b ) < 0 // a < b\n * comparisonCallback( a, b ) > 0 // a > b\n * comparisonCallback( a, b ) == 0 // a == b\n * ```\n *\n * @callback comparisonCallback\n * @param {Any} a -\n * The first value to test.\n * @param {Any} b -\n * The second value to test.\n * @return {Number} -\n * 0 if the two values are considered equal, a negative value if `a` is\n * considered less than `b`, and a positive value if `a` is considered\n * greater than `b`.\n * @see Rekord.compare\n * @see Rekord.compareNumbers\n */\n\nfunction equalsStrict(a, b)\n{\n return a === b;\n}\n\nfunction equalsWeak(a, b)\n{\n return a == b; // jshint ignore:line\n}\n\nfunction equalsCompare(a, b)\n{\n return compare( a, b ) === 0;\n}\n\nfunction equals(a, b)\n{\n if (a === b)\n {\n return true;\n }\n if (a === null || b === null)\n {\n return false;\n }\n if (a !== a && b !== b)\n {\n return true; // NaN === NaN\n }\n\n var at = typeof a;\n var bt = typeof b;\n var ar = isRegExp(a);\n var br = isRegExp(b);\n\n if (at === 'string' && br)\n {\n return b.test(a);\n }\n if (bt === 'string' && ar)\n {\n return a.test(b);\n }\n\n if (at !== bt)\n {\n return false;\n }\n\n var aa = isArray(a);\n var ba = isArray(b);\n if (aa !== ba)\n {\n return false;\n }\n\n if (aa)\n {\n if (a.length !== b.length)\n {\n return false;\n }\n\n for (var i = 0; i < a.length; i++)\n {\n if (!equals(a[i], b[i]))\n {\n return false;\n }\n }\n\n return true;\n }\n\n if (isDate(a))\n {\n return isDate(b) && equals( a.getTime(), b.getTime() );\n }\n if (ar)\n {\n return br && a.toString() === b.toString();\n }\n\n if (at === 'object')\n {\n for (var ap in a)\n {\n if (ap.charAt(0) !== '$' && !isFunction(a[ap]))\n {\n if (!(ap in b) || !equals(a[ap], b[ap]))\n {\n return false;\n }\n }\n }\n\n for (var bp in b)\n {\n if (bp.charAt(0) !== '$' && !isFunction(b[bp]))\n {\n if (!(bp in a))\n {\n return false;\n }\n }\n }\n\n return true;\n }\n\n return false;\n}\n\nfunction compareNumbers(a, b)\n{\n return (a === b ? 0 : (a < b ? -1 : 1));\n}\n\nfunction compare(a, b, nullsFirst)\n{\n if (a == b) // jshint ignore:line\n {\n return 0;\n }\n\n var av = isValue( a );\n var bv = isValue( b );\n\n if (av !== bv)\n {\n return (av && !nullsFirst) || (bv && nullsFirst) ? -1 : 1;\n }\n\n if (isDate(a))\n {\n a = a.getTime();\n }\n if (isDate(b))\n {\n b = b.getTime();\n }\n if (isNumber(a) && isNumber(b))\n {\n return compareNumbers(a, b);\n }\n if (isArray(a) && isArray(b))\n {\n return compareNumbers(a.length, b.length);\n }\n if (isBoolean(a) && isBoolean(b))\n {\n return (a ? -1 : 1);\n }\n\n return (a + '').localeCompare(b + '');\n}\n\n\nfunction addEventFunction(target, functionName, events, secret)\n{\n var on = secret ? '$on' : 'on';\n var off = secret ? '$off' : 'off';\n\n var eventFunction = function(callback, context)\n {\n var subject = this;\n var unlistened = false;\n\n function listener()\n {\n var result = callback.apply( context || subject, arguments );\n\n if ( result === false )\n {\n unlistener();\n }\n }\n\n function unlistener()\n {\n if ( !unlistened )\n {\n subject[ off ]( events, listener );\n unlistened = true;\n }\n }\n\n subject[ on ]( events, listener );\n\n return unlistener;\n };\n\n if (target.$methods)\n {\n Class.method( target, functionName, eventFunction );\n }\n else\n {\n Class.prop( target, functionName, eventFunction );\n }\n}\n\nfunction EventNode(before, callback, context, type, group)\n{\n this.next = before ? before : this;\n this.prev = before ? before.prev : this;\n\n if ( before )\n {\n before.prev.next = this;\n before.prev = this;\n }\n\n this.callback = callback;\n this.context = context;\n this.type = type;\n this.group = group || 0;\n}\n\nEventNode.Types =\n{\n Persistent: 1,\n Once: 2,\n After: 4\n};\n\nClass.create( EventNode,\n{\n remove: function()\n {\n var next = this.next;\n var prev = this.prev;\n\n if ( next !== this )\n {\n prev.next = next;\n next.prev = prev;\n this.next = this.prev = this;\n }\n },\n\n hasType: function(type)\n {\n return (this.type & type) !== 0;\n },\n\n trigger: function(group, args, after)\n {\n var type = this.type;\n var isAfter = this.hasType( EventNode.Types.After );\n\n if ( this.group !== group )\n {\n if ( (after && isAfter) || !isAfter )\n {\n this.group = group;\n this.callback.apply( this.context, args );\n }\n\n if ( this.hasType( EventNode.Types.Once ) )\n {\n this.remove();\n }\n }\n }\n});\n\n/**\n * Adds functions to the given object (or prototype) so you can listen for any\n * number of events on the given object, optionally once. Listeners can be\n * removed later.\n *\n * The following methods will be added to the given target:\n *\n * ```\n * target.on( events, callback, [context] )\n * target.once( events, callback, [context] )\n * target.after( events, callback, [context] )\n * target.off( events, callback )\n * target.trigger( events, [a, b, c...] )\n * ```\n *\n * Where...\n * - `events` is a string of space delimited events.\n * - `callback` is a function to invoke when the event is triggered.\n * - `context` is an object that should be the `this` when the callback is\n * invoked. If no context is given the default value is the object which has\n * the trigger function that was invoked.\n *\n * @memberof Rekord\n * @param {Object} [target] -\n * The object to add `on`, `once`, `off`, and `trigger` functions to.\n * @param {Boolean} [secret=false] -\n * If true - the functions will be prefixed with `$`.\n */\nfunction addEventful(target, secret)\n{\n\n var triggerId = 0;\n\n /**\n * A mixin which adds `on`, `once`, `after`, and `trigger` functions to\n * another object.\n *\n * @class Eventful\n * @memberof Rekord\n * @see Rekord.addEventful\n */\n\n /**\n * A mixin which adds `$on`, `$once`, `$after`, and `$trigger` functions to\n * another object.\n *\n * @class Eventful$\n * @memberof Rekord\n * @see Rekord.addEventful\n */\n\n // Adds a listener to $this\n function onListeners($this, eventsInput, callback, context, type)\n {\n if ( !isFunction( callback ) )\n {\n return noop;\n }\n\n var callbackContext = context || $this;\n var events = toArray( eventsInput, ' ' );\n var listeners = $this.$$on;\n\n if ( !listeners )\n {\n Class.prop( $this, '$$on', listeners = {} );\n }\n\n var nodes = [];\n\n for (var i = 0; i < events.length; i++)\n {\n var eventName = events[ i ];\n var eventListeners = listeners[ eventName ];\n\n if ( !eventListeners )\n {\n eventListeners = listeners[ eventName ] = new EventNode();\n }\n\n nodes.push( new EventNode( eventListeners, callback, callbackContext, type, triggerId ) );\n }\n\n return function ignore()\n {\n for (var i = 0; i < nodes.length; i++)\n {\n nodes[ i ].remove();\n }\n\n nodes.length = 0;\n };\n }\n\n /**\n * Listens for every occurrence of the given events and invokes the callback\n * each time any of them are triggered.\n *\n * @method on\n * @memberof Rekord.Eventful#\n * @param {String|Array} events -\n * The event or events to listen to.\n * @param {Function} callback -\n * The function to invoke when any of the events are invoked.\n * @param {Object} [context] -\n * The value of `this` when the callback is invoked. If not specified, the\n * reference of the object this function exists on will be `this`.\n * @return {Function} -\n * A function to invoke to stop listening to all of the events given.\n */\n\n /**\n * Listens for every occurrence of the given events and invokes the callback\n * each time any of them are triggered.\n *\n * @method $on\n * @memberof Rekord.Eventful$#\n * @param {String|Array} events -\n * The event or events to listen to.\n * @param {Function} callback -\n * The function to invoke when any of the events are invoked.\n * @param {Object} [context] -\n * The value of `this` when the callback is invoked. If not specified, the\n * reference of the object this function exists on will be `this`.\n * @return {Function} -\n * A function to invoke to stop listening to all of the events given.\n */\n\n function on(events, callback, context)\n {\n return onListeners( this, events, callback, context, EventNode.Types.Persistent );\n }\n\n /**\n * Listens for the first of the given events to be triggered and invokes the\n * callback once.\n *\n * @method once\n * @memberof Rekord.Eventful#\n * @param {String|Array} events -\n * The event or events to listen to.\n * @param {Function} callback -\n * The function to invoke when any of the events are invoked.\n * @param {Object} [context] -\n * The value of `this` when the callback is invoked. If not specified, the\n * reference of the object this function exists on will be `this`.\n * @return {Function} -\n * A function to invoke to stop listening to all of the events given.\n */\n\n /**\n * Listens for the first of the given events to be triggered and invokes the\n * callback once.\n *\n * @method $once\n * @memberof Rekord.Eventful$#\n * @param {String|Array} events -\n * The event or events to listen to.\n * @param {Function} callback -\n * The function to invoke when any of the events are invoked.\n * @param {Object} [context] -\n * The value of `this` when the callback is invoked. If not specified, the\n * reference of the object this function exists on will be `this`.\n * @return {Function} -\n * A function to invoke to stop listening to all of the events given.\n */\n\n function once(events, callback, context)\n {\n return onListeners( this, events, callback, context, EventNode.Types.Once );\n }\n\n function after(events, callback, context)\n {\n return onListeners( this, events, callback, context, EventNode.Types.After );\n }\n\n // Removes a listener from an array of listeners.\n function offListeners(listeners, event, callback)\n {\n if (listeners && event in listeners)\n {\n var eventListeners = listeners[ event ];\n var next, node = eventListeners.next;\n\n while (node !== eventListeners)\n {\n next = node.next;\n\n if (node.callback === callback)\n {\n node.remove();\n }\n\n node = next;\n }\n }\n }\n\n // Deletes a property from the given object if it exists\n function deleteProperty(obj, prop)\n {\n if ( obj && prop in obj )\n {\n delete obj[ prop ];\n }\n }\n\n /**\n * Stops listening for a given callback for a given set of events.\n *\n * **Examples:**\n *\n * target.off(); // remove all listeners\n * target.off('a b'); // remove all listeners on events a & b\n * target.off(['a', 'b']); // remove all listeners on events a & b\n * target.off('a', x); // remove listener x from event a\n *\n * @method off\n * @for addEventful\n * @param {String|Array|Object} [eventsInput]\n * @param {Function} [callback]\n * @chainable\n */\n function off(eventsInput, callback)\n {\n // Remove ALL listeners\n if ( !isDefined( eventsInput ) )\n {\n deleteProperty( this, '$$on' );\n }\n else\n {\n var events = toArray( eventsInput, ' ' );\n\n // Remove listeners for given events\n if ( !isFunction( callback ) )\n {\n for (var i = 0; i < events.length; i++)\n {\n deleteProperty( this.$$on, events[i] );\n }\n }\n // Remove specific listener\n else\n {\n for (var i = 0; i < events.length; i++)\n {\n offListeners( this.$$on, events[i], callback );\n }\n }\n }\n\n return this;\n }\n\n // Triggers listeneers for the given event\n function triggerListeners(listeners, event, args)\n {\n if (listeners && event in listeners)\n {\n var eventListeners = listeners[ event ];\n var triggerGroup = ++triggerId;\n var next, node = eventListeners.next;\n\n while (node !== eventListeners)\n {\n next = node.next;\n node.trigger( triggerGroup, args, false );\n node = next;\n }\n\n node = eventListeners.next;\n\n while (node !== eventListeners)\n {\n next = node.next;\n node.trigger( triggerGroup, args, true );\n node = next;\n }\n }\n }\n\n /**\n * Triggers a single event optionally passing an argument to any listeners.\n *\n * @method trigger\n * @for addEventful\n * @param {String} eventsInput\n * @param {Array} args\n * @chainable\n */\n function trigger(eventsInput, args)\n {\n try\n {\n var events = toArray( eventsInput, ' ' );\n\n for (var i = 0; i < events.length; i++)\n {\n triggerListeners( this.$$on, events[ i ], args );\n }\n }\n catch (ex)\n {\n Rekord.trigger( Rekord.Events.Error, [ex] );\n }\n\n return this;\n }\n\n var methods = null;\n\n if ( secret )\n {\n methods = {\n $on: on,\n $once: once,\n $after: after,\n $off: off,\n $trigger: trigger\n };\n }\n else\n {\n methods = {\n on: on,\n once: once,\n after: after,\n off: off,\n trigger: trigger\n };\n }\n\n if ( target.$methods )\n {\n Class.methods( target, methods );\n }\n else\n {\n Class.props( target, methods );\n }\n}\n\n\n// Given two objects, merge src into dst.\n// - If a property in src has a truthy value in ignoreMap then skip merging it.\n// - If a property exists in src and not in dst, the property is added to dst.\n// - If an array property exists in src and in dst, the src elements are added to dst.\n// - If an array property exists in dst and a non array value exists in src, added the value to the dst array.\n// - If a property in dst is an object, try to merge the property from src into it.\n// - If a property exists in dst that is not an object or array, replace it with the value in src.\nfunction merge(dst, src, ignoreMap)\n{\n if (isObject( dst ) && isObject( src ))\n {\n for (var prop in src)\n {\n if (!ignoreMap || !ignoreMap[ prop ])\n {\n var adding = src[ prop ];\n\n if (prop in dst)\n {\n var existing = dst[ prop ];\n\n if (isArray( existing ))\n {\n if (isArray( adding ))\n {\n existing.push.apply( existing, adding );\n }\n else\n {\n existing.push( adding );\n }\n }\n else if (isObject( existing ))\n {\n merge( existing, adding, ignoreMap );\n }\n else\n {\n dst[ prop ] = copy( adding, true );\n }\n }\n else\n {\n dst[ prop ] = copy( adding, true );\n }\n }\n }\n }\n\n return dst;\n}\n\n\n\nfunction applyOptions( target, options, defaults, secret )\n{\n options = options || {};\n\n for (var defaultProperty in defaults)\n {\n var defaultValue = defaults[ defaultProperty ];\n var option = options[ defaultProperty ];\n var valued = isValue( option );\n\n if ( !valued && defaultValue === undefined )\n {\n throw defaultProperty + ' is a required option';\n }\n else if ( valued )\n {\n target[ defaultProperty ] = option;\n }\n else\n {\n target[ defaultProperty ] = copy( defaultValue );\n }\n }\n\n for (var optionProperty in options)\n {\n if ( !(optionProperty in defaults) )\n {\n target[ optionProperty ] = options[ optionProperty ];\n }\n }\n\n if ( secret )\n {\n target.$options = options;\n }\n else\n {\n target.options = options;\n }\n}\n\n/**\n * Determines whether the properties on one object equals the properties on\n * another object.\n *\n * @memberof Rekord\n * @param {Object} test -\n * The object to test for matching.\n * @param {String|String[]} testFields -\n * The property name or array of properties to test for equality on `test`.\n * @param {Object} expected -\n * The object with the expected values.\n * @param {String|String[]} expectedFields -\n * The property name or array of properties to test for equality on `expected`.\n * @param {equalityCallback} [equals] -\n * The equality function which compares two values and returns whether they\n * are considered equivalent.\n * @return {Boolean} -\n * True if the `testFields` properties on `test` are equivalent to the\n * `expectedFields` on `expected` according to the `equals` function.\n */\nfunction propsMatch(test, testFields, expected, expectedFields, equals)\n{\n var equality = equals || Rekord.equals;\n\n if ( isString( testFields ) ) // && isString( expectedFields )\n {\n return equality( test[ testFields ], expected[ expectedFields ] );\n }\n else // if ( isArray( testFields ) && isArray( expectedFields ) )\n {\n for (var i = 0; i < testFields.length; i++)\n {\n var testProp = testFields[ i ];\n var expectedProp = expectedFields[ i ];\n\n if ( !equality( test[ testProp ], expected[ expectedProp ] ) )\n {\n return false;\n }\n }\n\n return true;\n }\n\n return false;\n}\n\n// Determines whether the given model has the given fields\nfunction hasFields(model, fields, exists)\n{\n if ( isArray( fields ) )\n {\n for (var i = 0; i < fields.length; i++)\n {\n if ( !exists( model[ fields[ i ] ] ) )\n {\n return false;\n }\n }\n\n return true;\n }\n else // isString( fields )\n {\n return exists( model[ fields ] );\n }\n}\n\nfunction clearFieldsReturnChanges(target, targetFields)\n{\n var changes = false;\n\n if ( isArray( targetFields ) )\n {\n for (var i = 0; i < targetFields.length; i++)\n {\n var targetField = targetFields[ i ];\n\n if ( target[ targetField ] )\n {\n target[ targetField ] = null;\n changes = true;\n }\n }\n }\n else\n {\n if ( target[ targetFields ] )\n {\n target[ targetFields ] = null;\n changes = true;\n }\n }\n\n return changes;\n}\n\nfunction updateFieldsReturnChanges(target, targetFields, source, sourceFields)\n{\n var changes = false;\n\n if ( isArray( targetFields ) ) // && isArray( sourceFields )\n {\n for (var i = 0; i < targetFields.length; i++)\n {\n var targetField = targetFields[ i ];\n var targetValue = target[ targetField ];\n var sourceField = sourceFields[ i ];\n var sourceValue = source[ sourceField ];\n\n if ( !equals( targetValue, sourceValue ) )\n {\n target[ targetField ] = copy( sourceValue );\n changes = true;\n }\n }\n }\n else\n {\n var targetValue = target[ targetFields ];\n var sourceValue = source[ sourceFields ];\n\n if ( !equals( targetValue, sourceValue ) )\n {\n target[ targetFields ] = copy( sourceValue );\n changes = true;\n }\n }\n\n return changes;\n}\n\n\nfunction grab(obj, props, copyValues)\n{\n var grabbed = {};\n\n for (var i = 0; i < props.length; i++)\n {\n var p = props[ i ];\n\n if ( p in obj )\n {\n grabbed[ p ] = copyValues ? copy( obj[ p ] ) : obj[ p ];\n }\n }\n\n return grabbed;\n}\n\nfunction pull(obj, props, copyValues)\n{\n if ( isString( props ) )\n {\n var pulledValue = obj[ props ];\n\n return copyValues ? copy( pulledValue ) : pulledValue;\n }\n else // isArray( props )\n {\n var pulled = [];\n\n for (var i = 0; i < props.length; i++)\n {\n var p = props[ i ];\n var pulledValue = obj[ p ];\n\n pulled.push( copyValues ? copy( pulledValue ) : pulledValue );\n }\n\n return pulled;\n }\n}\n\nfunction transfer(from, to)\n{\n for (var prop in from)\n {\n to[ prop ] = from[ prop ];\n }\n\n return to;\n}\n\nfunction collapse()\n{\n var target = {};\n\n for (var i = 0; i < arguments.length; i++)\n {\n var a = arguments[ i ];\n\n if ( isObject( a ) )\n {\n for (var prop in a)\n {\n if ( !(prop in target) )\n {\n target[ prop ] = a[ prop ];\n }\n }\n }\n }\n\n return target;\n}\n\nfunction clean(x)\n{\n for (var prop in x)\n {\n if ( prop.charAt(0) === '$' )\n {\n delete x[ prop ];\n }\n }\n\n return x;\n}\n\nfunction cleanFunctions(x)\n{\n for (var prop in x)\n {\n if ( isFunction( x[prop] ) )\n {\n delete x[ prop ];\n }\n }\n\n return x;\n}\n\nfunction copy(x, copyHidden)\n{\n if (x === null || x === undefined || typeof x !== 'object' || isFunction(x) || isRegExp(x))\n {\n return x;\n }\n\n if (isArray(x))\n {\n var c = [];\n\n for (var i = 0; i < x.length; i++)\n {\n c.push( copy(x[i], copyHidden) );\n }\n\n return c;\n }\n\n if (isDate(x))\n {\n return new Date( x.getTime() );\n }\n\n var c = {};\n\n for (var prop in x)\n {\n if (copyHidden || prop.charAt(0) !== '$')\n {\n c[ prop ] = copy( x[prop], copyHidden );\n }\n }\n\n return c;\n}\n\nfunction diff(curr, old, props, comparator)\n{\n var d = {};\n\n for (var i = 0; i < props.length; i++)\n {\n var p = props[ i ];\n\n if (!comparator( curr[ p ], old[ p ] ) )\n {\n d[ p ] = copy( curr[ p ] );\n }\n }\n\n return d;\n}\n\n\nfunction isParseInput(x)\n{\n return x.indexOf('.') !== -1 || x.indexOf('[') !== -1 || x.indexOf('(') !== -1;\n}\n\nfunction parse(expr, base)\n{\n return createParser( expr )( base );\n}\n\nparse.REGEX = /([\\w$]+)/g;\n\nfunction createParser(expr)\n{\n var regex = parse.REGEX;\n var nodes = [];\n var match = null;\n\n while ((match = regex.exec( expr )) !== null)\n {\n nodes.push( match[ 1 ] );\n }\n\n return function(base)\n {\n for (var i = 0; i < nodes.length && base !== undefined; i++)\n {\n var n = nodes[ i ];\n\n if ( isObject( base ) )\n {\n base = evaluate( base[ n ], true, base );\n }\n }\n\n return base;\n };\n}\n\nfunction isFormatInput(x)\n{\n return x.indexOf('{') !== -1;\n}\n\nfunction format(template, base)\n{\n return createFormatter( template )( base );\n}\n\nformat.REGEX = /[\\{\\}]/;\n\nfunction createFormatter(template)\n{\n // Every odd element in parts is a parse expression\n var parts = template.split( format.REGEX );\n\n for (var i = 1; i < parts.length; i += 2 )\n {\n parts[ i ] = createParser( parts[ i ] );\n }\n\n return function formatter(base)\n {\n var formatted = '';\n\n for (var i = 0; i < parts.length; i++)\n {\n if ( (i & 1) === 0 )\n {\n formatted += parts[ i ];\n }\n else\n {\n var parsed = parts[ i ]( base );\n\n formatted += isValue( parsed ) ? parsed : '';\n }\n }\n\n return formatted;\n };\n}\n\nfunction parseDate(x, utc)\n{\n if ( isString( x ) )\n {\n if ( Date.parse )\n {\n x = Date.parse( x );\n }\n\n if ( !isNumber( x ) )\n {\n x = new Date( x );\n }\n }\n if ( isNumber( x ) )\n {\n x = new Date( x );\n }\n if ( isDate( x ) && isNumber( x.getTime() ) )\n {\n if ( utc )\n {\n x = new Date( x.getUTCFullYear(), x.getUTCMonth(), x.getUTCDate(), x.getUTCHours(), x.getUTCMinutes(), x.getUTCSeconds() );\n }\n\n return x;\n }\n\n return false;\n}\n\n\n\n/**\n * A function for resolving a value from a given value. Typically used to\n * transform an object into one of it's properties.\n *\n * @callback propertyResolverCallback\n * @param {Any} model -\n * The model to use to resolve a value.\n * @return {Any} -\n * The resolved value.\n * @see Rekord.createPropertyResolver\n */\n\n\n/**\n * An expression which resolves a value from another value.\n *\n * ```javascript\n * // {age: 6, name: 'x', user: {first: 'tom'}}\n * 'age' // age property of an object\n * 'user.first' // sub property\n * '{age}, {user.first}' // a formatted string built from object values\n * function(a) {} // a function which returns a value itself\n * ['age', 'name'] // multiple properties resolves to an array of values\n * {age:null, user:'first'} // multiple properties including a sub property returns an object of values\n * ```\n *\n * @typedef {String|Function|Array|Object} propertyResolverInput\n */\n\nvar NumberResolvers = {};\n\nfunction saveNumberResolver(name, numbers, invalidValue)\n{\n var resolver = createNumberResolver( numbers, invalidValue );\n\n NumberResolvers[ name ] = resolver;\n\n return resolver;\n}\n\nfunction createNumberResolver(numbers, invalidValue)\n{\n var resolver = createPropertyResolver( numbers );\n\n if ( isString( numbers ) && numbers in NumberResolvers )\n {\n return NumberResolvers[ numbers ];\n }\n\n return function resolveNumber(model)\n {\n var parsed = parseFloat( resolver( model ) );\n\n return isNaN( parsed ) ? invalidValue : parsed;\n };\n}\n\nvar PropertyResolvers = {};\n\nfunction savePropertyResolver(name, properties)\n{\n var resolver = createPropertyResolver( properties );\n\n PropertyResolvers[ name ] = resolver;\n\n return resolver;\n}\n\n/**\n * Creates a function which resolves a value from another value given an\n * expression. This is often used to get a property value of an object.\n *\n * ```javascript\n * // x = {age: 6, name: 'tom', user: {first: 'jack'}}\n * createPropertyResolver()( x ) // x\n * createPropertyResolver( 'age' )( x ) // 6\n * createPropertyResolver( 'user.first' )( x ) // 'jack'\n * createPropertyResolver( '{name} & {user.first}')( x ) // 'tom & jack'\n * createPropertyResolver( ['name', 'age'] )( x ) // ['tom', 6]\n * createPropertyResolver( {age:null, user:'first'})( x ) // {age: 6, user:'jack'}\n * ```\n *\n * @memberof Rekord\n * @param {propertyResolverInput} [properties] -\n * The expression which converts one value into another.\n * @return {propertyResolverCallback} -\n * A function to take values and resolve new ones.\n */\nfunction createPropertyResolver(properties)\n{\n if ( isFunction( properties ) )\n {\n return properties;\n }\n else if ( isString( properties ) )\n {\n if ( properties in PropertyResolvers )\n {\n return PropertyResolvers[ properties ];\n }\n\n if ( isFormatInput( properties ) )\n {\n return createFormatter( properties );\n }\n else if ( isParseInput( properties ) )\n {\n return createParser( properties );\n }\n else\n {\n return function resolveProperty(model)\n {\n return model ? model[ properties ] : undefined;\n };\n }\n }\n else if ( isArray( properties ) )\n {\n return function resolveProperties(model)\n {\n return pull( model, properties );\n };\n }\n else if ( isObject( properties ) )\n {\n var propsArray = [];\n var propsResolver = [];\n\n for (var prop in properties)\n {\n propsArray.push( prop );\n propsResolver.push( createPropertyResolver( properties[ prop ] ) );\n }\n\n return function resolvePropertyObject(model)\n {\n var resolved = {};\n\n for (var i = 0; i < propsArray.length; i++)\n {\n var prop = propsArray[ i ];\n\n resolved[ prop ] = propsResolver[ i ]( model[ prop ] );\n }\n\n return resolved;\n };\n }\n else\n {\n return function resolveNone(model)\n {\n return model;\n };\n }\n}\n\n\nvar Settings = global.RekordSettings || win.RekordSettings || {};\n\nif ( win.document && win.document.currentScript )\n{\n var script = win.document.currentScript;\n\n if (script.getAttribute('native-array') !== null)\n {\n Settings.nativeArray = true;\n }\n}\n\n\nfunction camelCaseReplacer(match)\n{\n return match.length === 1 ? match.toUpperCase() : match.charAt(1).toUpperCase();\n}\n\nfunction toCamelCase(name)\n{\n return name.replace( toCamelCase.REGEX, camelCaseReplacer );\n}\n\ntoCamelCase.REGEX = /(^.|_.)/g;\n\nfunction split(x, delimiter, escape)\n{\n var regexDelimiter = isRegExp( delimiter ) ? delimiter : new RegExp( '(' + delimiter + ')' );\n var splits = x.split( regexDelimiter );\n var i = 0;\n var n = splits.length - 2;\n\n while (i < n)\n {\n var a = splits[ i ];\n var ae = a.length - escape.length;\n\n if ( a.substring( ae ) === escape )\n {\n var b = splits[ i + 1 ];\n var c = splits[ i + 2 ];\n var joined = a.substring( 0, ae ) + b + c;\n\n splits.splice( i, 3, joined );\n n -= 2;\n }\n else\n {\n i += 1;\n splits.splice( i, 1 );\n n -= 1;\n }\n }\n\n return splits;\n}\n\n\n/**\n * A function which takes a value (typically an object) and returns a true or\n * false value.\n *\n * @callback whereCallback\n * @param {Any} value -\n * The value to test.\n * @return {Boolean} -\n * Whether or not the value passed the test.\n * @see Rekord.createWhere\n * @see Rekord.saveWhere\n */\n\n/**\n * An expression which can be used to generate a function for testing a value\n * and returning a boolean result. The following types can be given and will\n * result in the following tests:\n *\n * - `String`: If a string & value are given - the generated function will test\n * if the object has a property with the given value. If a string is given\n * and no value is given - the generated function will test if the object\n * has the property and a non-null value.\n * - `Object`: If an object is given - the generated function will test all\n * properties of the given object and return true only if the object being\n * tested has the same values.\n * - `Array`: If an array is given - each element in the array is passed as\n * arguments to generate a new function. The returned function will only\n * return true if all generated functions return true - otherwise false.\n * - `whereCallback`: A function can be given which is immediately returned as\n * the test function.\n *\n * @typedef {String|Object|Array|whereCallback} whereInput\n */\n\n\n/**\n * A map of saved {@link whereCallback} functions.\n *\n * @type {Object}\n */\nvar Wheres = {};\n\n/**\n * Saves a function created with {@link Rekord.createWhere} to a cache of\n * filter functions which can be created more quickly in subsequent calls. It's\n * advised to make use of saved where's even in simpler scenarios for several\n * reasons:\n *\n * - You can name a comparison which is self documenting\n * - When refactoring, you only need to modify a single place in the code\n * - It's slightly more efficient (time & memory) to cache filter functions\n *\n * ```javascript\n * Rekord.saveWhere('whereName', 'field', true);\n * Rekord.createWhere('whereName'); // returns the same function except quicker\n * ```\n *\n * @memberof Rekord\n * @param {String} name -\n * The name of the filter function to save for later use.\n * @param {String|Object|Array|whereCallback} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @see Rekord.createWhere\n */\nfunction saveWhere(name, properties, values, equals)\n{\n var where = createWhere( properties, values, equals );\n\n Wheres[ name ] = where;\n\n return where;\n}\n\n/**\n * Creates a function which returns a true or false value given a test value.\n * This is also known as a filter function.\n *\n * ```javascript\n * Rekord.createWhere('field', true); // when an object has property where field=true\n * Rekord.createWhere('field'); // when an object has the property named field\n * Rekord.createWhere(function(){}); // a function can be given which is immediately returned\n * Rekord.createWhere(['field', function(){}, ['field', true]]); // when an object meets all of the above criteria\n * Rekord.createWhere({foo: 1, bar: 2}); // when an object has foo=1 and bar=2\n * Rekord.createWhere('field', true, myEquals); // A custom comparison function can be given.\n * Rekord.createWhere(); // always returns true\n * ```\n *\n * @memberof Rekord\n * @param {whereInput} [properties] -\n * The first expression used to generate a filter function.\n * @param {Any} [value] -\n * When the first argument is a string this argument will be treated as a\n * value to compare to the value of the named property on the object passed\n * through the filter function.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * An alternative function can be used to compare to values.\n * @return {whereCallback} -\n * A function which takes a value (typically an object) and returns a true\n * or false value.\n * @see Rekord.saveWhere\n */\nfunction createWhere(properties, value, equals)\n{\n var equality = equals || equalsStrict;\n\n if ( isFunction( properties ) )\n {\n return properties;\n }\n else if ( isArray( properties ) )\n {\n var parsed = [];\n\n for (var i = 0; i < properties.length; i++)\n {\n var where = properties[ i ];\n\n parsed.push( isArray( where ) ? createWhere.apply( this, where ) : createWhere( where ) );\n }\n\n return function whereMultiple(model)\n {\n for (var i = 0; i < parsed.length; i++)\n {\n if ( !parsed[ i ]( model ) )\n {\n return false;\n }\n }\n\n return true;\n };\n }\n else if ( isObject( properties ) )\n {\n var props = [];\n\n for (var prop in properties)\n {\n props.push({\n tester: exprEqualsTester( properties[ prop ], equality ),\n resolver: createPropertyResolver( prop )\n });\n }\n\n return function whereEqualsObject(model)\n {\n for (var i = 0; i < props.length; i++)\n {\n var prop = props[ i ];\n\n if ( !prop.tester( prop.resolver( model ) ) )\n {\n return false;\n }\n }\n\n return true;\n };\n }\n else if ( isString( properties ) )\n {\n if ( properties in Wheres )\n {\n return Wheres[ properties ];\n }\n\n var resolver = createPropertyResolver( properties );\n\n if ( isValue( value ) )\n {\n var tester = exprEqualsTester( value, equality );\n\n return function whereEqualsValue(model)\n {\n return tester( resolver( model ) );\n };\n }\n else\n {\n return function whereHasValue(model)\n {\n return isValue( resolver( model ) );\n };\n }\n }\n else\n {\n return function whereAll(model)\n {\n return true;\n };\n }\n}\n\nfunction expr(func)\n{\n func.expression = true;\n\n return func;\n}\n\nfunction exprEquals(value, test, equals)\n{\n return isExpr( value ) ? value( test, equals ) : equals( value, test );\n}\n\nfunction exprEqualsTester(value, equals)\n{\n if ( isExpr( value ) )\n {\n return function tester(test)\n {\n return value( test, equals );\n };\n }\n\n return function tester(test)\n {\n return equals( value, test );\n };\n}\n\nfunction isExpr(x)\n{\n return isFunction( x ) && x.expression;\n}\n\nfunction not(x)\n{\n if ( isExpr( x ) )\n {\n return expr(function notExpr(value, equals)\n {\n return !x( value, equals );\n });\n }\n\n if ( isFunction( x ) )\n {\n return function notWhere(value)\n {\n return !x( value );\n };\n }\n\n return expr(function notValue(value, equals)\n {\n return !equals( value, x );\n });\n}\n\nfunction oneOf(input)\n{\n var values = isArray( input ) ? input : AP.slice.call( arguments );\n\n return expr(function oneOfValue(value, equals)\n {\n for (var i = 0; i < values.length; i++)\n {\n if (exprEquals( values[ i ], value, equals ) )\n {\n return true;\n }\n }\n\n return false;\n });\n}\n\n\n/**\n * Creates a Rekord object given a set of options. A Rekord object is also the\n * constructor for creating instances of the Rekord object defined.\n *\n * @namespace\n * @param {Object} options\n * The options of\n */\nfunction Rekord(options)\n{\n var promise = Rekord.get( options.name );\n\n if ( promise.isComplete() )\n {\n return promise.results[0];\n }\n\n Rekord.trigger( Rekord.Events.Options, [options] );\n\n var database = new Database( options );\n\n var model = Class.dynamic(\n Model,\n new Model( database ),\n database.className,\n '(props, remoteData) { this.$init( props, remoteData ) }'\n );\n\n database.Model = model;\n model.Database = database;\n\n Rekord.classes[ database.name ] = model;\n\n Rekord.trigger( Rekord.Events.Plugins, [model, database, options] );\n\n if ( Rekord.autoload )\n {\n database.loadBegin(function onLoadFinish(success)\n {\n if ( success )\n {\n database.loadFinish();\n }\n });\n }\n else\n {\n Rekord.unloaded.push( database );\n }\n\n Rekord.get( database.name ).resolve( model );\n Rekord.get( database.className ).resolve( model );\n\n Rekord.debug( Rekord.Debugs.CREATION, database, options );\n\n return model;\n}\n\nRekord.classes = {};\n\nRekord.autoload = false;\n\nRekord.unloaded = [];\n\nRekord.loadPromise = null;\n\nRekord.load = function(callback, context)\n{\n var promise = Rekord.loadPromise = Rekord.loadPromise || new Promise( null, false );\n var loading = Rekord.unloaded.slice();\n var loaded = [];\n var loadedSuccess = [];\n\n promise.success( callback, context || this );\n\n Rekord.unloaded.length = 0;\n\n function onLoadFinish(success, db)\n {\n loadedSuccess.push( success );\n loaded.push( db );\n\n if ( loaded.length === loading.length )\n {\n for (var k = 0; k < loaded.length; k++)\n {\n var db = loaded[ k ];\n var success = loadedSuccess[ k ];\n\n if ( success )\n {\n db.loadFinish();\n }\n }\n\n promise.reset().resolve();\n }\n }\n\n for (var i = 0; i < loading.length; i++)\n {\n loading[ i ].loadBegin( onLoadFinish );\n }\n\n return promise;\n};\n\nRekord.promises = {};\n\nRekord.get = function(name)\n{\n var existing = Rekord.promises[ name ];\n\n if ( !existing )\n {\n existing = Rekord.promises[ name ] = new Promise( null, false );\n }\n\n return existing;\n};\n\nRekord.export = function()\n{\n var classes = Rekord.classes;\n\n for (var className in classes)\n {\n win[ className ] = classes[ className ];\n }\n};\n\nRekord.clear = function(removeListeners)\n{\n var classes = Rekord.classes;\n\n for (var className in classes)\n {\n classes[ className ].clear( removeListeners );\n }\n};\n\nRekord.reset = function(failOnPendingChanges, removeListeners)\n{\n var classes = Rekord.classes;\n\n if ( failOnPendingChanges )\n {\n for (var className in classes)\n {\n var db = classes[ className ].Database;\n\n if ( db.hasPending() )\n {\n return Promise.reject( db );\n }\n }\n }\n\n return Promise.singularity(this, function()\n {\n for (var className in classes)\n {\n var db = classes[ className ].Database;\n\n db.reset( false, removeListeners );\n }\n });\n};\n\nRekord.unload = function(names, reset, failOnPendingChanges, removeListeners)\n{\n var classes = Rekord.classes;\n var promises = Rekord.promises;\n\n if ( failOnPendingChanges )\n {\n for (var className in classes)\n {\n var db = classes[ className ].Database;\n var check = ( !isArray( names ) || indexOf( names, className ) !== false );\n\n if ( check && db.hasPending() )\n {\n return Promise.reject( db );\n }\n }\n }\n\n return Promise.singularity(this, function()\n {\n for (var className in classes)\n {\n var db = classes[ className ].Database;\n var check = ( !isArray( names ) || indexOf( names, className ) !== false );\n\n if ( check )\n {\n if ( reset )\n {\n db.reset( false, removeListeners );\n }\n\n delete classes[ className ];\n delete promises[ db.name ];\n delete promises[ db.className ];\n }\n }\n });\n};\n\n/**\n * A value which identifies a model instance. This can be the key of the model,\n * an array of values (if the model has composite keys), an object which at\n * least contains fields which identify the model, an instance of a model, the\n * reference to a Rekord instance, or a function.\n *\n * If a plain object is given and it shares the same key as an existing model -\n * the other fields on the object will be applied to the existing instance. If\n * a plain object is given and it's key doesn't map to an existing model - a new\n * one is created.\n *\n * If a reference to a Rekord instance is given - a new model instance is created\n * with default values.\n *\n * If a function is given - it's invoked and the returning value is used as the\n * value to identify the model instance.\n *\n * @typedef {String|Number|String[]|Number[]|Object|Rekord|Rekord.Model|Function} modelInput\n */\n\n /**\n * A key to a model instance.\n *\n * @typedef {String|Number} modelKey\n */\n\naddEventful( Rekord );\n\nRekord.Events =\n{\n Initialized: 'initialized',\n Plugins: 'plugins',\n Options: 'options',\n Online: 'online',\n Offline: 'offline',\n Error: 'error'\n};\n\n\nvar Cache =\n{\n None: 'none',\n Pending: 'pending',\n All: 'all'\n};\n\n\nvar Cascade =\n{\n None: 0,\n Local: 1,\n Rest: 2,\n NoLive: 3,\n Live: 4,\n NoRest: 5,\n Remote: 6,\n All: 7\n};\n\nfunction canCascade(cascade, type)\n{\n return !isNumber( cascade ) || (cascade & type) === type;\n}\n\nvar Load =\n{\n None: 0,\n All: 1,\n Lazy: 2,\n Both: 3\n};\n\n\n\nvar RestStatus =\n{\n Conflict: {409: true},\n NotFound: {404: true, 410: true},\n Offline: {0: true}\n};\n\nvar Save =\n{\n None: 0,\n Model: 4,\n Key: 5,\n Keys: 6\n};\n\nvar Store =\n{\n None: 0,\n Model: 1,\n Key: 2,\n Keys: 3\n};\n\n\nvar batchDepth = 0;\nvar batches = [];\nvar batchHandlers = [];\nvar batchOverwrites = [];\n\nfunction batch(namesInput, operationsInput, handler)\n{\n var names = toArray( namesInput, /\\s*,\\s/ );\n var operations = toArray( operationsInput, /\\s*,\\s/ );\n var batchID = batchHandlers.push( handler ) - 1;\n var batch = batches[ batchID ] = new Collection();\n\n for (var i = 0; i < names.length; i++)\n {\n var modelName = names[ i ];\n var modelHandler = createModelHandler( operations, batch );\n\n if ( isString( modelName ) )\n {\n if ( modelName in Rekord.classes )\n {\n modelHandler( Rekord.classes[ modelName ] );\n }\n else\n {\n earlyModelHandler( modelName, modelHandler );\n }\n }\n else if ( isRekord( modelName ) )\n {\n modelHandler( modelName );\n }\n else if ( modelName === true )\n {\n for (var databaseName in Rekord.classes)\n {\n modelHandler( Rekord.classes[ databaseName ] );\n }\n\n Rekord.on( Rekord.Events.Plugins, modelHandler );\n }\n else\n {\n throw modelName + ' is not a valid input for batching';\n }\n }\n}\n\nfunction earlyModelHandler(name, modelHandler)\n{\n var off = Rekord.on( Rekord.Events.Plugins, function(model, database)\n {\n if ( database.name === name )\n {\n modelHandler( model );\n\n off();\n }\n });\n}\n\nfunction createModelHandler(operations, batch)\n{\n return function(modelClass)\n {\n var db = modelClass.Database;\n var rest = db.rest;\n\n for (var i = 0; i < operations.length; i++)\n {\n var op = operations[ i ];\n\n batchOverwrites.push( rest, op, rest[ op ] );\n\n switch (op)\n {\n case 'all':\n rest.all = function(options, success, failure) // jshint ignore:line\n {\n batch.push({\n database: db,\n class: modelClass,\n operation: 'all',\n options: options,\n success: success,\n failure: failure\n });\n };\n break;\n case 'get':\n rest.get = function(model, options, success, failure) // jshint ignore:line\n {\n batch.push({\n database: db,\n class: modelClass,\n operation: 'get',\n options: options,\n success: success,\n failure: failure,\n model: model\n });\n };\n break;\n case 'create':\n rest.create = function(model, encoded, options, success, failure) // jshint ignore:line\n {\n batch.push({\n database: db,\n class: modelClass,\n operation: 'create',\n options: options,\n success: success,\n failure: failure,\n model: model,\n encoded: encoded\n });\n };\n break;\n case 'update':\n rest.update = function(model, encoded, options, success, failure) // jshint ignore:line\n {\n batch.push({\n database: db,\n class: modelClass,\n operation: 'update',\n options: options,\n success: success,\n failure: failure,\n model: model,\n encoded: encoded\n });\n };\n break;\n case 'remove':\n rest.remove = function(model, options, success, failure) // jshint ignore:line\n {\n batch.push({\n database: db,\n class: modelClass,\n operation: 'remove',\n options: options,\n success: success,\n failure: failure,\n model: model\n });\n };\n break;\n case 'query':\n rest.query = function(url, query, options, success, failure) // jshint ignore:line\n {\n batch.push({\n database: db,\n class: modelClass,\n operation: 'query',\n options: options,\n success: success,\n failure: failure,\n url: url,\n encoded: query\n });\n };\n break;\n default:\n throw op + ' is not a valid operation you can batch';\n }\n }\n };\n}\n\nfunction batchRun()\n{\n for (var i = 0; i < batches.length; i++)\n {\n var batch = batches[ i ];\n var handler = batchHandlers[ i ];\n\n if ( batch.length )\n {\n handler( batch );\n\n batch.clear();\n }\n }\n}\n\nfunction batchStart()\n{\n batchDepth++;\n}\n\nfunction batchEnd()\n{\n batchDepth--;\n\n if ( batchDepth === 0 )\n {\n batchRun();\n }\n}\n\nfunction batchClear()\n{\n for (var i = 0; i < batchOverwrites.length; i += 3)\n {\n var rest = batchOverwrites[ i + 0 ];\n var prop = batchOverwrites[ i + 1 ];\n var func = batchOverwrites[ i + 2 ];\n\n rest[ prop ] = func;\n }\n\n batches.length = 0;\n batchHandlers.length = 0;\n batchOverwrites.length = 0;\n}\n\nfunction batchExecute(func, context)\n{\n try\n {\n batchStart();\n\n func.apply( context );\n }\n catch (ex)\n {\n Rekord.trigger( Rekord.Events.Error, [ex] );\n\n throw ex;\n }\n finally\n {\n batchEnd();\n }\n}\n\nRekord.batch = batch;\nRekord.batchRun = batchRun;\nRekord.batchStart = batchStart;\nRekord.batchEnd = batchEnd;\nRekord.batchClear = batchClear;\nRekord.batchExecute = batchExecute;\nRekord.batchDepth = function() { return batchDepth; };\n\n\nRekord.debug = function(event, source) /*, data.. */\n{\n // up to the user\n};\n\n/**\n * Sets the debug implementation provided the factory function. This function\n * can only be called once - all subsequent calls will be ignored unless\n * `overwrite` is given as a truthy value.\n *\n * @memberof Rekord\n * @param {Function} factory -\n * The factory which provides debug implementations.\n * @param {Boolean} [overwrite=false] -\n * True if existing implementations are to be ignored and the given factory\n * should be the implementation.\n */\nRekord.setDebug = function(factory, overwrite)\n{\n if ( !Rekord.debugSet || overwrite )\n {\n Rekord.debug = factory;\n Rekord.debugSet = true;\n }\n};\n\nRekord.Debugs = {\n\n CREATION: 0, // options\n\n REST: 1, // options\n AUTO_REFRESH: 73, //\n\n MISSING_KEY: 33, // encoded\n\n REMOTE_UPDATE: 2, // encoded, Model\n REMOTE_CREATE: 3, // encoded, Model\n REMOTE_REMOVE: 4, // Model\n REMOTE_LOAD: 5, // encoded[]\n REMOTE_LOAD_OFFLINE: 6, //\n REMOTE_LOAD_ERROR: 7, // status\n REMOTE_LOAD_REMOVE: 8, // key\n REMOTE_LOAD_RESUME: 22, //\n\n LOCAL_LOAD: 9, // encoded[]\n LOCAL_RESUME_DELETE: 10, // Model\n LOCAL_RESUME_SAVE: 11, // Model\n LOCAL_LOAD_SAVED: 12, // Model\n\n REALTIME_SAVE: 13, // encoded, key\n REALTIME_REMOVE: 14, // key\n\n SAVE_VALUES: 15, // encoded, Model\n SAVE_PUBLISH: 16, // encoded, Model\n SAVE_CONFLICT: 17, // encoded, Model\n SAVE_UPDATE_FAIL: 18, // Model\n SAVE_ERROR: 19, // Model, status\n SAVE_OFFLINE: 20, // Model\n SAVE_RESUME: 21, // Model\n SAVE_REMOTE: 25, // Model\n SAVE_DELETED: 40, // Model\n\n SAVE_OLD_REVISION: 48, // Model, encoded\n\n SAVE_LOCAL: 23, // Model\n SAVE_LOCAL_ERROR: 24, // Model, error\n SAVE_LOCAL_DELETED: 38, // Model\n SAVE_LOCAL_BLOCKED: 39, // Model\n\n SAVE_REMOTE_DELETED: 41, // Model, [encoded]\n SAVE_REMOTE_BLOCKED: 42, // Model\n\n REMOVE_PUBLISH: 26, // key, Model\n REMOVE_LOCAL: 27, // key, Model\n REMOVE_MISSING: 28, // key, Model\n REMOVE_ERROR: 29, // status, key, Model\n REMOVE_OFFLINE: 30, // Model\n REMOVE_RESUME: 31, // Model\n REMOVE_REMOTE: 32, // Model\n REMOVE_CANCEL_SAVE: 47, // Model\n\n REMOVE_LOCAL_ERROR: 34, // Model, error\n REMOVE_LOCAL_BLOCKED: 44, // Model\n REMOVE_LOCAL_NONE: 45, // Model\n REMOVE_LOCAL_UNSAVED: 46, // Model\n\n REMOVE_REMOTE_BLOCKED: 43, // Model\n\n GET_LOCAL_SKIPPED: 104, // Model\n GET_LOCAL: 105, // Model, encoded\n GET_LOCAL_ERROR: 106, // Model, e\n GET_REMOTE: 107, // Model, data\n GET_REMOTE_ERROR: 108, // Model, data, status\n\n ONLINE: 35, //\n OFFLINE: 36, //\n\n PUBSUB_CREATED: 37, // PubSub\n\n HASONE_INIT: 53, // HasOne\n HASONE_NINJA_REMOVE: 49, // Model, relation\n HASONE_INITIAL_PULLED: 51, // Model, initial\n HASONE_INITIAL: 52, // Model, initial\n HASONE_CLEAR_MODEL: 54, // relation\n HASONE_SET_MODEL: 55, // relation\n HASONE_PRESAVE: 56, // Model, relation\n HASONE_POSTREMOVE: 57, // Model, relation\n HASONE_CLEAR_KEY: 58, // Model, local\n HASONE_UPDATE_KEY: 59, // Model, targetFields, Model, sourceFields\n HASONE_LOADED: 60, // Model, relation, [Model]\n HASONE_QUERY: 111, // Model, RemoteQuery, queryOption, query\n HASONE_QUERY_RESULTS: 112, // Model, RemoteQuery\n\n BELONGSTO_INIT: 61, // HasOne\n BELONGSTO_NINJA_REMOVE: 62, // Model, relation\n BELONGSTO_NINJA_SAVE: 63, // Model, relation\n BELONGSTO_INITIAL_PULLED: 64,// Model, initial\n BELONGSTO_INITIAL: 65, // Model, initial\n BELONGSTO_CLEAR_MODEL: 66, // relation\n BELONGSTO_SET_MODEL: 67, // relation\n BELONGSTO_POSTREMOVE: 69, // Model, relation\n BELONGSTO_CLEAR_KEY: 70, // Model, local\n BELONGSTO_UPDATE_KEY: 71, // Model, targetFields, Model, sourceFields\n BELONGSTO_LOADED: 72, // Model, relation, [Model]\n BELONGSTO_QUERY: 113, // Model, RemoteQuery, queryOption, query\n BELONGSTO_QUERY_RESULTS: 114,// Model, RemoteQuery\n\n HASREFERENCE_INIT: 131, // HasOne\n HASREFERENCE_NINJA_REMOVE: 132, // Model, relation\n HASREFERENCE_INITIAL_PULLED: 133, // Model, initial\n HASREFERENCE_INITIAL: 134, // Model, initial\n HASREFERENCE_CLEAR_MODEL: 135, // relation\n HASREFERENCE_SET_MODEL: 136, // relation\n HASREFERENCE_CLEAR_KEY: 137, // Model, local\n HASREFERENCE_UPDATE_KEY: 138, // Model, targetFields, Model, sourceFields\n HASREFERENCE_LOADED: 139, // Model, relation, [Model]\n HASREFERENCE_QUERY: 140, // Model, RemoteQuery, queryOption, query\n HASREFERENCE_QUERY_RESULTS: 141, // Model, RemoteQuery\n\n HASMANY_INIT: 74, // HasMany\n HASMANY_NINJA_REMOVE: 75, // Model, Model, relation\n HASMANY_NINJA_SAVE: 76, // Model, Model, relation\n HASMANY_INITIAL: 77, // Model, relation, initial\n HASMANY_INITIAL_PULLED: 78, // Model, relation\n HASMANY_REMOVE: 79, // relation, Model\n HASMANY_SORT: 80, // relation\n HASMANY_ADD: 81, // relation, Model\n HASMANY_LAZY_LOAD: 82, // relation, Model[]\n HASMANY_INITIAL_GRABBED: 83, // relation, Model\n HASMANY_NINJA_ADD: 84, // relation, Model\n HASMANY_AUTO_SAVE: 85, // relation\n HASMANY_PREREMOVE: 86, // Model, relation\n HASMANY_POSTSAVE: 87, // Model, relation\n HASMANY_QUERY: 115, // Model, RemoteQuery, queryOption, query\n HASMANY_QUERY_RESULTS: 116, // Model, RemoteQuery\n HASMANY_UPDATE_KEY: 129, // Model, targetFields, Model, sourceFields\n\n HASMANYTHRU_INIT: 88, // HasMany\n HASMANYTHRU_NINJA_REMOVE: 89, // Model, Model, relation\n HASMANYTHRU_NINJA_SAVE: 90, // Model, Model, relation\n HASMANYTHRU_NINJA_THRU_REMOVE: 91,// Model, Model, relation\n HASMANYTHRU_INITIAL: 92, // Model, relation, initial\n HASMANYTHRU_INITIAL_PULLED: 93, // Model, relation\n HASMANYTHRU_REMOVE: 94, // relation, Model\n HASMANYTHRU_SORT: 95, // relation\n HASMANYTHRU_ADD: 96, // relation, Model\n HASMANYTHRU_LAZY_LOAD: 97, // relation, Model[]\n HASMANYTHRU_INITIAL_GRABBED: 98, // relation, Model\n HASMANYTHRU_NINJA_ADD: 99, // relation, Model\n HASMANYTHRU_AUTO_SAVE: 100, // relation\n HASMANYTHRU_PREREMOVE: 101, // Model, relation\n HASMANYTHRU_POSTSAVE: 102, // Model, relation\n HASMANYTHRU_THRU_ADD: 103, // relation, Model\n HASMANYTHRU_THRU_REMOVE: 68, // relation, Model, Model\n HASMANYTHRU_QUERY: 117, // Model, RemoteQuery, queryOption, query\n HASMANYTHRU_QUERY_RESULTS: 118, // Model, RemoteQuery\n HASMANYTHRU_UPDATE_KEY: 130, // Model, targetFields, Model, sourceFields\n\n HASREMOTE_INIT: 50, // HasRemote\n HASREMOTE_SORT: 121, // relation\n HASREMOTE_NINJA_REMOVE: 109, // Model, Model, relation\n HASREMOTE_NINJA_SAVE: 110, // Model, Model, relation\n HASREMOTE_QUERY: 119, // Model, RemoteQuery, queryOption, query\n HASREMOTE_QUERY_RESULTS: 120, // Model, RemoteQuery\n\n HASLIST_INIT: 122, // HasList\n HASLIST_SORT: 123, // relation\n HASLIST_NINJA_REMOVE: 124, // Model, Model, relation\n HASLIST_NINJA_SAVE: 125, // Model, Model, relation\n HASLIST_REMOVE: 126, // HasList, relation, Model\n HASLIST_ADD: 127, // HasList, relation, Model\n HASLIST_INITIAL: 128 // HasList, Model, relation, initial\n};\n\n\nvar Lives = {};\n\n/**\n * The factory responsible for creating a service which publishes operations\n * and receives operations that have occurred. The first argument is a reference\n * to the Database and the second argument is a function to invoke when a\n * live operation occurs. This function must return a function that can be passed\n * an operation to be delegated to other clients.\n *\n * @param {Database} database\n * The database this live function is for.\n * @return {function} -\n * The function which sends operations.\n */\nLives.Default =\nRekord.defaultLive =\nRekord.live = function(database)\n{\n return {\n\n save: function(model, data)\n {\n // ignore save\n },\n\n remove: function(model)\n {\n // ignore remove\n }\n\n };\n};\n\n/**\n * Sets the live implementation provided the factory function. This function\n * can only be called once - all subsequent calls will be ignored unless\n * `overwrite` is given as a truthy value.\n *\n * @memberof Rekord\n * @param {Function} factory -\n * The factory which provides live implementations.\n * @param {Boolean} [overwrite=false] -\n * True if existing implementations are to be ignored and the given factory\n * should be the implementation.\n */\nRekord.setLive = function(factory, overwrite)\n{\n if ( !Rekord.liveSet || overwrite )\n {\n Rekord.live = factory;\n Rekord.liveSet = true;\n }\n};\n\n\n// Initial online\n\nRekord.isOnline = function()\n{\n return !win.navigator || win.navigator.onLine !== false;\n};\n\nRekord.online = Rekord.isOnline();\n\nRekord.forceOffline = false;\n\n// Set network status to online and notify all listeners\nRekord.setOnline = function()\n{\n Rekord.online = true;\n Rekord.debug( Rekord.Debugs.ONLINE );\n\n batchExecute(function()\n {\n Rekord.trigger( Rekord.Events.Online );\n });\n};\n\n// Set network status to offline and notify all listeners\nRekord.setOffline = function()\n{\n Rekord.online = false;\n Rekord.debug( Rekord.Debugs.OFFLINE );\n Rekord.trigger( Rekord.Events.Offline );\n};\n\n// This must be called manually - this will try to use built in support for\n// online/offline detection instead of solely using status codes of 0.\nRekord.listenToNetworkStatus = function()\n{\n if (win.addEventListener)\n {\n win.addEventListener( Rekord.Events.Online, Rekord.setOnline, false );\n win.addEventListener( Rekord.Events.Offline, Rekord.setOffline, false );\n }\n else\n {\n win.document.body.ononline = Rekord.setOnline;\n win.document.body.onoffline = Rekord.setOffline;\n }\n};\n\n// Check to see if the network status has changed.\nRekord.checkNetworkStatus = function()\n{\n var online = Rekord.isOnline();\n\n if ( Rekord.forceOffline )\n {\n online = false;\n }\n\n if (online === true && Rekord.online === false)\n {\n Rekord.setOnline();\n }\n\n else if (online === false && Rekord.online === true)\n {\n Rekord.setOffline();\n }\n};\n\n\nvar Rests = {};\n\n// Rekord.rest = function(options, success(data), failure(data, status))\n\nRests.Default =\nRekord.defaultRest =\nRekord.rest = function(database)\n{\n\n return {\n\n // success ( data[] )\n // failure ( data[], status )\n all: function( options, success, failure )\n {\n success( [] );\n },\n\n // success( data )\n // failure( data, status )\n get: function( model, options, success, failure )\n {\n failure( null, -1 );\n },\n\n // success ( data )\n // failure ( data, status )\n create: function( model, encoded, options, success, failure )\n {\n success( {} );\n },\n\n // success ( data )\n // failure ( data, status )\n update: function( model, encoded, options, success, failure )\n {\n success( {} );\n },\n\n // success ( data )\n // failure ( data, status )\n remove: function( model,options, success, failure )\n {\n success( {} );\n },\n\n // success ( data[] )\n // failure ( data[], status )\n query: function( url, query, options, success, failure )\n {\n success( [] );\n }\n\n };\n\n};\n\n/**\n * Sets the rest implementation provided the factory function. This function\n * can only be called once - all subsequent calls will be ignored unless\n * `overwrite` is given as a truthy value.\n *\n * @memberof Rekord\n * @param {Function} factory -\n * The factory which provides rest implementations.\n * @param {Boolean} [overwrite=false] -\n * True if existing implementations are to be ignored and the given factory\n * should be the implementation.\n */\nRekord.setRest = function(factory, overwrite)\n{\n if ( !Rekord.restSet || overwrite )\n {\n Rekord.rest = factory;\n Rekord.restSet = true;\n }\n};\n\n\nvar Stores = {};\n\n/**\n * A factory function for returning an object capable of storing objects for\n * retrieval later by the application.\n *\n * @param {Database} database\n * The database this store is for.\n * @return {Object} -\n * An object with put, remove, and all functions.\n */\nStores.Default =\nRekord.defaultStore =\nRekord.store = function(database)\n{\n return {\n\n /**\n * Places a record in the store with the given key.\n *\n * @param {String|Number} key\n * The key to store the record as.\n * @param {Object} record\n * The record to store.\n * @param {function} success\n * A function to invoke when the record is successfully stored with\n * the key. The arguments of the function should be the key and\n * record passed to this function.\n * @param {function} failure\n * A function to invoke when the record failed to be stored with the\n * key. The arguments of the function should be the key, record, and\n * an error that occurred if available.\n */\n put: function(key, record, success, failure)\n {\n success( key, record );\n },\n\n // TODO\n get: function(key, success, failure)\n {\n failure( key, undefined );\n },\n\n /**\n * Removes a record from the store with the given key.\n *\n * @param {String|Number} key\n * The key to remove from the store.\n * @param {function} success\n * A function to invoke when the record doesn't exist in the store.\n * The arguments of the function are the removedValue (if any) and\n * the key passed to this function.\n * @param {function} failure\n * A function to invoke when there was an issue removing the key\n * from the store. The arguments of the function are the key given\n * to this function and an error that occurred if available.\n */\n remove: function(key, success, failure)\n {\n success( key );\n },\n\n /**\n * Returns all records and their keys to the given success callback.\n *\n * @param {function} success\n * The function to invoke with the array of records and an array\n * of keys.\n * @param {function} failure\n * The function to invoke with the error that occurred if available.\n */\n all: function(success, failure)\n {\n success( [], [] );\n },\n\n\n /**\n * Resets the store so it contains ONLY the given keys & record pairs.\n *\n * @param {String[]} keys -\n * The array of keys.\n * @param {Object[]} records -\n * The array of records to save.\n * @param {function} success\n * The function to invoke with the array of records and an array\n * of keys.\n * @param {function} failure\n * The function to invoke with the error that occurred if available.\n */\n reset: function(keys, records, success, failure)\n {\n success( keys, records );\n }\n\n };\n\n};\n\n/**\n * Sets the store implementation provided the factory function. This function\n * can only be called once - all subsequent calls will be ignored unless\n * `overwrite` is given as a truthy value.\n *\n * @memberof Rekord\n * @param {Function} factory -\n * The factory which provides store implementations.\n * @param {Boolean} [overwrite=false] -\n * True if existing implementations are to be ignored and the given factory\n * should be the implementation.\n */\nRekord.setStore = function(factory, overwrite)\n{\n if ( !Rekord.storeSet || overwrite )\n {\n Rekord.store = factory;\n Rekord.storeSet = true;\n }\n};\n\n\nfunction Gate(callback)\n{\n var opened = false;\n var blocked = [];\n\n var gate = function()\n {\n if ( opened )\n {\n callback.apply( this, arguments );\n }\n else\n {\n blocked.push( this, AP.slice.apply( arguments ) );\n }\n };\n\n gate.open = function()\n {\n if ( !opened )\n {\n for (var i = 0; i < blocked.length; i += 2)\n {\n var context = blocked[ i ];\n var args = blocked[ i + 1 ];\n\n callback.apply( context, args );\n }\n\n blocked.length = 0;\n opened = true;\n }\n };\n\n return gate;\n}\n\n\n\n/**\n *\n * @constructor\n * @memberof Rekord\n * @augments Rekord.Eventful\n */\nfunction Database(options)\n{\n // Apply the options to this database!\n applyOptions( this, options, Defaults );\n\n // Create the key handler based on the given key\n this.keyHandler = isArray( this.key ) ?\n new KeyComposite( this ) : new KeySimple( this );\n\n // If key fields aren't in fields array, add them in\n this.keyHandler.addToFields( this.fields );\n\n // Properties\n this.modelsCached = this.models = ModelCollection.create( this );\n this.allCached = this.all = {};\n this.loaded = {};\n this.className = this.className || toCamelCase( this.name );\n this.initialized = false;\n this.pendingRefresh = false;\n this.localLoaded = false;\n this.remoteLoaded = false;\n this.firstRefresh = false;\n this.pendingOperations = 0;\n this.afterOnline = false;\n this.saveFields = copy( this.fields );\n this.readyPromise = new Promise( null, false );\n this.context = null;\n this.contextIndex = -1;\n\n // Prepare\n this.prepare( this, options );\n\n // Services\n this.rest = this.createRest( this );\n this.store = this.createStore( this );\n this.live = this.createLive( this );\n\n // Functions\n this.setComparator( this.comparator, this.comparatorNullsFirst );\n this.setRevision( this.revision );\n this.setSummarize( this.summarize );\n\n // Relations\n this.relations = {};\n this.relationNames = [];\n\n for (var relationType in options)\n {\n if ( !(relationType in Rekord.Relations) )\n {\n continue;\n }\n\n var RelationClass = Rekord.Relations[ relationType ];\n\n if ( !(RelationClass.prototype instanceof Relation ) )\n {\n continue;\n }\n\n var relationMap = options[ relationType ];\n\n for ( var name in relationMap )\n {\n var relationOptions = relationMap[ name ];\n var relation = new RelationClass();\n\n if ( isString( relationOptions ) )\n {\n relationOptions = {\n model: relationOptions\n };\n }\n else if ( !isObject( relationOptions ) )\n {\n relationOptions = {};\n }\n\n if ( !relationOptions.model && !relationOptions.discriminator )\n {\n relationOptions.model = name;\n }\n\n relation.init( this, name, relationOptions );\n\n if ( relation.save )\n {\n this.saveFields.push( name );\n }\n\n this.relations[ name ] = relation;\n this.relationNames.push( name );\n }\n }\n\n // Projections\n for (var projectionName in this.projections)\n {\n this.projections[ projectionName ] = Projection.parse( this, projectionName );\n }\n}\n\nfunction defaultEncode(model, data, forSaving)\n{\n var encodings = this.encodings;\n\n for (var prop in data)\n {\n if ( prop in encodings )\n {\n data[ prop ] = encodings[ prop ]( data[ prop ], model, prop, forSaving );\n }\n }\n\n return data;\n}\n\nfunction defaultDecode(rawData, data)\n{\n var decodings = this.decodings;\n var target = data || rawData;\n\n for (var prop in rawData)\n {\n if ( prop in decodings )\n {\n target[ prop ] = decodings[ prop ]( rawData[ prop ], rawData, prop );\n }\n else\n {\n target[ prop ] = rawData[ prop ];\n }\n }\n\n return target;\n}\n\nfunction defaultSummarize(model)\n{\n return model.$key();\n}\n\nfunction defaultCreateRest(database)\n{\n return database.rest === false ? Rekord.defaultRest( database ) : Rekord.rest( database );\n}\n\nfunction defaultCreateStore(database)\n{\n return database.store === false ? Rekord.defaultStore( database ) : Rekord.store( database );\n}\n\nfunction defaultCreateLive( database )\n{\n return database.live === false ? Rekord.defaultLive( database ) : Rekord.live( database );\n}\n\nfunction defaultResolveModel( response )\n{\n return response;\n}\n\nfunction defaultResolveModels( response )\n{\n return response;\n}\n\nDatabase.Events =\n{\n NoLoad: 'no-load',\n RemoteLoad: 'remote-load',\n LocalLoad: 'local-load',\n Updated: 'updated',\n ModelAdded: 'model-added',\n ModelUpdated: 'model-updated',\n ModelRemoved: 'model-removed',\n OperationsStarted: 'operations-started',\n OperationsFinished: 'operations-finished',\n Loads: 'no-load remote-load local-load',\n Changes: 'updated'\n};\n\nvar Defaults = Database.Defaults =\n{\n name: undefined, // required\n className: null, // defaults to toCamelCase( name )\n key: 'id',\n keySeparator: '/',\n fields: [],\n ignoredFields: {},\n defaults: {},\n publishAlways: [],\n saveAlways: [],\n comparator: null,\n comparatorNullsFirst: null,\n revision: null,\n traits: [],\n cascade: Cascade.All,\n load: Load.None,\n allComplete: false,\n loadRelations: true,\n autoRefresh: true,\n cache: Cache.All,\n fullSave: false,\n fullPublish: false,\n noReferences: false,\n encodings: {},\n decodings: {},\n projections: {},\n allOptions: null,\n fetchOptions: null,\n getOptions: null,\n updateOptions: null,\n createOptions: null,\n saveOptions: null,\n removeOptions: null,\n queryOptions: null,\n prune: {active: false, max: 0, keepAlive: 0, removeLocal: false},\n prepare: noop,\n encode: defaultEncode,\n decode: defaultDecode,\n resolveModel: defaultResolveModel,\n resolveModels: defaultResolveModels,\n summarize: defaultSummarize,\n createRest: defaultCreateRest,\n createStore: defaultCreateStore,\n createLive: defaultCreateLive\n};\n\nClass.create( Database,\n{\n\n setStoreEnabled: function(enabled)\n {\n if ( enabled )\n {\n if ( this.storeDisabled )\n {\n this.store = this.storeDisabled;\n this.storeDisabled = false;\n }\n }\n else if ( !this.storeDisabled )\n {\n this.storeDisabled = this.store;\n this.store = Rekord.defaultStore( this );\n }\n },\n\n setRestEnabled: function(enabled)\n {\n if ( enabled )\n {\n if ( this.restDisabled )\n {\n this.rest = this.restDisabled;\n this.restDisabled = false;\n }\n }\n else if ( !this.restDisabled )\n {\n this.restDisabled = this.rest;\n this.rest = Rekord.defaultRest( this );\n }\n },\n\n setLiveEnabled: function(enabled)\n {\n if ( enabled )\n {\n if ( this.liveDisabled )\n {\n this.live = this.liveDisabled;\n this.liveDisabled = false;\n }\n }\n else if ( !this.liveDisabled )\n {\n this.liveDisabled = this.live;\n this.live = Rekord.defaultLive( this );\n }\n },\n\n // Notifies a callback when the database has loaded (either locally or remotely).\n ready: function(callback, context, persistent)\n {\n return this.readyPromise.success( callback, context, persistent );\n },\n\n clearAll: function()\n {\n var db = this;\n\n if (db.context)\n {\n db.context.clear( this );\n }\n else\n {\n db.allCached = db.all = {};\n }\n },\n\n clear: function(removeListeners)\n {\n var db = this;\n\n db.clearAll();\n db.models.clear();\n\n if ( removeListeners )\n {\n db.off();\n }\n\n return db;\n },\n\n hasPending: function()\n {\n return this.models.contains(function(model)\n {\n return model.$isPending();\n });\n },\n\n reset: function(failOnPendingChanges, removeListeners)\n {\n var db = this;\n var promise = new Promise();\n\n if ( failOnPendingChanges && db.hasPending() )\n {\n promise.reject( db );\n }\n else\n {\n db.clear( removeListeners );\n\n db.store.reset( [], [],\n function()\n {\n promise.resolve( db );\n },\n function()\n {\n promise.reject( db );\n }\n );\n }\n\n return promise;\n },\n\n // Determines whether the given object has data to save\n hasData: function(saving)\n {\n if ( !isObject( saving ) )\n {\n return false;\n }\n\n for (var prop in saving)\n {\n if ( !this.ignoredFields[ prop ] )\n {\n return true;\n }\n }\n\n return false;\n },\n\n // Grab a model with the given input and notify the callback\n grabModel: function(input, callback, context, remoteData)\n {\n var db = this;\n var promise = new Promise();\n\n promise.success( callback, context || db );\n\n function checkModel()\n {\n var result = db.parseModel( input, remoteData );\n\n if ( result !== false && !promise.isComplete() && db.initialized )\n {\n var remoteLoaded = db.remoteLoaded || !db.hasLoad( Load.All );\n var missingModel = (result === null || !result.$isSaved());\n var lazyLoad = db.hasLoad( Load.Lazy );\n\n if ( lazyLoad && remoteLoaded && missingModel )\n {\n if ( !result )\n {\n result = db.keyHandler.buildObjectFromKey( db.keyHandler.buildKeyFromInput( input ) );\n }\n\n result.$once( Model.Events.RemoteGets, function()\n {\n if ( !promise.isComplete() )\n {\n if ( isObject( input ) )\n {\n result.$set( input );\n }\n\n promise.resolve( result.$isSaved() ? result : null );\n }\n });\n\n result.$refresh( Cascade.All, db.fetchOptions );\n }\n else\n {\n promise.resolve( result );\n }\n }\n\n return promise.isComplete() ? false : true;\n }\n\n if ( checkModel() )\n {\n db.ready( checkModel, db, true );\n }\n\n return promise;\n },\n\n // Parses the model from the given input\n //\n // Returns false if the input doesn't resolve to a model at the moment\n // Returns null if the input doesn't resolve to a model and all models have been remotely loaded\n //\n // parseModel( Rekord )\n // parseModel( Rekord.Model )\n // parseModel( 'uuid' )\n // parseModel( ['uuid'] )\n // parseModel( modelInstance )\n // parseModel( {name:'new model'} )\n // parseModel( {id:4, name:'new or existing model'} )\n //\n parseModel: function(input, remoteData)\n {\n var db = this;\n var keyHandler = db.keyHandler;\n var hasRemote = db.remoteLoaded || !db.hasLoad( Load.All );\n\n if ( !isValue( input ) )\n {\n return hasRemote ? null : false;\n }\n\n if ( isRekord( input ) )\n {\n input = new input();\n }\n if ( isFunction( input ) )\n {\n input = input();\n }\n\n var key = keyHandler.buildKeyFromInput( input );\n\n if ( input instanceof db.Model )\n {\n return input;\n }\n else if ( key in db.all )\n {\n var model = db.all[ key ];\n\n if ( isObject( input ) )\n {\n keyHandler.buildKeyFromRelations( input );\n\n if ( remoteData )\n {\n db.putRemoteData( input, key, model );\n }\n else\n {\n model.$set( input );\n }\n }\n\n return model;\n }\n else if ( isObject( input ) )\n {\n keyHandler.buildKeyFromRelations( input );\n\n if ( remoteData )\n {\n return db.putRemoteData( input );\n }\n else\n {\n return db.instantiate( db.decode( input ) );\n }\n }\n else if ( hasRemote )\n {\n return null;\n }\n\n return false;\n },\n\n // Sorts the models & notifies listeners that the database has been updated.\n updated: function()\n {\n this.sort(); // TODO remove\n this.trigger( Database.Events.Updated );\n },\n\n // Sets a revision comparision function for this database. It can be a field\n // name or a function. This is used to avoid updating model data that is older\n // than the model's current data.\n setRevision: function(revision)\n {\n if ( isFunction( revision ) )\n {\n this.revisionFunction = revision;\n }\n else if ( isString( revision ) )\n {\n this.revisionFunction = function(a, b)\n {\n var ar = isObject( a ) && revision in a ? a[ revision ] : undefined;\n var br = isObject( b ) && revision in b ? b[ revision ] : undefined;\n\n return ar === undefined || br === undefined ? false : compare( ar, br ) > 0;\n };\n }\n else\n {\n this.revisionFunction = function(a, b)\n {\n return false;\n };\n }\n },\n\n // Sets a comparator for this database. It can be a field name, a field name\n // with a minus in the front to sort in reverse, or a comparator function.\n setComparator: function(comparator, nullsFirst)\n {\n this.models.setComparator( comparator, nullsFirst );\n },\n\n addComparator: function(comparator, nullsFirst)\n {\n this.models.addComparator( comparator, nullsFirst );\n },\n\n setSummarize: function(summarize)\n {\n if ( isFunction( summarize ) )\n {\n this.summarize = summarize;\n }\n else if ( isString( summarize ) )\n {\n if ( indexOf( this.fields, summarize ) !== false )\n {\n this.summarize = function(model)\n {\n return isValue( model ) ? model[ summarize ] : model;\n };\n }\n else\n {\n this.summarize = createFormatter( summarize );\n }\n }\n else\n {\n this.summarize = function(model)\n {\n return model.$key();\n };\n }\n },\n\n // Sorts the database if it isn't sorted.\n sort: function()\n {\n this.models.sort();\n },\n\n // Determines whether this database is sorted.\n isSorted: function()\n {\n return this.models.isSorted();\n },\n\n clean: function()\n {\n var db = this;\n var keys = db.models.keys;\n var models = db.models;\n\n db.clearAll();\n\n for (var i = 0; i < keys.length; i++)\n {\n db.addReference( models[ i ], keys[ i ] );\n }\n },\n\n // Handles when we receive data from the server - either from\n // a publish, refresh, or values being returned on a save.\n putRemoteData: function(encoded, key, model, overwrite)\n {\n if ( !isObject( encoded ) )\n {\n return model;\n }\n\n var db = this;\n var key = key || db.keyHandler.getKey( encoded, true );\n\n // The remote source might be crazy, if the key isn't there then log it and ignore it\n if ( !isValue( key ) )\n {\n Rekord.debug( Rekord.Debugs.MISSING_KEY, db, encoded );\n\n return;\n }\n\n var model = model || db.all[ key ];\n var decoded = db.decode( copy( encoded ) );\n\n // Reject the data if it's a lower revision\n if ( model )\n {\n var revisionRejected = this.revisionFunction( model, encoded );\n\n if ( revisionRejected )\n {\n Rekord.debug( Rekord.Debugs.SAVE_OLD_REVISION, db, model, encoded );\n\n return model;\n }\n }\n\n // If the model already exists, update it.\n if ( model )\n {\n if ( db.keyHandler.hasKeyChange( model, decoded ) )\n {\n key = model.$setKey( db.keyHandler.getKey( decoded, true ) );\n }\n\n db.addReference( model, key );\n\n if ( !model.$saved )\n {\n model.$saved = {};\n }\n\n var current = model; // model.$toJSON( true );\n var conflicts = {};\n var conflicted = false;\n var updated = {};\n var previous = {};\n var saved = {};\n var notReallySaved = isEmpty( model.$saved );\n var relations = db.relations;\n var compareTo = db.decode( model.$saved ); // model.$saved\n\n for (var prop in encoded)\n {\n if ( prop.charAt(0) === '$' )\n {\n continue;\n }\n\n if ( prop in relations )\n {\n model.$set( prop, encoded[ prop ], true );\n\n continue;\n }\n\n var currentValue = current[ prop ];\n var savedValue = compareTo[ prop ];\n\n previous[ prop ] = model[ prop ];\n saved[ prop ] = savedValue;\n\n if ( notReallySaved || overwrite || equals( currentValue, savedValue ) )\n {\n model[ prop ] = decoded[ prop ];\n updated[ prop ] = encoded[ prop ];\n\n if ( model.$local )\n {\n model.$local[ prop ] = encoded[ prop ];\n }\n }\n else\n {\n conflicts[ prop ] = encoded[ prop ];\n conflicted = true;\n }\n\n model.$saved[ prop ] = copy( encoded[ prop ] );\n }\n\n if ( conflicted )\n {\n model.$trigger( Model.Events.PartialUpdate, [encoded, updated, previous, saved, conflicts] );\n }\n else\n {\n model.$trigger( Model.Events.FullUpdate, [encoded, updated, previous, saved, conflicts] );\n }\n\n model.$trigger( Model.Events.RemoteUpdate, [encoded, updated, previous, saved, conflicts] );\n\n model.$addOperation( SaveNow );\n\n if ( !db.models.has( key ) )\n {\n db.saveReference( model, key );\n db.trigger( Database.Events.ModelAdded, [model, true] );\n }\n }\n // The model doesn't exist, create it.\n else\n {\n model = db.createModel( decoded, true );\n\n if ( model )\n {\n if ( db.cache === Cache.All )\n {\n model.$local = model.$toJSON( false );\n model.$local.$status = model.$status;\n model.$saved = model.$local.$saved = model.$toJSON( true );\n\n model.$addOperation( SaveNow );\n }\n else\n {\n model.$saved = model.$toJSON( true );\n }\n }\n }\n\n return model;\n },\n\n createModel: function(decoded, remoteData)\n {\n var db = this;\n var model = db.instantiate( decoded, remoteData );\n\n if ( model.$invalid === true )\n {\n Rekord.debug( Rekord.Debugs.MISSING_KEY, db, decoded );\n\n return;\n }\n\n var key = model.$key();\n\n if ( !db.models.has( key ) )\n {\n db.saveReference( model, key );\n db.trigger( Database.Events.ModelAdded, [model, remoteData] );\n }\n\n return model;\n },\n\n destroyModel: function(model, modelKey)\n {\n this.pruneModel( model, modelKey );\n\n model.$trigger( Model.Events.RemoteAndRemove );\n\n Rekord.debug( Rekord.Debugs.REMOTE_REMOVE, this, model );\n },\n\n pruneModel: function(model, modelKey)\n {\n var db = this;\n var key = modelKey || model.$key();\n\n db.removeReference( key );\n db.models.remove( key );\n db.trigger( Database.Events.ModelRemoved, [model] );\n },\n\n removeReference: function(key)\n {\n delete this.all[ key ];\n },\n\n hasPruning: function()\n {\n return this.prune.max || this.prune.keepAlive;\n },\n\n pruneModels: function()\n {\n var db = this;\n var prune = db.prune;\n var models = db.models;\n\n if (prune.max || prune.keepAlive)\n {\n if (prune.active)\n {\n var youngestAllowed = now() - prune.keepAlive;\n\n var pruneModel = function(model)\n {\n if (prune.removeLocal)\n {\n model.$remove( Cascade.Local );\n }\n else\n {\n db.pruneModel( model );\n }\n };\n\n var isTooYoung = function(model)\n {\n return model.$touched <= youngestAllowed;\n };\n\n while ( prune.max && models.length > prune.max )\n {\n var youngest = models.minModel('$touched');\n\n if (youngest)\n {\n pruneModel( youngest );\n }\n }\n\n if ( prune.keepAlive )\n {\n models.eachWhere( pruneModel, isTooYoung );\n }\n }\n }\n },\n\n destroyLocalUncachedModel: function(model, key)\n {\n var db = this;\n\n if ( model )\n {\n if ( model.$hasChanges() )\n {\n delete model.$saved;\n\n db.keyHandler.removeKey( model );\n\n model.$trigger( Model.Events.Detach );\n\n return false;\n }\n\n db.destroyModel( model, key );\n\n return true;\n }\n\n return false;\n },\n\n destroyLocalCachedModel: function(model, key)\n {\n var db = this;\n\n if ( model )\n {\n // If a model was removed remotely but the model has changes - don't remove it.\n if ( model.$hasChanges() )\n {\n // Removed saved history and the current ID\n delete model.$saved;\n\n db.keyHandler.removeKey( model );\n\n if ( model.$local )\n {\n delete model.$local.$saved;\n\n db.keyHandler.removeKey( model.$local );\n }\n\n model.$trigger( Model.Events.Detach );\n\n model.$addOperation( SaveNow );\n\n return false;\n }\n\n model.$addOperation( RemoveNow );\n\n db.destroyModel( model, key );\n }\n else\n {\n db.store.remove( key, function(removedValue)\n {\n if (removedValue)\n {\n Rekord.debug( Rekord.Debugs.REMOTE_REMOVE, db, removedValue );\n }\n });\n\n // The model didn't exist\n return false;\n }\n\n return true;\n },\n\n // Destroys a model locally because it doesn't exist remotely\n destroyLocalModel: function(key)\n {\n var db = this;\n var model = db.all[ key ];\n\n if ( db.cache === Cache.All )\n {\n return db.destroyLocalCachedModel( model, key );\n }\n else\n {\n return db.destroyLocalUncachedModel( model, key );\n }\n },\n\n loadFinish: function()\n {\n var db = this;\n\n batchExecute(function()\n {\n for (var key in db.loaded)\n {\n var model = db.loaded[ key ];\n\n if ( model.$status === Model.Status.RemovePending )\n {\n Rekord.debug( Rekord.Debugs.LOCAL_RESUME_DELETE, db, model );\n\n model.$addOperation( RemoveRemote );\n }\n else\n {\n if ( model.$status === Model.Status.SavePending )\n {\n Rekord.debug( Rekord.Debugs.LOCAL_RESUME_SAVE, db, model );\n\n model.$addOperation( SaveRemote );\n }\n else\n {\n Rekord.debug( Rekord.Debugs.LOCAL_LOAD_SAVED, db, model );\n }\n\n db.saveReference( model, key, true );\n }\n }\n });\n\n db.loaded = {};\n db.updated();\n\n if ( db.hasLoad( Load.All ) )\n {\n if ( db.pendingOperations === 0 )\n {\n db.refresh();\n }\n else\n {\n db.firstRefresh = true;\n }\n }\n },\n\n hasLoad: function(load)\n {\n return (this.load & load) !== 0;\n },\n\n loadBegin: function(onLoaded)\n {\n var db = this;\n\n function onLocalLoad(records, keys)\n {\n Rekord.debug( Rekord.Debugs.LOCAL_LOAD, db, records );\n\n for (var i = 0; i < records.length; i++)\n {\n var encoded = records[ i ];\n var key = keys[ i ];\n var decoded = db.decode( copy( encoded, true ) );\n var model = db.instantiate( decoded, true );\n\n if ( model.$invalid === true )\n {\n Rekord.debug( Rekord.Debugs.MISSING_KEY, db, encoded );\n\n break;\n }\n\n model.$local = encoded;\n model.$saved = encoded.$saved;\n\n if ( model.$status !== Model.Status.Removed )\n {\n db.loaded[ key ] = model;\n db.addReference( model, key );\n }\n }\n\n db.localLoaded = true;\n db.triggerLoad( Database.Events.LocalLoad );\n\n onLoaded( true, db );\n }\n\n function onLocalError()\n {\n db.loadNone();\n\n onLoaded( false, db );\n }\n\n if ( db.hasLoad( Load.All ) && db.autoRefresh )\n {\n Rekord.after( Rekord.Events.Online, db.onOnline, db );\n }\n\n if ( db.cache === Cache.None )\n {\n db.loadNone();\n\n onLoaded( false, db );\n }\n else\n {\n db.store.all( onLocalLoad, onLocalError );\n }\n },\n\n triggerLoad: function(loadEvent, additionalParameters)\n {\n var db = this;\n\n db.initialized = true;\n db.trigger( loadEvent, [ db ].concat( additionalParameters || [] ) );\n db.readyPromise.reset().resolve( db );\n },\n\n loadNone: function()\n {\n var db = this;\n\n if ( db.hasLoad( Load.All ) )\n {\n db.refresh();\n }\n else\n {\n db.triggerLoad( Database.Events.NoLoad );\n }\n },\n\n onOnline: function()\n {\n var db = this;\n\n db.afterOnline = true;\n\n if ( db.pendingOperations === 0 )\n {\n db.onOperationRest();\n }\n },\n\n onOperationRest: function()\n {\n var db = this;\n\n if ( ( db.autoRefresh && db.remoteLoaded && db.afterOnline ) || db.firstRefresh )\n {\n db.afterOnline = false;\n db.firstRefresh = false;\n\n Rekord.debug( Rekord.Debugs.AUTO_REFRESH, db );\n\n db.refresh();\n }\n },\n\n handleRefreshSuccess: function(promise)\n {\n var db = this;\n\n return function onRefreshSuccess(response)\n {\n var models = db.resolveModels( response );\n var mapped = {};\n\n for (var i = 0; i < models.length; i++)\n {\n var model = db.putRemoteData( models[ i ] );\n\n if ( model )\n {\n var key = model.$key();\n\n mapped[ key ] = model;\n }\n }\n\n if ( db.allComplete )\n {\n var keys = db.models.keys().slice();\n\n for (var i = 0; i < keys.length; i++)\n {\n var k = keys[ i ];\n\n if ( !(k in mapped) )\n {\n var old = db.models.get( k );\n\n if ( old.$saved )\n {\n Rekord.debug( Rekord.Debugs.REMOTE_LOAD_REMOVE, db, k );\n\n db.destroyLocalModel( k );\n }\n }\n }\n }\n\n db.remoteLoaded = true;\n db.triggerLoad( Database.Events.RemoteLoad );\n\n db.updated();\n\n Rekord.debug( Rekord.Debugs.REMOTE_LOAD, db, models );\n\n promise.resolve( db.models );\n };\n },\n\n handleRefreshFailure: function(promise)\n {\n var db = this;\n\n return function onRefreshFailure(response, status)\n {\n if ( status === 0 )\n {\n Rekord.checkNetworkStatus();\n\n if ( !Rekord.online )\n {\n db.pendingRefresh = true;\n\n Rekord.once( Rekord.Events.Online, db.onRefreshOnline, db );\n }\n\n Rekord.debug( Rekord.Debugs.REMOTE_LOAD_OFFLINE, db );\n }\n else\n {\n Rekord.debug( Rekord.Debugs.REMOTE_LOAD_ERROR, db, status );\n\n db.triggerLoad( Database.Events.NoLoad, [response] );\n }\n\n promise.reject( db.models );\n };\n },\n\n executeRefresh: function(success, failure)\n {\n this.rest.all( this.allOptions, success, failure );\n },\n\n // Loads all data remotely\n refresh: function(callback, context)\n {\n var db = this;\n var promise = new Promise();\n var success = this.handleRefreshSuccess( promise );\n var failure = this.handleRefreshFailure( promise );\n\n promise.complete( callback, context || db );\n\n batchExecute(function()\n {\n db.executeRefresh( success, failure );\n });\n\n return promise;\n },\n\n onRefreshOnline: function()\n {\n var db = this;\n\n Rekord.debug( Rekord.Debugs.REMOTE_LOAD_RESUME, db );\n\n if ( db.pendingRefresh )\n {\n db.pendingRefresh = false;\n\n db.refresh();\n }\n },\n\n // Returns a model\n get: function(key)\n {\n return this.all[ this.keyHandler.buildKeyFromInput( key ) ];\n },\n\n filter: function(isValid)\n {\n var all = this.all;\n var filtered = [];\n\n for (var key in all)\n {\n var model = all[ key ];\n\n if ( isValid( model ) )\n {\n filtered.push( model );\n }\n }\n\n return filtered;\n },\n\n hasTrait: function(trait, comparator)\n {\n var cmp = comparator || equals;\n\n return isArray( this.traits ) && indexOf( this.traits, trait, cmp ) !== false;\n },\n\n hasTraits: function(traits, comparator)\n {\n for (var i = 0; i < traits.length; i++)\n {\n if ( !this.hasTrait( traits[ i ], comparator ) )\n {\n return false;\n }\n }\n\n return true;\n },\n\n liveSave: function(key, encoded)\n {\n this.putRemoteData( encoded, key );\n this.updated();\n\n Rekord.debug( Rekord.Debugs.REALTIME_SAVE, this, encoded, key );\n },\n\n liveRemove: function(key)\n {\n if ( this.destroyLocalModel( key ) )\n {\n this.updated();\n }\n\n Rekord.debug( Rekord.Debugs.REALTIME_REMOVE, this, key );\n },\n\n // Return an instance of the model with the data as initial values\n instantiate: function(data, remoteData)\n {\n return new this.Model( data, remoteData );\n },\n\n addReference: function(model, key)\n {\n if (!this.noReferences)\n {\n this.all[ key || model.$key() ] = model;\n }\n },\n\n saveReference: function(model, key, delaySort)\n {\n if ( !this.noReferences )\n {\n this.models.put( key || model.$key(), model, delaySort );\n }\n },\n\n // Save the model\n save: function(model, cascade, options)\n {\n var db = this;\n\n if ( model.$isDeleted() )\n {\n Rekord.debug( Rekord.Debugs.SAVE_DELETED, db, model );\n\n return;\n }\n\n var key = model.$key();\n var existing = db.models.has( key );\n\n if ( existing )\n {\n db.trigger( Database.Events.ModelUpdated, [model] );\n\n model.$trigger( Model.Events.UpdateAndSave );\n }\n else\n {\n db.saveReference( model, key );\n db.trigger( Database.Events.ModelAdded, [model] );\n db.updated();\n\n model.$trigger( Model.Events.CreateAndSave );\n }\n\n model.$addOperation( SaveLocal, cascade, options );\n },\n\n // Remove the model\n remove: function(model, cascade, options)\n {\n var db = this;\n\n // If we have it in the models, remove it!\n this.removeFromModels( model );\n\n // If we're offline and we have a pending save - cancel the pending save.\n if ( model.$status === Model.Status.SavePending )\n {\n Rekord.debug( Rekord.Debugs.REMOVE_CANCEL_SAVE, db, model );\n }\n\n model.$status = Model.Status.RemovePending;\n\n model.$addOperation( RemoveLocal, cascade, options );\n },\n\n removeFromModels: function(model)\n {\n var db = this;\n var key = model.$key();\n\n if ( db.models.has( key ) )\n {\n db.models.remove( key );\n db.trigger( Database.Events.ModelRemoved, [model] );\n db.updated();\n\n model.$trigger( Model.Events.Removed );\n }\n }\n\n});\n\naddEventful( Database );\n\naddEventFunction( Database, 'change', Database.Events.Changes );\n\n\n/**\n * An instance\n *\n * @constructor\n * @memberof Rekord\n * @augments Rekord.Eventful$\n * @param {Rekord.Database} db\n * The database instance used in model instances.\n */\nfunction Model(db)\n{\n Class.prop( this, '$db', db );\n\n /**\n * @property {Database} $db\n * The reference to the database this model is stored in.\n */\n\n /**\n * @property {Object} [$saved]\n * An object of encoded data representing the values saved remotely.\n * If this object does not exist - the model hasn't been created\n * yet.\n */\n\n /**\n * @property {Object} [$local]\n * The object of encoded data that is stored locally. It's $saved\n * property is the same object as this $saved property.\n */\n\n /**\n * @property {Boolean} $status\n * Whether there is a pending save for this model.\n */\n}\n\nModel.Events =\n{\n Created: 'created',\n Saved: 'saved',\n PreSave: 'pre-save',\n PostSave: 'post-save',\n PreRemove: 'pre-remove',\n PostRemove: 'post-remove',\n PartialUpdate: 'partial-update',\n FullUpdate: 'full-update',\n Updated: 'updated',\n Detach: 'detach',\n Change: 'change',\n CreateAndSave: 'created saved',\n UpdateAndSave: 'updated saved',\n KeyUpdate: 'key-update',\n RelationUpdate: 'relation-update',\n Removed: 'removed',\n RemoteUpdate: 'remote-update',\n LocalSave: 'local-save',\n LocalSaveFailure: 'local-save-failure',\n LocalSaves: 'local-save local-save-failure',\n RemoteSave: 'remote-save',\n RemoteSaveFailure: 'remote-save-failure',\n RemoteSaveOffline: 'remote-save-offline',\n RemoteSaves: 'remote-save remote-save-failure remote-save-offline',\n LocalRemove: 'local-remove',\n LocalRemoveFailure: 'local-remove-failure',\n LocalRemoves: 'local-remove local-remove-failure',\n RemoteRemove: 'remote-remove',\n RemoteRemoveFailure: 'remote-remove-failure',\n RemoteRemoveOffline: 'remote-remove-offline',\n RemoteRemoves: 'remote-remove remote-remove-failure remote-remove-offline',\n LocalGet: 'local-get',\n LocalGetFailure: 'local-get-failure',\n LocalGets: 'local-get local-get-failure',\n RemoteGet: 'remote-get',\n RemoteGetFailure: 'remote-get-failure',\n RemoteGetOffline: 'remote-get-offline',\n RemoteGets: 'remote-get remote-get-failure remote-get-offline',\n RemoteAndRemove: 'remote-remove removed',\n SavedRemoteUpdate: 'saved remote-update',\n OperationsStarted: 'operations-started',\n OperationsFinished: 'operations-finished',\n KeyChange: 'key-change',\n Changes: 'saved remote-update key-update relation-update removed key-change change'\n};\n\nModel.Status =\n{\n Synced: 0,\n SavePending: 1,\n RemovePending: 2,\n Removed: 3\n};\n\nModel.Blocked =\n{\n toString: true,\n valueOf: true\n};\n\nClass.create( Model,\n{\n\n $init: function(props, remoteData)\n {\n this.$status = Model.Status.Synced;\n\n Class.props(this, {\n $operation: null,\n $relations: {},\n $dependents: new Dependents( this ),\n $savedState: false,\n $saved: false,\n $local: false,\n $touched: now()\n });\n\n if ( remoteData )\n {\n var key = this.$db.keyHandler.getKey( props, true );\n\n if ( !isValue( key ) )\n {\n Class.prop( this, '$invalid', true );\n\n return;\n }\n\n this.$db.addReference( this, key );\n this.$set( props, undefined, remoteData );\n }\n else\n {\n this.$reset( props );\n }\n\n if ( this.$db.loadRelations )\n {\n var databaseRelations = this.$db.relations;\n\n for (var name in databaseRelations)\n {\n var relation = databaseRelations[ name ];\n\n if ( !relation.lazy )\n {\n this.$getRelation( name, undefined, remoteData );\n }\n }\n }\n },\n\n $load: function(relations)\n {\n if ( isArray( relations ) )\n {\n for (var i = 0; i < relations.length; i++)\n {\n this.$getRelation( relations[ i ] );\n }\n }\n else if ( isString( relations ) )\n {\n this.$getRelation( relations );\n }\n else\n {\n var databaseRelations = this.$db.relations;\n\n for (var name in databaseRelations)\n {\n this.$getRelation( name );\n }\n }\n },\n\n $reset: function(props)\n {\n var def = this.$db.defaults;\n var fields = this.$db.fields;\n var relations = this.$db.relations;\n var keyHandler = this.$db.keyHandler;\n var keyFields = this.$db.key;\n\n if ( !isEmpty( def ) )\n {\n for (var i = 0; i < fields.length; i++)\n {\n var prop = fields[ i ];\n var defaultValue = def[ prop ];\n var evaluatedValue = evaluate( defaultValue );\n\n this[ prop ] = evaluatedValue;\n }\n }\n else\n {\n for (var i = 0; i < fields.length; i++)\n {\n var prop = fields[ i ];\n\n this[ prop ] = undefined;\n }\n }\n\n var key = null;\n\n // First try pulling key from properties (only if it hasn't been\n // initialized through defaults)\n if ( props )\n {\n key = keyHandler.getKey( props, true );\n }\n\n // If the key wasn't specified, try generating it on this model\n if ( !isValue( key ) )\n {\n key = keyHandler.getKey( this );\n }\n // The key was specified in the properties, apply it to this model\n else\n {\n updateFieldsReturnChanges( this, keyFields, props, keyFields );\n }\n\n // The key exists on this model - place the reference of this model\n // in the all map and set the cached key.\n if ( isValue( key ) )\n {\n this.$db.addReference( this, key );\n this.$$key = key;\n }\n\n // Apply the default relation values now that this key is most likely populated\n if ( !isEmpty( def ) )\n {\n for (var prop in relations)\n {\n if ( prop in def )\n {\n var defaultValue = def[ prop ];\n var evaluatedValue = evaluate( defaultValue );\n var hasRelation = !!this.$relations[ prop ];\n var relation = this.$getRelation( prop, evaluatedValue );\n\n if ( hasRelation )\n {\n relation.set( this, evaluatedValue );\n }\n }\n }\n }\n\n // Set the remaing properties\n this.$set( props );\n },\n\n $set: function(props, value, remoteData, avoidChange)\n {\n if ( isObject( props ) )\n {\n for (var prop in props)\n {\n this.$set( prop, props[ prop ], remoteData, true );\n }\n }\n else if ( isString( props ) )\n {\n if ( Model.Blocked[ props ] )\n {\n return;\n }\n\n var exists = this.$hasRelation( props );\n var relation = this.$getRelation( props, value, remoteData );\n\n if ( relation )\n {\n if ( exists )\n {\n relation.set( this, value, remoteData );\n }\n }\n else\n {\n this[ props ] = value;\n }\n }\n\n if ( !avoidChange && isValue( props ) )\n {\n this.$trigger( Model.Events.Change, [props, value] );\n }\n },\n\n $get: function(props, copyValues)\n {\n if ( isArray( props ) )\n {\n return grab( this, props, copyValues );\n }\n else if ( isObject( props ) )\n {\n for (var p in props)\n {\n props[ p ] = copyValues ? copy( this[ p ] ) : this[ p ];\n }\n\n return props;\n }\n else if ( isString( props ) )\n {\n if ( Model.Blocked[ props ] )\n {\n return;\n }\n\n var relation = this.$getRelation( props );\n\n if ( relation )\n {\n var values = relation.get( this );\n\n return copyValues ? copy( values ) : values;\n }\n else\n {\n return copyValues ? copy( this[ props ] ) : this[ props ];\n }\n }\n },\n\n $decode: function()\n {\n this.$db.decode( this );\n },\n\n $sync: function(prop, removeUnrelated)\n {\n var relation = this.$getRelation( prop );\n\n if ( relation )\n {\n relation.sync( this, removeUnrelated );\n }\n },\n\n $relate: function(prop, relate, remoteData)\n {\n var relation = this.$getRelation( prop );\n\n if ( relation )\n {\n relation.relate( this, relate, remoteData );\n }\n },\n\n $unrelate: function(prop, unrelated, remoteData)\n {\n var relation = this.$getRelation( prop );\n\n if ( relation )\n {\n relation.unrelate( this, unrelated, remoteData );\n }\n },\n\n $isRelated: function(prop, related)\n {\n var relation = this.$getRelation( prop );\n\n return relation && relation.isRelated( this, related );\n },\n\n $hasRelation: function(prop)\n {\n return prop in this.$relations;\n },\n\n $getRelation: function(prop, initialValue, remoteData)\n {\n var databaseRelations = this.$db.relations;\n var relation = databaseRelations[ prop ];\n\n if ( relation )\n {\n if ( !(prop in this.$relations) )\n {\n relation.load( this, initialValue, remoteData );\n }\n\n return relation;\n }\n\n return false;\n },\n\n $save: function(setProperties, setValue, cascade, options)\n {\n if ( isObject( setProperties ) )\n {\n options = cascade;\n cascade = setValue;\n setValue = undefined;\n }\n else if ( isNumber( setProperties ) )\n {\n options = setValue;\n cascade = setProperties;\n setValue = undefined;\n setProperties = undefined;\n }\n\n if ( !isNumber( cascade ) )\n {\n cascade = this.$db.cascade;\n }\n\n if ( this.$isDeleted() )\n {\n Rekord.debug( Rekord.Debugs.SAVE_DELETED, this.$db, this );\n\n return Promise.resolve( this );\n }\n\n if ( !this.$hasKey() )\n {\n throw 'Key missing from model';\n }\n\n var promise = createModelPromise( this, cascade,\n Model.Events.RemoteSave,\n Model.Events.RemoteSaveFailure,\n Model.Events.RemoteSaveOffline,\n Model.Events.LocalSave,\n Model.Events.LocalSaveFailure\n );\n\n return Promise.singularity( promise, this, function(singularity)\n {\n batchExecute(function()\n {\n this.$touch();\n\n this.$db.addReference( this );\n\n if ( setProperties !== undefined )\n {\n this.$set( setProperties, setValue );\n }\n\n this.$trigger( Model.Events.PreSave, [this] );\n\n this.$db.save( this, cascade, options );\n\n this.$db.pruneModels();\n\n this.$trigger( Model.Events.PostSave, [this] );\n\n }, this );\n });\n },\n\n $remove: function(cascade, options)\n {\n var cascade = isNumber( cascade ) ? cascade : this.$db.cascade;\n\n if ( !this.$exists() )\n {\n return Promise.resolve( this );\n }\n\n var promise = createModelPromise( this, cascade,\n Model.Events.RemoteRemove,\n Model.Events.RemoteRemoveFailure,\n Model.Events.RemoteRemoveOffline,\n Model.Events.LocalRemove,\n Model.Events.LocalRemoveFailure\n );\n\n return Promise.singularity( promise, this, function(singularity)\n {\n batchExecute(function()\n {\n this.$trigger( Model.Events.PreRemove, [this] );\n\n this.$db.remove( this, cascade, options );\n\n this.$trigger( Model.Events.PostRemove, [this] );\n\n }, this );\n });\n },\n\n $refresh: function(cascade, options)\n {\n var promise = createModelPromise( this, cascade,\n Model.Events.RemoteGet,\n Model.Events.RemoteGetFailure,\n Model.Events.RemoteGetOffline,\n Model.Events.LocalGet,\n Model.Events.LocalGetFailure\n );\n\n if ( canCascade( cascade, Cascade.Rest ) )\n {\n this.$addOperation( GetRemote, cascade, options );\n }\n else if ( canCascade( cascade, Cascade.Local ) )\n {\n this.$addOperation( GetLocal, cascade, options );\n }\n else\n {\n promise.resolve( this );\n }\n\n return promise;\n },\n\n $autoRefresh: function(cascade, options)\n {\n var callRefresh = function()\n {\n this.$refresh( cascade, options );\n };\n\n Rekord.on( Rekord.Events.Online, callRefresh, this );\n\n return this;\n },\n\n $cancel: function(reset, options)\n {\n if ( this.$saved )\n {\n this.$save( this.$saved, this.$db.cascade, options );\n }\n else if ( reset )\n {\n this.$reset();\n }\n },\n\n $clone: function(properties)\n {\n // If field is given, evaluate the value and use it instead of value on this object\n // If relation is given, call clone on relation\n\n var db = this.$db;\n var key = db.key;\n var fields = db.fields;\n var relations = db.relations;\n var values = {};\n\n for (var i = 0; i < fields.length; i++)\n {\n var f = fields[ i ];\n\n if ( properties && f in properties )\n {\n values[ f ] = evaluate( properties[ f ] );\n }\n else if ( f in this )\n {\n values[ f ] = copy( this[ f ] );\n }\n }\n\n if ( isString( key ) )\n {\n delete values[ key ];\n }\n\n var cloneKey = db.keyHandler.getKey( values );\n var modelKey = this.$key();\n\n if ( cloneKey === modelKey )\n {\n throw 'A clone cannot have the same key as the original model.';\n }\n\n for (var relationName in relations)\n {\n if ( properties && relationName in properties )\n {\n relations[ relationName ].preClone( this, values, properties[ relationName ] );\n }\n }\n\n var clone = db.instantiate( values );\n var relationValues = {};\n\n for (var relationName in relations)\n {\n if ( properties && relationName in properties )\n {\n relations[ relationName ].postClone( this, relationValues, properties[ relationName ] );\n }\n }\n\n clone.$set( relationValues );\n\n return clone;\n },\n\n $push: function(fields)\n {\n this.$savedState = this.$db.encode( this, grab( this, fields || this.$db.fields, true ), false );\n },\n\n $pop: function(dontDiscard)\n {\n if ( isObject( this.$savedState ) )\n {\n this.$set( this.$savedState );\n\n if ( !dontDiscard )\n {\n this.$discard();\n }\n }\n },\n\n $discard: function()\n {\n this.$savedState = false;\n },\n\n $exists: function()\n {\n return !this.$isDeleted() && this.$db.models.has( this.$key() );\n },\n\n $addOperation: function(OperationType, cascade, options)\n {\n var operation = new OperationType( this, cascade, options );\n\n if ( !this.$operation )\n {\n this.$operation = operation;\n this.$operation.execute();\n }\n else\n {\n this.$operation.queue( operation );\n }\n },\n\n $toJSON: function( forSaving )\n {\n var encoded = this.$db.encode( this, grab( this, this.$db.fields, true ), forSaving );\n\n var databaseRelations = this.$db.relations;\n var relations = this.$relations;\n\n for (var name in relations)\n {\n databaseRelations[ name ].encode( this, encoded, forSaving );\n }\n\n return encoded;\n },\n\n $changed: function()\n {\n this.$trigger( Model.Events.Change );\n },\n\n $updated: function()\n {\n this.$changed();\n this.$db.trigger( Database.Events.ModelUpdated, [this] );\n },\n\n $key: function(quietly)\n {\n if ( !this.$$key )\n {\n this.$$key = this.$db.keyHandler.getKey( this, quietly );\n }\n\n return this.$$key;\n },\n\n $keys: function()\n {\n return this.$db.keyHandler.getKeys( this );\n },\n\n $uid: function()\n {\n return this.$db.name + '$' + this.$key();\n },\n\n $hasKey: function()\n {\n return hasFields( this, this.$db.key, isValue );\n },\n\n $setKey: function(key, skipApplication)\n {\n var db = this.$db;\n var newKey = db.keyHandler.buildKeyFromInput(key);\n var oldKey = this.$$key;\n\n if (newKey !== oldKey)\n {\n if (!db.keyChanges)\n {\n throw 'Key changes are not supported, see the documentation on how to enable key changes.';\n }\n\n db.removeReference( oldKey );\n db.addReference( this, newKey );\n\n this.$$key = newKey;\n\n if ( !skipApplication )\n {\n db.keyHandler.applyKey( newKey, this );\n }\n\n this.$trigger( Model.Events.KeyChange, [this, oldKey, newKey] );\n }\n\n return newKey;\n },\n\n $remote: function(encoded, overwrite)\n {\n this.$db.putRemoteData( encoded, this.$key(), this, overwrite );\n },\n\n $isSynced: function()\n {\n return this.$status === Model.Status.Synced;\n },\n\n $isSaving: function()\n {\n return this.$status === Model.Status.SavePending;\n },\n\n $isPending: function()\n {\n return this.$status === Model.Status.SavePending || this.$status === Model.Status.RemovePending;\n },\n\n $isDeleted: function()\n {\n return this.$status >= Model.Status.RemovePending;\n },\n\n $isSaved: function()\n {\n return !!this.$saved;\n },\n\n $isSavedLocally: function()\n {\n return !!this.$local;\n },\n\n $isNew: function()\n {\n return !(this.$saved || this.$local);\n },\n\n $touch: function()\n {\n if ( this.$db.hasPruning() )\n {\n this.$touched = now();\n }\n },\n\n $project: function(projectionInput)\n {\n var projection = Projection.parse( this.$db, projectionInput );\n\n return projection.project( this );\n },\n\n $getChanges: function(alreadyDecoded)\n {\n var db = this.$db;\n var saved = db.decode( this.$saved, {} );\n var encoded = alreadyDecoded || this;\n var fields = db.saveFields;\n\n return saved ? diff( encoded, saved, fields, equals ) : encoded;\n },\n\n $hasChanges: function(local)\n {\n var compareTo = local ? this.$local : this.$saved;\n\n if (!compareTo)\n {\n return true;\n }\n\n var db = this.$db;\n var ignore = db.ignoredFields;\n var saved = db.decode( compareTo, {} );\n var fields = db.saveFields;\n\n for (var i = 0; i < fields.length; i++)\n {\n var prop = fields[ i ];\n var currentValue = this[ prop ];\n var savedValue = saved[ prop ];\n\n if ( ignore[ prop ] )\n {\n continue;\n }\n\n if ( !equals( currentValue, savedValue ) )\n {\n return true;\n }\n }\n\n return false;\n },\n\n $hasChange: function(prop, local)\n {\n var compareTo = local ? this.$local : this.$saved;\n\n if (!compareTo)\n {\n return true;\n }\n\n var db = this.$db;\n var decoder = db.decodings[ prop ];\n var currentValue = this[ prop ];\n var savedValue = decoder ? decoder( compareTo[ prop ], compareTo, prop ) : compareTo[ prop ];\n\n return !equals( currentValue, savedValue );\n },\n\n $listenForOnline: function(cascade, options)\n {\n if (!this.$offline)\n {\n this.$offline = true;\n\n Rekord.once( Rekord.Events.Online, this.$resume, this );\n }\n\n Class.props(this,\n {\n $resumeCascade: cascade,\n $resumeOptions: options\n });\n },\n\n $resume: function()\n {\n if (this.$status === Model.Status.RemovePending)\n {\n Rekord.debug( Rekord.Debugs.REMOVE_RESUME, this );\n\n this.$addOperation( RemoveRemote, this.$resumeCascade, this.$resumeOptions );\n }\n else if (this.$status === Model.Status.SavePending)\n {\n Rekord.debug( Rekord.Debugs.SAVE_RESUME, this );\n\n this.$addOperation( SaveRemote, this.$resumeCascade, this.$resumeOptions );\n }\n\n this.$offline = false;\n },\n\n toString: function()\n {\n return this.$db.className + ' ' + JSON.stringify( this.$toJSON() );\n }\n\n});\n\naddEventful( Model, true );\n\naddEventFunction( Model, '$change', Model.Events.Changes, true );\n\nfunction createModelPromise(model, cascade, restSuccess, restFailure, restOffline, localSuccess, localFailure)\n{\n var promise = new Promise( null, false );\n\n if ( canCascade( cascade, Cascade.Rest ) )\n {\n var off1 = model.$once( restSuccess, function(data) {\n off2();\n off3();\n promise.resolve( model, data );\n });\n var off2 = model.$once( restFailure, function(data, status) {\n off1();\n off3();\n promise.reject( model, status, data );\n });\n var off3 = model.$once( restOffline, function() {\n off1();\n off2();\n promise.noline( model );\n });\n }\n else if ( canCascade( cascade, Cascade.Local ) )\n {\n var off1 = model.$once( localSuccess, function(data)\n {\n off2();\n promise.resolve( model, data );\n });\n var off2 = model.$once( localFailure, function(data, status)\n {\n off1();\n promise.reject( model, data );\n });\n }\n else\n {\n promise.resolve( model );\n }\n\n return promise;\n}\n\n\n/**\n * A Map has the key-to-value benefits of a map and iteration benefits of an\n * array. This is especially beneficial when most of the time the contents of\n * the structure need to be iterated and order doesn't matter (since removal\n * performs a swap which breaks insertion order).\n *\n * @constructor\n * @memberof Rekord\n */\nfunction Map()\n{\n /**\n * An array of the values in this map.\n * @member {Array}\n */\n this.values = [];\n\n /**\n * An array of the keys in this map.\n * @type {Array}\n */\n this.keys = [];\n\n /**\n * An object of key to index mappings.\n * @type {Object}\n */\n this.indices = {};\n}\n\nClass.create( Map,\n{\n\n /**\n * Resets the map by initializing the values, keys, and indexes.\n *\n * @return {Rekord.Map} -\n * The reference to this map.\n */\n reset: function()\n {\n this.values.length = 0;\n this.keys.length = 0;\n this.indices = {};\n\n return this;\n },\n\n /**\n * Puts the value in the map by the given key.\n *\n * @param {String} key\n * @param {V} value\n * @return {Rekord.Map} -\n * The reference to this map.\n */\n put: function(key, value)\n {\n if ( key in this.indices )\n {\n this.values[ this.indices[ key ] ] = value;\n }\n else\n {\n this.indices[ key ] = this.values.length;\n AP.push.call( this.values, value );\n AP.push.call( this.keys, key );\n }\n\n return this;\n },\n\n /**\n * Returns the value mapped by the given key.\n *\n * @param {String} key\n * @return {V}\n */\n get: function(key)\n {\n return this.values[ this.indices[ key ] ];\n },\n\n /**\n * Removes the value by a given key\n *\n * @param {String} key\n * @return {Rekord.Map} -\n * The reference to this map.\n */\n remove: function(key)\n {\n var index = this.indices[ key ];\n\n if ( isNumber( index ) )\n {\n this.removeAt( index );\n }\n\n return this;\n },\n\n /**\n * Removes the value & key at the given index.\n *\n * @param {Number} index\n * @return {Rekord.Map} -\n * The reference to this map.\n */\n removeAt: function(index)\n {\n var key = this.keys[ index ];\n var lastValue = AP.pop.apply( this.values );\n var lastKey = AP.pop.apply( this.keys );\n\n if ( index < this.values.length )\n {\n this.values[ index ] = lastValue;\n this.keys[ index ] = lastKey;\n this.indices[ lastKey ] = index;\n }\n\n delete this.indices[ key ];\n\n return this;\n },\n\n /**\n * Returns whether this map has a value for the given key.\n *\n * @param {String} key\n * @return {Boolean}\n */\n has: function(key)\n {\n return key in this.indices;\n },\n\n /**\n * Returns the number of elements in the map.\n *\n * @return {Number}\n */\n size: function()\n {\n return this.values.length;\n },\n\n subtract: function(map, dest)\n {\n var out = dest || new Map();\n var n = this.size();\n var values = this.values;\n var keys = this.keys;\n\n for (var i = 0; i < n; i++)\n {\n var v = values[ i ];\n var k = keys[ i ];\n\n if ( !map.has( k ) )\n {\n out.put( k, v );\n }\n }\n\n return out;\n },\n\n /**\n * Passes all values & keys in this map to a callback and if it returns a\n * truthy value then the key and value are placed in the destination map.\n *\n * @param {Function} callback [description]\n * @param {Rekord.Map} [dest] [description]\n * @return {Rekord.Map} [description]\n */\n filter: function(callback, dest)\n {\n var out = dest || new Map();\n var n = this.size();\n var values = this.values;\n var keys = this.keys;\n\n for (var i = 0; i < n; i++)\n {\n var v = values[ i ];\n var k = keys[ i ];\n\n if ( callback( v, k ) )\n {\n out.put( k, v );\n }\n }\n\n return out;\n },\n\n /**\n * Reverses the order of the underlying values & keys.\n *\n * @return {Rekord.Map} -\n * The referense to this map.\n */\n reverse: function()\n {\n reverse( this.values );\n reverse( this.keys );\n\n this.rebuildIndex();\n\n return this;\n },\n\n /**\n *\n * @param {function} comparator [description]\n * @return {Boolean} [description]\n */\n isSorted: function(comparator)\n {\n return isSorted( comparator, this.values );\n },\n\n /**\n * Sorts the underlying values & keys given a value compare function.\n *\n * @param {function} comparator\n * A function which accepts two values and returns a number used for\n * sorting. If the first argument is less than the second argument, a\n * negative number should be returned. If the arguments are equivalent\n * then 0 should be returned, otherwise a positive number should be\n * returned.\n * @return {Map} -\n * The reference to this map.\n */\n sort: function(comparator)\n {\n var map = this;\n\n // Sort this partition!\n function partition(left, right)\n {\n var pivot = map.values[ Math.floor((right + left) / 2) ];\n var i = left;\n var j = right;\n\n while (i <= j)\n {\n while (comparator( map.values[i], pivot ) < 0)\n {\n i++;\n }\n while (comparator( map.values[j], pivot ) > 0)\n {\n j--;\n }\n\n if (i <= j)\n {\n swap( map.values, i, j );\n swap( map.keys, i, j );\n i++;\n j--;\n }\n }\n\n return i;\n }\n\n // Quicksort\n function qsort(left, right)\n {\n var index = partition( left, right );\n\n if (left < index - 1)\n {\n qsort( left, index - 1 );\n }\n\n if (index < right)\n {\n qsort( index, right );\n }\n }\n\n var right = this.size() - 1;\n\n // Are there elements to sort?\n if ( right > 0 )\n {\n qsort( 0, right );\n\n this.rebuildIndex();\n }\n\n return this;\n },\n\n /**\n * Rebuilds the index based on the keys.\n *\n * @return {Rekord.Map} -\n * The reference to this map.\n */\n rebuildIndex: function()\n {\n this.indices = {};\n\n for (var i = 0, l = this.keys.length; i < l; i++)\n {\n this.indices[ this.keys[ i ] ] = i;\n }\n\n return this;\n },\n\n /**\n * Builds an object contain the keys and values in this map.\n *\n * @return {Object} -\n * The built object.\n */\n toObject: function(out)\n {\n var target = out || {};\n var keys = this.keys;\n var values = this.values;\n\n for (var i = 0; i < keys.length; i++)\n {\n target[ keys[ i ] ] = values[ i ];\n }\n\n return target;\n }\n\n});\n\n\nfunction Dependents(subject)\n{\n this.map = {};\n this.listeners = {};\n\n this.subject = subject;\n}\n\nClass.create( Dependents,\n{\n\n add: function(model, relator)\n {\n var key = model.$uid();\n\n this.map[ key ] = model;\n\n if ( model.$db.keyChanges && !this.listeners[ key ] )\n {\n var listener = this.handleKeyChange( relator );\n\n this.listeners[ key ] = model.$on( Model.Events.KeyChange, listener, this );\n }\n },\n\n remove: function(model)\n {\n var key = model.$uid();\n\n evaluate( this.listeners[ key ] );\n\n delete this.listeners[ key ];\n delete this.map[ key ];\n },\n\n handleKeyChange: function(relator)\n {\n return function(model, oldKey, newKey)\n {\n var prefix = model.$db.name + '$';\n\n oldKey = prefix + oldKey;\n newKey = prefix + newKey;\n\n this.listeners[ newKey ] = this.listeners[ oldKey ];\n this.map[ newKey ] = this.map[ oldKey ];\n\n delete this.listeners[ oldKey ];\n delete this.map[ oldKey ];\n\n relator.updateForeignKey( this.subject, model, true );\n };\n },\n\n isSaved: function(callbackOnSaved, contextOnSaved)\n {\n var dependents = this.map;\n var off = noop;\n\n var onDependentSave = function()\n {\n callbackOnSaved.apply( contextOnSaved || this, arguments );\n\n off();\n };\n\n for (var uid in dependents)\n {\n var dependent = dependents[ uid ];\n\n if ( !dependent.$isSaved() )\n {\n off = dependent.$once( Model.Events.RemoteSaves, onDependentSave );\n\n return false;\n }\n }\n\n return true;\n }\n\n});\n\n\n// field\n// relation.field\n// relations[pluckValue]\n// relations?savedWhere[pluckValue]\n// relations{pluckKey: pluckValue}\n// relation(subprojection)\n// relations(subprojection)\n// relations?savedWhere(subprojection)\n// expression|filter\n// expression?savedWhere\n// alias:expression\n// expression#resolve\n// relations@sum=field\n\nfunction Projection(database, input)\n{\n this.database = database;\n this.input = input;\n this.projections = {};\n\n for (var i = 0; i < input.length; i++)\n {\n this.addProjection( input[ i ] );\n }\n}\n\nClass.create( Projection,\n{\n\n addProjection: function(input)\n {\n var projection = this;\n var alias = input;\n var aliasIndex = input.indexOf( Projection.ALIAS_DELIMITER );\n\n if (aliasIndex > 0)\n {\n alias = input.substring( 0, aliasIndex );\n input = input.substring( aliasIndex + 1 );\n }\n\n var word = '';\n var words = [];\n var tokens = ['property'];\n var types = [this.database];\n var i = 0;\n var resolvers = [];\n\n var processWord = function(word)\n {\n if (!word)\n {\n return;\n }\n\n var token = tokens[0];\n var handler = Projection.TOKEN_HANDLER[ token ];\n\n words.unshift( word );\n\n if (handler && handler.post)\n {\n resolvers.push( handler.post( words, tokens, types, projection ) );\n }\n };\n\n var processToken = function(token)\n {\n var handler = Projection.TOKEN_HANDLER[ token ];\n\n tokens.unshift( token );\n\n if (handler && handler.pre)\n {\n resolvers.push( handler.pre( words, tokens, types, projection ) );\n }\n };\n\n for (var i = 0; i < input.length; i++)\n {\n var c = input.charAt( i );\n var token = Projection.TOKENS[ c ];\n\n if (token)\n {\n processWord( word );\n processToken( token );\n\n word = '';\n }\n else\n {\n word += c;\n }\n }\n\n processWord( word );\n\n var resolver = function(value) {\n return value;\n };\n\n for (var i = resolvers.length - 1; i >= 0; i--) {\n resolver = resolvers[ i ]( resolver );\n }\n\n this.projections[ alias ] = resolver;\n },\n\n project: function(model)\n {\n var out = {};\n\n for (var alias in this.projections)\n {\n out[ alias ] = this.projections[ alias ]( model );\n }\n\n return out;\n }\n\n});\n\nProjection.TOKENS =\n{\n '.': 'property',\n '?': 'where',\n '|': 'filter',\n '#': 'resolve',\n '(': 'subStart',\n ')': 'subEnd',\n '[': 'pluckValueStart',\n ']': 'pluckValueEnd',\n '{': 'pluckObjectStart',\n ':': 'pluckObjectDelimiter',\n '}': 'pluckObjectEnd',\n '@': 'aggregateStart',\n '=': 'aggregateProperty'\n};\n\nProjection.TOKEN_HANDLER =\n{\n\n property:\n {\n post: function(words, tokens, types, projection)\n {\n var propertyName = words[0];\n var sourceType = types[0];\n\n if (!(sourceType instanceof Database))\n {\n throw ('The property ' + propertyName + ' can only be taken from a Model');\n }\n\n var relation = sourceType.relations[ propertyName ];\n\n if (relation)\n {\n if (relation instanceof RelationSingle)\n {\n types.unshift( relation.model.Database );\n }\n else\n {\n types.unshift( relation );\n }\n }\n\n var fieldIndex = indexOf( sourceType.fields, propertyName );\n\n if (fieldIndex === false && !relation)\n {\n throw ('The property ' + propertyName + ' does not exist as a field or relation on the Model ' + sourceType.name );\n }\n\n return function(resolver)\n {\n return function(model)\n {\n if ( !isValue( model ) )\n {\n return null;\n }\n\n return resolver( model.$get( propertyName ) );\n };\n };\n }\n },\n\n filter:\n {\n post: function(words, tokens, types, projection)\n {\n var filterName = words[0];\n var filter = Rekord.Filters[ filterName ];\n\n if (!filter)\n {\n throw (filterName + ' is not a valid filter function');\n }\n\n return function(resolver)\n {\n return function(value)\n {\n if ( !isValue( value ) )\n {\n return null;\n }\n\n return resolver( filter( value ) );\n };\n };\n }\n },\n\n resolve:\n {\n post: function(words, tokens, types, projection)\n {\n var resolveName = words[0];\n\n return function(resolver)\n {\n return function(source)\n {\n if ( !isValue( source ) )\n {\n return null;\n }\n\n var value = source[ resolveName ];\n\n if ( isFunction( value ) )\n {\n value = value.apply( source );\n }\n\n return resolver( value );\n };\n };\n }\n },\n\n where:\n {\n post: function(words, tokens, types, projection)\n {\n var whereName = words[0];\n var whereFrom = types[0];\n var where = Rekord.Wheres[ whereName ];\n\n if (!where)\n {\n throw (whereName + ' is not a valid where expression');\n }\n\n if (!(whereFrom instanceof RelationMultiple))\n {\n throw (whereName + ' where expressions can only be used on relations');\n }\n\n return function(resolver)\n {\n return function(relation)\n {\n if ( !isValue( relation ) )\n {\n return null;\n }\n\n return resolver( relation.where( where ) );\n };\n };\n }\n },\n\n aggregateProperty:\n {\n post: function(words, tokens, types, projection)\n {\n var property = words[0];\n var aggregateFunction = words[1];\n var aggregateFrom = types[0];\n\n if (tokens[1] !== 'aggregateStart')\n {\n throw ('Aggregate function syntax error, a = must follow a @');\n }\n\n if (!(aggregateFrom instanceof Relation))\n {\n throw ('Aggregate functions like ' + aggregateFunction + ' from ' + aggregateFrom + ' can only be used on relations');\n }\n\n return function (resolver)\n {\n return function (relation)\n {\n if ( !isValue( relation ) )\n {\n return null;\n }\n\n return resolver( relation[ aggregateFunction ]( property ) );\n };\n };\n }\n },\n\n subEnd:\n {\n pre: function(words, tokens, types, projection)\n {\n var projectionName = words[0];\n var whereFrom = types[0];\n\n if (tokens[1] !== 'subStart')\n {\n throw ('Sub projection syntax error, an ending ) requires a starting (');\n }\n\n if (!(whereFrom instanceof Relation))\n {\n throw ('Sub projections like ' + projectionName + ' from ' + words[1] + ' can only be used on relations');\n }\n\n if (!whereFrom.model.Database.projections[ projectionName ])\n {\n throw ('The projection ' + projectionName + ' does not exist on ' + whereFrom.model.Database.name);\n }\n\n if (whereFrom instanceof RelationSingle)\n {\n return function(resolver)\n {\n return function (relation)\n {\n if ( !isValue( relation ) )\n {\n return null;\n }\n\n return resolver( relation.$project( projectionName ) );\n };\n };\n }\n else\n {\n return function(resolver)\n {\n return function(relations)\n {\n if ( !isValue( relations ) )\n {\n return null;\n }\n\n return resolver( relations.project( projectionName ) );\n };\n };\n }\n }\n },\n\n pluckValueEnd:\n {\n pre: function(words, tokens, types, projection)\n {\n var properties = words[0];\n var whereFrom = types[0];\n\n if (tokens[1] !== 'pluckValueStart')\n {\n throw ('Pluck value syntax error, an ending ] requires a starting [');\n }\n\n if (!(whereFrom instanceof RelationMultiple))\n {\n throw ('Pluck values like ' + properties + ' from ' + words[1] + ' can only be used on relations');\n }\n\n return function (resolver)\n {\n return function (relations)\n {\n if ( !isValue( relations ) )\n {\n return null;\n }\n\n return resolver( relations.pluck( properties ) );\n };\n };\n }\n },\n\n pluckObjectEnd:\n {\n pre: function(words, tokens, types, projection)\n {\n var properties = words[0];\n var keys = words[1];\n var whereFrom = types[0];\n\n if (tokens[1] !== 'pluckObjectDelimiter' || tokens[2] !== 'pluckObjectStart')\n {\n throw ('Pluck object syntax error, must be {key: value}');\n }\n\n if (!(whereFrom instanceof RelationMultiple))\n {\n throw ('Pluck values like ' + properties + ' from ' + words[1] + ' can only be used on relations');\n }\n\n return function (resolver)\n {\n return function (relations)\n {\n if ( !isValue( relations ) )\n {\n return null;\n }\n\n return resolver( relations.pluck( properties, keys ) );\n };\n };\n }\n }\n};\n\nProjection.ALIAS_DELIMITER = ':';\n\nProjection.parse = function(database, input)\n{\n var originalInput = input;\n\n if ( isString( input ) )\n {\n input = database.projections[ input ];\n }\n\n if ( isArray( input ) )\n {\n input = new Projection( database, input );\n }\n\n if (!(input instanceof Projection))\n {\n throw (originalInput + ' is not a valid projection');\n }\n\n return input;\n};\n\n\nfunction Context(models)\n{\n this.databases = [];\n this.alls = [];\n this.models = [];\n\n if ( isEmpty( models ) )\n {\n for (var name in Rekord.classes)\n {\n this.add( name );\n }\n }\n else if ( isArray( models ) )\n {\n for (var i = 0; i < models.length; i++)\n {\n this.add( models[ i ] );\n }\n }\n}\n\nContext.start = function(models)\n{\n var context = new Context( models );\n\n context.apply();\n\n return context;\n};\n\nClass.create( Context,\n{\n\n add: function(type)\n {\n if ( isString( type ) )\n {\n type = Rekord.classes[ type ];\n }\n\n if ( isRekord( type ) )\n {\n type = type.Database;\n }\n\n if ( type instanceof Database )\n {\n this.databases.push( type );\n this.alls.push( {} );\n this.models.push( new ModelCollection( type ) );\n }\n },\n\n getApplied: function()\n {\n var applied = 0;\n\n this.each(function(db)\n {\n if (db.context === this)\n {\n applied++;\n }\n });\n\n return applied / this.databases.length;\n },\n\n apply: function()\n {\n this.each( this.applyDatabase );\n },\n\n applyDatabase: function(db, all, models, i)\n {\n db.all = all;\n db.models = models;\n db.context = this;\n db.contextIndex = i;\n },\n\n discard: function()\n {\n this.each( this.discardDatabase );\n },\n\n discardDatabase: function(db)\n {\n if (db.context === this)\n {\n db.all = db.allCached;\n db.models = db.modelsCached;\n db.context = null;\n db.contextIndex = -1;\n }\n },\n\n destroy: function()\n {\n this.each( this.destroyDatabase );\n\n this.databases.length = 0;\n this.alls.length = 0;\n this.models.length = 0;\n },\n\n destroyDatabase: function(db, alls, models, i)\n {\n this.discardDatabase( db );\n\n this.databases[ i ] = null;\n this.alls[ i ] = null;\n this.models[ i ].clear();\n this.models[ i ] = null;\n },\n\n clear: function(db)\n {\n this.alls[ db.contextIndex ] = {};\n },\n\n each: function(iterator)\n {\n var dbs = this.databases;\n var alls = this.alls;\n var models = this.models;\n\n for (var i = 0; i < dbs.length; i++)\n {\n iterator.call( this, dbs[ i ], alls[ i ], models[ i ], i );\n }\n }\n\n});\n\n\nfunction KeyHandler()\n{\n\n}\n\nClass.create( KeyHandler,\n{\n\n init: function(database)\n {\n this.key = database.key;\n this.keySeparator = database.keySeparator;\n this.database = database;\n },\n\n getKey: function(model, quietly)\n {\n var field = this.key;\n var modelKey = this.buildKey( model, field );\n\n if ( hasFields( model, field, isValue ) )\n {\n return modelKey;\n }\n else if ( !quietly )\n {\n throw 'Composite key not supplied.';\n }\n\n return null;\n },\n\n buildKeyFromRelations: function(input)\n {\n if ( isObject( input ) )\n {\n var relations = this.database.relations;\n\n for (var relationName in relations)\n {\n if ( relationName in input )\n {\n relations[ relationName ].buildKey( input );\n }\n }\n }\n },\n\n buildKeyFromInput: function(input)\n {\n if ( input instanceof this.database.Model )\n {\n return input.$key();\n }\n else if ( isArray( input ) ) // && isArray( this.key )\n {\n return input.join( this.keySeparator );\n }\n else if ( isObject( input ) )\n {\n return this.buildKey( input );\n }\n\n return input;\n }\n\n});\n\n\nfunction KeySimple(database)\n{\n this.init( database );\n}\n\nClass.extend( KeyHandler, KeySimple,\n{\n getKeys: function(model)\n {\n return this.buildKey( model );\n },\n\n removeKey: function(model)\n {\n var field = this.key;\n\n delete model[ field ];\n },\n\n buildKey: function(input, otherFields)\n {\n this.buildKeyFromRelations( input );\n\n var field = otherFields || this.key;\n var key = input[ field ];\n\n if ( !isValue( key ) )\n {\n key = input[ field ] = uuid();\n }\n\n return key;\n },\n\n buildObjectFromKey: function(key)\n {\n var field = this.key;\n var props = {};\n\n props[ field ] = key;\n\n return this.database.instantiate( props );\n },\n\n hasKeyChange: function(a, b)\n {\n var field = this.key;\n var akey = a[ field ];\n var bkey = b[ field ];\n\n return isValue( akey ) && isValue( bkey ) && akey !== bkey;\n },\n\n addToFields: function(out)\n {\n var field = this.key;\n\n if ( indexOf( out, field ) === false )\n {\n out.unshift( field );\n }\n },\n\n isValid: function(key)\n {\n return isValue( key );\n },\n\n copyFields: function(target, targetFields, source, sourceFields)\n {\n var targetValue = target[ targetFields ];\n var sourceValue = source[ sourceFields ];\n\n if ( !isValue( targetValue ) && isValue( sourceValue ) )\n {\n target[ targetFields ] = copy( sourceValue );\n }\n },\n\n inKey: function(field)\n {\n if ( isArray( field ) )\n {\n for (var i = 0; i < field.length; i++)\n {\n if ( field[ i ] === this.key )\n {\n return true;\n }\n }\n\n return false;\n }\n\n return field === this.key;\n },\n\n setKeyField: function(key, field, source, target)\n {\n if ( field === target )\n {\n key[ field ] = source[ this.key ];\n }\n },\n\n applyKey: function(input, target)\n {\n target[ this.key ] = input;\n }\n\n});\n\n\nfunction KeyComposite(database)\n{\n this.init( database );\n}\n\nClass.extend( KeyHandler, KeyComposite,\n{\n getKeys: function(input, otherFields)\n {\n this.buildKeyFromRelations( input );\n\n return pull( input, otherFields || this.key );\n },\n\n removeKey: function(model)\n {\n var fields = this.key;\n\n for (var i = 0; i < fields.length; i++)\n {\n delete model[ fields[ i ] ];\n }\n },\n\n buildKey: function(input, otherFields)\n {\n return this.getKeys( input, otherFields ).join( this.keySeparator );\n },\n\n buildObjectFromKey: function(key)\n {\n var fields = this.key;\n var props = {};\n\n if ( isString( key ) )\n {\n key = key.split( this.keySeparator );\n }\n\n for (var i = 0; i < fields.length; i++)\n {\n props[ fields[ i ] ] = key[ i ];\n }\n\n return this.database.instantiate( props );\n },\n\n hasKeyChange: function(a, b)\n {\n var fields = this.key;\n\n for (var i = 0; i < fields.length; i++)\n {\n var akey = a[ fields[ i ] ];\n var bkey = b[ fields[ i ] ];\n\n if ( isValue( akey ) && isValue( bkey ) && akey !== bkey )\n {\n return true;\n }\n }\n\n return false;\n },\n\n addToFields: function(out)\n {\n var fields = this.key;\n\n for (var i = fields.length - 1; i >= 0; i--)\n {\n if ( indexOf( out, fields[ i ] ) === false )\n {\n out.unshift( fields[ i ] );\n }\n }\n },\n\n isValid: function(key)\n {\n return isValue( key );\n },\n\n copyFields: function(target, targetFields, source, sourceFields)\n {\n for (var i = 0; i < targetFields.length; i++)\n {\n var targetValue = target[ targetFields[ i ] ];\n var sourceValue = source[ sourceFields[ i ] ];\n\n if ( !isValue( targetValue ) && isValue( sourceValue ) )\n {\n target[ targetFields[ i ] ] = copy( sourceValue );\n }\n }\n },\n\n inKey: function(field)\n {\n if ( isArray( field ) )\n {\n for (var i = 0; i < field.length; i++)\n {\n if ( indexOf( this.key, field[ i ] ) !== false )\n {\n return true;\n }\n }\n\n return false;\n }\n\n return indexOf( this.key, field ) !== false;\n },\n\n setKeyField: function(key, field, source, target)\n {\n var index = indexOf( target );\n\n if ( index !== false )\n {\n key[ field ] = source[ this.key[ index ] ];\n }\n },\n\n applyKey: function(input, target)\n {\n var fields = this.key;\n\n if ( isString( input ) )\n {\n input = input.split( this.keySeparator );\n }\n\n for (var i = 0; i < fields.length; i++)\n {\n target[ fields[ i ] ] = input[ i ];\n }\n }\n\n});\n\n\n/**\n * An extension of the Array class adding many useful functions and events. This\n * is the base collection class in Rekord.\n *\n * A collection of any type can be created via {@link Rekord.collect}.\n *\n * ```\n * var nc = new Rekord.Collection([1, 2, 3, 4]);\n * ```\n *\n * @constructor\n * @memberof Rekord\n * @augments Rekord.Eventful\n * @extends Array\n * @param {Array} [values] 0\n * The initial set of values in this collection.\n * @see Rekord.collect\n */\nfunction Collection(values)\n{\n this.addAll( values, true );\n}\n\n/**\n* A comparator to keep the collection sorted with.\n*\n* @memberof Rekord.Collection#\n* @member {comparisonCallback} [comparator]\n*/\n\n/**\n * The events a collection can emit.\n *\n * {@link Rekord.Collection#event:add Add}\n * {@link Rekord.Collection#event:adds Adds}\n * {@link Rekord.Collection#event:sort Sort}\n * {@link Rekord.Collection#event:remove Remove}\n * {@link Rekord.Collection#event:removes Removes}\n * {@link Rekord.Collection#event:updates Updates}\n * {@link Rekord.Collection#event:reset Reset}\n * {@link Rekord.Collection#event:cleared Cleared}\n * {@link Rekord.Collection#event:changes Changes}\n *\n * @static\n */\nCollection.Events =\n{\n /**\n * An event triggered when a single value is added to a collection.\n *\n * @event Rekord.Collection#add\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @argument {T} value -\n * The value added.\n * @argument {number} index -\n * The index where the value was added.\n * @see Rekord.Collection#add\n * @see Rekord.Collection#insertAt\n * @see Rekord.ModelCollection#add\n * @see Rekord.ModelCollection#push\n */\n Add: 'add',\n\n /**\n * An event triggered when multiple values are added to a collection.\n *\n * @event Rekord.Collection#adds\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @argument {T[]} value -\n * The values added.\n * @argument {number|number[]} indices -\n * The index or indices where the values were added.\n * @see Rekord.Collection#addAll\n * @see Rekord.ModelCollection#addAll\n */\n Adds: 'adds',\n\n /**\n * An event triggered when a collection is sorted. This may automatically\n * be triggered by any method that modifies the collection.\n *\n * @event Rekord.Collection#sort\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @see Rekord.Collection#sort\n * @see Rekord.ModelCollection#sort\n */\n Sort: 'sort',\n\n /**\n * An event triggered when a collection has an element removed at a given index.\n *\n * @event Rekord.Collection#remove\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @argument {Any} removing -\n * The element that was removed.\n * @argument {Number} index -\n * The index where the element was removed at.\n * @see Rekord.Collection#remove\n * @see Rekord.Collection#removeAt\n * @see Rekord.ModelCollection#remove\n */\n Remove: 'remove',\n\n /**\n * An event triggered when a collection has multiple elements removed.\n *\n * @event Rekord.Collection#removes\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @argument {Any[]} removed -\n * The array of elements removed from the collection.\n * @argument {number|number[]} indices -\n * The index where the values were removed OR the array of indices.\n * @argument {number} [removeCount] -\n * The number of values removed at the given index (undefined if array specified).\n * @see Rekord.Collection#removeAll\n * @see Rekord.Collection#removeWhere\n */\n Removes: 'removes',\n\n /**\n * An event triggered when a collection has elements modified.\n *\n * @event Rekord.Collection#updates\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @argument {Array} updated -\n * The array of elements modified.\n * @see Rekord.ModelCollection#update\n * @see Rekord.ModelCollection#updateWhere\n */\n Updates: 'updates',\n\n /**\n * An event triggered when a collection's elements are entirely replaced by\n * a new set of elements.\n *\n * @event Rekord.Collection#reset\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @argument {Array} updated -\n * The array of elements modified.\n * @see Rekord.FilteredCollection#sync\n * @see Rekord.ModelCollection#reset\n */\n Reset: 'reset',\n\n /**\n * An event triggered when a collection is cleared of all elements.\n *\n * @event Rekord.Collection#cleared\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @see Rekord.Collection#clear\n */\n Cleared: 'cleared',\n\n /**\n * All events triggered by a collection when the contents of the collection changes.\n *\n * @event Rekord.Collection#changes\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n */\n Changes: 'add adds sort remove removes updates reset cleared'\n\n};\n\nClass.extend( Array, Collection,\n{\n\n /**\n * Sets the comparator for this collection and performs a sort.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {ComparatorInput} comparator -\n * The comparator input to convert to a comparison function.\n * @param {Boolean} [nullsFirst=false] -\n * When a comparison is done involving a null/undefined value this can\n * determine which is ordered before the other.\n * @emits Rekord.Collection#sort\n * @see Rekord.createComparator\n * @return {Rekord.Collection}\n */\n setComparator: function(comparator, nullsFirst)\n {\n this.comparator = createComparator( comparator, nullsFirst );\n this.sort();\n\n return this;\n },\n\n /**\n * Adds a comparator to the existing comparator. This added comparator is ran\n * after the current comparator when it finds two elements equal. If no\n * comparator exists on this collection then it's set to the given comparator.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {ComparatorInput} comparator -\n * The comparator input to convert to a comparison function.\n * @param {Boolean} [nullsFirst=false] -\n * When a comparison is done involving a null/undefined value this can\n * determine which is ordered before the other.\n * @emits Rekord.Collection#sort\n * @see Rekord.createComparator\n * @return {Rekord.Collection}\n */\n addComparator: function(comparator, nullsFirst)\n {\n this.comparator = addComparator( this.comparator, comparator, nullsFirst );\n this.sort();\n\n return this;\n },\n\n /**\n * Determines if the collection is currently sorted based on the current\n * comparator of the collection unless a comparator is given\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {ComparatorInput} [comparator] -\n * The comparator input to convert to a comparison function.\n * @param {Boolean} [nullsFirst=false] -\n * When a comparison is done involving a null/undefined value this can\n * determine which is ordered before the other.\n * @see Rekord.createComparator\n * @return {Boolean}\n */\n isSorted: function(comparator, nullsFirst)\n {\n var cmp = comparator ? createComparator( comparator, nullsFirst ) : this.comparator;\n\n return isSorted( cmp, this );\n },\n\n /**\n * Sorts the elements in this collection based on the current comparator\n * unless a comparator is given. If a comparator is given it will not override\n * the current comparator, subsequent operations to the collection may trigger\n * a sort if the collection has a comparator.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {ComparatorInput} [comparator] -\n * The comparator input to convert to a comparison function.\n * @param {Boolean} [nullsFirst=false] -\n * When a comparison is done involving a null/undefined value this can\n * determine which is ordered before the other.\n * @param {Boolean} [ignorePrimitive=false] -\n * Sorting is automatically done for non-primitive collections if a\n * comparator exists. This flag ensures primitive collections aren't sorted\n * after every operation.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#sort\n * @see Rekord.createComparator\n */\n sort: function(comparator, nullsFirst, ignorePrimitive)\n {\n var cmp = comparator ? createComparator( comparator, nullsFirst ) : this.comparator;\n\n if ( !isSorted( cmp, this ) || ( !ignorePrimitive && !cmp && isPrimitiveArray( this ) ) )\n {\n AP.sort.call( this, cmp );\n\n this.trigger( Collection.Events.Sort, [this] );\n }\n\n return this;\n },\n\n /**\n * Resets the values in this collection with a new collection of values.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Any[]} [values] -\n * The new array of values in this collection.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#reset\n */\n reset: function(values)\n {\n this.length = 0;\n\n if ( isArray( values ) )\n {\n AP.push.apply( this, values );\n }\n else if ( isValue( values ) )\n {\n AP.push.call( this, values );\n }\n\n this.trigger( Collection.Events.Reset, [this] );\n this.sort( undefined, undefined, true );\n\n return this;\n },\n\n /**\n * Creates a limited view of this collection known as a page. The resulting\n * page object changes when this collection changes. At the very least the\n * page size is required, and a starting page index can be specified.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Number} pageSize -\n * The maximum number of elements allowed in the page at once.\n * @param {Number} [pageIndex=0]\n * The starting page offset. This isn't an element offset, but the element\n * offset can be calculated by multiplying the page index by the page size.\n * @return {Rekord.Page} -\n * The newly created Page.\n */\n page: function(pageSize, pageIndex)\n {\n return new Page( this, pageSize, pageIndex );\n },\n\n /**\n * Creates a sub view of this collection known as a filtered collection. The\n * resulting collection changes when this collection changes. Any time an\n * element is added or removed to this collection it may be added or removed\n * from the filtered collection if it fits the filter function. The filter\n * function is created by passing the arguments of this function to\n * {@link Rekord.createWhere}.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.FilteredCollection} -\n * The newly created live filtered view of this collection.\n * @see Rekord.createWhere\n */\n filtered: function(whereProperties, whereValue, whereEquals)\n {\n var filter = createWhere( whereProperties, whereValue, whereEquals );\n\n return FilteredCollection.create( this, filter );\n },\n\n /**\n * Creates a copy of this collection with elements that match the supplied\n * parameters. The parameters are passed to the {@link Rekord.createWhere}\n * to generate a function which tests each element of this collection for\n * inclusion in the newly created collection.\n *\n * ```javascript\n * var isEven = function() { return x % 2 == 0; };\n * var c = Rekord.collect(1, 2, 3, 4, 5);\n * var w = c.where(isEven); // [2, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @param {Array} [out=this.cloneEmpty()] -\n * The array to place the elements that match.\n * @return {Rekord.Collection} -\n * The copy of this collection ran through a filtering function.\n * @see Rekord.createWhere\n */\n where: function(whereProperties, whereValue, whereEquals, out)\n {\n var where = createWhere( whereProperties, whereValue, whereEquals );\n var target = out || this.cloneEmpty();\n\n for (var i = 0; i < this.length; i++)\n {\n var a = this[ i ];\n\n if ( where( a ) )\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Returns a collection with elements that exist in this collection but does\n * not exist in the given collection.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * var b = Rekord.collect(1, 3, 5);\n * var c = a.subtract( b ); // [2, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Array} collection -\n * The array of elements that shouldn't exist in the resulting collection.\n * @param {Array} [out=this.cloneEmpty()] -\n * The array to place the elements that exist in this collection but not in\n * the given collection. If this is not given - a collection of this type\n * will be created.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to an element that exists in the given\n * collection.\n * @return {Array} -\n * The collection of elements that exist in this collection and not the\n * given collection.\n */\n subtract: function(collection, out, equals)\n {\n var target = out || this.cloneEmpty();\n var equality = equals || equalsStrict;\n\n for (var i = 0; i < this.length; i++)\n {\n var a = this[ i ];\n var exists = false;\n\n for (var j = 0; j < collection.length && !exists; j++)\n {\n exists = equality( a, collection[ j ] );\n }\n\n if (!exists)\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Returns a collection of elements that are shared between this collection\n * and the given collection.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * var b = Rekord.collect(1, 3, 5);\n * var c = a.intersect( b ); // [1, 3]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Array} collection -\n * The collection of elements to intersect with this collection.\n * @param {Array} [out=this.cloneEmpty()] -\n * The array to place the elements that exist in both this collection and\n * the given collection. If this is not given - a collection of this type\n * will be created.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to an element that exists in the given\n * collection.\n * @return {Array} -\n * The collection of elements that exist in both collections.\n */\n intersect: function(collection, out, equals)\n {\n var target = out || this.cloneEmpty();\n var equality = equals || equalsStrict;\n\n for (var i = 0; i < collection.length; i++)\n {\n var a = collection[ i ];\n var exists = false;\n\n for (var j = 0; j < this.length && !exists; j++)\n {\n exists = equality( a, this[ j ] );\n }\n\n if (exists)\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Returns a collection of elements that exist in the given collection but\n * not in this collection.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * var b = Rekord.collect(1, 3, 5);\n * var c = a.complement( b ); // [5]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Array} collection -\n * The array of elements that could exist in the resulting collection.\n * @param {Array} [out=this.cloneEmpty()] -\n * The array to place the elements that exist in given collection but not\n * in this collection. If this is not given - a collection of this type\n * will be created.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to an element that exists in the given\n * collection.\n * @return {Array} -\n * The collection of elements that exist in the given collection and not\n * this collection.\n */\n complement: function(collection, out, equals)\n {\n var target = out || this.cloneEmpty();\n var equality = equals || equalsStrict;\n\n for (var i = 0; i < collection.length; i++)\n {\n var a = collection[ i ];\n var exists = false;\n\n for (var j = 0; j < this.length && !exists; j++)\n {\n exists = equality( a, this[ j ] );\n }\n\n if (!exists)\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Clears all elements from this collection.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * a.clear(); // []\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#sort\n */\n clear: function()\n {\n this.length = 0;\n this.trigger( Collection.Events.Cleared, [this] );\n\n return this;\n },\n\n\n /**\n * Adds an element to this collection - sorting the collection if a\n * comparator is set on this collection and `delaySort` is not a specified or\n * a true value.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * a.add( 5 ); // [1, 2, 3, 4, 5]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Any} value -\n * The value to add to this collection.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#add\n * @emits Rekord.Collection#sort\n */\n add: function(value, delaySort)\n {\n var i = this.length;\n\n AP.push.call( this, value );\n\n this.trigger( Collection.Events.Add, [this, value, i] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n\n return this;\n },\n\n /**\n * Adds one or more elements to the end of this collection - sorting the\n * collection if a comparator is set on this collection.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * a.push( 5, 6, 7 ); // 7\n * a // [1, 2, 3, 4, 5, 6, 7]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {...Any} value -\n * The values to add to this collection.\n * @return {Number} -\n * The new length of this collection.\n * @emits Rekord.Collection#add\n * @emits Rekord.Collection#sort\n */\n push: function()\n {\n var values = AP.slice.apply(arguments);\n var i = this.length;\n\n AP.push.apply( this, values );\n\n this.trigger( Collection.Events.Adds, [this, values, i] );\n\n this.sort( undefined, undefined, true );\n\n return this.length;\n },\n\n /**\n * Adds one or more elements to the beginning of this collection - sorting the\n * collection if a comparator is set on this collection.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * a.unshift( 5, 6, 7 ); // 7\n * a // [5, 6, 7, 1, 2, 3, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {...Any} value -\n * The values to add to this collection.\n * @return {Number} -\n * The new length of this collection.\n * @emits Rekord.Collection#adds\n * @emits Rekord.Collection#sort\n */\n unshift: function()\n {\n var values = arguments;\n\n AP.unshift.apply( this, values );\n\n this.trigger( Collection.Events.Adds, [this, AP.slice.apply(values), 0] );\n\n this.sort( undefined, undefined, true );\n\n return this.length;\n },\n\n /**\n * Adds all elements in the given array to this collection - sorting the\n * collection if a comparator is set on this collection and `delaySort` is\n * not specified or a true value.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * a.addAll( [5, 6] ); // [1, 2, 3, 4, 5, 6]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Any[]} values -\n * The values to add to this collection.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#adds\n * @emits Rekord.Collection#sort\n */\n addAll: function(values, delaySort)\n {\n if ( isArray( values ) && values.length )\n {\n var i = this.length;\n\n AP.push.apply( this, values );\n\n this.trigger( Collection.Events.Adds, [this, values, i] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n }\n\n return this;\n },\n\n /**\n * Inserts an element into this collection at the given index - sorting the\n * collection if a comparator is set on this collection and `delaySort` is not\n * specified or a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.insertAt( 0, 0 ); // [0, 1, 2, 3, 4]\n * c.insertAt( 2, 1.5 ); // [0, 1, 1.5, 2, 3, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Number} i -\n * The index to insert the element at.\n * @param {Any} value -\n * The value to insert into the collection.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#add\n * @emits Rekord.Collection#sort\n */\n insertAt: function(i, value, delaySort)\n {\n AP.splice.call( this, i, 0, value );\n\n this.trigger( Collection.Events.Add, [this, value, i] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n\n return this;\n },\n\n /**\n * Removes the last element in this collection and returns it - sorting the\n * collection if a comparator is set on this collection and `delaySort` is\n * no specified or a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.pop(); // 4\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Any} -\n * The element removed from the end of the collection.\n * @emits Rekord.Collection#remove\n * @emits Rekord.Collection#sort\n */\n pop: function(delaySort)\n {\n var removed = AP.pop.apply( this );\n var i = this.length;\n\n this.trigger( Collection.Events.Remove, [this, removed, i] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n\n return removed;\n },\n\n /**\n * Removes the first element in this collection and returns it - sorting the\n * collection if a comparator is set on this collection and `delaySort` is\n * no specified or a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.shift(); // 1\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Any} -\n * The element removed from the beginning of the collection.\n * @emits Rekord.Collection#remove\n * @emits Rekord.Collection#sort\n */\n shift: function(delaySort)\n {\n var removed = AP.shift.apply( this );\n\n this.trigger( Collection.Events.Remove, [this, removed, 0] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n\n return removed;\n },\n\n /**\n * Removes the element in this collection at the given index `i` - sorting\n * the collection if a comparator is set on this collection and `delaySort` is\n * not specified or a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.removeAt( 1 ); // 2\n * c.removeAt( 5 ); // undefined\n * c // [1, 3, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Number} i -\n * The index of the element to remove.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Any} -\n * The element removed, or undefined if the index was invalid.\n * @emits Rekord.Collection#remove\n * @emits Rekord.Collection#sort\n */\n removeAt: function(i, delaySort)\n {\n var removing;\n\n if (i >= 0 && i < this.length)\n {\n removing = this[ i ];\n\n AP.splice.call( this, i, 1 );\n this.trigger( Collection.Events.Remove, [this, removing, i] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n }\n\n return removing;\n },\n\n /**\n * Removes the given value from this collection if it exists - sorting the\n * collection if a comparator is set on this collection and `delaySort` is not\n * specified or a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.remove( 1 ); // 1\n * c.remove( 5 ); // undefined\n * c // [2, 3, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Any} value -\n * The value to remove from this collection if it exists.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to the given value.\n * @return {Any} -\n * The element removed from this collection.\n * @emits Rekord.Collection#remove\n * @emits Rekord.Collection#sort\n */\n remove: function(value, delaySort, equals)\n {\n var i = this.indexOf( value, equals );\n var element = this[ i ];\n\n if ( i !== -1 )\n {\n this.removeAt( i, delaySort );\n }\n\n return element;\n },\n\n /**\n * Removes the given values from this collection - sorting the collection if\n * a comparator is set on this collection and `delaySort` is not specified or\n * a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.removeAll( [1, 5] ); // [1]\n * c // [2, 3, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Any[]} values -\n * The values to remove from this collection if they exist.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to any of the given values.\n * @return {Any[]} -\n * The elements removed from this collection.\n * @emits Rekord.Collection#removes\n * @emits Rekord.Collection#sort\n */\n removeAll: function(values, delaySort, equals)\n {\n var removed = [];\n var removedIndices = [];\n\n if ( isArray( values ) && values.length )\n {\n for (var i = 0; i < values.length; i++)\n {\n var value = values[ i ];\n var k = this.indexOf( value, equals );\n\n if ( k !== -1 )\n {\n removedIndices.push( k );\n removed.push( value );\n }\n }\n\n removedIndices.sort();\n\n for (var i = removedIndices.length - 1; i >= 0; i--)\n {\n AP.splice.call( this, removedIndices[ i ], 1 );\n }\n\n this.trigger( Collection.Events.Removes, [this, removed, removedIndices] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n }\n\n return removed;\n },\n\n /**\n * Removes elements from this collection that meet the specified criteria. The\n * given criteria are passed to {@link Rekord.createWhere} to create a filter\n * function. All elements removed are returned\n *\n * ```javascript\n * var isEven = function(x) { return x % 2 === 0; };\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.removeWhere( isEven ); // [2, 4];\n * c // [1, 3]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @param {Array} [out=this.cloneEmpty()] -\n * The array to place the elements that match.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#removes\n * @emits Rekord.Collection#sort\n * @see Rekord.createWhere\n */\n removeWhere: function(whereProperties, whereValue, whereEquals, out, delaySort)\n {\n var where = createWhere( whereProperties, whereValue, whereEquals );\n var removed = out || this.cloneEmpty();\n var removedIndices = [];\n\n for (var i = 0; i < this.length; i++)\n {\n var value = this[ i ];\n\n if ( where( value ) )\n {\n removedIndices.push( i );\n removed.push( value );\n }\n }\n\n for (var i = removedIndices.length - 1; i >= 0; i--)\n {\n AP.splice.call( this, removedIndices[ i ], 1 );\n }\n\n this.trigger( Collection.Events.Removes, [this, removed, removedIndices] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n\n return removed;\n },\n\n /**\n * Splices elements out of and into this collection - sorting the collection\n * if a comparator is set on this collection.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Number} start -\n * Index at which to start changing the array (with origin 0). If greater\n * than the length of the array, actual starting index will be set to the\n * length of the array. If negative, will begin that many elements from the end.\n * @param {Number} deleteCount -\n * An integer indicating the number of old array elements to remove. If\n * deleteCount is 0, no elements are removed. In this case, you should\n * specify at least one new element. If deleteCount is greater than the\n * number of elements left in the array starting at start, then all of the\n * elements through the end of the array will be deleted.\n * If deleteCount is omitted, deleteCount will be equal to (arr.length - start).\n * @param {...Any} values -\n * The elements to add to the array, beginning at the start index. If you\n * don't specify any elements, splice() will only remove elements from the array.\n * @return {Any[]} -\n * The array of deleted elements.\n * @emits Rekord.Collection#removes\n * @emits Rekord.Collection#adds\n * @emits Rekord.Collection#sort\n */\n splice: function(start, deleteCount)\n {\n var adding = AP.slice.call( arguments, 2 );\n var removed = AP.splice.apply( this, arguments );\n\n if ( deleteCount )\n {\n this.trigger( Collection.Events.Removes, [this, removed, start, deleteCount] );\n }\n\n if ( adding.length )\n {\n this.trigger( Collection.Events.Adds, [this, adding, start] );\n }\n\n this.sort( undefined, undefined, true );\n\n return removed;\n },\n\n /**\n * Reverses the order of elements in this collection.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.reverse(); // [4, 3, 2, 1]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#updates\n */\n reverse: function()\n {\n if ( AP.reverse )\n {\n AP.reverse.apply( this );\n }\n else\n {\n reverse( this );\n }\n\n this.trigger( Collection.Events.Updates, [this] );\n\n return this;\n },\n\n /**\n * Returns the index of the given element in this collection or returns -1\n * if the element doesn't exist in this collection.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.indexOf( 1 ); // 0\n * c.indexOf( 2 ); // 1\n * c.indexOf( 5 ); // -1\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Any} value -\n * The value to search for.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to the given value.\n * @return {Number} -\n * The index of the element in this collection or -1 if it was not found.\n * @see Rekord.equals\n * @see Rekord.equalsStrict\n */\n indexOf: function(value, equals)\n {\n var equality = equals || equalsStrict;\n\n for (var i = 0; i < this.length; i++)\n {\n if ( equality( value, this[ i ] ) )\n {\n return i;\n }\n }\n\n return -1;\n },\n\n /**\n * Returns the element with the minimum value given a comparator.\n *\n * ```javascript\n * var c = Rekord.collect({age: 4}, {age: 5}, {age: 6}, {age: 3});\n * c.minModel('age'); // {age: 3}\n * c.minModel('-age'); // {age: 6}\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {comparatorInput} comparator -\n * The comparator which calculates the minimum model.\n * @param {Any} [startingValue]\n * The initial minimum value. If a value is specified, it's compared\n * against all elements in this collection until the comparator function\n * finds a more minimal value. If it doesn't - this is the value returned.\n * @return {Any} -\n * The minimum element in the collection given the comparator function.\n * @see Rekord.createComparator\n */\n minModel: function(comparator, startingValue)\n {\n var cmp = createComparator( comparator || this.comparator, false );\n var min = startingValue;\n\n for (var i = 0; i < this.length; i++)\n {\n if ( cmp( min, this[i] ) > 0 )\n {\n min = this[i];\n }\n }\n\n return min;\n },\n\n /**\n * Returns the element with the maximum value given a comparator.\n *\n * ```javascript\n * var c = Rekord.collect({age: 4}, {age: 5}, {age: 6}, {age: 3});\n * c.maxModel('age'); // {age: 6}\n * c.maxModel('-age'); // {age: 3}\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {comparatorInput} comparator -\n * The comparator which calculates the maximum model.\n * @param {Any} [startingValue] -\n * The initial maximum value. If a value is specified, it's compared\n * against all elements in this collection until the comparator function\n * finds a more maximal value. If it doesn't - this is the value returned.\n * @return {Any} -\n * The maximum element in the collection given the comparator function.\n * @see Rekord.createComparator\n */\n maxModel: function(comparator, startingValue)\n {\n var cmp = createComparator( comparator || this.comparator, true );\n var max = startingValue;\n\n for (var i = 0; i < this.length; i++)\n {\n if ( cmp( max, this[i] ) < 0 )\n {\n max = this[i];\n }\n }\n\n return max;\n },\n\n /**\n * Returns the minimum value for the given property expression out of all the\n * elements this collection.\n *\n * ```javascript\n * var c = Rekord.collect({age: 6}, {age: 5}, {notage: 5});\n * c.min('age'); // 5\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [properties] -\n * The expression which takes an element in this container and resolves a\n * value that can be compared to the current minimum.\n * @param {Any} [startingValue] -\n * The initial minimum value. If a value is specified, it's compared\n * against all elements in this collection until the comparator function\n * finds a more minimal value. If it doesn't - this is the value returned.\n * @param {compareCallback} [compareFunction=Rekord.compare] -\n * A comparison function to use.\n * @return {Any} -\n * The minimum value found.\n * @see Rekord.createPropertyResolver\n * @see Rekord.compare\n */\n min: function(properties, startingValue, compareFunction)\n {\n var comparator = compareFunction || compare;\n var resolver = createPropertyResolver( properties );\n var min = startingValue;\n\n for (var i = 0; i < this.length; i++)\n {\n var resolved = resolver( this[ i ] );\n\n if ( comparator( min, resolved, false ) > 0 )\n {\n min = resolved;\n }\n }\n\n return min;\n },\n\n /**\n * Returns the maximum value for the given property expression out of all the\n * elements this collection.\n *\n * ```javascript\n * var c = Rekord.collect({age: 6}, {age: 5}, {notage: 5});\n * c.max('age'); // 6\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [properties] -\n * The expression which takes an element in this container and resolves a\n * value that can be compared to the current maximum.\n * @param {Any} [startingValue] -\n * The initial maximum value. If a value is specified, it's compared\n * against all elements in this collection until the comparator function\n * finds a more maximal value. If it doesn't - this is the value returned.\n * @param {compareCallback} [compareFunction=Rekord.compare] -\n * A comparison function to use.\n * @return {Any} -\n * The maximum value found.\n * @see Rekord.createPropertyResolver\n * @see Rekord.compare\n */\n max: function(properties, startingValue, compareFunction)\n {\n var comparator = compareFunction || compare;\n var resolver = createPropertyResolver( properties );\n var max = startingValue;\n\n for (var i = 0; i < this.length; i++)\n {\n var resolved = resolver( this[ i ] );\n\n if ( comparator( max, resolved, true ) < 0 )\n {\n max = resolved;\n }\n }\n\n return max;\n },\n\n /**\n * Returns the first element where the given expression is true.\n *\n * ```javascript\n * var c = Rekord.collect([{x: 5}, {y: 6}, {y: 6, age: 8}, {z: 7}]);\n * c.firstWhere('y', 6); // {x: 6}\n * c.firstWhere(); // {x: 5}\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [whereProperties] -\n * The expression used to create a function to test the elements in this\n * collection.\n * @param {Any} [whereValue] -\n * When the first argument is a string this argument will be treated as a\n * value to compare to the value of the named property on the object passed\n * through the filter function.\n * @param {equalityCallback} [whereEquals=Rekord.equalsStrict] -\n * An alternative function can be used to compare to values.\n * @return {Any} -\n * The first element in this collection that matches the given expression.\n * @see Rekord.createWhere\n */\n firstWhere: function(whereProperties, whereValue, whereEquals)\n {\n var where = createWhere( whereProperties, whereValue, whereEquals );\n\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n\n if ( where( model ) )\n {\n return model;\n }\n }\n\n return null;\n },\n\n /**\n * Returns the first non-null value in this collection given a property\n * expression. If no non-null values exist for the given property expression,\n * then undefined will be returned.\n *\n * ```javascript\n * var c = Rekord.collect([{x: 5}, {y: 6}, {y: 4}, {z: 7}]);\n * c.first('y'); // 6\n * c.first(); // {x: 5}\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [properties] -\n * The expression which converts one value into another.\n * @return {Any} -\n * @see Rekord.createPropertyResolver\n * @see Rekord.isValue\n */\n first: function(properties)\n {\n var resolver = createPropertyResolver( properties );\n\n for (var i = 0; i < this.length; i++)\n {\n var resolved = resolver( this[ i ] );\n\n if ( isValue( resolved ) )\n {\n return resolved;\n }\n }\n },\n\n /**\n * Returns the last element where the given expression is true.\n *\n * ```javascript\n * var c = Rekord.collect([{x: 5}, {y: 6}, {y: 6, age: 8}, {z: 7}]);\n * c.lastWhere('y', 6); // {x: 6, age: 8}\n * c.lastWhere(); // {z: 7}\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [properties] -\n * The expression used to create a function to test the elements in this\n * collection.\n * @param {Any} [value] -\n * When the first argument is a string this argument will be treated as a\n * value to compare to the value of the named property on the object passed\n * through the filter function.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * An alternative function can be used to compare to values.\n * @return {Any} -\n * The last element in this collection that matches the given expression.\n * @see Rekord.createWhere\n */\n lastWhere: function(properties, value, equals)\n {\n var where = createWhere( properties, value, equals );\n\n for (var i = this.length - 1; i >= 0; i--)\n {\n var model = this[ i ];\n\n if ( where( model ) )\n {\n return model;\n }\n }\n\n return null;\n },\n\n /**\n * Returns the last non-null value in this collection given a property\n * expression. If no non-null values exist for the given property expression,\n * then undefined will be returned.\n *\n * ```javascript\n * var c = Rekord.collect([{x: 5}, {y: 6}, {y: 4}, {z: 7}]);\n * c.last('y'); // 4\n * c.last(); // {z: 7}\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [properties] -\n * The expression which converts one value into another.\n * @return {Any} -\n * @see Rekord.createPropertyResolver\n * @see Rekord.isValue\n */\n last: function(properties)\n {\n var resolver = createPropertyResolver( properties );\n\n for (var i = this.length - 1; i >= 0; i--)\n {\n var resolved = resolver( this[ i ] );\n\n if ( isValue( resolved ) )\n {\n return resolved;\n }\n }\n },\n\n /**\n * Iterates over all elements in this collection and passes them through the\n * `resolver` function. The returned value is passed through the `validator`\n * function and if that returns true the resolved value is passed through the\n * `process` function. After iteration, the `getResult` function is executed\n * and the returned value is returned by this function.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Function} resolver -\n * The function which takes an element in this collection and returns a\n * value based on that element.\n * @param {Function} validator -\n * The function which takes the resolved value and determines whether it\n * passes some test.\n * @param {Function} process -\n * The function which is given the resolved value if it passes the test.\n * @param {Function} getResult -\n * The function which is executed at the end of iteration and the result is\n * is returned by this function.\n * @return {Any} -\n * The value returned by `getResult`.\n */\n aggregate: function(resolver, validator, process, getResult)\n {\n for (var i = 0; i < this.length; i++)\n {\n var resolved = resolver( this[ i ] );\n\n if ( validator( resolved ) )\n {\n process( resolved );\n }\n }\n\n return getResult();\n },\n\n /**\n * Sums all numbers resolved from the given property expression and returns\n * the result.\n *\n * ```javascript\n * var c = Rekord.collect([2, 3, 4]);\n * c.sum(); // 9\n * var d = Rekord.collect([{age: 5}, {age: 4}, {age: 2}]);\n * d.sum('age'); // 11\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [numbers]\n * The expression which converts an element in this collection to a number.\n * @return {Number} -\n * The sum of all valid numbers found in this collection.\n * @see Rekord.createNumberResolver\n */\n sum: function(numbers)\n {\n var resolver = createNumberResolver( numbers );\n var result = 0;\n\n function process(x)\n {\n result += x;\n }\n\n function getResult()\n {\n return result;\n }\n\n return this.aggregate( resolver, isNumber, process, getResult );\n },\n\n /**\n * Averages all numbers resolved from the given property expression and\n * returns the result.\n *\n * ```javascript\n * var c = Rekord.collect([2, 3, 4]);\n * c.avg(); // 3\n * var d = Rekord.collect([{age: 5}, {age: 4}, {age: 2}]);\n * d.avg('age'); // 3.66666\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [numbers]\n * The expression which converts an element in this collection to a number.\n * @return {Number} -\n * The average of all valid numbers found in this collection.\n * @see Rekord.createNumberResolver\n */\n avg: function(numbers)\n {\n var resolver = createNumberResolver( numbers );\n var result = 0;\n var total = 0;\n\n function process(x)\n {\n result += x;\n total++;\n }\n\n function getResult()\n {\n return total === 0 ? 0 : result / total;\n }\n\n return this.aggregate( resolver, isNumber, process, getResult );\n },\n\n /**\n * Counts the number of elements in this collection that past the test\n * function generated by {@link Rekord.createWhere}.\n *\n * ```javascript\n * var c = Rekord.collect([{name: 't1', done: 1}, {name: 't2', done: 0}, {name: 't3', done: 1}, {name: 't4'}]);\n * c.countWhere('done'); // 3\n * c.countWhere('done', 0); // 1\n * c.countWhere('done', 1); // 2\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [properties] -\n * The expression used to create a function to test the elements in this\n * collection.\n * @param {Any} [value] -\n * When the first argument is a string this argument will be treated as a\n * value to compare to the value of the named property on the object passed\n * through the filter function.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * An alternative function can be used to compare to values.\n * @return {Number} -\n * The number of elements in the collection that passed the test.\n * @see Rekord.createWhere\n */\n countWhere: function(properties, value, equals)\n {\n var where = createWhere( properties, value, equals );\n var met = 0;\n\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n\n if ( where( model ) )\n {\n met++;\n }\n }\n\n return met;\n },\n\n /**\n * Counts the number of elements in this collection that has a value for the\n * given property expression.\n *\n * ```javascript\n * var c = Rekord.collect([{age: 2}, {age: 3}, {taco: 4}]);\n * c.count('age'); // 2\n * c.count('taco'); // 1\n * c.count(); // 3\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [properties] -\n * The expression which converts one value into another.\n * @return {Number} -\n * The number of elements that had values for the property expression.\n * @see Rekord.createPropertyResolver\n * @see Rekord.isValue\n */\n count: function(properties)\n {\n if ( !isValue( properties ) )\n {\n return this.length;\n }\n\n var resolver = createPropertyResolver( properties );\n var result = 0;\n\n for (var i = 0; i < this.length; i++)\n {\n var resolved = resolver( this[ i ] );\n\n if ( isValue( resolved ) )\n {\n result++;\n }\n }\n\n return result;\n },\n\n /**\n * Plucks values from elements in the collection. If only a `values` property\n * expression is given the result will be an array of resolved values. If the\n * `keys` property expression is given, the result will be an object where the\n * property of the object is determined by the key expression.\n *\n * ```javascript\n * var c = Rekord.collect([{age: 2, nm: 'T'}, {age: 4, nm: 'R'}, {age: 5, nm: 'G'}]);\n * c.pluck(); // c\n * c.pluck('age'); // [2, 4, 5]\n * c.pluck('age', 'nm'); // {T: e, R: 4, G: 5}\n * c.pluck(null, 'nm'); // {T: {age: 2, nm: 'T'}, R: {age: 4, nm: 'R'}, G: {age: 5, nm: 'G'}}\n * c.pluck('{age}-{nm}'); // ['2-T', '4-R', '5-G']\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [values] -\n * The expression which converts an element into a value to pluck.\n * @param {propertyResolverInput} [keys] -\n * The expression which converts an element into an object property (key).\n * @return {Array|Object} -\n * The plucked values.\n * @see Rekord.createPropertyResolver\n */\n pluck: function(values, keys)\n {\n var valuesResolver = createPropertyResolver( values );\n\n if ( keys )\n {\n var keysResolver = createPropertyResolver( keys );\n var result = {};\n\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n var value = valuesResolver( model );\n var key = keysResolver( model );\n\n result[ key ] = value;\n }\n\n return result;\n }\n else\n {\n var result = [];\n\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n var value = valuesResolver( model );\n\n result.push( value );\n }\n\n return result;\n }\n },\n\n /**\n * Iterates over each element in this collection and passes the element and\n * it's index to the given function. An optional function context can be given.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Function} callback -\n * The function to invoke for each element of this collection passing the\n * element and the index where it exists.\n * @param {Object} [context] -\n * The context to the callback function.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n */\n each: function(callback, context)\n {\n var callbackContext = context || this;\n\n for (var i = 0; i < this.length; i++)\n {\n var item = this[ i ];\n\n callback.call( callbackContext, item, i );\n\n if ( this[ i ] !== item )\n {\n i--;\n }\n }\n\n return this;\n },\n\n /**\n * Iterates over each element in this collection that matches the where\n * expression and passes the element and it's index to the given function.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Function} callback -\n * The function to invoke for each element of this collection passing the\n * element and the index where it exists.\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n */\n eachWhere: function(callback, properties, values, equals)\n {\n var where = createWhere( properties, values, equals );\n\n for (var i = 0; i < this.length; i++)\n {\n var item = this[ i ];\n\n if ( where( item ) )\n {\n callback.call( this, item, i );\n\n if ( this[ i ] !== item )\n {\n i--;\n }\n }\n }\n\n return this;\n },\n\n /**\n * Reduces all the elements of this collection to a single value. All elements\n * are passed to a function which accepts the currently reduced value and the\n * current element and returns the new reduced value.\n *\n * ```javascript\n * var reduceIt = function(curr, elem) {\n * return curr + ( elem[0] * elem[1] );\n * };\n * var c = Rekord.collect([[2, 1], [3, 2], [5, 6]]);\n * c.reduce( reduceIt, 0 ); // 38\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Function} reducer -\n * A function which accepts the current reduced value and an element and\n * returns the new reduced value.\n * @param {Any} [initialValue] -\n * The first value to pass to the reducer function.\n * @return {Any} -\n * The reduced value.\n */\n reduce: function(reducer, initialValue)\n {\n for (var i = 0; i < this.length; i++)\n {\n initialValue = reducer( initialValue, this[ i ] );\n }\n\n return initialValue;\n },\n\n /**\n * Returns a random element in this collection.\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Any} -\n * The randomly chosen element from this collection.\n */\n random: function()\n {\n var i = Math.floor( Math.random() * this.length );\n\n return this[ i ];\n },\n\n /**\n * Breaks up the collection into an array of arrays of a maximum size (chunks).\n * A destination array can be used to avoid re-allocating arrays.\n *\n * ```javascript\n * var c = Rekord.collect([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n * c.chunk(4); // [[1, 2, 3, 4], [5, 6, 7, 8], [9]]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Number} chunkSize -\n * The maximum number of elements that can exist in a chunk.\n * @param {Array} [out] -\n * The destination array to place the chunks.\n * @return {Array} -\n * The array of chunks of elements taken from this collection.\n */\n chunk: function(chunkSize, out)\n {\n var outer = out || [];\n var outerIndex = 0;\n var inner = outer[ outerIndex ] = outer[ outerIndex ] || [];\n var innerIndex = 0;\n\n for (var i = 0; i < this.length; i++)\n {\n inner[ innerIndex ] = this[ i ];\n\n if ( ++innerIndex >= chunkSize )\n {\n innerIndex = 0;\n outerIndex++;\n inner.length = chunkSize;\n inner = outer[ outerIndex ] = outer[ outerIndex ] || [];\n }\n }\n\n if ( innerIndex !== 0 )\n {\n outerIndex++;\n }\n\n inner.length = innerIndex;\n outer.length = outerIndex;\n\n return outer;\n },\n\n /**\n * Determines whether at least one element in this collection matches the\n * given criteria.\n *\n * ```javascript\n * var c = Rekord.collect([{age: 2}, {age: 6}]);\n * c.contains('age', 2); // true\n * c.contains('age', 3); // false\n * c.contains('age'); // true\n * c.contains('name'); // false\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [properties] -\n * The expression used to create a function to test the elements in this\n * collection.\n * @param {Any} [value] -\n * When the first argument is a string this argument will be treated as a\n * value to compare to the value of the named property on the object passed\n * through the filter function.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * An alternative function can be used to compare to values.\n * @return {Boolean} -\n * True if any of the elements passed the test function, otherwise false.\n * @see Rekord.createWhere\n */\n contains: function(properties, value, equals)\n {\n var where = createWhere( properties, value, equals );\n\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n\n if ( where( model ) )\n {\n return true;\n }\n }\n\n return false;\n },\n\n /**\n * Groups the elements into sub collections given some property expression to\n * use as the value to group by.\n *\n * ```javascript\n * var c = Rekord.collect([\n * { name: 'Tom', age: 6, group: 'X' },\n * { name: 'Jon', age: 7, group: 'X' },\n * { name: 'Rob', age: 8, group: 'X' },\n * { name: 'Bon', age: 9, group: 'Y' },\n * { name: 'Ran', age: 10, group: 'Y' },\n * { name: 'Man', age: 11, group: 'Y' },\n * { name: 'Tac', age: 12, group: 'Z' }\n * ]);\n *\n * c.group({by: 'group'});\n * // [{group: 'X', $count: 3, $group: [...]},\n * // {group: 'Y', $count: 3, $group: [...]},\n * // {group: 'Z', $count: 1, $group: [.]}]\n *\n * c.group({by: 'group', select: {age: 'avg', name: 'first'}});\n * // [{group: 'X', age: 7, name: 'Tom', $count: 3, $group: [...]},\n * // {group: 'Y', age: 9, name: 'Bon', $count: 3, $group: [...]},\n * // {group: 'Z', age: 12, name: 'Tac', $count: 1, $group: [.]}]\n *\n * c.group({by: 'group', track: false, count: false});\n * // [{group: 'X'}, {group: 'Y'}, {group: 'Z'}]\n *\n * var havingMoreThanOne = function(grouping, groupElements) {\n * return groupElements.length > 0;\n * };\n * c.group({by: 'group', select: {age: 'avg'}, comparator: '-age', having: havingMoreThanOne, track: false, count: false});\n * // [{group: 'Y', age: 9},\n * // {group: 'X', age: 7}]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Object} grouping -\n * An object specifying how elements in this collection are to be grouped\n * and what properties from the elements should be aggregated in the\n * resulting groupings.\n * - `by`: A property expression that resolves how elements will be grouped.\n * - `select`: An object which contains properties that should be aggregated where the value is the aggregate collection function to call (sum, avg, count, first, last, etc).\n * - `having`: A having expression which takes a grouping and the grouped elements and determines whether the grouping should be in the final result.\n * - `comparator`: A comparator for sorting the resulting collection of groupings.\n * - `comparatorNullsFirst`: Whether nulls should be sorted to the top.\n * - `track`: Whether all elements in the group should exist in a collection in the `$group` property of each grouping.\n * - `count`: Whether the number of elements in the group should be placed in the `$count` property of each grouping.\n * @return {Rekord.Collection} -\n * A collection of groupings.\n */\n group: function(grouping)\n {\n var by = createPropertyResolver( grouping.by );\n var having = createWhere( grouping.having, grouping.havingValue, grouping.havingEquals );\n var select = grouping.select || {};\n var map = {};\n\n if ( isString( grouping.by ) )\n {\n if ( !(grouping.by in select) )\n {\n select[ grouping.by ] = 'first';\n }\n }\n else if ( isArray( grouping.by ) )\n {\n for (var prop in grouping.by)\n {\n if ( !(prop in select) )\n {\n select[ prop ] = 'first';\n }\n }\n }\n\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n var key = by( model );\n var group = map[ key ];\n\n if ( !group )\n {\n group = map[ key ] = this.cloneEmpty();\n }\n\n group.add( model, true );\n }\n\n var groupings = this.cloneEmpty();\n\n groupings.setComparator( grouping.comparator, grouping.comparatorNullsFirst );\n\n for (var key in map)\n {\n var grouped = {};\n var groupArray = map[ key ];\n\n for (var propName in select)\n {\n var aggregator = select[ propName ];\n\n if ( isString( aggregator ) )\n {\n grouped[ propName ] = groupArray[ aggregator ]( propName );\n }\n else if ( isFunction( aggregator ) )\n {\n grouped[ propName ] = aggregator( groupArray, propName );\n }\n }\n\n if ( grouping.track !== false )\n {\n grouped.$group = groupArray;\n }\n\n if ( grouping.count !== false )\n {\n grouped.$count = groupArray.length;\n }\n\n if ( having( grouped, groupArray ) )\n {\n groupings.push( grouped );\n }\n }\n\n groupings.sort();\n\n return groupings;\n },\n\n /**\n * Returns a copy of this collection as a plain Array.\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Array} -\n * The copy of this collection as a plain array.\n */\n toArray: function()\n {\n return this.slice();\n },\n\n /**\n * Returns a clone of this collection.\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Rekord.Collection} -\n * The reference to a clone collection.\n */\n clone: function()\n {\n return this.constructor.create( this );\n },\n\n /**\n * Returns an empty clone of this collection.\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Rekord.Collection} -\n * The reference to a clone collection.\n */\n cloneEmpty: function()\n {\n return this.constructor.create();\n }\n\n});\n\naddEventful( Collection );\n\n/**\n * Adds a listener for change events on this collection.\n *\n * @method change\n * @memberof Rekord.Collection#\n * @param {Function} callback -\n * A function to call every time a change occurs in this collection.\n * @param {Object} [context] -\n * The desired context (this) for the given callback function.\n * @return {Function} -\n * A function to call to stop listening for change events.\n * @see Rekord.Collection#event:changes\n */\naddEventFunction( Collection, 'change', Collection.Events.Changes );\n\n\n// The methods necessary for a filtered collection.\nvar Filtering = {\n\n bind: function()\n {\n Class.props(this, {\n onAdd: bind( this, Filtering.handleAdd ),\n onAdds: bind( this, Filtering.handleAdds ),\n onRemove: bind( this, Filtering.handleRemove ),\n onRemoves: bind( this, Filtering.handleRemoves ),\n onReset: bind( this, Filtering.handleReset ),\n onUpdates: bind( this, Filtering.handleUpdates ),\n onCleared: bind( this, Filtering.handleCleared )\n });\n },\n\n init: function(base, filter)\n {\n if ( this.base !== base )\n {\n if ( this.base )\n {\n this.disconnect();\n }\n\n Class.prop( this, 'base', base );\n\n this.connect();\n }\n\n Class.prop( this, 'filter', filter );\n\n this.sync();\n\n return this;\n },\n\n setFilter: function(whereProperties, whereValue, whereEquals)\n {\n this.filter = createWhere( whereProperties, whereValue, whereEquals );\n this.sync();\n\n return this;\n },\n\n connect: function()\n {\n this.base.on( Collection.Events.Add, this.onAdd );\n this.base.on( Collection.Events.Adds, this.onAdds );\n this.base.on( Collection.Events.Remove, this.onRemove );\n this.base.on( Collection.Events.Removes, this.onRemoves );\n this.base.on( Collection.Events.Reset, this.onReset );\n this.base.on( Collection.Events.Updates, this.onUpdates );\n this.base.on( Collection.Events.Cleared, this.onCleared );\n\n return this;\n },\n\n disconnect: function()\n {\n this.base.off( Collection.Events.Add, this.onAdd );\n this.base.off( Collection.Events.Adds, this.onAdds );\n this.base.off( Collection.Events.Remove, this.onRemove );\n this.base.off( Collection.Events.Removes, this.onRemoves );\n this.base.off( Collection.Events.Reset, this.onReset );\n this.base.off( Collection.Events.Updates, this.onUpdates );\n this.base.off( Collection.Events.Cleared, this.onCleared );\n\n return this;\n },\n\n sync: function()\n {\n var base = this.base;\n var filter = this.filter;\n var matches = [];\n\n for (var i = 0; i < base.length; i++)\n {\n var value = base[ i ];\n\n if ( filter( value ) )\n {\n matches.push( value );\n }\n }\n\n return this.reset( matches );\n },\n\n handleAdd: function(collection, value)\n {\n var filter = this.filter;\n\n if ( filter( value ) )\n {\n this.add( value );\n }\n },\n\n handleAdds: function(collection, values)\n {\n var filter = this.filter;\n var filtered = [];\n\n for (var i = 0; i < values.length; i++)\n {\n var value = values[ i ];\n\n if ( filter( value ) )\n {\n filtered.push( value );\n }\n }\n\n this.addAll( filtered );\n },\n\n handleRemove: function(collection, value)\n {\n this.remove( value );\n },\n\n handleRemoves: function(collection, values)\n {\n this.removeAll( values );\n },\n\n handleReset: function(collection)\n {\n this.sync();\n },\n\n handleUpdates: function(collection, updates)\n {\n var filter = this.filter;\n\n for (var i = 0; i < updates.length; i++)\n {\n var value = updates[ i ];\n\n if ( filter( value ) )\n {\n this.add( value, true );\n }\n else\n {\n this.remove( value, true );\n }\n }\n\n this.sort();\n },\n\n handleCleared: function(collection)\n {\n this.clear();\n },\n\n clone: function()\n {\n return this.constructor.create( this.base, this.filter );\n },\n\n cloneEmpty: function()\n {\n return this.constructor.create( this.base, this.filter );\n }\n\n};\n\n\n/**\n *\n * @constructor\n * @memberof Rekord\n * @augments Rekord.Eventful\n */\nfunction Page(collection, pageSize, pageIndex)\n{\n this.onChanges = bind( this, this.handleChanges );\n this.pageSize = pageSize;\n this.pageIndex = pageIndex || 0;\n this.pageCount = 0;\n this.setCollection( collection );\n}\n\nPage.Events =\n{\n Change: 'change',\n Changes: 'change'\n};\n\nClass.extend( Array, Page,\n{\n\n setPageSize: function(pageSize)\n {\n this.pageSize = pageSize;\n this.handleChanges();\n },\n\n setPageIndex: function(pageIndex)\n {\n this.goto( pageIndex );\n },\n\n setCollection: function(collection)\n {\n if ( collection !== this.collection )\n {\n if ( this.collection )\n {\n this.disconnect();\n }\n\n this.collection = collection;\n this.connect();\n this.handleChanges( true );\n }\n },\n\n connect: function()\n {\n this.collection.on( Collection.Events.Changes, this.onChanges );\n },\n\n disconnect: function()\n {\n this.collection.off( Collection.Events.Changes, this.onChanges );\n },\n\n goto: function(pageIndex)\n {\n var actualIndex = this.page( pageIndex );\n\n if ( actualIndex !== this.pageIndex )\n {\n this.pageIndex = actualIndex;\n this.update();\n this.trigger( Page.Events.Change, [ this ] );\n }\n },\n\n next: function()\n {\n this.goto( this.pageIndex + 1 );\n },\n\n prev: function()\n {\n this.goto( this.pageIndex - 1 );\n },\n\n jump: function(to)\n {\n this.goto( to );\n },\n\n first: function()\n {\n this.goto( 0 );\n },\n\n last: function()\n {\n this.goto( this.pageCount - 1 );\n },\n\n total: function()\n {\n return this.collection.length;\n },\n\n pages: function()\n {\n return Math.ceil( this.total() / this.pageSize );\n },\n\n page: function(index)\n {\n return Math.max( 0, Math.min( index, this.pages() - 1 ) );\n },\n\n can: function(index)\n {\n return this.total() && index >= 0 && index < this.pageCount;\n },\n\n canFirst: function()\n {\n return this.canPrev();\n },\n\n canLast: function()\n {\n return this.canNext();\n },\n\n canPrev: function()\n {\n return this.total() && this.pageIndex > 0;\n },\n\n canNext: function()\n {\n return this.total() && this.pageIndex < this.pageCount - 1;\n },\n\n handleChanges: function(forceApply)\n {\n var pageCount = this.pages();\n var pageIndex = this.page( this.pageIndex );\n var apply = forceApply || this.pageIndex !== pageIndex || this.length !== this.pageSize;\n var changes = apply || this.pageCount !== pageCount;\n\n this.pageIndex = pageIndex;\n this.pageCount = pageCount;\n\n if ( apply )\n {\n this.update();\n }\n if ( changes )\n {\n this.trigger( Page.Events.Change, [ this ] );\n }\n },\n\n update: function()\n {\n var source = this.collection;\n var n = source.length;\n var start = this.pageIndex * this.pageSize;\n var end = Math.min( start + this.pageSize, n );\n var length = end - start;\n\n this.length = 0;\n\n for (var i = 0; i < length; i++)\n {\n this.push( source[ start++ ] );\n }\n },\n\n more: function(pages)\n {\n var source = this.collection;\n var limit = source.length;\n var pageCount = pages || 1;\n var offset = this.pageIndex * this.pageSize;\n var start = offset + this.length;\n var adding = this.pageSize * pageCount;\n var desiredEnd = start + adding;\n var actualEnd = Math.min( limit, desiredEnd );\n\n while (start < actualEnd)\n {\n this.push( source[ start++ ] );\n }\n },\n\n toArray: function()\n {\n return this.slice();\n }\n\n});\n\naddEventful( Page );\n\naddEventFunction( Page, 'change', Page.Events.Changes );\n\n\n/**\n * An extension of the {@link Rekord.Collection} class which is a filtered view\n * of another collection.\n *\n * ```javascript\n * var isEven = function(x) { return x % 2 === 0; };\n * var c = Rekord.collect([1, 2, 3, 4, 5, 6, 7]);\n * var f = c.filtered( isEven );\n * f; // [2, 4, 6]\n * c.add( 8 );\n * c.remove( 2 );\n * f; // [4, 6, 8]\n * ```\n *\n * @constructor\n * @memberof Rekord\n * @extends Rekord.Collection\n * @param {Rekord.Collection} base -\n * The collection to listen to for changes to update this collection.\n * @param {whereCallback} filter -\n * The function which determines whether an element in the base collection\n * should exist in this collection.\n * @see Rekord.Collection#filtered\n */\nfunction FilteredCollection(base, filter)\n{\n this.bind();\n this.init( base, filter );\n}\n\n/**\n * The collection to listen to for changes to update this collection.\n *\n * @memberof Rekord.FilteredCollection#\n * @member {Rekord.Collection} base\n */\n\n /**\n * The function which determines whether an element in the base collection\n * should exist in this collection.\n *\n * @memberof Rekord.FilteredCollection#\n * @member {whereCallback} filter\n */\n\nClass.extend( Collection, FilteredCollection,\n{\n\n /**\n * Generates the handlers which are passed to the base collection when this\n * filtered collection is connected or disconnected - which happens on\n * initialization and subsequent calls to {@link FilteredCollection#init}.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n */\n bind: Filtering.bind,\n\n /**\n * Initializes the filtered collection by setting the base collection and the\n * filtering function.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @param {Rekord.Collection} base -\n * The collection to listen to for changes to update this collection.\n * @param {whereCallback} filter -\n * The function which determines whether an element in the base collection\n * should exist in this collection.\n * @return {Rekord.FilteredCollection} -\n * The reference to this collection.\n * @emits Rekord.Collection#reset\n */\n init: Filtering.init,\n\n /**\n * Sets the filter function of this collection and re-sychronizes it with the\n * base collection.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.FilteredCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @emits Rekord.Collection#reset\n */\n setFilter: Filtering.setFilter,\n\n /**\n * Registers callbacks with events of the base collection.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @return {Rekord.FilteredCollection} -\n * The reference to this collection.\n */\n connect: Filtering.connect,\n\n /**\n * Unregisters callbacks with events from the base collection.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @return {Rekord.FilteredCollection} -\n * The reference to this collection.\n */\n disconnect: Filtering.disconnect,\n\n /**\n * Synchronizes this collection with the base collection. Synchronizing\n * involves iterating over the base collection and passing each element into\n * the filter function and if it returns a truthy value it's added to this\n * collection.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @return {Rekord.FilteredCollection} -\n * The reference to this collection.\n * @emits Rekord.Collection#reset\n */\n sync: Filtering.sync,\n\n /**\n * Returns a clone of this collection.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @return {Rekord.FilteredCollection} -\n * The reference to a clone collection.\n */\n clone: Filtering.clone,\n\n /**\n * Returns an empty clone of this collection.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @return {Rekord.FilteredCollection} -\n * The reference to a clone collection.\n */\n cloneEmpty: Filtering.cloneEmpty\n\n});\n\n\n/**\n * An extension of the {@link Rekord.Collection} class for {@link Rekord.Model}\n * instances.\n *\n * @constructor\n * @memberof Rekord\n * @extends Rekord.Collection\n * @param {Rekord.Database} database -\n * The database for the models in this collection.\n * @param {modelInput[]} [models] -\n * The initial array of models in this collection.\n * @param {Boolean} [remoteData=false] -\n * If the models array is from a remote source. Remote sources place the\n * model directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @see Rekord.Models.boot\n * @see Rekord.Models.collect\n */\nfunction ModelCollection(database, models, remoteData)\n{\n this.init( database, models, remoteData );\n}\n\n/**\n * The map of models which keeps an index (by model key) of the models.\n *\n * @memberof Rekord.ModelCollection#\n * @member {Rekord.Map} map\n */\n\n/**\n * The database for the models in this collection.\n *\n * @memberof Rekord.ModelCollection#\n * @member {Rekord.Database} database\n */\n\nClass.extend( Collection, ModelCollection,\n{\n\n /**\n * Initializes the model collection by setting the database, the initial set\n * of models, and whether the initial set of models is from a remote source.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Rekord.Database} database -\n * The database for the models in this collection.\n * @param {modelInput[]} [models] -\n * The initial array of models in this collection.\n * @param {Boolean} [remoteData=false] -\n * If the models array is from a remote source. Remote sources place the\n * model directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#reset\n */\n init: function(database, models, remoteData)\n {\n Class.props(this, {\n database: database,\n map: new Map()\n });\n\n this.map.values = this;\n this.reset( models, remoteData );\n\n return this;\n },\n\n /**\n * Documented in Collection.js\n */\n sort: function(comparator, comparatorNullsFirst)\n {\n var cmp = comparator ? createComparator( comparator, comparatorNullsFirst ) : this.comparator;\n\n if ( !isSorted( cmp, this ) )\n {\n this.map.sort( cmp );\n\n this.trigger( Collection.Events.Sort, [this] );\n }\n\n return this;\n },\n\n /**\n * Takes input provided to the collection for adding, removing, or querying\n * and generates the key which uniquely identifies a model.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput} input -\n * The input to convert to a key.\n * @return {modelKey} -\n * The key built from the input.\n */\n buildKeyFromInput: function(input)\n {\n return this.database.keyHandler.buildKeyFromInput( input );\n },\n\n /**\n * Takes input provided to this collection for adding, removing, or querying\n * and returns a model instance. An existing model can be referenced or a new\n * model can be created on the spot.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput} input -\n * The input to convert to a model instance.\n * @param {Boolean} [remoteData=false] -\n * If the model is from a remote source. Remote sources place the model\n * directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @return {Rekord.Model} -\n * A model instance parsed from the input.\n */\n parseModel: function(input, remoteData)\n {\n return this.database.parseModel( input, remoteData );\n },\n\n /**\n * Creates a sub view of this collection known as a filtered collection. The\n * resulting collection changes when this collection changes. Any time an\n * element is added or removed to this collection it may be added or removed\n * from the filtered collection if it fits the filter function. The filter\n * function is created by passing the arguments of this function to\n * {@link Rekord.createWhere}.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.FilteredModelCollection} -\n * The newly created live filtered view of this collection.\n * @see Rekord.createWhere\n */\n filtered: function(whereProperties, whereValue, whereEquals)\n {\n var filter = createWhere( whereProperties, whereValue, whereEquals );\n\n return FilteredModelCollection.create( this, filter );\n },\n\n /**\n * Documented in Collection.js\n *\n * @see Rekord.ModelCollection#buildKeyFromInput\n */\n subtract: function(models, out)\n {\n var target = out || this.cloneEmpty();\n\n for (var i = 0; i < this.length; i++)\n {\n var a = this[ i ];\n var key = a.$key();\n var exists = false;\n\n if ( models instanceof ModelCollection )\n {\n exists = models.has( key );\n }\n else\n {\n for (var k = 0; k < models.length && !exists; k++)\n {\n var modelKey = this.buildKeyFromInput( models[ k ] );\n\n exists = (key === modelKey);\n }\n }\n\n if (!exists)\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Documented in Collection.js\n */\n intersect: function(models, out)\n {\n var target = out || this.cloneEmpty();\n\n for (var i = 0; i < models.length; i++)\n {\n var a = models[ i ];\n var key = this.buildKeyFromInput( a );\n\n if ( this.has( key ) )\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Documented in Collection.js\n */\n complement: function(models, out)\n {\n var target = out || this.cloneEmpty();\n\n for (var i = 0; i < models.length; i++)\n {\n var a = models[ i ];\n var key = this.buildKeyFromInput( a );\n\n if ( !this.has( key ) )\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Documented in Collection.js\n */\n clear: function()\n {\n var cleared = this.map.reset();\n\n this.trigger( Collection.Events.Cleared, [this] );\n\n return cleared;\n },\n\n /**\n * Resets the models in this collection with a new collection of models.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput[]} [models] -\n * The initial array of models in this collection.\n * @param {Boolean} [remoteData=false] -\n * If the models array is from a remote source. Remote sources place the\n * model directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.ModelCollection#parseModel\n * @emits Rekord.ModelCollection#reset\n */\n reset: function(models, remoteData)\n {\n var map = this.map;\n\n map.reset();\n\n if ( isArray( models ) )\n {\n for (var i = 0; i < models.length; i++)\n {\n var model = models[ i ];\n var parsed = this.parseModel( model, remoteData );\n\n if ( parsed )\n {\n map.put( parsed.$key(), parsed );\n }\n }\n }\n else if ( isObject( models ) )\n {\n var parsed = this.parseModel( models, remoteData );\n\n if ( parsed )\n {\n map.put( parsed.$key(), parsed );\n }\n }\n\n this.trigger( Collection.Events.Reset, [this] );\n this.sort();\n\n return this;\n },\n\n /**\n * Returns whether this collection contains a model with the given key.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelKey} key -\n * The key of the model to check for existence.\n * @return {Boolean} -\n * True if a model with the given key exists in this collection, otherwise\n * false.\n */\n has: function(key)\n {\n return this.map.has( key );\n },\n\n /**\n * Returns the model in this collection with the given key.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelKey} key -\n * The key of the model to return.\n * @return {Rekord.Model} -\n * The model instance for the given key, or undefined if a model wasn't\n * found.\n */\n get: function(key)\n {\n return this.map.get( key );\n },\n\n /**\n * Places a model in this collection providing a key to use.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelKey} key -\n * The key of the model.\n * @param {Rekord.Model} model -\n * The model instance to place in the collection.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#add\n * @emits Rekord.ModelCollection#sort\n */\n put: function(key, model, delaySort)\n {\n this.map.put( key, model );\n this.trigger( Collection.Events.Add, [this, model, this.map.indices[ key ]] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n },\n\n /**\n * Adds a model to this collection - sorting the collection if a comparator\n * is set on this collection and `delaySort` is not a specified or a true\n * value.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput} input -\n * The model to add to this collection.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @param {Boolean} [remoteData=false] -\n * If the model is from a remote source. Remote sources place the model\n * directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#add\n * @emits Rekord.ModelCollection#sort\n */\n add: function(input, delaySort, remoteData)\n {\n var model = this.parseModel( input, remoteData );\n var key = model.$key();\n\n this.map.put( key, model );\n this.trigger( Collection.Events.Add, [this, model, this.map.indices[ key ]] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n\n return this;\n },\n\n /**\n * Adds one or more models to the end of this collection - sorting the\n * collection if a comparator is set on this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {...modelInput} value -\n * The models to add to this collection.\n * @return {Number} -\n * The new length of this collection.\n * @emits Rekord.ModelCollection#add\n * @emits Rekord.ModelCollection#sort\n */\n push: function()\n {\n var values = AP.slice.apply( arguments );\n var indices = [];\n\n for (var i = 0; i < values.length; i++)\n {\n var model = this.parseModel( values[ i ] );\n var key = model.$key();\n\n this.map.put( key, model );\n indices.push( this.map.indices[ key ] );\n }\n\n this.trigger( Collection.Events.Adds, [this, values, indices] );\n this.sort();\n\n return this.length;\n },\n\n /**\n * @method\n * @memberof Rekord.ModelCollection#\n * @see Rekord.ModelCollection#push\n * @param {...modelInput} value -\n * The values to add to this collection.\n * @return {Number} -\n * The new length of this collection.\n * @emits Rekord.ModelCollection#adds\n * @emits Rekord.ModelCollection#sort\n */\n unshift: function()\n {\n return this.push.apply( this, arguments );\n },\n\n /**\n * Adds all models in the given array to this collection - sorting the\n * collection if a comparator is set on this collection and `delaySort` is\n * not specified or a true value.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput[]} models -\n * The models to add to this collection.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @param {Boolean} [remoteData=false] -\n * If the model is from a remote source. Remote sources place the model\n * directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#adds\n * @emits Rekord.ModelCollection#sort\n */\n addAll: function(models, delaySort, remoteData)\n {\n if ( isArray( models ) )\n {\n var indices = [];\n\n for (var i = 0; i < models.length; i++)\n {\n var model = this.parseModel( models[ i ], remoteData );\n var key = model.$key();\n\n this.map.put( key, model );\n indices.push( this.map.indices[ key ] );\n }\n\n this.trigger( Collection.Events.Adds, [this, models, indices] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n }\n },\n\n /**\n * @method\n * @memberof Rekord.ModelCollection#\n * @see Rekord.ModelCollection#add\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#add\n * @emits Rekord.ModelCollection#sort\n */\n insertAt: function(i, value, delaySort)\n {\n return this.add( value, delaySort );\n },\n\n /**\n * Removes the last model in this collection and returns it - sorting the\n * collection if a comparator is set on this collection and `delaySort` is\n * no specified or a true value.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @return {Rekord.Model} -\n * The model removed from the end of the collection.\n * @emits Rekord.ModelCollection#remove\n * @emits Rekord.ModelCollection#sort\n */\n pop: function(delaySort)\n {\n var i = this.length - 1;\n var removed = this[ i ];\n\n this.map.removeAt( i );\n this.trigger( Collection.Events.Remove, [this, removed, i] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n\n return removed;\n },\n\n /**\n * Removes the first model in this collection and returns it - sorting the\n * collection if a comparator is set on this collection and `delaySort` is\n * no specified or a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.shift(); // 1\n * ```\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @return {Rekord.Model} -\n * The model removed from the beginning of the collection.\n * @emits Rekord.ModelCollection#remove\n * @emits Rekord.ModelCollection#sort\n */\n shift: function(delaySort)\n {\n var removed = this[ 0 ];\n\n this.map.removeAt( 0 );\n this.trigger( Collection.Events.Remove, [this, removed, 0] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n\n return removed;\n },\n\n /**\n * Removes the model in this collection at the given index `i` - sorting\n * the collection if a comparator is set on this collection and `delaySort` is\n * not specified or a true value.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Number} i -\n * The index of the model to remove.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @return {Rekord.Model} -\n * The model removed, or undefined if the index was invalid.\n * @emits Rekord.ModelCollection#remove\n * @emits Rekord.ModelCollection#sort\n */\n removeAt: function(i, delaySort)\n {\n var removing;\n\n if (i >= 0 && i < this.length)\n {\n removing = this[ i ];\n\n this.map.removeAt( i );\n this.trigger( Collection.Events.Remove, [this, removing, i] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n }\n\n return removing;\n },\n\n /**\n * Removes the given model from this collection if it exists - sorting the\n * collection if a comparator is set on this collection and `delaySort` is not\n * specified or a true value.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput} input -\n * The model to remove from this collection if it exists.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to the given value.\n * @return {Rekord.Model} -\n * The element removed from this collection.\n * @emits Rekord.ModelCollection#remove\n * @emits Rekord.ModelCollection#sort\n */\n remove: function(input, delaySort)\n {\n var key = this.buildKeyFromInput( input );\n var removing = this.map.get( key );\n\n if ( removing )\n {\n var i = this.map.indices[ key ];\n\n this.map.remove( key );\n this.trigger( Collection.Events.Remove, [this, removing, i] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n }\n\n return removing;\n },\n\n /**\n * Removes the given models from this collection - sorting the collection if\n * a comparator is set on this collection and `delaySort` is not specified or\n * a true value.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput[]} inputs -\n * The models to remove from this collection if they exist.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @return {Rekord.Model[]} -\n * The models removed from this collection.\n * @emits Rekord.ModelCollection#removes\n * @emits Rekord.ModelCollection#sort\n */\n removeAll: function(inputs, delaySort)\n {\n var map = this.map;\n var removed = [];\n var removedIndices = [];\n\n for (var i = 0; i < inputs.length; i++)\n {\n var key = this.buildKeyFromInput( inputs[ i ] );\n var removing = map.get( key );\n\n if ( removing )\n {\n removedIndices.push( map.indices[ key ] );\n removed.push( removing );\n }\n }\n\n removedIndices.sort();\n\n for (var i = removedIndices.length - 1; i >= 0; i--)\n {\n map.removeAt( removedIndices[ i ] );\n }\n\n this.trigger( Collection.Events.Removes, [this, removed, removedIndices] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n\n return removed;\n },\n\n /**\n * Returns the index of the given model in this collection or returns -1\n * if the model doesn't exist in this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput} input -\n * The model to search for.\n * @return {Number} -\n * The index of the model in this collection or -1 if it was not found.\n */\n indexOf: function(input)\n {\n var key = this.buildKeyFromInput( input );\n var index = this.map.indices[ key ];\n\n return index === undefined ? -1 : index;\n },\n\n /**\n * Rebuilds the internal index which maps keys to the index of the model in\n * this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n */\n rebuild: function()\n {\n this.map.rebuildIndex();\n },\n\n /**\n * Returns the array of keys that correspond to the models in this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @return {modelKey[]} -\n * The array of model keys.\n */\n keys: function()\n {\n return this.map.keys;\n },\n\n /**\n * Reverses the order of models in this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#updates\n */\n reverse: function()\n {\n this.map.reverse();\n\n this.trigger( Collection.Events.Updates, [this] );\n\n return this;\n },\n\n /**\n * Splices elements out of and into this collection - sorting the collection\n * if a comparator is set on this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Number} start -\n * Index at which to start changing the array (with origin 0). If greater\n * than the length of the array, actual starting index will be set to the\n * length of the array. If negative, will begin that many elements from the end.\n * @param {Number} deleteCount -\n * An integer indicating the number of old array elements to remove. If\n * deleteCount is 0, no elements are removed. In this case, you should\n * specify at least one new element. If deleteCount is greater than the\n * number of elements left in the array starting at start, then all of the\n * elements through the end of the array will be deleted.\n * If deleteCount is omitted, deleteCount will be equal to (arr.length - start).\n * @param {...Any} values -\n * The elements to add to the array, beginning at the start index. If you\n * don't specify any elements, splice() will only remove elements from the array.\n * @return {Any[]} -\n * The array of deleted elements.\n * @emits Rekord.ModelCollection#removes\n * @emits Rekord.ModelCollection#adds\n * @emits Rekord.ModelCollection#sort\n */\n splice: function(start, deleteCount)\n {\n var adding = AP.slice.call( arguments, 2 );\n\n var addingKeys = [start, deleteCount];\n for (var i = 0; i < adding.length; i++)\n {\n addingKeys.push( this.buildKeyFromInput( adding[ i ] ) );\n }\n\n var removed = AP.splice.apply( this, arguments );\n\n AP.splice.apply( this.map.keys, addingKeys );\n\n if ( deleteCount )\n {\n this.trigger( Collection.Events.Removes, [this, removed, start, deleteCount] );\n }\n\n if ( adding.length )\n {\n this.trigger( Collection.Events.Adds, [this, adding, start] );\n }\n\n this.sort();\n\n return removed;\n },\n\n /**\n * Removes the models from this collection where the given expression is true.\n * The first argument, if `true`, can call {@link Rekord.Model#$remove} on each\n * model removed from this colleciton.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Boolean} [callRemove=false] -\n * Whether {@link Rekord.Model#$remove} should be called on each removed model.\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @param {Array} [out=this.cloneEmpty()] -\n * The array to place the elements that match.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Rekord.Model[]} -\n * An array of models removed from this collection.\n * @emits Rekord.ModelCollection#removes\n * @emits Rekord.ModelCollection#sort\n */\n removeWhere: function(callRemove, whereProperties, whereValue, whereEquals, out, delaySort, cascade, options)\n {\n var where = createWhere( whereProperties, whereValue, whereEquals );\n var removed = out || this.cloneEmpty();\n var removedIndices = [];\n\n batchExecute(function()\n {\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n\n if ( where( model ) )\n {\n removedIndices.push( i );\n removed.push( model );\n }\n }\n\n for (var i = 0; i < removed.length; i++)\n {\n var model = removed[ i ];\n var key = model.$key();\n\n this.map.remove( key );\n\n if ( callRemove )\n {\n model.$remove( cascade, options );\n }\n }\n\n }, this );\n\n this.trigger( Collection.Events.Removes, [this, removed, removedIndices] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n\n return removed;\n },\n\n /**\n * Updates the given property(s) in all models in this collection with the\n * given value. If `avoidSave` is not a truthy value then\n * {@link Rekord.Model#$save} is called on every model in this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {String|Object} props -\n * The property or properties to update.\n * @param {Any} [value] -\n * The value to set if a String `props` is given.\n * @param {Boolean} [remoteData=false] -\n * If the properties are from a remote source. Remote sources place the\n * model directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @param {Boolean} [avoidSave=false] -\n * True for NOT calling {@link Rekord.Model#$save}, otherwise false.\n * @param {Number} [cascade] -\n * Which operations should be performed out of: store, rest, & live.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#updates\n * @emits Rekord.ModelCollection#sort\n */\n update: function(props, value, remoteData, avoidSave, cascade, options)\n {\n batchExecute(function()\n {\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n\n model.$set( props, value, remoteData );\n\n if ( !avoidSave )\n {\n model.$save( cascade, options );\n }\n }\n\n }, this );\n\n this.trigger( Collection.Events.Updates, [this, this] );\n this.sort();\n\n return this;\n },\n\n /**\n * Updates the given property(s) in models in this collection which pass the\n * `where` function with the given value. If `avoidSave` is not a truthy value\n * then {@link Rekord.Model#$save} is called on every model in this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereCallback} where -\n * The function which determines whether a model should be updated.\n * @param {String|Object} props -\n * The property or properties to update.\n * @param {*} [value] -\n * The value to set if a String `props` is given.\n * @param {Boolean} [remoteData=false] -\n * If the properties are from a remote source. Remote sources place the\n * model directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @param {Boolean} [avoidSave=false] -\n * True for NOT calling {@link Rekord.Model#$save}, otherwise false.\n * @param {Number} [cascade] -\n * Which operations should be performed out of: store, rest, & live.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @return {Rekord.Model[]} -\n * An array of models updated.\n * @emits Rekord.ModelCollection#updates\n * @emits Rekord.ModelCollection#sort\n */\n updateWhere: function(where, props, value, remoteData, avoidSave, cascade, options)\n {\n var updated = [];\n\n batchExecute(function()\n {\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n\n if ( where( model ) )\n {\n model.$set( props, value, remoteData );\n\n if ( !avoidSave )\n {\n model.$save( cascade, options );\n }\n\n updated.push( model );\n }\n }\n\n }, this );\n\n this.trigger( Collection.Events.Updates, [this, updated] );\n this.sort();\n\n return updated;\n },\n\n /**\n * Calls {@link Rekord.Model#$push} on models in this collection that meet\n * the given where expression.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {String[]} [fields] -\n * The set of fields to save for later popping or discarding. If not\n * specified, all model fields will be saved.\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$push\n */\n pushWhere: function(fields, properties, value, equals)\n {\n function pushIt(model)\n {\n model.$push( fields );\n }\n\n return this.eachWhere( pushIt, properties, value, equals );\n },\n\n /**\n * Calls {@link Rekord.Model#$pop} on models in this collection that meet\n * the given where expression.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Boolean} [dontDiscard=false] -\n * Whether to remove the saved state after the saved state has been applied\n * back to the model. A falsy value will result in\n * {@link Rekord.Model#$discard} being called.\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$pop\n */\n popWhere: function(dontDiscard, properties, value, equals)\n {\n function popIt(model)\n {\n model.$pop( dontDiscard );\n }\n\n return this.eachWhere( popIt, properties, value, equals );\n },\n\n /**\n * Calls {@link Rekord.Model#$discard} on models in this collection that meet\n * the given where expression.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$discard\n */\n discardWhere: function(properties, value, equals)\n {\n function discardIt(model)\n {\n model.$discard();\n }\n\n return this.eachWhere( discardIt, properties, value, equals );\n },\n\n /**\n * Calls {@link Rekord.Model#$cancel} on models in this collection that meet\n * the given where expression.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Boolean} [reset=false] -\n * If reset is true and the model doesn't have a saved state -\n * {@link Rekord.Model#$reset} will be called.\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$cancel\n */\n cancelWhere: function(reset, properties, value, equals)\n {\n function cancelIt(model)\n {\n model.$cancel( reset );\n }\n\n batchExecute(function()\n {\n this.eachWhere( cancelIt, properties, value, equals );\n\n }, this );\n\n return this;\n },\n\n /**\n * Calls {@link Rekord.Model#$refresh} on models in this collection that meet\n * the given where expression.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @param {Number} [cascade] -\n * Which operations should be performed out of: store, rest, & live.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$refresh\n */\n refreshWhere: function(properties, value, equals, cascade, options)\n {\n function refreshIt(model)\n {\n model.$refresh( cascade, options );\n }\n\n batchExecute(function()\n {\n this.eachWhere( refreshIt, properties, value, equals );\n\n }, this );\n\n return this;\n },\n\n /**\n * Calls {@link Rekord.Model#$save} on models in this collection that meet\n * the given where expression.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @param {Object} [props={}] -\n * Properties to apply to each model in the collection that pass the where\n * expression.\n * @param {Number} [cascade] -\n * Which operations should be performed out of: store, rest, & live.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$refresh\n */\n saveWhere: function(properties, value, equals, props, cascade, options)\n {\n function saveIt(model)\n {\n model.$save( props, cascade, options );\n }\n\n batchExecute(function()\n {\n this.eachWhere( saveIt, properties, value, equals );\n\n }, this );\n\n return this;\n },\n\n /**\n * Returns whether this collection has at least one model with changes. An\n * additional where expression can be given to only check certain models.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @return {Boolean} -\n * True if at least one model has changes, otherwise false.\n * @see Rekord.createWhere\n * @see Rekord.Model#$hasChanges\n */\n hasChanges: function(properties, value, equals)\n {\n var where = createWhere( properties, value, equals );\n\n var hasChanges = function( model )\n {\n return where( model ) && model.$hasChanges();\n };\n\n return this.contains( hasChanges );\n },\n\n /**\n * Returns a collection of all changes for each model. The changes are keyed\n * into the collection by the models key. An additional where expression can\n * be given to only check certain models.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @param {Rekord.ModelCollection} [out] -\n * The collection to add the changes to.\n * @return {Rekord.ModelCollection} -\n * The collection with all changes to models in this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$hasChanges\n * @see Rekord.Model#$getChanges\n */\n getChanges: function(properties, value, equals, out)\n {\n var where = createWhere( properties, value, equals );\n var changes = out && out instanceof ModelCollection ? out : this.cloneEmpty();\n\n this.each(function(model)\n {\n if ( where( model ) && model.$hasChanges() )\n {\n changes.put( model.$key(), model.$getChanges() );\n }\n });\n\n return changes;\n },\n\n // TODO\n project: function(projectionInput, out)\n {\n var target = out || [];\n var projection = Projection.parse( this.database, projectionInput );\n\n for (var i = 0; i < this.length; i++)\n {\n target.push( projection.project( this[ i ] ) );\n }\n\n return target;\n },\n\n /**\n * Converts this collection into an object where the keys of the models are\n * the object properties and the models are the values.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Object} [out] -\n * The object to place the models in.\n * @return {Object} -\n * The object containing the models in this collection.\n */\n toObject: function(out)\n {\n return this.map.toObject( out );\n },\n\n /**\n * Returns a clone of this collection. Optionally the models in this\n * collection can also be cloned.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Boolean} [cloneModels=false] -\n * Whether or not the models should be cloned as well.\n * @param {Boolean} [cloneProperties] -\n * The properties object which defines what fields should be given a\n * different (non-cloned) value and which relations need to be cloned.\n * @return {Rekord.ModelCollection} -\n * The reference to a clone collection.\n * @see Rekord.Model#$clone\n */\n clone: function(cloneModels, cloneProperties)\n {\n var source = this;\n\n if ( cloneModels )\n {\n source = [];\n\n for (var i = 0; i < this.length; i++)\n {\n source[ i ] = this[ i ].$clone( cloneProperties );\n }\n }\n\n return ModelCollection.create( this.database, source, true );\n },\n\n /**\n * Returns an empty clone of this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @return {Rekord.ModelCollection} -\n * The reference to a clone collection.\n */\n cloneEmpty: function()\n {\n return ModelCollection.create( this.database );\n }\n\n});\n\n\n/**\n * An extension of the {@link Rekord.ModelCollection} class which is a filtered\n * view of another model collection. Changes made to the base collection are\n * reflected in the filtered collection - possibly resulting in additions and\n * removals from the filtered collection.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var finished = Task.filtered('done', true);\n * finished; // will always contain tasks that are done\n * ```\n *\n * @constructor\n * @memberof Rekord\n * @extends Rekord.ModelCollection\n * @param {Rekord.ModelCollection} base -\n * The model collection to listen to for changes to update this collection.\n * @param {whereCallback} filter -\n * The function which determines whether a model in the base collection\n * should exist in this collection.\n * @see Rekord.Collection#filtered\n */\nfunction FilteredModelCollection(base, filter)\n{\n this.bind();\n this.init( base, filter );\n}\n\n/**\n * The collection to listen to for changes to update this collection.\n *\n * @memberof Rekord.FilteredModelCollection#\n * @member {Rekord.ModelCollection} base\n */\n\n /**\n * The function which determines whether an element in the base collection\n * should exist in this collection.\n *\n * @memberof Rekord.FilteredModelCollection#\n * @member {whereCallback} filter\n */\n\nClass.extend( ModelCollection, FilteredModelCollection,\n{\n\n /**\n * Generates the handlers which are passed to the base collection when this\n * filtered collection is connected or disconnected - which happens on\n * initialization and subsequent calls to {@link FilteredModelCollection#init}.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n */\n bind: function()\n {\n Filtering.bind.apply( this );\n\n Class.props(this, {\n onModelUpdated: bind( this, this.handleModelUpdate )\n });\n },\n\n /**\n * Initializes the filtered collection by setting the base collection and the\n * filtering function.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @param {Rekord.ModelCollection} base -\n * The model collection to listen to for changes to update this collection.\n * @param {whereCallback} filter -\n * The function which determines whether a model in the base collection\n * should exist in this collection.\n * @return {Rekord.FilteredModelCollection} -\n * The reference to this collection.\n * @emits Rekord.Collection#reset\n */\n init: function(base, filter)\n {\n if ( this.base )\n {\n this.base.database.off( Database.Events.ModelUpdated, this.onModelUpdated );\n }\n\n ModelCollection.prototype.init.call( this, base.database );\n\n Filtering.init.call( this, base, filter );\n\n base.database.on( Database.Events.ModelUpdated, this.onModelUpdated );\n\n return this;\n },\n\n /**\n * Sets the filter function of this collection and re-sychronizes it with the\n * base collection.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.FilteredModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @emits Rekord.Collection#reset\n */\n setFilter: Filtering.setFilter,\n\n /**\n * Registers callbacks with events of the base collection.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @return {Rekord.FilteredModelCollection} -\n * The reference to this collection.\n */\n connect: Filtering.connect,\n\n /**\n * Unregisters callbacks with events from the base collection.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @return {Rekord.FilteredModelCollection} -\n * The reference to this collection.\n */\n disconnect: Filtering.disconnect,\n\n /**\n * Synchronizes this collection with the base collection. Synchronizing\n * involves iterating over the base collection and passing each element into\n * the filter function and if it returns a truthy value it's added to this\n * collection.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @return {Rekord.FilteredModelCollection} -\n * The reference to this collection.\n * @emits Rekord.Collection#reset\n */\n sync: Filtering.sync,\n\n /**\n * Handles the ModelUpdated event from the database.\n */\n handleModelUpdate: function(model)\n {\n var exists = this.has( model.$key() );\n var matches = this.filter( model );\n\n if ( exists && !matches )\n {\n this.remove( model );\n }\n if ( !exists && matches )\n {\n this.add( model );\n }\n },\n\n /**\n * Returns a clone of this collection.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @return {Rekord.FilteredModelCollection} -\n * The reference to a clone collection.\n */\n clone: Filtering.clone,\n\n /**\n * Returns an empty clone of this collection.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @return {Rekord.FilteredModelCollection} -\n * The reference to a clone collection.\n */\n cloneEmpty: Filtering.cloneEmpty\n\n});\n\n\n/**\n * An extension of the {@link Rekord.ModelCollection} class for relationships.\n *\n * @constructor\n * @memberof Rekord\n * @extends Rekord.ModelCollection\n * @param {Rekord.Database} database -\n * The database for the models in this collection.\n * @param {Rekord.Model} model -\n * The model instance all models in this collection are related to.\n * @param {Rekord.Relation} relator -\n * The relation instance responsible for relating/unrelating models.\n * @param {modelInput[]} [models] -\n * The initial array of models in this collection.\n * @param {Boolean} [remoteData=false] -\n * If the models array is from a remote source. Remote sources place the\n * model directly into the database while local sources aren't stored in the\n * database until they're saved.\n */\nfunction RelationCollection(database, model, relator, models, remoteData)\n{\n Class.props(this, {\n model: model,\n relator: relator\n });\n\n this.init( database, models, remoteData );\n}\n\n/**\n * The model instance all models in this collection are related to.\n *\n * @memberof Rekord.RelationCollection#\n * @member {Rekord.Model} model\n */\n\n /**\n * The relation instance responsible for relating/unrelating models.\n *\n * @memberof Rekord.RelationCollection#\n * @member {Rekord.Relation} relator\n */\n\nClass.extend( ModelCollection, RelationCollection,\n{\n\n /**\n * Sets the entire set of models which are related. If a model is specified\n * that doesn't exist in this collection a relationship is added. If a model\n * in this collection is not specified in the `input` the relationship is\n * removed. Depending on the relationship, adding and removing relationships\n * may result in the saving or deleting of models.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @param {modelInput|modelInput[]} [input] -\n * The model or array of models to relate. If input isn't specified, all\n * models currently related are unrelated.\n * @param {boolean} [remoteData=false] -\n * Whether this change is due to remote changes or changes that should not\n * trigger removes or saves.\n * @return {Rekord.RelationCollection} -\n * The reference to this collection.\n */\n set: function(input, remoteData)\n {\n this.relator.set( this.model, input, remoteData );\n\n return this;\n },\n\n /**\n * Relates one or more models to this collection's model. If a model is\n * specified that is already related then it has no effect.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @param {modelInput|modelInput[]} input -\n * The model or array of models to relate.\n * @param {boolean} [remoteData=false] -\n * Whether this change is due to remote changes or changes that should not\n * trigger removes or saves.\n * @return {Rekord.RelationCollection} -\n * The reference to this collection.\n */\n relate: function(input, remoteData)\n {\n this.relator.relate( this.model, input, remoteData );\n\n return this;\n },\n\n /**\n * Unrelates one or more models from this collection's model. If a model is\n * specified that is not related then it has no effect. If no models are\n * specified then all models in this collection are unrelated.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @param {modelInput|modelInput[]} input -\n * The model or array of models to relate.\n * @param {boolean} [remoteData=false] -\n * Whether this change is due to remote changes or changes that should not\n * trigger removes or saves.\n * @return {Rekord.RelationCollection} -\n * The reference to this collection.\n */\n unrelate: function(input, remoteData)\n {\n this.relator.unrelate( this.model, input, remoteData );\n\n return this;\n },\n\n /**\n * Syncrhonizes the related models in this collection by re-evaluating all\n * models for a relationship.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @param {boolean} [removeUnrelated=false] -\n * Whether to remove models that are no longer related. The $remove\n * function is not called on these models.\n * @return {Rekord.RelationCollection} -\n * The reference to this collection.\n */\n sync: function(removeUnrelated)\n {\n this.relator.sync( this.model, removeUnrelated );\n\n return this;\n },\n\n /**\n * Unrelates any models in this collection which meet the where expression.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.RelationCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.RelationCollection.unrelate\n * @see Rekord.RelationCollection.where\n */\n unrelateWhere: function(properties, value, equals)\n {\n return this.unrelate( this.where( properties, value, equals, [] ) );\n },\n\n /**\n * Determines whether one or more models all exist in this collection.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @param {modelInput|modelInput[]} input -\n * The model or array of models to check for existence.\n * @return {Boolean} -\n * True if all models are related - otherwise false.\n */\n isRelated: function(input)\n {\n return this.relator.isRelated( this.model, input );\n },\n\n /**\n * Returns a clone of this collection.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @return {Rekord.RelationCollection} -\n * The reference to a clone collection.\n */\n clone: function()\n {\n return RelationCollection.create( this.database, this.model, this.relator, this, true );\n },\n\n /**\n * Returns an empty clone of this collection.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @return {Rekord.RelationCollection} -\n * The reference to a clone collection.\n */\n cloneEmpty: function()\n {\n return RelationCollection.create( this.database, this.model, this.relator );\n }\n\n});\n\n\n/**\n * Overrides functions in the given model collection to turn it into a collection\n * which contains models with a discriminator field.\n *\n * @param {Rekord.ModelCollection} collection -\n * The collection instance with discriminated models.\n * @param {String} discriminator -\n * The name of the field which contains the discriminator.\n * @param {Object} discriminatorsToModel -\n * A map of discriminators to the Rekord instances.\n * @return {Rekord.ModelCollection} -\n * The reference to the given collection.\n */\nfunction DiscriminateCollection(collection, discriminator, discriminatorsToModel)\n{\n Class.props( collection,\n {\n discriminator: discriminator,\n discriminatorsToModel: discriminatorsToModel\n });\n\n // Original Functions\n var buildKeyFromInput = collection.buildKeyFromInput;\n var parseModel = collection.parseModel;\n var clone = collection.clone;\n var cloneEmpty = collection.cloneEmpty;\n\n Class.props( collection,\n {\n\n /**\n * Builds a key from input. Discriminated collections only accept objects as\n * input - otherwise there's no way to determine the discriminator. If the\n * discriminator on the input doesn't map to a Rekord instance OR the input\n * is not an object the input will be returned instead of a model instance.\n *\n * @param {modelInput} input -\n * The input to create a key for.\n * @return {Any} -\n * The built key or the given input if a key could not be built.\n */\n buildKeyFromInput: function(input)\n {\n if ( isObject( input ) )\n {\n var discriminatedValue = input[ this.discriminator ];\n var model = this.discriminatorsToModel[ discriminatedValue ];\n\n if ( model )\n {\n return model.Database.keyHandler.buildKeyFromInput( input );\n }\n }\n\n return input;\n },\n\n /**\n * Takes input and returns a model instance. The input is expected to be an\n * object, any other type will return null.\n *\n * @param {modelInput} input -\n * The input to parse to a model instance.\n * @param {Boolean} [remoteData=false] -\n * Whether or not the input is coming from a remote source.\n * @return {Rekord.Model} -\n * The model instance parsed or null if none was found.\n */\n parseModel: function(input, remoteData)\n {\n if ( input instanceof Model )\n {\n return input;\n }\n\n var discriminatedValue = isValue( input ) ? input[ this.discriminator ] : null;\n var model = this.discriminatorsToModel[ discriminatedValue ];\n\n return model ? model.Database.parseModel( input, remoteData ) : null;\n },\n\n /**\n * Returns a clone of this collection.\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Rekord.Collection} -\n * The reference to a clone collection.\n */\n clone: function()\n {\n return DiscriminateCollection( clone.apply( this ), discriminator, discriminatorsToModel );\n },\n\n /**\n * Returns an empty clone of this collection.\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Rekord.Collection} -\n * The reference to a clone collection.\n */\n cloneEmpty: function()\n {\n return DiscriminateCollection( cloneEmpty.apply( this ), discriminator, discriminatorsToModel );\n }\n\n });\n\n return collection;\n}\n\n\n/**\n * Options you can pass to {@link Rekord.Search} or {@link Rekord.Model.search}.\n *\n * @typedef {Object} searchOptions\n * @property {Function} [$encode] -\n * A function which converts the search into an object to pass to the\n * specified methods.\n * @property {Function} [$decode] -\n * A function which takes the data returned from the server and returns\n * The array of models which are to be placed in the\n * {@link Rekord.Search#$results} property.\n */\n\n/**\n *\n * @constructor\n * @memberof Rekord\n */\nfunction Search(database, url, options, props, run)\n{\n this.$init( database, url, options, props, run );\n}\n\nSearch.Defaults =\n{\n};\n\nClass.create( Search,\n{\n\n $getDefaults: function()\n {\n return Search.Defaults;\n },\n\n $init: function(database, url, options, props, run)\n {\n applyOptions( this, options, this.$getDefaults(), true );\n\n Class.prop( this, '$db', database );\n\n this.$append = false;\n this.$url = url;\n this.$set( props );\n this.$results = ModelCollection.create( database );\n this.$results.$search = this;\n this.$promise = Promise.resolve( this );\n\n if ( run )\n {\n this.$run();\n }\n },\n\n $set: function(props)\n {\n if ( isObject( props ) )\n {\n transfer( props, this );\n }\n\n return this;\n },\n\n $unset: function()\n {\n for (var prop in this)\n {\n if ( prop.charAt(0) !== '$' )\n {\n delete this[ prop ];\n }\n }\n\n return this;\n },\n\n $run: function(url, props)\n {\n this.$url = url || this.$url;\n this.$set( props );\n\n var promise = new Promise();\n var encoded = this.$encode();\n var success = bind( this, this.$handleSuccess( promise ) );\n var failure = bind( this, this.$handleFailure( promise ) );\n var options = this.$options || this.$db.queryOptions;\n\n batchExecute(function()\n {\n this.$cancel();\n this.$promise = promise;\n this.$db.rest.query( this.$url, encoded, options, success, failure );\n\n }, this );\n\n return this.$promise;\n },\n\n $handleSuccess: function(promise)\n {\n return function(response)\n {\n if ( !this.$promise.isPending() || promise !== this.$promise )\n {\n return;\n }\n\n var models = this.$decode.apply( this, arguments );\n\n if ( this.$append )\n {\n this.$results.addAll( models, false, true );\n }\n else\n {\n this.$results.reset( models, true );\n }\n\n this.$promise.resolve( this, response, this.$results );\n };\n },\n\n $handleFailure: function(promise)\n {\n return function(response, status)\n {\n if ( !this.$promise.isPending() || promise !== this.$promise )\n {\n return;\n }\n\n var offline = RestStatus.Offline[ status ];\n\n if ( offline )\n {\n Rekord.checkNetworkStatus();\n\n offline = !Rekord.online;\n }\n\n if ( offline )\n {\n this.$promise.noline( this, response, status );\n }\n else\n {\n this.$promise.reject( this, response, status );\n }\n };\n },\n\n $cancel: function()\n {\n this.$promise.cancel();\n },\n\n $clear: function()\n {\n this.$results.clear();\n },\n\n $encode: function()\n {\n return cleanFunctions( copy( this ) );\n },\n\n $decode: function(models)\n {\n return models;\n },\n\n $key: function()\n {\n return '';\n },\n\n $change: function(callback, context)\n {\n return this.$results.change( callback, context );\n }\n\n});\n\n\n/**\n * Options you can pass to {@link Rekord.SearchPaged} or\n * {@link Rekord.Model.searchPaged}.\n *\n * @typedef {Object} searchPageOptions\n * @property {Number} [page_size=10] -\n * The size of the pages.\n * @property {Number} [page_index=0] -\n * The index of the search page.\n * @property {Number} [total=0] -\n * The total number of models that exist in the search without pagination\n * - this is expected to be provided by the remote search response.\n * @property {Function} [$encode] -\n * A function which converts the search into an object to pass to the\n * specified methods.\n * @property {Function} [$decode] -\n * A function which takes the data returned from the server and updates\n * this search with the results and paging information.\n * @property {Function} [$decodeResults] -\n * A function which takes the data returned from the server and returns the\n * array of models which are to be placed in the\n * {@link Rekord.Search#$results} property.\n * @property {Function} [$updatePageSize] -\n * A function which takes the data returned from the server and sets an\n * updated page size of the search.\n * @property {Function} [$updatePageIndex] -\n * A function which takes the data returned from the server and sets an\n * updated page index of the search.\n * @property {Function} [$updateTotal] -\n * A function which takes the data returned from the server and sets an\n * updated total of the search.\n */\n\nfunction SearchPaged(database, url, options, props, run)\n{\n this.$init( database, url, options, props, run );\n}\n\nSearchPaged.Defaults =\n{\n page_size: 10,\n page_index: 0,\n total: 0\n};\n\nClass.extend( Search, SearchPaged,\n{\n\n $getDefaults: function()\n {\n return SearchPaged.Defaults;\n },\n\n $goto: function(index, dontRun)\n {\n var pageIndex = this.$getPageIndex();\n var pageCount = this.$getPageCount();\n var desired = Math.max( 0, Math.min( index, pageCount - 1 ) );\n\n if ( pageIndex !== desired )\n {\n this.$setPageIndex( desired );\n\n if ( !dontRun )\n {\n this.$append = false;\n this.$run();\n }\n }\n\n return this.$promise;\n },\n\n $more: function()\n {\n var next = this.$getPageIndex() + 1;\n\n if ( next < this.$getPageCount() )\n {\n this.$setPageIndex( next );\n this.$append = true;\n this.$run();\n this.$promise.complete( this.$onMoreEnd, this );\n }\n\n return this.$promise;\n },\n\n $onMoreEnd: function()\n {\n this.$append = false;\n },\n\n $first: function(dontRun)\n {\n return this.$goto( 0, dontRun );\n },\n\n $last: function(dontRun)\n {\n return this.$goto( this.$getPageCount() - 1, dontRun );\n },\n\n $prev: function(dontRun)\n {\n return this.$goto( this.$getPageIndex() - 1, dontRun );\n },\n\n $next: function(dontRun)\n {\n return this.$goto( this.$getPageIndex() + 1, dontRun );\n },\n\n $total: function()\n {\n return this.$getTotal();\n },\n\n $pages: function()\n {\n return this.$getPageCount();\n },\n\n $page: function(index)\n {\n return Math.max( 0, Math.min( index, this.$pages() - 1 ) );\n },\n\n $can: function(index)\n {\n return this.$getTotal() && index >= 0 && index < this.$getPageCount();\n },\n\n $canFirst: function()\n {\n return this.$canPrev();\n },\n\n $canLast: function()\n {\n return this.$canNext();\n },\n\n $canPrev: function()\n {\n return this.$getTotal() && this.$getPageIndex() > 0;\n },\n\n $canNext: function()\n {\n return this.$getTotal() && this.$getPageIndex() < this.$getPageCount() - 1;\n },\n\n $decode: function(response)\n {\n this.$updatePageSize( response );\n this.$updatePageIndex( response );\n this.$updateTotal( response );\n\n return this.$decodeResults( response );\n },\n\n $decodeResults: function(response)\n {\n return response.results;\n },\n\n $updatePageSize: function(response)\n {\n if ( isNumber( response.page_size ) )\n {\n this.page_size = response.page_size;\n }\n },\n\n $setPageSize: function(page_size)\n {\n this.page_size = page_size;\n },\n\n $getPageSize: function()\n {\n return this.page_size;\n },\n\n $updatePageIndex: function(response)\n {\n if ( isNumber( response.page_index ) )\n {\n this.page_index = response.page_index;\n }\n },\n\n $setPageIndex: function(page_index)\n {\n this.page_index = page_index || 0;\n },\n\n $getPageIndex: function()\n {\n return this.page_index;\n },\n\n $getPageOffset: function()\n {\n return this.page_index * this.page_size;\n },\n\n $updateTotal: function(response)\n {\n if ( isNumber( response.total ) )\n {\n this.total = response.total;\n }\n },\n\n $setTotal: function(total)\n {\n this.total = total || 0;\n },\n\n $getTotal: function()\n {\n return this.total;\n },\n\n $getPageCount: function()\n {\n return Math.ceil( this.$getTotal() / this.$getPageSize() );\n }\n\n});\n\n\nfunction Promise(executor, cancelable)\n{\n this.status = Promise.Status.Pending;\n this.cancelable = cancelable !== false;\n this.nexts = [];\n\n Class.prop( this, 'results', null );\n\n if ( isFunction( executor ) )\n {\n executor(\n bind(this, this.resolve),\n bind(this, this.reject),\n bind(this, this.noline),\n bind(this, this.cancel)\n );\n }\n}\n\nPromise.Status =\n{\n Pending: 'pending',\n Success: 'success',\n Failure: 'failure',\n Offline: 'offline',\n Canceled: 'canceled'\n};\n\nPromise.Events =\n{\n Success: 'success',\n Failure: 'failure',\n Offline: 'offline',\n Canceled: 'canceled',\n Unsuccessful: 'failure offline canceled',\n Complete: 'success failure offline canceled'\n};\n\nPromise.all = function(iterable)\n{\n var all = new Promise();\n var successes = 0;\n var goal = iterable.length;\n var results = [];\n\n function handleSuccess()\n {\n results.push( AP.slice.apply( arguments ) );\n\n if ( ++successes === goal )\n {\n all.resolve( results );\n }\n }\n\n for (var i = 0; i < iterable.length; i++)\n {\n var p = iterable[ i ];\n\n if ( p instanceof Promise )\n {\n p.then( handleSuccess, all.reject, all.noline, all.cancel, all );\n }\n else\n {\n goal--;\n }\n }\n\n return all;\n};\n\nPromise.race = function(iterable)\n{\n var race = new Promise();\n\n for (var i = 0; i < iterable.length; i++)\n {\n var p = iterable[ i ];\n\n if ( p instanceof Promise )\n {\n p.bind( race );\n }\n }\n\n return race;\n};\n\nPromise.reject = function(reason)\n{\n var p = new Promise();\n p.reject.apply( p, arguments );\n return p;\n};\n\nPromise.resolve = function()\n{\n var p = new Promise();\n p.resolve.apply( p, arguments );\n return p;\n};\n\nPromise.noline = function(reason)\n{\n var p = new Promise();\n p.noline.apply( p, arguments );\n return p;\n};\n\nPromise.cancel = function()\n{\n var p = new Promise();\n p.cancel.apply( p, arguments );\n return p;\n};\n\nPromise.then = function()\n{\n var p = new Promise();\n p.resolve();\n return p.then.apply( p, arguments );\n};\n\nPromise.singularity = (function()\n{\n var singularity = null;\n var singularityResult = null;\n var consuming = false;\n var promiseCount = 0;\n var promiseComplete = 0;\n\n function handleSuccess()\n {\n if ( ++promiseComplete === promiseCount )\n {\n singularity.resolve( singularityResult );\n }\n }\n\n function bindPromise(promise)\n {\n promiseCount++;\n promise.then( handleSuccess, singularity.reject, singularity.noline, null, singularity );\n }\n\n return function(promiseOrContext, contextOrCallback, callbackOrNull)\n {\n var promise = promiseOrContext;\n var context = contextOrCallback;\n var callback = callbackOrNull;\n\n if (!(promise instanceof Promise))\n {\n promise = false;\n context = promiseOrContext;\n callback = contextOrCallback;\n }\n\n if ( !consuming )\n {\n consuming = true;\n singularity = new Promise( null, false );\n singularityResult = context;\n promiseCount = 0;\n promiseComplete = 0;\n\n if (promise)\n {\n bindPromise( promise );\n }\n\n try\n {\n callback.call( context, singularity );\n }\n catch (ex)\n {\n Rekord.trigger( Rekord.Events.Error, [ex] );\n\n throw ex;\n }\n finally\n {\n consuming = false;\n }\n }\n else\n {\n if (promise)\n {\n bindPromise( promise );\n }\n\n callback.call( context, singularity );\n }\n\n if (promiseCount === 0)\n {\n singularity.resolve();\n }\n\n return singularity;\n };\n\n})();\n\nClass.create( Promise,\n{\n resolve: function()\n {\n this.finish( Promise.Status.Success, Promise.Events.Success, arguments );\n },\n\n reject: function()\n {\n this.finish( Promise.Status.Failure, Promise.Events.Failure, arguments );\n },\n\n noline: function()\n {\n this.finish( Promise.Status.Offline, Promise.Events.Offline, arguments );\n },\n\n cancel: function()\n {\n if ( this.cancelable )\n {\n this.finish( Promise.Status.Canceled, Promise.Events.Canceled, arguments );\n }\n },\n\n bind: function(promise)\n {\n this.success( promise.resolve, promise );\n this.failure( promise.reject, promise );\n this.offline( promise.noline, promise );\n this.canceled( promise.cancel, promise );\n },\n\n then: function(success, failure, offline, canceled, context, persistent )\n {\n // The promise which can be resolved if any of the callbacks return\n // a Promise which is resolved.\n var next = new Promise();\n\n this.success( success, context, persistent, next );\n this.failure( failure, context, persistent, next );\n this.offline( offline, context, persistent, next );\n this.canceled( canceled, context, persistent, next );\n this.addNext( next );\n \n return next;\n },\n\n addNext: function(next)\n {\n var nexts = this.nexts;\n\n if (nexts.length === 0)\n {\n // If this promise is not successful, let all chained promises know.\n this.unsuccessful(function()\n {\n for (var i = 0; i < nexts.length; i++)\n {\n nexts[ i ].finish( this.status, this.status, arguments );\n }\n });\n }\n\n nexts.push( next );\n },\n\n reset: function(clearListeners)\n {\n this.status = Promise.Status.Pending;\n\n if ( clearListeners )\n {\n this.off();\n }\n\n return this;\n },\n\n finish: function(status, events, results)\n {\n if ( this.status === Promise.Status.Pending )\n {\n this.results = AP.slice.apply( results );\n this.status = status;\n this.trigger( events, results );\n }\n },\n\n listenFor: function(immediate, events, callback, context, persistent, next)\n {\n if ( isFunction( callback ) )\n {\n var handleEvents = function()\n {\n var result = callback.apply( context || this, this.results );\n\n if ( result instanceof Promise &&\n next instanceof Promise &&\n next.isPending() )\n {\n result.bind( next );\n }\n };\n\n if ( this.status === Promise.Status.Pending )\n {\n if ( persistent )\n {\n this.on( events, handleEvents, this );\n }\n else\n {\n this.once( events, handleEvents, this );\n }\n }\n else if ( immediate )\n {\n handleEvents.apply( this );\n }\n }\n\n return this;\n },\n\n success: function(callback, context, persistent, next)\n {\n return this.listenFor( this.isSuccess(), Promise.Events.Success, callback, context, persistent, next );\n },\n\n unsuccessful: function(callback, context, persistent, next)\n {\n return this.listenFor( this.isUnsuccessful(), Promise.Events.Unsuccessful, callback, context, persistent, next );\n },\n\n failure: function(callback, context, persistent, next)\n {\n return this.listenFor( this.isFailure(), Promise.Events.Failure, callback, context, persistent, next );\n },\n\n catch: function(callback, context, persistent, next)\n {\n return this.listenFor( this.isFailure(), Promise.Events.Failure, callback, context, persistent, next );\n },\n\n offline: function(callback, context, persistent, next)\n {\n return this.listenFor( this.isOffline(), Promise.Events.Offline, callback, context, persistent, next );\n },\n\n canceled: function(callback, context, persistent, next)\n {\n return this.listenFor( this.isCanceled(), Promise.Events.Canceled, callback, context, persistent, next );\n },\n\n complete: function(callback, context, persistent, next)\n {\n return this.listenFor( true, Promise.Events.Complete, callback, context, persistent, next );\n },\n\n isSuccess: function()\n {\n return this.status === Promise.Status.Success;\n },\n\n isUnsuccessful: function()\n {\n return this.status !== Promise.Status.Success && this.status !== Promise.Status.Pending;\n },\n\n isFailure: function()\n {\n return this.status === Promise.Status.Failure;\n },\n\n isOffline: function()\n {\n return this.status === Promise.Status.Offline;\n },\n\n isCanceled: function()\n {\n return this.status === Promise.Status.Canceled;\n },\n\n isPending: function()\n {\n return this.status === Promise.Status.Pending;\n },\n\n isComplete: function()\n {\n return this.status !== Promise.Status.Pending;\n }\n\n});\n\naddEventful( Promise );\n\n\nfunction Operation()\n{\n}\n\nClass.create( Operation,\n{\n\n reset: function(model, cascade, options)\n {\n this.model = model;\n this.cascade = isNumber( cascade ) ? cascade : Cascade.All;\n this.options = options;\n this.db = model.$db;\n this.next = null;\n this.finished = false;\n },\n\n canCascade: function(cascade)\n {\n var expected = cascade || this.cascading;\n var actual = this.cascade;\n\n return (expected & actual) !== 0;\n },\n\n notCascade: function(expected)\n {\n var actual = this.cascade;\n\n return (expected & actual) === 0;\n },\n\n queue: function(operation)\n {\n if ( this.next && !operation.interrupts )\n {\n this.next.queue( operation );\n }\n else\n {\n this.next = operation;\n this.model.$trigger( Model.Events.OperationsStarted );\n }\n },\n\n tryNext: function(OperationType)\n {\n var setNext = !this.next;\n\n if ( setNext )\n {\n this.next = new OperationType( this.model, this.cascade, this.options );\n }\n\n return setNext;\n },\n\n insertNext: function(OperationType)\n {\n var op = new OperationType( this.model, this.cascade, this.options );\n\n op.next = this.next;\n this.next = op;\n },\n\n execute: function()\n {\n if ( this.db.pendingOperations === 0 )\n {\n this.db.trigger( Database.Events.OperationsStarted );\n }\n\n this.db.pendingOperations++;\n\n try\n {\n this.run( this.db, this.model );\n }\n catch (ex)\n {\n this.finish();\n\n Rekord.trigger( Rekord.Events.Error, [ex] );\n\n throw ex;\n }\n },\n\n run: function(db, model)\n {\n throw 'Operation.run Not implemented';\n },\n\n finish: function()\n {\n if ( !this.finished )\n {\n this.finished = true;\n this.model.$operation = this.next;\n\n if ( this.next )\n {\n this.next.execute();\n }\n\n this.db.pendingOperations--;\n\n if ( !this.next )\n {\n this.model.$trigger( Model.Events.OperationsFinished );\n }\n\n if ( this.db.pendingOperations === 0 )\n {\n this.db.onOperationRest();\n this.db.trigger( Database.Events.OperationsFinished );\n }\n }\n\n return this;\n },\n\n success: function()\n {\n return bind( this, this.handleSuccess );\n },\n\n handleSuccess: function()\n {\n try\n {\n this.onSuccess.apply( this, arguments );\n }\n catch (ex)\n {\n Rekord.trigger( Rekord.Events.Error, [ex] );\n\n throw ex;\n }\n finally\n {\n this.finish();\n }\n },\n\n onSuccess: function()\n {\n\n },\n\n failure: function()\n {\n return bind( this, this.handleFailure );\n },\n\n handleFailure: function()\n {\n try\n {\n this.onFailure.apply( this, arguments );\n }\n catch (ex)\n {\n Rekord.trigger( Rekord.Events.Error, [ex] );\n\n throw ex;\n }\n finally\n {\n this.finish();\n }\n },\n\n onFailure: function()\n {\n\n }\n\n});\n\nfunction GetLocal(model, cascade, options)\n{\n this.reset( model, cascade, options );\n}\n\nClass.extend( Operation, GetLocal,\n{\n\n cascading: Cascade.Local,\n\n interrupts: false,\n\n type: 'GetLocal',\n\n run: function(db, model)\n {\n if ( model.$isDeleted() )\n {\n model.$trigger( Model.Events.LocalGetFailure, [model] );\n\n this.finish();\n }\n else if ( this.canCascade() && db.cache === Cache.All )\n {\n db.store.get( model.$key(), this.success(), this.failure() );\n }\n else\n {\n Rekord.debug( Rekord.Debugs.GET_LOCAL_SKIPPED, model );\n\n model.$trigger( Model.Events.LocalGet, [model] );\n\n this.insertNext( GetRemote );\n this.finish();\n }\n },\n\n onSuccess: function(key, encoded)\n {\n var model = this.model;\n\n if ( isObject( encoded ) )\n {\n model.$set( encoded );\n }\n\n Rekord.debug( Rekord.Debugs.GET_LOCAL, model, encoded );\n\n model.$trigger( Model.Events.LocalGet, [model] );\n\n if ( this.canCascade( Cascade.Rest ) && !model.$isDeleted() )\n {\n this.insertNext( GetRemote );\n }\n },\n\n onFailure: function(e)\n {\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.GET_LOCAL, model, e );\n\n model.$trigger( Model.Events.LocalGetFailure, [model] );\n\n if ( this.canCascade( Cascade.Rest ) && !model.$isDeleted() )\n {\n this.insertNext( GetRemote );\n }\n }\n\n});\n\nfunction GetRemote(model, cascade, options)\n{\n this.reset( model, cascade, options );\n}\n\nClass.extend( Operation, GetRemote,\n{\n\n cascading: Cascade.Rest,\n\n interrupts: false,\n\n type: 'GetRemote',\n\n run: function(db, model)\n {\n if ( model.$isDeleted() )\n {\n model.$trigger( Model.Events.RemoteGetFailure, [model] );\n\n this.finish();\n }\n else if ( this.canCascade() )\n {\n batchExecute(function()\n {\n db.rest.get( model, this.options || db.getOptions, this.success(), this.failure() );\n\n }, this );\n }\n else\n {\n model.$trigger( Model.Events.RemoteGet, [model] );\n\n this.finish();\n }\n },\n\n onSuccess: function(response)\n {\n var db = this.db;\n var data = db.resolveModel( response );\n var model = this.model;\n\n if ( isObject( data ) )\n {\n db.putRemoteData( data, model.$key(), model, true );\n }\n\n Rekord.debug( Rekord.Debugs.GET_REMOTE, model, data );\n\n model.$trigger( Model.Events.RemoteGet, [model] );\n },\n\n onFailure: function(response, status)\n {\n var db = this.db;\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.GET_REMOTE_ERROR, model, response, status );\n\n if ( RestStatus.NotFound[ status ] )\n {\n this.insertNext( RemoveNow );\n\n db.destroyModel( model );\n\n model.$trigger( Model.Events.RemoteGetFailure, [model, response] );\n }\n else if ( RestStatus.Offline[ status ] )\n {\n model.$trigger( Model.Events.RemoteGetOffline, [model, response] );\n }\n else\n {\n model.$trigger( Model.Events.RemoteGetFailure, [model, response] );\n }\n }\n\n});\n\nfunction RemoveCache(model, cascade)\n{\n this.reset( model, cascade );\n}\n\nClass.extend( Operation, RemoveCache,\n{\n\n cascading: Cascade.None,\n\n interrupts: true,\n\n type: 'RemoveCache',\n\n run: function(db, model)\n {\n if ( db.cache === Cache.None )\n {\n this.finish();\n }\n else\n {\n db.store.remove( model.$key(), this.success(), this.failure() );\n }\n }\n\n});\n\nfunction RemoveLocal(model, cascade)\n{\n this.reset( model, cascade );\n}\n\nClass.extend( Operation, RemoveLocal,\n{\n\n cascading: Cascade.Local,\n\n interrupts: true,\n\n type: 'RemoveLocal',\n\n run: function(db, model)\n {\n model.$status = Model.Status.RemovePending;\n\n if ( db.cache === Cache.None || !model.$local || !this.canCascade() )\n {\n Rekord.debug( Rekord.Debugs.REMOVE_LOCAL_NONE, model );\n\n model.$trigger( Model.Events.LocalRemove, [model] );\n\n this.insertNext( RemoveRemote );\n this.finish();\n }\n else if ( model.$saved && this.canCascade( Cascade.Rest ) )\n {\n model.$local.$status = model.$status;\n\n db.store.put( model.$key(), model.$local, this.success(), this.failure() );\n }\n else\n {\n Rekord.debug( Rekord.Debugs.REMOVE_LOCAL_UNSAVED, model );\n\n db.store.remove( model.$key(), this.success(), this.failure() );\n }\n },\n\n onSuccess: function(key, encoded, previousValue)\n {\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.REMOVE_LOCAL, model );\n\n model.$trigger( Model.Events.LocalRemove, [model] );\n\n if ( model.$saved && this.canCascade( Cascade.Remote ) )\n {\n model.$addOperation( RemoveRemote, this.cascade, this.options );\n }\n },\n\n onFailure: function(e)\n {\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.REMOVE_LOCAL_ERROR, model, e );\n\n model.$trigger( Model.Events.LocalRemoveFailure, [model] );\n\n if ( model.$saved && this.canCascade( Cascade.Remote ) )\n {\n model.$addOperation( RemoveRemote, this.cascade, this.options );\n }\n }\n\n});\n\nfunction RemoveNow(model, cascade)\n{\n this.reset( model, cascade );\n}\n\nClass.extend( Operation, RemoveNow,\n{\n\n cascading: Cascade.Local,\n\n interrupts: true,\n\n type: 'RemoveNow',\n\n run: function(db, model)\n {\n var key = model.$key();\n\n model.$status = Model.Status.RemovePending;\n\n db.removeFromModels( model );\n\n if ( db.cache === Cache.None || !this.canCascade() )\n {\n this.finishRemove();\n this.finish();\n }\n else\n {\n db.store.remove( key, this.success(), this.failure() );\n }\n },\n\n onSuccess: function()\n {\n this.finishRemove();\n },\n\n onFailure: function()\n {\n this.finishRemove();\n },\n\n finishRemove: function()\n {\n var model = this.model;\n\n model.$status = Model.Status.Removed;\n\n delete model.$local;\n delete model.$saving;\n delete model.$publish;\n delete model.$saved;\n }\n\n});\n\nfunction RemoveRemote(model, cascade, options)\n{\n this.reset( model, cascade, options );\n}\n\nClass.extend( Operation, RemoveRemote,\n{\n\n cascading: Cascade.Remote,\n\n interrupts: true,\n\n type: 'RemoveRemote',\n\n run: function(db, model)\n {\n if ( this.notCascade( Cascade.Rest ) )\n {\n this.liveRemove();\n\n model.$trigger( Model.Events.RemoteRemove, [model] );\n\n this.finish();\n }\n else\n {\n model.$status = Model.Status.RemovePending;\n\n batchExecute(function()\n {\n db.rest.remove( model, this.options || this.removeOptions, this.success(), this.failure() );\n\n }, this );\n }\n },\n\n onSuccess: function(data)\n {\n this.finishRemove();\n },\n\n onFailure: function(response, status)\n {\n var model = this.model;\n var key = model.$key();\n\n if ( RestStatus.NotFound[ status ] )\n {\n Rekord.debug( Rekord.Debugs.REMOVE_MISSING, model, key );\n\n this.finishRemove( true );\n }\n else if ( RestStatus.Offline[ status ] )\n {\n // Looks like we're offline!\n Rekord.checkNetworkStatus();\n\n // If we are offline, wait until we're online again to resume the delete\n if (!Rekord.online)\n {\n model.$listenForOnline( this.cascade );\n\n model.$trigger( Model.Events.RemoteRemoveOffline, [model, response] );\n }\n else\n {\n model.$trigger( Model.Events.RemoteRemoveFailure, [model, response] );\n }\n\n Rekord.debug( Rekord.Debugs.REMOVE_OFFLINE, model, response );\n }\n else\n {\n Rekord.debug( Rekord.Debugs.REMOVE_ERROR, model, status, key, response );\n\n model.$trigger( Model.Events.RemoteRemoveFailure, [model, response] );\n }\n },\n\n finishRemove: function(notLive)\n {\n var db = this.db;\n var model = this.model;\n var key = model.$key();\n\n Rekord.debug( Rekord.Debugs.REMOVE_REMOTE, model, key );\n\n // Successfully removed!\n model.$status = Model.Status.Removed;\n\n // Successfully Removed!\n model.$trigger( Model.Events.RemoteRemove, [model] );\n\n // Remove from local storage now\n this.insertNext( RemoveNow );\n\n // Remove it live!\n if ( !notLive )\n {\n this.liveRemove();\n }\n\n // Remove the model reference for good!\n db.removeReference( key );\n },\n\n liveRemove: function()\n {\n if ( this.canCascade( Cascade.Live ) )\n {\n var db = this.db;\n var model = this.model;\n var key = model.$key();\n\n // Publish REMOVE\n Rekord.debug( Rekord.Debugs.REMOVE_PUBLISH, model, key );\n\n db.live.remove( model );\n }\n }\n\n});\n\nfunction SaveLocal(model, cascade, options)\n{\n this.reset( model, cascade, options );\n}\n\nClass.extend( Operation, SaveLocal,\n{\n\n cascading: Cascade.Local,\n\n interrupts: false,\n\n type: 'SaveLocal',\n\n run: function(db, model)\n {\n if ( model.$isDeleted() )\n {\n Rekord.debug( Rekord.Debugs.SAVE_LOCAL_DELETED, model );\n\n model.$trigger( Model.Events.LocalSaveFailure, [model] );\n\n this.finish();\n }\n else if ( db.cache === Cache.None || !this.canCascade() )\n {\n if ( this.canCascade( Cascade.Remote ) )\n {\n if ( this.tryNext( SaveRemote ) )\n {\n this.markSaving( db, model );\n }\n }\n\n model.$trigger( Model.Events.LocalSave, [model] );\n\n this.finish();\n }\n else\n {\n var key = model.$key();\n var local = model.$toJSON( false );\n\n this.markSaving( db, model );\n\n if ( model.$local )\n {\n transfer( local, model.$local );\n }\n else\n {\n model.$local = local;\n\n if ( model.$saved )\n {\n model.$local.$saved = model.$saved;\n }\n }\n\n model.$local.$status = model.$status;\n model.$local.$saving = model.$saving;\n model.$local.$publish = model.$publish;\n\n db.store.put( key, model.$local, this.success(), this.failure() );\n }\n },\n\n markSaving: function(db, model)\n {\n var remote = model.$toJSON( true );\n var changes = model.$getChanges( remote );\n\n var saving = db.fullSave ? remote : this.grabAlways( db.saveAlways, changes, remote );\n var publish = db.fullPublish ? remote : this.grabAlways( db.publishAlways, changes, remote );\n\n model.$status = Model.Status.SavePending;\n model.$saving = saving;\n model.$publish = publish;\n },\n\n grabAlways: function(always, changes, encoded)\n {\n var changesCopy = null;\n\n if ( always.length )\n {\n for (var i = 0; i < always.length; i++)\n {\n var prop = always[ i ];\n\n if ( !(prop in changes) )\n {\n if ( !changesCopy )\n {\n changesCopy = copy( changes );\n }\n\n changesCopy[ prop ] = encoded[ prop ];\n }\n }\n }\n\n return changesCopy || changes;\n },\n\n clearLocal: function(model)\n {\n model.$status = Model.Status.Synced;\n\n model.$local.$status = model.$status;\n\n delete model.$local.$saving;\n delete model.$local.$publish;\n\n this.insertNext( SaveNow );\n },\n\n onSuccess: function(key, encoded, previousValue)\n {\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.SAVE_LOCAL, model );\n\n if ( this.cascade )\n {\n this.tryNext( SaveRemote );\n }\n else\n {\n this.clearLocal( model );\n }\n\n model.$trigger( Model.Events.LocalSave, [model] );\n },\n\n onFailure: function(e)\n {\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.SAVE_LOCAL_ERROR, model, e );\n\n if ( this.cascade )\n {\n this.tryNext( SaveRemote );\n }\n else\n {\n this.clearLocal( model );\n }\n\n model.$trigger( Model.Events.LocalSaveFailure, [model] );\n }\n\n});\n\nfunction SaveNow(model, cascade)\n{\n this.reset( model, cascade );\n}\n\nClass.extend( Operation, SaveNow,\n{\n\n cascading: Cascade.Local,\n\n interrupts: false,\n\n type: 'SaveNow',\n\n run: function(db, model)\n {\n var key = model.$key();\n var local = model.$local;\n\n if ( db.cache === Cache.All && key && local && this.canCascade() )\n {\n db.store.put( key, local, this.success(), this.failure() );\n }\n else\n {\n this.finish();\n }\n }\n\n});\n\nfunction SaveRemote(model, cascade, options)\n{\n this.reset( model, cascade, options );\n}\n\nClass.extend( Operation, SaveRemote,\n{\n\n cascading: Cascade.Remote,\n\n interrupts: false,\n\n type: 'SaveRemote',\n\n run: function(db, model)\n {\n if ( model.$isDeleted() )\n {\n Rekord.debug( Rekord.Debugs.SAVE_REMOTE_DELETED, model );\n\n this.markSynced( model, true, Model.Events.RemoteSaveFailure, null );\n this.finish();\n }\n else if ( !model.$dependents.isSaved( this.tryAgain, this ) )\n {\n this.finish();\n }\n else if ( !db.hasData( model.$saving ) || this.notCascade( Cascade.Rest ) )\n {\n this.liveSave();\n this.markSynced( model, true, Model.Events.RemoteSave, null );\n this.finish();\n }\n else\n {\n model.$status = Model.Status.SavePending;\n\n batchExecute(function()\n {\n if ( model.$saved )\n {\n db.rest.update( model, model.$saving, this.options || db.updateOptions || db.saveOptions, this.success(), this.failure() );\n }\n else\n {\n db.rest.create( model, model.$saving, this.options || db.createOptions || db.saveOptions, this.success(), this.failure() );\n }\n\n }, this );\n }\n },\n\n onSuccess: function(response)\n {\n var db = this.db;\n var data = db.resolveModel( response );\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.SAVE_REMOTE, model );\n\n this.handleData( data );\n },\n\n onFailure: function(response, status)\n {\n var operation = this;\n var db = this.db;\n var data = db.resolveModel( response );\n var model = this.model;\n\n // A non-zero status means a real problem occurred\n if ( RestStatus.Conflict[ status ] ) // 409 Conflict\n {\n Rekord.debug( Rekord.Debugs.SAVE_CONFLICT, model, data );\n\n this.handleData( data );\n }\n else if ( RestStatus.NotFound[ status ] )\n {\n Rekord.debug( Rekord.Debugs.SAVE_UPDATE_FAIL, model );\n\n this.insertNext( RemoveNow );\n\n db.destroyModel( model );\n\n model.$trigger( Model.Events.RemoteSaveFailure, [model, response] );\n }\n else if ( RestStatus.Offline[ status ] )\n {\n // Check the network status right now\n Rekord.checkNetworkStatus();\n\n // If not online for sure, try saving once online again\n if (!Rekord.online)\n {\n model.$listenForOnline( this.cascade );\n\n model.$trigger( Model.Events.RemoteSaveOffline, [model, response] );\n }\n else\n {\n this.markSynced( model, true, Model.Events.RemoteSaveFailure, response );\n }\n\n Rekord.debug( Rekord.Debugs.SAVE_OFFLINE, model, response );\n }\n else\n {\n Rekord.debug( Rekord.Debugs.SAVE_ERROR, model, status );\n\n this.markSynced( model, true, Model.Events.RemoteSaveFailure, response );\n }\n },\n\n markSynced: function(model, saveNow, eventType, response)\n {\n model.$status = Model.Status.Synced;\n\n this.clearPending( model );\n\n if ( saveNow )\n {\n this.insertNext( SaveNow );\n }\n\n if ( eventType )\n {\n model.$trigger( eventType, [model, response] );\n }\n },\n\n clearPending: function(model)\n {\n delete model.$saving;\n delete model.$publish;\n\n if ( model.$local )\n {\n model.$local.$status = model.$status;\n\n delete model.$local.$saving;\n delete model.$local.$publish;\n }\n },\n\n handleData: function(data)\n {\n var db = this.db;\n var model = this.model;\n var saving = model.$saving;\n\n // Check deleted one more time before updating model.\n if ( model.$isDeleted() )\n {\n Rekord.debug( Rekord.Debugs.SAVE_REMOTE_DELETED, model, data );\n\n return this.clearPending( model );\n }\n\n Rekord.debug( Rekord.Debugs.SAVE_VALUES, model, saving );\n\n // If the model hasn't been saved before - create the record where the\n // local and model point to the same object.\n if ( !model.$saved )\n {\n model.$saved = model.$local ? (model.$local.$saved = {}) : {};\n }\n\n // Tranfer all saved fields into the saved object\n transfer( saving, model.$saved );\n\n // Update the model with the return data\n if ( !isEmpty( data ) )\n {\n db.putRemoteData( data, model.$key(), model );\n }\n\n this.liveSave( data );\n this.markSynced( model, false, Model.Events.RemoteSave, null );\n\n if ( db.cache === Cache.Pending )\n {\n this.insertNext( RemoveCache );\n }\n else\n {\n this.insertNext( SaveNow );\n }\n },\n\n liveSave: function(data)\n {\n var db = this.db;\n var model = this.model;\n\n if ( isObject(data) )\n {\n transfer( data, model.$publish );\n }\n\n if ( this.canCascade( Cascade.Live ) && db.hasData( model.$publish ) )\n {\n // Publish saved data to everyone else\n Rekord.debug( Rekord.Debugs.SAVE_PUBLISH, model, model.$publish );\n\n db.live.save( model, model.$publish );\n }\n },\n\n tryAgain: function()\n {\n var model = this.model;\n\n model.$addOperation( SaveLocal, this.cascade, this.options );\n }\n\n});\n\n\nfunction Relation()\n{\n\n}\n\nRekord.Relations = {};\n\nRelation.Defaults =\n{\n model: null,\n lazy: false,\n store: Store.None,\n save: Save.None,\n auto: true,\n autoCascade: Cascade.All,\n autoOptions: null,\n property: true,\n preserve: true,\n clearKey: true,\n dynamic: false,\n discriminator: 'discriminator',\n discriminators: {},\n discriminatorToModel: {}\n};\n\nClass.create( Relation,\n{\n\n debugQuery: null,\n debugQueryResults: null,\n\n getDefaults: function(database, field, options)\n {\n return Relation.Defaults;\n },\n\n /**\n * Initializes this relation with the given database, field, and options.\n *\n * @param {Rekord.Database} database [description]\n * @param {String} field [description]\n * @param {Object} options [description]\n */\n init: function(database, field, options)\n {\n applyOptions( this, options, this.getDefaults( database, field, options ) );\n\n this.database = database;\n this.name = field;\n this.options = options;\n this.initialized = false;\n this.property = this.property || (indexOf( database.fields, this.name ) !== false);\n this.discriminated = !isEmpty( this.discriminators );\n\n if ( this.discriminated )\n {\n if ( !Polymorphic )\n {\n throw 'Polymorphic feature is required to use the discriminated option.';\n }\n\n Class.props( this, Polymorphic );\n }\n\n this.setReferences( database, field, options );\n },\n\n setReferences: function(database, field, options)\n {\n if ( !isRekord( this.model ) )\n {\n Rekord.get( this.model ).complete( this.setModelReference( database, field, options ), this );\n }\n else\n {\n this.onInitialized( database, field, options );\n }\n },\n\n /**\n *\n */\n setModelReference: function(database, field, options)\n {\n return function(rekord)\n {\n this.model = rekord;\n\n this.onInitialized( database, field, options );\n };\n },\n\n /**\n *\n */\n onInitialized: function(database, fields, options)\n {\n\n },\n\n finishInitialization: function()\n {\n this.initialized = true;\n this.load.open();\n },\n\n /**\n * Loads the model.$relation variable with what is necessary to get, set,\n * relate, and unrelate models. If property is true, look at model[ name ]\n * to load models/keys. If it contains values that don't exist or aren't\n * actually related\n *\n * @param {Rekord.Model} model [description]\n */\n\n load: Gate(function(model, initialValue, remoteData)\n {\n\n }),\n\n set: function(model, input, remoteData)\n {\n\n },\n\n relate: function(model, input, remoteData)\n {\n\n },\n\n unrelate: function(model, input, remoteData)\n {\n\n },\n\n sync: function(model, removeUnrelated)\n {\n\n },\n\n isRelated: function(model, input)\n {\n\n },\n\n preClone: function(model, clone, properties)\n {\n\n },\n\n postClone: function(model, clone, properties)\n {\n\n },\n\n get: function(model)\n {\n return model.$relations[ this.name ].related;\n },\n\n encode: function(model, out, forSaving)\n {\n var relation = model.$relations[ this.name ];\n var mode = forSaving ? this.save : this.store;\n\n if ( relation && mode )\n {\n var related = relation.related;\n\n if ( isArray( related ) )\n {\n out[ this.name ] = this.getStoredArray( related, mode );\n }\n else // if ( isObject( related ) )\n {\n out[ this.name ] = this.getStored( related, mode );\n }\n }\n },\n\n ready: function(callback)\n {\n this.model.Database.ready( callback, this );\n },\n\n listenToModelAdded: function(callback)\n {\n this.model.Database.on( Database.Events.ModelAdded, callback, this );\n },\n\n executeQuery: function(model)\n {\n if ( !Search )\n {\n throw 'Search feature is required to use the query option.';\n }\n\n var queryOption = this.query;\n var queryOptions = this.queryOptions;\n var queryData = this.queryData;\n var query = isString( queryOption ) ? format( queryOption, model ) : queryOption;\n var search = this.model.search( query, queryOptions, queryData );\n\n Rekord.debug( this.debugQuery, this, model, search, queryOption, query, queryData );\n\n var promise = search.$run();\n\n promise.complete( this.handleExecuteQuery( model ), this );\n\n return search;\n },\n\n handleExecuteQuery: function(model)\n {\n return function onExecuteQuery(search)\n {\n var results = search.$results;\n\n Rekord.debug( this.debugQueryResults, this, model, search );\n\n for (var i = 0; i < results.length; i++)\n {\n this.relate( model, results[ i ], true );\n }\n };\n },\n\n createRelationCollection: function(model)\n {\n return RelationCollection.create( this.model.Database, model, this );\n },\n\n createCollection: function(initial)\n {\n return ModelCollection.create( this.model.Database, initial );\n },\n\n parseModel: function(input, remoteData)\n {\n return this.model.Database.parseModel( input, remoteData );\n },\n\n grabInitial: function( model, fields )\n {\n if ( hasFields( model, fields, isValue ) )\n {\n return pull( model, fields );\n }\n },\n\n grabModel: function(input, callback, remoteData)\n {\n this.model.Database.grabModel( input, callback, this, remoteData );\n },\n\n grabModels: function(relation, initial, callback, remoteData)\n {\n var db = this.model.Database;\n\n for (var i = 0; i < initial.length; i++)\n {\n var input = initial[ i ];\n var key = db.keyHandler.buildKeyFromInput( input );\n\n relation.pending[ key ] = true;\n\n if ( input instanceof Model )\n {\n callback.call( this, input );\n }\n else\n {\n db.grabModel( input, callback, this, remoteData );\n }\n }\n },\n\n buildKey: function(input)\n {\n\n },\n\n setProperty: function(relation)\n {\n if ( this.property )\n {\n var model = relation.parent;\n var propertyName = this.name;\n var applied = !!relation.dynamicSet;\n\n if ( !applied && this.dynamic && Object.defineProperty )\n {\n var relator = this;\n\n Object.defineProperty( model, propertyName,\n {\n enumerable: true,\n\n set: function(input)\n {\n relator.set( model, input );\n },\n get: function()\n {\n return relation.related;\n }\n });\n\n applied = relation.dynamicSet = true;\n }\n\n if ( !applied )\n {\n model[ propertyName ] = relation.related;\n }\n\n if ( relation.lastRelated !== relation.related )\n {\n model.$trigger( Model.Events.RelationUpdate, [this, relation] );\n\n relation.lastRelated = relation.related;\n }\n }\n },\n\n isModelArray: function(input)\n {\n if ( !isArray( input ) )\n {\n return false;\n }\n\n var relatedDatabase = this.model.Database;\n var relatedKey = relatedDatabase.key;\n\n if ( !isArray( relatedKey ) )\n {\n return true;\n }\n\n if ( relatedKey.length !== input.length )\n {\n return true;\n }\n\n for ( var i = 0; i < input.length; i++ )\n {\n if ( !isNumber( input[ i ] ) && !isString( input[ i ] ) )\n {\n return true;\n }\n }\n\n return false;\n },\n\n clearFields: function(target, targetFields, remoteData, cascade)\n {\n var changes = clearFieldsReturnChanges( target, targetFields );\n\n if ( changes && !remoteData && this.auto && !target.$isNew() )\n {\n target.$save( cascade || this.autoCascade, this.autoOptions );\n }\n\n return changes;\n },\n\n updateFields: function(target, targetFields, source, sourceFields, remoteData)\n {\n var changes = updateFieldsReturnChanges( target, targetFields, source, sourceFields );\n\n if ( changes )\n {\n if ( this.auto && !target.$isNew() && !remoteData )\n {\n target.$save( this.autoCascade, this.autoOptions );\n }\n\n target.$trigger( Model.Events.KeyUpdate, [target, source, targetFields, sourceFields] );\n }\n\n return changes;\n },\n\n updateForeignKey: function(target, source, remoteData)\n {\n var targetFields = this.getTargetFields( target );\n var sourceFields = this.getSourceFields( source );\n var targetKey = target.$key();\n var targetKeyHandler = target.$db.keyHandler;\n var keyChanges = target.$db.keyChanges;\n\n Rekord.debug( this.debugUpdateKey, this, target, targetFields, source, sourceFields );\n\n this.updateFields( target, targetFields, source, sourceFields, remoteData );\n\n if ( keyChanges && remoteData )\n {\n var targetNewKey = targetKeyHandler.getKey( target, true );\n\n if ( targetKeyHandler.inKey( targetFields ) && targetNewKey !== targetKey )\n {\n target.$setKey( targetNewKey, true );\n }\n }\n },\n\n clearForeignKey: function(related, remoteData)\n {\n var key = this.getTargetFields( related );\n\n Rekord.debug( this.debugClearKey, this, related, key );\n\n this.clearFields( related, key, remoteData );\n },\n\n getTargetFields: function(target)\n {\n return target.$db.key;\n },\n\n getSourceFields: function(source)\n {\n return source.$db.key;\n },\n\n getStoredArray: function(relateds, mode)\n {\n if ( !mode )\n {\n return null;\n }\n\n var stored = [];\n\n for (var i = 0; i < relateds.length; i++)\n {\n var related = this.getStored( relateds[ i ], mode );\n\n if ( related !== null )\n {\n stored.push( related );\n }\n }\n\n return stored;\n },\n\n getStored: function(related, mode)\n {\n if ( related )\n {\n switch (mode)\n {\n case Save.Model:\n return related.$toJSON( true );\n\n case Store.Model:\n if ( related.$local )\n {\n return related.$local;\n }\n\n var local = related.$toJSON( false );\n\n if ( related.$saved )\n {\n local.$saved = related.$saved;\n }\n\n return local;\n\n case Save.Key:\n case Store.Key:\n return related.$key();\n\n case Save.Keys:\n case Store.Keys:\n return related.$keys();\n\n }\n }\n\n return null;\n }\n\n});\n\nfunction RelationSingle()\n{\n}\n\nClass.extend( Relation, RelationSingle,\n{\n\n debugInit: null,\n debugClearModel: null,\n debugSetModel: null,\n debugLoaded: null,\n debugClearKey: null,\n debugUpdateKey: null,\n\n onInitialized: function(database, field, options)\n {\n if ( !this.discriminated )\n {\n var relatedDatabase = this.model.Database;\n\n this.local = this.local || ( relatedDatabase.name + '_' + relatedDatabase.key );\n }\n\n Rekord.debug( this.debugInit, this );\n\n this.finishInitialization();\n },\n\n set: function(model, input, remoteData)\n {\n if ( isEmpty( input ) )\n {\n this.unrelate( model, undefined, remoteData );\n }\n else\n {\n var relation = model.$relations[ this.name ];\n var related = this.parseModel( input, remoteData );\n\n if ( related && relation.related !== related )\n {\n this.clearModel( relation, remoteData );\n this.setRelated( relation, related, remoteData );\n }\n }\n },\n\n relate: function(model, input, remoteData)\n {\n var relation = model.$relations[ this.name ];\n var related = this.parseModel( input, remoteData );\n\n if ( related && relation.related !== related )\n {\n this.clearModel( relation, remoteData );\n this.setRelated( relation, related, remoteData );\n }\n },\n\n unrelate: function(model, input, remoteData)\n {\n var relation = model.$relations[ this.name ];\n var related = this.parseModel( input );\n\n if ( !related || relation.related === related )\n {\n this.clearRelated( relation, remoteData );\n }\n },\n\n isRelated: function(model, input)\n {\n var relation = model.$relations[ this.name ];\n var related = this.parseModel( input );\n\n return related === relation.related;\n },\n\n setRelated: function(relation, related, remoteData)\n {\n if ( !related.$isDeleted() )\n {\n this.setModel( relation, related );\n this.updateForeignKey( relation.parent, related, remoteData );\n this.setProperty( relation );\n }\n },\n\n clearRelated: function(relation, remoteData, dontClear)\n {\n if ( remoteData )\n {\n var related = relation.related;\n\n if ( related && related.$isSaving() )\n {\n return;\n }\n }\n\n this.clearModel( relation, remoteData, dontClear );\n this.setProperty( relation );\n },\n\n clearModel: function(relation, remoteData, dontClear)\n {\n var related = relation.related;\n\n if ( related )\n {\n Rekord.debug( this.debugClearModel, this, relation );\n\n if (relation.onSaved)\n {\n related.$off( Model.Events.Saved, relation.onSaved );\n }\n if (relation.onRemoved)\n {\n related.$off( Model.Events.Removed, relation.onRemoved );\n }\n\n relation.related = null;\n relation.dirty = true;\n relation.loaded = true;\n\n relation.parent.$dependents.remove( related );\n\n if ( !dontClear && !remoteData )\n {\n if ( this.clearKey )\n {\n this.clearForeignKey( relation.parent, remoteData );\n }\n }\n }\n },\n\n setModel: function(relation, related)\n {\n if (relation.onSaved)\n {\n related.$on( Model.Events.Saved, relation.onSaved, this );\n }\n\n if (relation.onRemoved)\n {\n related.$on( Model.Events.Removed, relation.onRemoved, this );\n }\n\n relation.related = related;\n relation.dirty = true;\n relation.loaded = true;\n\n if ( this.isDependent( relation, related ) )\n {\n relation.parent.$dependents.add( related, this );\n }\n\n Rekord.debug( this.debugSetModel, this, relation );\n },\n\n isDependent: function(relation, related)\n {\n return true;\n },\n\n handleModel: function(relation, remoteData, ignoreLoaded)\n {\n return function(related)\n {\n var model = relation.parent;\n\n Rekord.debug( this.debugLoaded, this, model, relation, related );\n\n if ( relation.loaded === false || ignoreLoaded )\n {\n if ( related && !related.$isDeleted() )\n {\n this.setModel( relation, related, remoteData );\n this.updateForeignKey( model, related, remoteData );\n }\n else\n {\n if ( this.query )\n {\n relation.query = this.executeQuery( model );\n }\n else if ( !this.preserve )\n {\n this.clearForeignKey( model, remoteData );\n }\n }\n\n relation.loaded = true;\n\n this.setProperty( relation );\n }\n };\n },\n\n isRelatedFactory: function(model)\n {\n var local = this.local;\n\n return function hasForeignKey(related)\n {\n return propsMatch( model, local, related, related.$db.key );\n };\n },\n\n getTargetFields: function(target)\n {\n return this.local;\n },\n\n buildKey: function(input)\n {\n var related = input[ this.name ];\n var key = this.local;\n\n if ( isObject( related ) && this.model )\n {\n var modelDatabase = this.model.Database;\n var foreign = modelDatabase.key;\n\n modelDatabase.keyHandler.copyFields( input, key, related, foreign );\n }\n }\n\n});\n\nfunction RelationMultiple()\n{\n}\n\nClass.extend( Relation, RelationMultiple,\n{\n\n debugAutoSave: null,\n debugInitialGrabbed: null,\n debugSort: null,\n\n handleExecuteQuery: function(model)\n {\n return function onExecuteQuery(search)\n {\n var relation = model.$relations[ this.name ];\n var results = search.$results;\n\n Rekord.debug( this.debugQueryResults, this, model, search );\n\n this.bulk( relation, function()\n {\n for (var i = 0; i < results.length; i++)\n {\n this.addModel( relation, results[ i ], true );\n }\n });\n\n this.sort( relation );\n this.checkSave( relation, true );\n };\n },\n\n bulk: function(relation, callback, remoteData)\n {\n relation.delaySorting = true;\n relation.delaySaving = true;\n\n callback.apply( this );\n\n relation.delaySorting = false;\n relation.delaySaving = false;\n\n this.sort( relation );\n this.checkSave( relation, remoteData );\n },\n\n set: function(model, input, remoteData)\n {\n if ( isEmpty( input ) )\n {\n this.unrelate( model, undefined, remoteData );\n }\n else\n {\n var relation = model.$relations[ this.name ];\n var existing = relation.related;\n var given = this.createCollection();\n\n if ( this.isModelArray( input ) )\n {\n for (var i = 0; i < input.length; i++)\n {\n var related = this.parseModel( input[ i ], remoteData );\n\n if ( related )\n {\n given.add( related );\n }\n }\n }\n else\n {\n var related = this.parseModel( input, remoteData );\n\n if ( related )\n {\n given.add( related );\n }\n }\n\n var removing = existing.subtract( given );\n var adding = given.subtract( existing );\n\n this.bulk( relation, function()\n {\n for (var i = 0; i < adding.length; i++)\n {\n this.addModel( relation, adding[ i ], remoteData );\n }\n\n for (var i = 0; i < removing.length; i++)\n {\n this.removeModel( relation, removing[ i ], remoteData );\n }\n\n }, remoteData);\n }\n },\n\n relate: function(model, input, remoteData)\n {\n var relation = model.$relations[ this.name ];\n\n if ( this.isModelArray( input ) )\n {\n this.bulk( relation, function()\n {\n for (var i = 0; i < input.length; i++)\n {\n var related = this.parseModel( input[ i ], remoteData );\n\n if ( related )\n {\n this.addModel( relation, related, remoteData );\n }\n }\n });\n }\n else if ( isValue( input ) )\n {\n var related = this.parseModel( input, remoteData );\n\n if ( related )\n {\n this.addModel( relation, related, remoteData );\n }\n }\n },\n\n unrelate: function(model, input, remoteData)\n {\n var relation = model.$relations[ this.name ];\n\n if ( this.isModelArray( input ) )\n {\n this.bulk( relation, function()\n {\n for (var i = 0; i < input.length; i++)\n {\n var related = this.parseModel( input[ i ] );\n\n if ( related )\n {\n this.removeModel( relation, related, remoteData );\n }\n }\n });\n }\n else if ( isValue( input ) )\n {\n var related = this.parseModel( input );\n\n if ( related )\n {\n this.removeModel( relation, related, remoteData );\n }\n }\n else\n {\n var all = relation.related;\n\n this.bulk( relation, function()\n {\n for (var i = all.length - 1; i >= 0; i--)\n {\n this.removeModel( relation, all[ i ], remoteData );\n }\n });\n }\n },\n\n isRelated: function(model, input)\n {\n var relation = model.$relations[ this.name ];\n var existing = relation.related;\n\n if ( this.isModelArray( input ) )\n {\n for (var i = 0; i < input.length; i++)\n {\n var related = this.parseModel( input[ i ] );\n\n if ( related && !existing.has( related.$key() ) )\n {\n return false;\n }\n }\n\n return input.length > 0;\n }\n else if ( isValue( input ) )\n {\n var related = this.parseModel( input );\n\n return related && existing.has( related.$key() );\n }\n\n return false;\n },\n\n canRemoveRelated: function(related, remoteData)\n {\n return !remoteData || !related.$isSaving();\n },\n\n checkSave: function(relation, remoteData)\n {\n if ( !relation.delaySaving && !remoteData && relation.parent.$exists() )\n {\n if ( this.store === Store.Model || this.save === Save.Model )\n {\n Rekord.debug( this.debugAutoSave, this, relation );\n\n relation.parent.$save( this.saveParentCascade, this.saveParentOptions );\n }\n }\n },\n\n handleModel: function(relation, remoteData, ignoreLoaded)\n {\n return function (related)\n {\n var pending = relation.pending;\n var key = related.$key();\n\n if ( key in pending || ignoreLoaded )\n {\n Rekord.debug( this.debugInitialGrabbed, this, relation, related );\n\n this.addModel( relation, related, remoteData );\n\n delete pending[ key ];\n }\n };\n },\n\n sort: function(relation)\n {\n var related = relation.related;\n\n if ( !relation.delaySorting )\n {\n Rekord.debug( this.debugSort, this, relation );\n\n related.sort( this.comparator );\n\n relation.parent.$trigger( Model.Events.RelationUpdate, [this, relation] );\n }\n }\n\n});\n\nfunction BelongsTo()\n{\n}\n\nRekord.Relations.belongsTo = BelongsTo;\n\nBelongsTo.Defaults =\n{\n model: null,\n lazy: false,\n query: false,\n store: Store.None,\n save: Save.None,\n auto: true,\n autoCascade: Cascade.All,\n autoOptions: null,\n property: true,\n preserve: true,\n clearKey: true,\n dynamic: false,\n local: null,\n cascade: Cascade.Local,\n cascadeRemoveOptions: null,\n discriminator: 'discriminator',\n discriminators: {},\n discriminatorToModel: {}\n};\n\nClass.extend( RelationSingle, BelongsTo,\n{\n\n type: 'belongsTo',\n\n debugInit: Rekord.Debugs.BELONGSTO_INIT,\n debugClearModel: Rekord.Debugs.BELONGSTO_CLEAR_MODEL,\n debugSetModel: Rekord.Debugs.BELONGSTO_SET_MODEL,\n debugLoaded: Rekord.Debugs.BELONGSTO_LOADED,\n debugClearKey: Rekord.Debugs.BELONGSTO_CLEAR_KEY,\n debugUpdateKey: Rekord.Debugs.BELONGSTO_UPDATE_KEY,\n debugQuery: Rekord.Debugs.BELONGSTO_QUERY,\n debugQueryResults: Rekord.Debugs.BELONGSTO_QUERY_RESULTS,\n\n getDefaults: function(database, field, options)\n {\n return BelongsTo.Defaults;\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n isRelated: this.isRelatedFactory( model ),\n related: null,\n loaded: false,\n\n onRemoved: function()\n {\n Rekord.debug( Rekord.Debugs.BELONGSTO_NINJA_REMOVE, this, model, relation );\n\n model.$remove( this.cascade, this.cascadeRemoveOptions );\n this.clearRelated( relation, false, true );\n },\n\n onSaved: function()\n {\n Rekord.debug( Rekord.Debugs.BELONGSTO_NINJA_SAVE, this, model, relation );\n\n if ( !relation.isRelated( relation.related ) )\n {\n this.clearRelated( relation, false, true );\n }\n }\n };\n\n model.$on( Model.Events.PostRemove, this.postRemove, this );\n model.$on( Model.Events.KeyUpdate, this.onKeyUpdate, this );\n\n if ( isEmpty( initialValue ) )\n {\n initialValue = this.grabInitial( model, this.local );\n\n if ( initialValue )\n {\n Rekord.debug( Rekord.Debugs.BELONGSTO_INITIAL_PULLED, this, model, initialValue );\n }\n }\n\n if ( !isEmpty( initialValue ) )\n {\n Rekord.debug( Rekord.Debugs.BELONGSTO_INITIAL, this, model, initialValue );\n\n this.grabModel( initialValue, this.handleModel( relation, remoteData ), remoteData );\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( model );\n }\n }),\n\n sync: function(model, removeUnrelated)\n {\n var relation = model.$relations[ this.name ];\n var relatedValue = this.grabInitial( model, this.local );\n var remoteData = true;\n var ignoreLoaded = true;\n var dontClear = true;\n\n if ( relation )\n {\n if ( !isEmpty( relatedValue ) )\n {\n this.grabModel( relatedValue, this.handleModel( relation, remoteData, ignoreLoaded ), remoteData );\n }\n else if ( removeUnrelated )\n {\n this.clearRelated( relation, remoteData, dontClear );\n }\n }\n },\n\n postRemove: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation )\n {\n Rekord.debug( Rekord.Debugs.BELONGSTO_POSTREMOVE, this, model, relation );\n\n this.clearModel( relation );\n this.setProperty( relation );\n }\n },\n\n onKeyUpdate: function(model, related, modelFields, relatedFields)\n {\n if ( this.local === modelFields )\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation && related !== relation.related )\n {\n this.clearModel( relation, false, true );\n this.setModel( relation, related );\n this.setProperty( relation );\n }\n }\n }\n\n});\n\nfunction HasOne()\n{\n}\n\nRekord.Relations.hasOne = HasOne;\n\nHasOne.Defaults =\n{\n model: null,\n lazy: false,\n query: false,\n store: Store.None,\n save: Save.None,\n saveCascade: Cascade.All,\n saveOptions: null,\n auto: true,\n autoCascade: Cascade.All,\n autoOptions: null,\n property: true,\n preserve: true,\n clearKey: true,\n dynamic: false,\n local: null,\n cascade: Cascade.All,\n cascadeRemoveOptions: null,\n discriminator: 'discriminator',\n discriminators: {},\n discriminatorToModel: {}\n};\n\nClass.extend( RelationSingle, HasOne,\n{\n\n type: 'hasOne',\n\n debugInit: Rekord.Debugs.HASONE_INIT,\n debugClearModel: Rekord.Debugs.HASONE_CLEAR_MODEL,\n debugSetModel: Rekord.Debugs.HASONE_SET_MODEL,\n debugLoaded: Rekord.Debugs.HASONE_LOADED,\n debugClearKey: Rekord.Debugs.HASONE_CLEAR_KEY,\n debugUpdateKey: Rekord.Debugs.HASONE_UPDATE_KEY,\n debugQuery: Rekord.Debugs.HASONE_QUERY,\n debugQueryResults: Rekord.Debugs.HASONE_QUERY_RESULTS,\n\n getDefaults: function(database, field, options)\n {\n return HasOne.Defaults;\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n isRelated: this.isRelatedFactory( model ),\n related: null,\n loaded: false,\n dirty: false,\n saving: false,\n child: equals( this.local, model.$db.key ),\n\n onRemoved: function()\n {\n Rekord.debug( Rekord.Debugs.HASONE_NINJA_REMOVE, this, model, relation );\n\n this.clearRelated( relation, false, true );\n }\n };\n\n model.$on( Model.Events.PreSave, this.preSave, this );\n model.$on( Model.Events.PostRemove, this.postRemove, this );\n\n if ( isEmpty( initialValue ) )\n {\n initialValue = this.grabInitial( model, this.local );\n\n if ( initialValue )\n {\n Rekord.debug( Rekord.Debugs.HASONE_INITIAL_PULLED, this, model, initialValue );\n }\n }\n\n if ( !isEmpty( initialValue ) )\n {\n Rekord.debug( Rekord.Debugs.HASONE_INITIAL, this, model, initialValue );\n\n this.populateInitial( initialValue, relation, model );\n this.grabModel( initialValue, this.handleModel( relation, remoteData ), remoteData );\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( model );\n }\n }),\n\n populateInitial: function(initialValue, relation, model)\n {\n if ( isObject( initialValue ) && relation.child )\n {\n var src = toArray( this.local );\n var dst = toArray( this.model.Database.key );\n\n for (var k = 0; k < src.length; k++)\n {\n initialValue[ dst[ k ] ] = model[ src[ k ] ];\n }\n }\n },\n\n sync: function(model, removeUnrelated)\n {\n var relation = model.$relations[ this.name ];\n var relatedValue = this.grabInitial( model, this.local );\n var remoteData = true;\n var ignoreLoaded = true;\n var dontClear = true;\n\n if ( relation )\n {\n if ( !isEmpty( relatedValue ) )\n {\n this.populateInitial( relatedValue, relation, model );\n this.grabModel( relatedValue, this.handleModel( relation, remoteData, ignoreLoaded ), remoteData );\n }\n else if ( removeUnrelated )\n {\n this.clearRelated( relation, remoteData, dontClear );\n }\n }\n },\n\n isDependent: function(relation, related)\n {\n return !relation.child;\n },\n\n preClone: function(model, clone, properties)\n {\n var related = this.get( model );\n\n if ( related )\n {\n var relatedClone = related.$clone( properties );\n\n updateFieldsReturnChanges( clone, this.local, relatedClone, relatedClone.$db.key );\n\n clone[ this.name ] = relatedClone;\n }\n },\n\n preSave: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation && relation.related )\n {\n var related = relation.related;\n\n if ( relation.dirty || related.$hasChanges() )\n {\n Rekord.debug( Rekord.Debugs.HASONE_PRESAVE, this, model, relation );\n\n relation.saving = true;\n\n related.$save( this.saveCascade, this.saveOptions );\n\n relation.saving = false;\n relation.dirty = false;\n }\n }\n },\n\n postRemove: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation )\n {\n if ( this.cascade )\n {\n Rekord.debug( Rekord.Debugs.HASONE_POSTREMOVE, this, model, relation );\n\n this.clearModel( relation );\n }\n }\n },\n\n clearModel: function(relation, remoteData)\n {\n var related = relation.related;\n\n if ( related )\n {\n Rekord.debug( this.debugClearModel, this, relation );\n\n related.$off( Model.Events.Removed, relation.onRemoved );\n\n if ( this.cascade && !related.$isDeleted() )\n {\n related.$remove( this.cascade, this.cascadeRemoveOptions );\n }\n\n relation.related = null;\n relation.dirty = true;\n relation.loaded = true;\n\n relation.parent.$dependents.remove( related );\n\n if ( this.clearKey )\n {\n this.clearForeignKey( relation.parent, remoteData );\n }\n }\n }\n\n});\n\nfunction HasMany()\n{\n}\n\nRekord.Relations.hasMany = HasMany;\n\nHasMany.Defaults =\n{\n model: null,\n lazy: false,\n query: false,\n store: Store.None,\n save: Save.None,\n auto: true,\n autoCascade: Cascade.All,\n autoOptions: null,\n property: true,\n preserve: true,\n clearKey: true,\n dynamic: false,\n foreign: null,\n comparator: null,\n comparatorNullsFirst: false,\n listenForRelated: true,\n loadRelated: true,\n where: false,\n saveParentCascade: Cascade.All,\n saveParentOptions: null,\n cascadeRemove: Cascade.Local,\n cascadeRemoveOptions: null,\n cascadeSave: Cascade.None,\n cascadeSaveOptions: null,\n discriminator: 'discriminator',\n discriminators: {},\n discriminatorToModel: {}\n};\n\nClass.extend( RelationMultiple, HasMany,\n{\n\n type: 'hasMany',\n\n debugAutoSave: Rekord.Debugs.HASMANY_AUTO_SAVE,\n debugInitialGrabbed: Rekord.Debugs.HASMANY_INITIAL_GRABBED,\n debugSort: Rekord.Debugs.HASMANY_SORT,\n debugQuery: Rekord.Debugs.HASMANY_QUERY,\n debugQueryResults: Rekord.Debugs.HASMANY_QUERY_RESULTS,\n debugUpdateKey: Rekord.Debugs.HASMANY_UPDATE_KEY,\n\n getDefaults: function(database, field, options)\n {\n return HasMany.Defaults;\n },\n\n onInitialized: function(database, field, options)\n {\n this.foreign = this.foreign || ( database.name + '_' + database.key );\n this.comparator = createComparator( this.comparator, this.comparatorNullsFirst );\n\n Rekord.debug( Rekord.Debugs.HASMANY_INIT, this );\n\n this.finishInitialization();\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relator = this;\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n pending: {},\n isRelated: this.isRelatedFactory( model ),\n related: this.createRelationCollection( model ),\n saving: false,\n delaySorting: false,\n delaySaving: false,\n\n onRemoved: function() // this = model removed\n {\n Rekord.debug( Rekord.Debugs.HASMANY_NINJA_REMOVE, relator, model, this, relation );\n\n relator.removeModel( relation, this, true, true );\n },\n\n onSaved: function() // this = model saved\n {\n if ( relation.saving )\n {\n return;\n }\n\n Rekord.debug( Rekord.Debugs.HASMANY_NINJA_SAVE, relator, model, this, relation );\n\n if ( !relation.isRelated( this ) )\n {\n relator.removeModel( relation, this, false, true );\n }\n else\n {\n relator.sort( relation );\n relator.checkSave( relation );\n }\n },\n\n onChange: function()\n {\n if ( relation.saving )\n {\n return;\n }\n\n if ( relator.where && !relator.where( this ) )\n {\n relator.removeModel( relation, this, false, true );\n }\n }\n\n };\n\n model.$on( Model.Events.PostSave, this.postSave, this );\n model.$on( Model.Events.PreRemove, this.preRemove, this );\n\n // When models are added to the related database, check if it's related to this model\n if ( this.listenForRelated )\n {\n this.listenToModelAdded( this.handleModelAdded( relation ) );\n }\n\n // If the model's initial value is an array, populate the relation from it!\n if ( isArray( initialValue ) )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_INITIAL, this, model, relation, initialValue );\n\n this.grabModels( relation, initialValue, this.handleModel( relation, remoteData ), remoteData );\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( model );\n }\n else if ( this.loadRelated )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_INITIAL_PULLED, this, model, relation );\n\n this.ready( this.handleLazyLoad( relation ) );\n }\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n sync: function(model, removeUnrelated)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation )\n {\n var existing = relation.related;\n var remoteData = true;\n var dontClear = true;\n var relator = this;\n\n var onRelated = function(related)\n {\n if ( removeUnrelated )\n {\n var given = this.createCollection();\n given.reset( related );\n\n existing.each(function(existingModel)\n {\n if ( !given.has( existingModel.$key() ) )\n {\n relator.removeModel( relation, existingModel, remoteData, dontClear );\n }\n });\n }\n };\n\n this.ready( this.handleLazyLoad( relation, onRelated ) );\n }\n },\n\n postClone: function(model, clone, properties)\n {\n var related = this.get( model );\n\n if ( related )\n {\n var relatedClones = [];\n\n updateFieldsReturnChanges( properties, this.foreign, clone, model.$db.key );\n\n properties[ this.foreign ] = clone[ model.$db.key ];\n\n for (var i = 0; i < related.length; i++)\n {\n relatedClones.push( related[ i ].$clone( properties ) );\n }\n\n clone[ this.name ] = relatedClones;\n }\n },\n\n postSave: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation && this.cascadeSave )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_POSTSAVE, this, model, relation );\n\n batchExecute(function()\n {\n relation.saving = true;\n relation.delaySaving = true;\n\n var models = relation.related;\n\n for (var i = 0; i < models.length; i++)\n {\n var related = models[ i ];\n\n if ( !related.$isDeleted() && related.$hasChanges() )\n {\n related.$save( this.cascadeSave, this.cascadeSaveOptions );\n }\n }\n\n relation.saving = false;\n relation.delaySaving = false;\n\n }, this );\n }\n },\n\n preRemove: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation && this.cascadeRemove )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_PREREMOVE, this, model, relation );\n\n batchExecute(function()\n {\n this.bulk( relation, function()\n {\n var models = relation.related;\n\n for (var i = models.length - 1; i >= 0; i--)\n {\n var related = models[ i ];\n\n related.$remove( this.cascadeRemove, this.cascadeRemoveOptions );\n }\n });\n\n }, this );\n }\n },\n\n handleModelAdded: function(relation)\n {\n return function (related, remoteData)\n {\n if ( relation.isRelated( related ) )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_NINJA_ADD, this, relation, related );\n\n this.addModel( relation, related, remoteData );\n }\n };\n },\n\n handleLazyLoad: function(relation, onRelated)\n {\n return function (relatedDatabase)\n {\n var related = relatedDatabase.filter( relation.isRelated );\n\n Rekord.debug( Rekord.Debugs.HASMANY_LAZY_LOAD, this, relation, related );\n\n if ( onRelated )\n {\n onRelated.call( this, related );\n }\n\n if ( related.length )\n {\n this.bulk( relation, function()\n {\n for (var i = 0; i < related.length; i++)\n {\n this.addModel( relation, related[ i ] );\n }\n });\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( relation.parent );\n }\n };\n },\n\n addModel: function(relation, related, remoteData)\n {\n if ( related.$isDeleted() || (this.where && !this.where( related ) ) )\n {\n return;\n }\n\n var model = relation.parent;\n var target = relation.related;\n var key = related.$key();\n var adding = !target.has( key );\n\n if ( adding )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_ADD, this, relation, related );\n\n target.put( key, related );\n\n related.$on( Model.Events.Removed, relation.onRemoved );\n related.$on( Model.Events.SavedRemoteUpdate, relation.onSaved );\n\n if ( this.where )\n {\n related.$on( Model.Events.Change, relation.onChange );\n }\n\n related.$dependents.add( model, this );\n\n this.updateForeignKey( related, model, remoteData );\n\n this.sort( relation );\n this.checkSave( relation, remoteData );\n }\n\n return adding;\n },\n\n removeModel: function(relation, related, remoteData, dontClear)\n {\n if ( !this.canRemoveRelated( related, remoteData ) )\n {\n return;\n }\n\n var model = relation.parent;\n var target = relation.related;\n var pending = relation.pending;\n var key = related.$key();\n var removing = target.has( key );\n\n if ( removing )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_REMOVE, this, relation, related );\n\n target.remove( key );\n\n related.$off( Model.Events.Removed, relation.onRemoved );\n related.$off( Model.Events.SavedRemoteUpdate, relation.onSaved );\n related.$off( Model.Events.Change, relation.onChange );\n\n related.$dependents.remove( model );\n\n if ( !dontClear )\n {\n if ( this.clearKey )\n {\n this.clearForeignKey( related, remoteData );\n }\n\n if ( this.cascadeRemove )\n {\n if ( remoteData )\n {\n if ( canCascade( this.cascadeRemove, Cascade.Local ) )\n {\n related.$remove( Cascade.Local );\n }\n }\n else\n {\n related.$remove( this.cascadeRemove, this.cascadeRemoveOptions );\n }\n }\n }\n\n this.sort( relation );\n this.checkSave( relation, remoteData );\n }\n\n delete pending[ key ];\n\n return removing;\n },\n\n isRelatedFactory: function(model)\n {\n var foreign = this.foreign;\n var local = model.$db.key;\n\n return function(related)\n {\n return propsMatch( related, foreign, model, local );\n };\n },\n\n getTargetFields: function(target)\n {\n return this.foreign;\n }\n\n});\n\nfunction HasManyThrough()\n{\n}\n\nRekord.Relations.hasManyThrough = HasManyThrough;\n\nHasManyThrough.Defaults =\n{\n model: null,\n lazy: false,\n query: false,\n store: Store.None,\n save: Save.None,\n auto: true,\n autoCascade: Cascade.All,\n autoOptions: null,\n property: true,\n dynamic: false,\n through: undefined,\n local: null,\n foreign: null,\n comparator: null,\n comparatorNullsFirst: false,\n listenForRelated: true,\n loadRelated: true,\n where: false,\n cascadeRemove: Cascade.NoRest,\n cascadeSave: Cascade.All,\n cascadeSaveOptions: null,\n cascadeSaveRelated: Cascade.None,\n cascadeSaveRelatedOptions: null,\n saveParentCascade: Cascade.All,\n saveParentOptions: null,\n cascadeRemoveThroughOptions: null,\n discriminator: 'discriminator',\n discriminators: {},\n discriminatorToModel: {}\n};\n\nClass.extend( RelationMultiple, HasManyThrough,\n{\n\n type: 'hasManyThrough',\n\n debugAutoSave: Rekord.Debugs.HASMANYTHRU_AUTO_SAVE,\n debugInitialGrabbed: Rekord.Debugs.HASMANYTHRU_INITIAL_GRABBED,\n debugSort: Rekord.Debugs.HASMANYTHRU_SORT,\n debugQuery: Rekord.Debugs.HASMANYTHRU_QUERY,\n debugQueryResults: Rekord.Debugs.HASMANYTHRU_QUERY_RESULTS,\n debugUpdateKey: Rekord.Debugs.HASMANYTHRU_UPDATE_KEY,\n\n getDefaults: function(database, field, options)\n {\n return HasManyThrough.Defaults;\n },\n\n onInitialized: function(database, field, options)\n {\n if ( !this.discriminated )\n {\n var relatedDatabase = this.model.Database;\n\n this.foreign = this.foreign || ( relatedDatabase.name + '_' + relatedDatabase.key );\n }\n\n this.local = this.local || ( database.name + '_' + database.key );\n this.comparator = createComparator( this.comparator, this.comparatorNullsFirst );\n\n if ( !isRekord( options.through ) )\n {\n Rekord.get( options.through ).complete( this.setThrough, this );\n }\n else\n {\n this.setThrough( options.through );\n }\n\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_INIT, this );\n },\n\n setThrough: function(through)\n {\n this.through = through;\n\n this.finishInitialization();\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relator = this;\n var throughDatabase = this.through.Database;\n\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n isRelated: this.isRelatedFactory( model ),\n pending: {},\n related: this.createRelationCollection( model ),\n throughs: new Map(),\n saving: false,\n delaySorting: false,\n delaySaving: false,\n\n onRemoved: function() // this = model removed\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_NINJA_REMOVE, relator, model, this, relation );\n\n relator.removeModel( relation, this );\n },\n\n onSaved: function() // this = model saved\n {\n if ( relation.saving )\n {\n return;\n }\n\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_NINJA_SAVE, relator, model, this, relation );\n\n relator.sort( relation );\n relator.checkSave( relation );\n },\n\n onChange: function()\n {\n if ( relation.saving )\n {\n return;\n }\n\n if ( relator.where && !relator.where( this ) )\n {\n relator.removeModel( relation, this );\n }\n },\n\n onThroughRemoved: function() // this = through removed\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_NINJA_THRU_REMOVE, relator, model, this, relation );\n\n relator.removeModelFromThrough( relation, this );\n }\n\n };\n\n // Populate the model's key if it's missing\n model.$on( Model.Events.PostSave, this.postSave, this );\n model.$on( Model.Events.PreRemove, this.preRemove, this );\n\n // When models are added to the related database, check if it's related to this model\n if ( this.listenForRelated )\n {\n throughDatabase.on( Database.Events.ModelAdded, this.handleModelAdded( relation ), this );\n }\n\n // If the model's initial value is an array, populate the relation from it!\n if ( isArray( initialValue ) )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_INITIAL, this, model, relation, initialValue );\n\n this.grabModels( relation, initialValue, this.handleModel( relation, remoteData ), remoteData );\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( model );\n }\n else if ( this.loadRelated )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_INITIAL_PULLED, this, model, relation );\n\n throughDatabase.ready( this.handleLazyLoad( relation ), this );\n }\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n sync: function(model, removeUnrelated)\n {\n var throughDatabase = this.through.Database;\n var relation = model.$relations[ this.name ];\n\n if ( relation )\n {\n var existing = relation.throughs.values;\n var remoteData = true;\n var relator = this;\n\n var onRelated = function(throughs)\n {\n if ( removeUnrelated )\n {\n var given = this.createCollection();\n given.reset( throughs );\n\n for (var i = 0; i < existing.length; i++)\n {\n var existingThrough = existing[ i ];\n\n if ( !given.has( existingThrough.$key() ) )\n {\n relator.removeModelFromThrough( relation, existingThrough, remoteData );\n }\n }\n }\n };\n\n throughDatabase.ready( this.handleLazyLoad( relation, onRelated ), this );\n }\n },\n\n preClone: function(model, clone, properties)\n {\n var related = this.get( model );\n\n if ( related )\n {\n clone[ this.name ] = related.slice();\n }\n },\n\n postSave: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n batchExecute(function()\n {\n if ( relation && this.cascadeSave )\n {\n var throughs = relation.throughs.values;\n\n for (var i = 0; i < throughs.length; i++)\n {\n var through = throughs[ i ];\n\n if ( !through.$isDeleted() && through.$hasChanges() )\n {\n through.$save( this.cascadeSave, this.cascadeSaveOptions );\n }\n }\n }\n\n if ( relation && this.cascadeSaveRelated )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_PRESAVE, this, model, relation );\n\n relation.saving = true;\n relation.delaySaving = true;\n\n var models = relation.related;\n\n for (var i = 0; i < models.length; i++)\n {\n var related = models[ i ];\n\n if ( !related.$isDeleted() && related.$hasChanges() )\n {\n related.$save( this.cascadeSaveRelated, this.cascadeSaveRelatedOptions );\n }\n }\n\n relation.saving = false;\n relation.delaySaving = false;\n }\n\n }, this );\n },\n\n preRemove: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation && this.cascadeRemove )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_PREREMOVE, this, model, relation );\n\n batchExecute(function()\n {\n this.bulk( relation, function()\n {\n var throughs = relation.throughs.values;\n\n for (var i = 0; i < throughs.length; i++)\n {\n var through = throughs[ i ];\n\n through.$remove( this.cascadeRemove, this.cascadeRemoveThroughOptions );\n }\n });\n\n }, this );\n }\n },\n\n handleModelAdded: function(relation)\n {\n return function (through, remoteData)\n {\n if ( relation.isRelated( through ) && !relation.throughs.has( through.$key() ) )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_NINJA_ADD, this, relation, through );\n\n this.addModelFromThrough( relation, through, remoteData );\n }\n };\n },\n\n handleLazyLoad: function(relation, onRelated)\n {\n return function (throughDatabase)\n {\n var throughs = throughDatabase.filter( relation.isRelated );\n\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_LAZY_LOAD, this, relation, throughs );\n\n if ( onRelated )\n {\n onRelated.call( this, throughs );\n }\n\n if ( throughs.length )\n {\n this.bulk( relation, function()\n {\n for (var i = 0; i < throughs.length; i++)\n {\n this.addModelFromThrough( relation, throughs[ i ] );\n }\n });\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( relation.parent );\n }\n };\n },\n\n addModel: function(relation, related, remoteData)\n {\n if ( related.$isDeleted() || (this.where && !this.where( related ) ) )\n {\n return;\n }\n\n var adding = this.finishAddModel( relation, related, remoteData );\n\n if ( adding )\n {\n this.addThrough( relation, related, remoteData );\n }\n\n return adding;\n },\n\n addThrough: function(relation, related, remoteData)\n {\n var throughDatabase = this.through.Database;\n var throughKey = this.createThroughKey( relation, related );\n\n throughDatabase.grabModel( throughKey, this.onAddThrough( relation, remoteData ), this, remoteData );\n },\n\n onAddThrough: function(relation, remoteData)\n {\n return function onAddThrough(through)\n {\n this.finishAddThrough( relation, through, remoteData );\n };\n },\n\n addModelFromThrough: function(relation, through, remoteData)\n {\n if ( through.$isDeleted() )\n {\n return;\n }\n\n // TODO polymoprhic logic\n var relatedDatabase = this.model.Database;\n var relatedKey = relatedDatabase.keyHandler.buildKey( through, this.foreign );\n\n relatedDatabase.grabModel( relatedKey, this.onAddModelFromThrough( relation, through, remoteData ), this, remoteData );\n },\n\n onAddModelFromThrough: function(relation, through, remoteData)\n {\n return function onAddModelFromThrough(related)\n {\n if ( related && ( !this.where || this.where( related ) ) )\n {\n this.finishAddThrough( relation, through, remoteData );\n this.finishAddModel( relation, related, remoteData );\n }\n };\n },\n\n finishAddThrough: function(relation, through, remoteData)\n {\n var model = relation.parent;\n var throughs = relation.throughs;\n var throughKey = through.$key();\n var added = !throughs.has( throughKey );\n\n if ( added )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_THRU_ADD, this, relation, through );\n\n throughs.put( throughKey, through );\n\n through.$on( Model.Events.Removed, relation.onThroughRemoved );\n\n through.$dependents.add( model, this );\n\n if ( !remoteData && this.cascadeSave )\n {\n if ( model.$isSaved() )\n {\n through.$save( this.cascadeSave, this.cascadeSaveOptions );\n }\n else\n {\n through.$save( Cascade.None );\n }\n }\n }\n\n return added;\n },\n\n finishAddModel: function(relation, related, remoteData)\n {\n var relateds = relation.related;\n var relatedKey = related.$key();\n var adding = !relateds.has( relatedKey );\n\n if ( adding )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_ADD, this, relation, related );\n\n relateds.put( relatedKey, related );\n\n related.$on( Model.Events.Removed, relation.onRemoved );\n related.$on( Model.Events.SavedRemoteUpdate, relation.onSaved );\n\n if ( this.where )\n {\n related.$on( Model.Events.Change, relation.onChange );\n }\n\n this.sort( relation );\n\n if ( !remoteData )\n {\n this.checkSave( relation );\n }\n }\n\n return adding;\n },\n\n removeModel: function(relation, related, remoteData)\n {\n var relatedKey = related.$key();\n var relateds = relation.related;\n var actualRelated = relateds.get( relatedKey );\n\n if ( actualRelated )\n {\n if ( this.removeThrough( relation, related, remoteData ) )\n {\n this.finishRemoveRelated( relation, relatedKey, remoteData );\n }\n }\n },\n\n removeThrough: function(relation, related, remoteData)\n {\n var throughDatabase = this.through.Database;\n var keyObject = this.createThroughKey( relation, related );\n var key = throughDatabase.keyHandler.getKey( keyObject );\n var throughs = relation.throughs;\n var through = throughs.get( key );\n\n return this.finishRemoveThrough( relation, through, related, true, remoteData );\n },\n\n removeModelFromThrough: function(relation, through, remoteData)\n {\n var relatedDatabase = this.model.Database;\n var relatedKey = relatedDatabase.keyHandler.buildKey( through, this.foreign );\n\n if ( this.finishRemoveThrough( relation, through, undefined, undefined, remoteData ) )\n {\n this.finishRemoveRelated( relation, relatedKey, remoteData );\n }\n },\n\n finishRemoveThrough: function(relation, through, related, callRemove, remoteData)\n {\n var model = relation.parent;\n var removing = !!through;\n\n if ( removing )\n {\n if ( !this.canRemoveRelated( through, remoteData ) )\n {\n return false;\n }\n\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_THRU_REMOVE, this, relation, through, related );\n\n var throughs = relation.throughs;\n var throughKey = through.$key();\n\n through.$off( Model.Events.Removed, relation.onThroughRemoved );\n\n through.$dependents.remove( model );\n\n if ( callRemove )\n {\n through.$remove( remoteData ? Cascade.Local : Cascade.All, this.cascadeRemoveThroughOptions );\n }\n\n throughs.remove( throughKey );\n }\n\n return removing;\n },\n\n finishRemoveRelated: function(relation, relatedKey, remoteData)\n {\n var pending = relation.pending;\n var relateds = relation.related;\n var related = relateds.get( relatedKey );\n\n if ( related )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_REMOVE, this, relation, related );\n\n relateds.remove( relatedKey );\n\n related.$off( Model.Events.Removed, relation.onRemoved );\n related.$off( Model.Events.SavedRemoteUpdate, relation.onSaved );\n related.$off( Model.Events.Change, relation.onChange );\n\n this.sort( relation );\n this.checkSave( relation, remoteData );\n }\n\n delete pending[ relatedKey ];\n\n return related;\n },\n\n isRelatedFactory: function(model)\n {\n var foreign = model.$db.key;\n var local = this.local;\n\n return function(through)\n {\n return propsMatch( through, local, model, foreign );\n };\n },\n\n createThroughKey: function(relation, related)\n {\n var model = relation.parent;\n var modelKeys = model.$db.keyHandler;\n var relatedKeys = this.model.Database.keyHandler;\n var throughDatabase = this.through.Database;\n var throughKey = throughDatabase.key;\n var key = {};\n\n for (var i = 0; i < throughKey.length; i++)\n {\n var prop = throughKey[ i ];\n\n modelKeys.setKeyField( key, prop, related, this.foreign );\n relatedKeys.setKeyField( key, prop, model, this.local );\n }\n\n return key;\n },\n\n getTargetFields: function(target)\n {\n return this.local;\n }\n\n});\n\nfunction HasRemote()\n{\n}\n\nRekord.Relations.hasRemote = HasRemote;\n\nHasRemote.Defaults =\n{\n model: undefined,\n lazy: false,\n query: false,\n store: Store.None,\n save: Save.None,\n auto: false,\n property: true,\n dynamic: false,\n comparator: null,\n comparatorNullsFirst: false,\n where: false,\n autoRefresh: false // Model.Events.RemoteGets\n};\n\nClass.extend( RelationMultiple, HasRemote,\n{\n\n type: 'hasRemote',\n\n debugSort: Rekord.Debugs.HASREMOTE_SORT,\n debugQuery: Rekord.Debugs.HASREMOTE_QUERY,\n debugQueryResults: Rekord.Debugs.HASREMOTE_QUERY_RESULTS,\n\n getDefaults: function(database, field, options)\n {\n return HasRemote.Defaults;\n },\n\n onInitialized: function(database, field, options)\n {\n this.comparator = createComparator( this.comparator, this.comparatorNullsFirst );\n\n Rekord.debug( Rekord.Debugs.HASREMOTE_INIT, this );\n\n this.finishInitialization();\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relator = this;\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n pending: {},\n related: this.createRelationCollection( model ),\n delaySorting: false,\n delaySaving: false,\n\n onRemoved: function() // this = model removed\n {\n Rekord.debug( Rekord.Debugs.HASREMOTE_NINJA_REMOVE, relator, model, this, relation );\n\n relator.removeModel( relation, this, true );\n },\n\n onSaved: function() // this = model saved\n {\n Rekord.debug( Rekord.Debugs.HASREMOTE_NINJA_SAVE, relator, model, this, relation );\n\n relator.sort( relation );\n relator.checkSave( relation );\n },\n\n onChange: function()\n {\n if ( relation.saving )\n {\n return;\n }\n\n if ( relator.where && !relator.where( this ) )\n {\n relator.removeModel( relation, this, true );\n }\n }\n\n };\n\n // Populate the model's key if it's missing\n model.$key();\n\n // If auto refresh was specified, execute the query on refresh\n if ( this.autoRefresh )\n {\n model.$on( this.autoRefresh, this.onRefresh( relation ), this );\n }\n\n // Execute query!\n relation.query = this.executeQuery( model );\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n onRefresh: function(relation)\n {\n return function handleRefresh()\n {\n relation.query = this.executeQuery( relation.parent );\n };\n },\n\n addModel: function(relation, related, remoteData)\n {\n if ( related.$isDeleted() || (this.where && !this.where( related ) ) )\n {\n return;\n }\n\n var model = relation.parent;\n var target = relation.related;\n var key = related.$key();\n var adding = !target.has( key );\n\n if ( adding )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_ADD, this, relation, related );\n\n target.put( key, related );\n\n related.$on( Model.Events.Removed, relation.onRemoved );\n related.$on( Model.Events.SavedRemoteUpdate, relation.onSaved );\n\n if ( this.where )\n {\n related.$on( Model.Events.Change, relation.onChange );\n }\n\n this.sort( relation );\n this.checkSave( relation, remoteData );\n }\n\n return adding;\n },\n\n removeModel: function(relation, related, remoteData)\n {\n if ( !this.canRemoveRelated( related, remoteData ) )\n {\n return;\n }\n\n var model = relation.parent;\n var target = relation.related;\n var pending = relation.pending;\n var key = related.$key();\n\n if ( target.has( key ) )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_REMOVE, this, relation, related );\n\n target.remove( key );\n\n related.$off( Model.Events.Removed, relation.onRemoved );\n related.$off( Model.Events.SavedRemoteUpdate, relation.onSaved );\n related.$off( Model.Events.Change, relation.onChange );\n\n this.sort( relation );\n this.checkSave( relation, remoteData );\n }\n\n delete pending[ key ];\n }\n\n});\n\nfunction HasList()\n{\n}\n\nRekord.Relations.hasList = HasList;\n\nHasList.Defaults =\n{\n model: undefined,\n lazy: false,\n store: Store.Model,\n save: Save.Model,\n auto: false,\n property: true,\n dynamic: false,\n comparator: null,\n comparatorNullsFirst: false\n};\n\nClass.extend( RelationMultiple, HasList,\n{\n\n type: 'hasList',\n\n debugSort: Rekord.Debugs.HASLIST_SORT,\n\n getDefaults: function(database, field, options)\n {\n return HasList.Defaults;\n },\n\n onInitialized: function(database, field, options)\n {\n this.comparator = createComparator( this.comparator, this.comparatorNullsFirst );\n\n Rekord.debug( Rekord.Debugs.HASLIST_INIT, this );\n\n this.finishInitialization();\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relator = this;\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n pending: {},\n related: this.createRelationCollection( model ),\n delaySorting: false,\n delaySaving: false,\n\n onRemoved: function() // this = model removed\n {\n Rekord.debug( Rekord.Debugs.HASLIST_NINJA_REMOVE, relator, model, this, relation );\n\n relator.removeModel( relation, this, true );\n },\n\n onSaved: function() // this = model saved\n {\n Rekord.debug( Rekord.Debugs.HASLIST_NINJA_SAVE, relator, model, this, relation );\n\n relator.sort( relation );\n relator.checkSave( relation );\n }\n\n };\n\n // If the model's initial value is an array, populate the relation from it!\n if ( isArray( initialValue ) )\n {\n Rekord.debug( Rekord.Debugs.HASLIST_INITIAL, this, model, relation, initialValue );\n\n this.grabModels( relation, initialValue, this.handleModel( relation, remoteData ), remoteData );\n }\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n addModel: function(relation, related, remoteData)\n {\n if ( related.$isDeleted() )\n {\n return;\n }\n\n var model = relation.parent;\n var target = relation.related;\n var key = related.$key();\n var adding = !target.has( key );\n\n if ( adding )\n {\n Rekord.debug( Rekord.Debugs.HASLIST_ADD, this, relation, related );\n\n target.put( key, related );\n\n related.$on( Model.Events.Removed, relation.onRemoved );\n related.$on( Model.Events.SavedRemoteUpdate, relation.onSaved );\n\n this.sort( relation );\n\n if ( !remoteData )\n {\n this.checkSave( relation );\n }\n }\n\n return adding;\n },\n\n removeModel: function(relation, related, remoteData)\n {\n if ( !this.canRemoveRelated( related, remoteData ) )\n {\n return;\n }\n\n var model = relation.parent;\n var target = relation.related;\n var pending = relation.pending;\n var key = related.$key();\n\n if ( target.has( key ) )\n {\n Rekord.debug( Rekord.Debugs.HASLIST_REMOVE, this, relation, related );\n\n target.remove( key );\n\n related.$off( Model.Events.Removed, relation.onRemoved );\n related.$off( Model.Events.SavedRemoteUpdate, relation.onSaved );\n\n this.sort( relation );\n this.checkSave( relation );\n }\n\n delete pending[ key ];\n },\n\n postClone: function(model, clone, properties)\n {\n var related = this.get( model );\n\n if ( related )\n {\n var relatedClones = [];\n\n for (var i = 0; i < related.length; i++)\n {\n relatedClones.push( related[ i ].$clone() );\n }\n\n clone[ this.name ] = relatedClones;\n }\n }\n\n});\n\nfunction HasReference()\n{\n}\n\nRekord.Relations.hasReference = HasReference;\n\nHasReference.Defaults =\n{\n model: null,\n lazy: false,\n query: false,\n store: Store.None,\n save: Save.None,\n property: true,\n dynamic: false\n};\n\nClass.extend( RelationSingle, HasReference,\n{\n\n type: 'hasReference',\n\n debugInit: Rekord.Debugs.HASREFERENCE_INIT,\n debugClearModel: Rekord.Debugs.HASREFERENCE_CLEAR_MODEL,\n debugSetModel: Rekord.Debugs.HASREFERENCE_SET_MODEL,\n debugLoaded: Rekord.Debugs.HASREFERENCE_LOADED,\n debugQuery: Rekord.Debugs.HASREFERENCE_QUERY,\n debugQueryResults: Rekord.Debugs.HASREFERENCE_QUERY_RESULTS,\n\n getDefaults: function(database, field, options)\n {\n return HasReference.Defaults;\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n related: null,\n loaded: false,\n dirty: false,\n\n onRemoved: function()\n {\n Rekord.debug( Rekord.Debugs.HASREFERENCE_NINJA_REMOVE, this, model, relation );\n\n this.clearRelated( relation, false, true );\n }\n };\n\n if ( !isEmpty( initialValue ) )\n {\n Rekord.debug( Rekord.Debugs.HASREFERENCE_INITIAL, this, model, initialValue );\n\n this.grabModel( initialValue, this.handleModel( relation ), remoteData );\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( model );\n }\n }),\n\n preClone: function(model, clone, properties)\n {\n var related = this.get( model );\n\n if ( related )\n {\n clone[ this.name ] = related.$clone( properties );\n }\n },\n\n isDependent: function(relation, related)\n {\n return false;\n },\n\n updateForeignKey: function()\n {\n // nothing\n },\n\n clearForeignKey: function()\n {\n // nothing\n },\n\n});\n\n\nvar Polymorphic =\n{\n\n setReferences: function(database, field, options)\n {\n this.isRelatedFactory = this.isRelatedDiscriminatedFactory( this.isRelatedFactory );\n\n this.loadDiscriminators(function()\n {\n this.onInitialized( database, field, options );\n });\n },\n\n isRelatedDiscriminatedFactory: function(isRelatedFactory)\n {\n return function (model)\n {\n var isRelated = isRelatedFactory.call( this, model );\n var discriminator = this.getDiscriminatorForModel( model );\n var discriminatorField = this.discriminator;\n\n return function (related)\n {\n if ( !isRelated( related ) )\n {\n return false;\n }\n\n return equals( discriminator, related[ discriminatorField ] );\n };\n };\n },\n\n loadDiscriminators: function(onLoad)\n {\n var discriminators = this.discriminators;\n var total = sizeof( discriminators );\n var loaded = 0;\n\n function handleLoaded()\n {\n if ( ++loaded === total )\n {\n onLoad.apply( this );\n }\n }\n\n for (var name in discriminators)\n {\n var discriminator = discriminators[ name ];\n\n Rekord.get( name ).complete( this.setDiscriminated( discriminator, handleLoaded ), this );\n }\n },\n\n setDiscriminated: function(discriminator, onLoad)\n {\n return function(rekord)\n {\n this.discriminators[ rekord.Database.name ] = discriminator;\n this.discriminators[ rekord.Database.className ] = discriminator;\n this.discriminatorToModel[ discriminator ] = rekord;\n\n onLoad.apply( this );\n };\n },\n\n createRelationCollection: function(model)\n {\n return DiscriminateCollection( RelationCollection.create( undefined, model, this ), this.discriminator, this.discriminatorToModel );\n },\n\n createCollection: function()\n {\n return DiscriminateCollection( ModelCollection.create(), this.discriminator, this.discriminatorToModel );\n },\n\n ready: function(callback)\n {\n var models = this.discriminatorToModel;\n\n for ( var prop in models )\n {\n var model = models[ prop ];\n\n model.Database.ready( callback, this );\n }\n },\n\n listenToModelAdded: function(callback)\n {\n var models = this.discriminatorToModel;\n\n for ( var prop in models )\n {\n var model = models[ prop ];\n\n model.Database.on( Database.Events.ModelAdded, callback, this );\n }\n },\n\n executeQuery: function(model)\n {\n var queryOption = this.query;\n var queryOptions = this.queryOptions;\n var queryData = this.queryData;\n var query = isString( queryOption ) ? format( queryOption, model ) : queryOption;\n var search = model.search( query, queryOptions );\n\n if ( isObject( queryData ) )\n {\n search.$set( queryData );\n }\n\n DiscriminateCollection( search.$results, this.discriminator, this.discriminatorToModel );\n\n var promise = search.$run();\n promise.complete( this.handleExecuteQuery( model ), this );\n\n return search;\n },\n\n parseModel: function(input, remoteData)\n {\n if ( input instanceof Model )\n {\n return input;\n }\n else if ( isObject( input ) )\n {\n var db = this.getDiscriminatorDatabase( input );\n\n if ( db )\n {\n return db.parseModel( input, remoteData );\n }\n }\n\n return false;\n },\n\n clearFields: function(target, targetFields, remoteData)\n {\n var changes = clearFieldsReturnChanges( target, targetFields );\n\n if ( target[ this.discriminator ] )\n {\n target[ this.discriminator ] = null;\n changes = true;\n }\n\n if ( changes && !remoteData && this.auto && !target.$isNew() )\n {\n target.$save( this.autoCascade, this.autoOptions );\n }\n\n return changes;\n },\n\n updateFields: function(target, targetFields, source, sourceFields, remoteData)\n {\n var changes = updateFieldsReturnChanges( target, targetFields, source, sourceFields );\n\n var targetField = this.discriminator;\n var targetValue = target[ targetField ];\n var sourceValue = this.getDiscriminatorForModel( source );\n\n if ( !equals( targetValue, sourceValue ) )\n {\n target[ targetField ] = sourceValue;\n changes = true;\n }\n\n if ( changes )\n {\n if ( this.auto && !target.$isNew() && !remoteData )\n {\n target.$save( this.autoCascade, this.autoOptions );\n }\n\n target.$trigger( Model.Events.KeyUpdate, [target, source, targetFields, sourceFields] );\n }\n\n return changes;\n },\n\n grabInitial: function( model, fields )\n {\n var discriminator = this.discriminator;\n var discriminatorValue = model[ discriminator ];\n\n if ( hasFields( model, fields, isValue ) && isValue( discriminatorValue ) )\n {\n var related = this.discriminatorToModel[ discriminatorValue ];\n\n if ( related.Database )\n {\n var db = related.Database;\n var initial = {};\n\n initial[ discriminator ] = discriminatorValue;\n\n updateFieldsReturnChanges( initial, db.key, model, fields );\n\n return initial;\n }\n }\n },\n\n grabModel: function(input, callback, remoteData)\n {\n if ( input instanceof Model )\n {\n callback.call( this, input );\n }\n // At the moment I don't think this will ever work - if we are given a plain\n // object we can't really determine the related database.\n else if ( isObject( input ) )\n {\n var db = this.getDiscriminatorDatabase( input );\n\n if ( db !== false )\n {\n db.grabModel( input, callback, this, remoteData );\n }\n }\n },\n\n grabModels: function(relation, initial, callback, remoteData)\n {\n for (var i = 0; i < initial.length; i++)\n {\n var input = initial[ i ];\n\n if ( input instanceof Model )\n {\n relation.pending[ input.$key() ] = true;\n\n callback.call( this, input );\n }\n // At the moment I don't think this will ever work - if we are given a plain\n // object we can't really determine the related database.\n else if ( isObject( input ) )\n {\n var db = this.getDiscriminatorDatabase( input );\n\n if ( db )\n {\n var key = db.keyHandler.buildKeyFromInput( input );\n\n relation.pending[ key ] = true;\n\n db.grabModel( input, callback, this, remoteData );\n }\n }\n }\n },\n\n ownsForeignKey: function()\n {\n return true;\n },\n\n isModelArray: function(input)\n {\n return isArray( input );\n },\n\n getDiscriminator: function(model)\n {\n return model[ this.discriminator ];\n },\n\n getDiscriminatorDatabase: function(model)\n {\n var discriminator = this.getDiscriminator( model );\n var model = this.discriminatorToModel[ discriminator ];\n\n return model ? model.Database : false;\n },\n\n getDiscriminatorForModel: function(model)\n {\n return this.discriminators[ model.$db.name ];\n }\n\n};\n\n\nRekord.shard = function(methods)\n{\n return function createRestSharding(database)\n {\n var shard = new Shard( database );\n\n Class.props( shard, methods );\n\n shard.initialize( database );\n\n return shard;\n };\n};\n\nfunction Shard(database)\n{\n this.database = database;\n}\n\nClass.create( Shard,\n{\n\n STATUS_FAIL_ALL: 500,\n STATUS_FAIL_GET: 500,\n STATUS_FAIL_CREATE: 500,\n STATUS_FAIL_UPDATE: 500,\n STATUS_FAIL_REMOVE: 500,\n STATUS_FAIL_QUERY: 500,\n\n ATOMIC_ALL: false,\n ATOMIC_GET: false,\n ATOMIC_CREATE: true,\n ATOMIC_UPDATE: true,\n ATOMIC_REMOVE: false,\n ATOMIC_QUERY: true,\n\n getShards: function(forRead)\n {\n throw 'getShards not implemented';\n },\n\n getShardForModel: function(model, forRead)\n {\n throw 'getShardForModel not implemented';\n },\n\n getShardsForModel: function(model, forRead)\n {\n var single = this.getShardForModel( model, forRead );\n\n return single ? [ single ] : this.getShards( forRead );\n },\n\n getShardsForQuery: function(url, query)\n {\n return this.getShards();\n },\n\n initialize: function(database)\n {\n\n },\n\n all: function(options, success, failure)\n {\n var shards = this.getShards( true );\n var all = [];\n\n function invoke(shard, onShardSuccess, onShardFailure)\n {\n shard.all( options, onShardSuccess, onShardFailure );\n }\n function onSuccess(models)\n {\n if ( isArray( models ) )\n {\n all.push.apply( all, models );\n }\n }\n function onComplete(successful, alreadyFailed, failedStatus)\n {\n if ( successful || (all.length && !this.ATOMIC_ALL) )\n {\n success( all );\n }\n else if ( !alreadyFailed )\n {\n failure( all, isDefined( failedStatus ) ? failedStatus : this.STATUS_FAIL_ALL );\n }\n }\n\n this.multiplex( shards, this.ATOMIC_ALL, invoke, onSuccess, failure, onComplete );\n },\n\n get: function(model, options, success, failure)\n {\n var shards = this.getShardsForModel( model, true );\n var gotten = null;\n\n function invoke(shard, onShardSuccess, onShardFailure)\n {\n shard.get( model, options, onShardSuccess, onShardFailure );\n }\n function onSuccess(data)\n {\n if ( gotten === null && isObject( data ) )\n {\n gotten = data;\n }\n }\n function onComplete(successful, alreadyFailed, failedStatus)\n {\n if ( gotten !== null )\n {\n success( gotten );\n }\n else\n {\n failure( gotten, isDefined( failedStatus ) ? failedStatus : this.STATUS_FAIL_GET );\n }\n }\n\n this.multiplex( shards, this.ATOMIC_GET, invoke, onSuccess, noop, onComplete );\n },\n\n create: function( model, encoded, options, success, failure )\n {\n var shards = this.getShardsForModel( model, false );\n var returned = null;\n\n function invoke(shard, onShardSuccess, onShardFailure)\n {\n shard.create( model, encoded, options, onShardSuccess, onShardFailure );\n }\n function onSuccess(data)\n {\n if ( returned === null && isObject( returned ) )\n {\n returned = data;\n }\n }\n function onComplete(successful, alreadyFailed, failedStatus)\n {\n if ( successful )\n {\n success( returned );\n }\n else\n {\n failure( returned, isDefined( failedStatus ) ? failedStatus : this.STATUS_FAIL_CREATE );\n }\n }\n\n this.multiplex( shards, this.ATOMIC_CREATE, invoke, onSuccess, noop, onComplete );\n },\n\n update: function( model, encoded, options, success, failure )\n {\n var shards = this.getShardsForModel( model, false );\n var returned = null;\n\n function invoke(shard, onShardSuccess, onShardFailure)\n {\n shard.update( model, encoded, options, onShardSuccess, onShardFailure );\n }\n function onSuccess(data)\n {\n if ( returned === null && isObject( returned ) )\n {\n returned = data;\n }\n }\n function onComplete(successful, alreadyFailed, failedStatus)\n {\n if ( successful )\n {\n success( returned );\n }\n else\n {\n failure( returned, isDefined( failedStatus ) ? failedStatus : this.STATUS_FAIL_UPDATE );\n }\n }\n\n this.multiplex( shards, this.ATOMIC_UPDATE, invoke, onSuccess, noop, onComplete );\n },\n\n remove: function( model, options, success, failure )\n {\n var shards = this.getShardsForModel( model, false );\n var returned = null;\n\n function invoke(shard, onShardSuccess, onShardFailure)\n {\n shard.remove( model, options, onShardSuccess, onShardFailure );\n }\n function onSuccess(data)\n {\n if ( returned === null && isObject( returned ) )\n {\n returned = data;\n }\n }\n function onComplete(successful, alreadyFailed, failedStatus)\n {\n if ( successful )\n {\n success( returned );\n }\n else\n {\n failure( returned, isDefined( failedStatus ) ? failedStatus : this.STATUS_FAIL_REMOVE );\n }\n }\n\n this.multiplex( shards, this.ATOMIC_REMOVE, invoke, onSuccess, noop, onComplete );\n },\n\n query: function( url, query, options, success, failure )\n {\n var shards = this.getShardsForQuery( url, query );\n var results = [];\n\n function invoke(shard, onShardSuccess, onShardFailure)\n {\n shard.query( url, query, options, onShardSuccess, onShardFailure );\n }\n function onSuccess(models)\n {\n if ( isArray( models ) )\n {\n results.push.apply( results, models );\n }\n }\n function onComplete(successful, alreadyFailed, failedStatus)\n {\n if ( successful || (results.length && !this.ATOMIC_QUERY) )\n {\n success( results );\n }\n else if ( !alreadyFailed )\n {\n failure( results, isDefined( failedStatus ) ? failedStatus : this.STATUS_FAIL_QUERY );\n }\n }\n\n this.multiplex( shards, this.ATOMIC_QUERY, invoke, onSuccess, noop, onComplete );\n },\n\n multiplex: function(shards, atomic, invoke, onSuccess, onFailure, onComplete)\n {\n var successful = true;\n var failureCalled = false;\n var failedStatus;\n var total = 0;\n\n function onShardComplete()\n {\n if ( ++total === shards.length )\n {\n onComplete.call( this, successful, failureCalled, failedStatus );\n }\n }\n function onShardSuccess(data)\n {\n if ( successful || !atomic )\n {\n onSuccess.apply( this, arguments );\n }\n\n onShardComplete();\n }\n function onShardFailure(data, status)\n {\n if ( successful )\n {\n successful = false;\n\n if ( atomic )\n {\n failureCalled = true;\n onFailure.apply( this, arguments );\n }\n }\n\n if ( isNumber( status ) && (failedStatus === undefined || status < failedStatus) )\n {\n failedStatus = status;\n }\n\n onShardComplete();\n }\n\n if ( !isArray( shards ) || shards.length === 0 )\n {\n onComplete.call( this, false, false, failedStatus );\n }\n else\n {\n for (var i = 0; i < shards.length; i++)\n {\n invoke.call( this, shards[ i ], onShardSuccess, onShardFailure );\n }\n }\n }\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Returns the reference to the collection which contains all saved models.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var t0 = Task.create({name: 't0', done: true}); // saves\n * var t1 = new Task({name: 't1'});\n * Task.all(); // [t0]\n * ```\n *\n * @method all\n * @memberof Rekord.Model\n * @return {Rekord.ModelCollection} -\n * The reference to the collection of models.\n */\n model.all = function()\n {\n return db.models;\n };\n \n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Creates a collection of models.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var t0 = Task.create({id: 34, name: 't0'});\n * var t1 = new Task({name: 't1'});\n * var t2 = {name: 't2'};\n *\n * var c = Task.collect( 34, t1, t2 ); // or Task.collect( [34, t1, t2] )\n * c; // [t0, t1, t2]\n * ```\n *\n * @method collect\n * @memberof Rekord.Model\n * @param {modelInput[]|...modelInput} models -\n * The array of models to to return as a collection.\n * @return {Rekord.ModelCollection} -\n * The collection created.\n */\n model.array = function(a)\n {\n var models = arguments.length > 1 || !isArray(a) ?\n AP.slice.call( arguments ) : a;\n\n return ModelCollection.native( db, models );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Returns the model at the given index.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var t0 = Task.create({name: 't0', done: true}); // saves\n * var t1 = new Task({name: 't1'});\n * Task.at( 0 ); // t0\n * ```\n *\n * @method at\n * @memberof Rekord.Model\n * @param {Number} index -\n * The index of the model to return.\n * @return {Rekord.Model} -\n * The reference to the model at the given index.\n */\n model.at = function(index)\n {\n return db.models[ index ];\n };\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Returns an instance of a model or model collection with remote data (from\n * the server). If the model(s) exist locally then the values passed in will\n * overwrite the current values of the models. This is typically used to\n * bootstrap data from the server in your webpage.\n *\n * ```javascript\n * var User = Rekord({\n * fields: ['name', 'email']\n * });\n * var currentUser = User.boot({\n * id: 1234,\n * name: 'Administrator',\n * email: 'rekordjs@gmail.com'\n * });\n * var friends = User.boot([\n * { id: 'c1', name: 'Cat 1', email: 'cat1@gmail.com' },\n * { id: 'c2', name: 'Cat 2', email: 'cat2@gmail.com' }\n * ]);\n * ```\n *\n * @method boot\n * @memberof Rekord.Model\n * @param {modelInput[]|Object}\n * @return {Rekord.ModelCollection|Rekord.Model} -\n * The collection or model bootstrapped.\n */\n model.boot = function( input )\n {\n if ( isArray( input ) )\n {\n return ModelCollection.create( db, input, true );\n }\n else if ( isObject( input ) )\n {\n return db.putRemoteData( input );\n }\n\n return input;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n \n model.clear = function(removeListeners)\n {\n return db.clear( removeListeners );\n };\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Creates a collection of models.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var t0 = Task.create({id: 34, name: 't0'});\n * var t1 = new Task({name: 't1'});\n * var t2 = {name: 't2'};\n *\n * var c = Task.collect( 34, t1, t2 ); // or Task.collect( [34, t1, t2] )\n * c; // [t0, t1, t2]\n * ```\n *\n * @method collect\n * @memberof Rekord.Model\n * @param {modelInput[]|...modelInput} models -\n * The array of models to to return as a collection.\n * @return {Rekord.ModelCollection} -\n * The collection created.\n */\n model.collect = function(a)\n {\n var models = arguments.length > 1 || !isArray(a) ?\n AP.slice.call( arguments ) : a;\n\n return ModelCollection.create( db, models );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Counts the number of models which pass the given where expression.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var t0 = Task.create({name: 't0', done: true}); // saves\n * var t1 = Task.create({name: 't1', done: false});\n * Task.count('done', true); // 1\n * ```\n *\n * @method count\n * @memberof Rekord.Model\n * @return {Number} -\n * The number of models which pass the given where expression.\n */\n model.count = function(properties, value, equals)\n {\n return db.models.countWhere( properties, value, equals );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Creates a model instance, saves it, and returns it.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name'],\n * defaults: {\n * name: 'New Task'\n * }\n * });\n * var t0 = Task.create({id: 34, name: 't0'});\n * var t1 = Task.create({name: 't1'}); // id generated with uuid\n * var t2 = Task.create(); // name populated with default 'New Task'\n * ```\n *\n * @method create\n * @memberof Rekord.Model\n * @param {Object} [props] -\n * The initial values for the new model - if any.\n * @param {Number} [cascade] -\n * Which operations should be performed out of: store, rest, & live.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @return {Rekord.Model} -\n * The saved model instance.\n */\n model.create = function( props, cascade, options )\n {\n var instance = isObject( props ) ?\n db.createModel( props ) :\n db.instantiate();\n\n instance.$save( cascade, options );\n\n return instance;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n var dynamics = collapse( options.dynamic, Defaults.dynamic );\n\n if ( !isEmpty( dynamics ) )\n {\n for ( var property in dynamics )\n {\n addDynamicProperty( model.prototype, property, dynamics[ property ] );\n }\n }\n});\n\nfunction addDynamicProperty(modelPrototype, property, definition)\n{\n var get = isFunction( definition ) ? definition :\n ( isObject( definition ) && isFunction( definition.get ) ? definition.get : noop );\n var set = isObject( definition ) && isFunction( definition.set ) ? definition.set : noop;\n\n if ( Object.defineProperty )\n {\n Object.defineProperty( modelPrototype, property,\n {\n configurable: false,\n enumerable: true,\n get: get,\n set: set\n });\n }\n else\n {\n var $init = modelPrototype.$init;\n\n modelPrototype.$init = function()\n {\n $init.apply( this, arguments );\n\n var lastCalculatedValue = this[ property ] = get.apply( this );\n\n var handleChange = function()\n {\n var current = this[ property ];\n\n if ( current !== lastCalculatedValue )\n {\n set.call( this, current );\n }\n else\n {\n lastCalculatedValue = this[ property ] = get.apply( this );\n }\n };\n\n this.$after( Model.Events.Changes, handleChange, this );\n };\n }\n}\n\naddPlugin(function(model, db, options)\n{\n var events = collapse( options.events, Defaults.events );\n\n if ( !isEmpty( events ) )\n {\n var modelEvents = [];\n var databaseEvents = [];\n\n for ( var eventType in events )\n {\n var callback = events[ eventType ];\n var eventName = toCamelCase( eventType );\n\n var databaseEventString = Database.Events[ eventName ];\n var modelEventString = Model.Events[ eventName ];\n\n if ( databaseEventString )\n {\n parseEventListeners( databaseEventString, callback, false, databaseEvents );\n }\n\n if ( modelEventString )\n {\n parseEventListeners( modelEventString, callback, true, modelEvents );\n }\n }\n\n applyEventListeners( db, databaseEvents );\n\n if ( modelEvents.length )\n {\n Class.replace( model, '$init', function($init)\n {\n return function()\n {\n $init.apply( this, arguments );\n\n applyEventListeners( this, modelEvents );\n };\n });\n }\n }\n\n});\n\nfunction parseEventListeners(events, callback, secret, out)\n{\n var map = {\n on: secret ? '$on' : 'on',\n once: secret ? '$once' : 'once',\n after: secret ? '$after' : 'after'\n };\n\n var listeners = out || [];\n\n if ( isFunction( callback ) )\n {\n listeners.push(\n {\n when: map.on,\n events: events,\n invoke: callback\n });\n }\n else if ( isArray( callback ) && callback.length === 2 && isFunction( callback[0] ) )\n {\n listeners.push(\n {\n when: map.on,\n events: events,\n invoke: callback[0],\n context: callback[1]\n });\n }\n else if ( isObject( callback ) )\n {\n for ( var eventType in callback )\n {\n if ( eventType in map )\n {\n var subcallback = callback[ eventType ];\n var when = map[ eventType ];\n\n if ( isFunction( subcallback ) )\n {\n listeners.push(\n {\n when: when,\n events: events,\n invoke: subcallback\n });\n }\n else if ( isArray( subcallback ) && subcallback.length === 2 && isFunction( subcallback[0] ) )\n {\n listeners.push(\n {\n when: when,\n events: events,\n invoke: subcallback[0],\n context: subcallback[1]\n });\n }\n }\n }\n }\n\n return listeners;\n}\n\nfunction applyEventListeners(target, listeners)\n{\n for (var i = 0; i < listeners.length; i++)\n {\n var l = listeners[ i ];\n\n target[ l.when ]( l.events, l.invoke, l.context );\n }\n}\n\naddPlugin(function(model, db, options)\n{\n var extend = options.extend || Defaults.extend;\n\n if ( !isRekord( extend ) )\n {\n return;\n }\n\n var defaults = Defaults;\n var edb = extend.Database;\n var eoptions = edb.options;\n\n function tryOverwrite(option)\n {\n if ( !options[ option ] )\n {\n db[ option ] = edb[ option ];\n }\n }\n\n function tryMerge(option)\n {\n var dbo = db[ option ];\n var edbo = edb[ option ];\n\n for (var prop in edbo)\n {\n if ( !(prop in dbo ) )\n {\n dbo[ prop ] = edbo[ prop ];\n }\n }\n }\n\n function tryUnshift(options, sourceOptions)\n {\n var source = edb[ sourceOptions || options ];\n var target = db[ options ];\n\n for (var i = source.length - 1; i >= 0; i--)\n {\n var k = indexOf( target, source[ i ] );\n\n if ( k !== false )\n {\n target.splice( k, 1 );\n }\n\n target.unshift( source[ i ] );\n }\n }\n\n tryOverwrite( 'keySeparator' );\n tryMerge( 'defaults' );\n tryMerge( 'ignoredFields' );\n tryOverwrite( 'loadRelations' );\n tryOverwrite( 'load' );\n tryOverwrite( 'autoRefresh' );\n tryOverwrite( 'cache' );\n tryOverwrite( 'fullSave' );\n tryOverwrite( 'fullPublish' );\n tryMerge( 'encodings' );\n tryMerge( 'decodings' );\n tryOverwrite( 'summarize' );\n tryUnshift( 'fields' );\n tryUnshift( 'saveFields', 'fields' );\n\n if ( !options.comparator )\n {\n db.setComparator( eoptions.comparator, eoptions.comparatorNullsFirst );\n }\n\n if ( !options.revision )\n {\n db.setRevision( eoptions.revision );\n }\n\n if ( !options.summarize )\n {\n db.setSummarize( eoptions.summarize );\n }\n\n for (var name in edb.relations)\n {\n if ( name in db.relations )\n {\n continue;\n }\n\n var relation = edb.relations[ name ];\n var relationCopy = new relation.constructor();\n\n relationCopy.init( db, name, relation.options );\n\n if ( relationCopy.save )\n {\n db.saveFields.push( name );\n }\n\n db.relations[ name ] = relationCopy;\n db.relationNames.push( name );\n }\n\n db.rest = Rekord.rest( db );\n db.store = Rekord.store( db );\n db.live = Rekord.live( db );\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Gets the local model matching the given input (or creates one) and loads\n * it from the remote source ({@link Rekord.rest}). If `callback` is specified\n * then it is invoked with the instance once it's loaded.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var t0 = Task.fetch( 34, function(task) {\n * task; // {id: 34 name: 'Remotely Loaded'}\n * });\n * t0; // {id: 34} until remotely loaded\n * ```\n *\n * @method fetch\n * @memberof Rekord.Model\n * @param {modelInput} input -\n * The model input used to determine the key and load the model.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @param {Function} [callback] -\n * The function to invoke passing the reference of the model once it's\n * successfully remotely loaded.\n * @param {Object} [context] -\n * The context (this) for the callback.\n * @return {Rekord.Model} -\n * The model instance.\n */\n model.fetch = function( input, options, callback, context )\n {\n var key = db.keyHandler.buildKeyFromInput( input );\n var instance = db.get( key );\n\n if ( !instance )\n {\n instance = db.keyHandler.buildObjectFromKey( key );\n\n if ( isObject( input ) )\n {\n instance.$set( input );\n }\n }\n\n if ( isFunction( callback ) )\n {\n var callbackContext = context || this;\n\n instance.$once( Model.Events.RemoteGets, function()\n {\n callback.call( callbackContext, instance );\n });\n }\n\n instance.$refresh( Cascade.Rest, options );\n\n return instance;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Returns the collection of all local models and tries to reload them (and\n * any additional models returned) from a remote source ({@link Rekord.rest}).\n * If `callback` is specified then it is invoked with the collections all\n * models once it's loaded.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var tasks0 = Task.fetchAll( function(tasks1) {\n * tasks0 // tasks1\n * });\n * ```\n *\n * @method fetchAll\n * @memberof Rekord.Model\n * @param {Function} [callback] -\n * The function to invoke passing the reference of the model collection\n * when it's successfully remotely loaded.\n * @param {Object} [context] -\n * The context (this) for the callback.\n * @return {Rekord.ModelCollection} -\n * The collection of all models of this type.\n */\n model.fetchAll = function(callback, context)\n {\n db.refresh( callback, context );\n\n return db.models;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n var files = options.files || Defaults.files;\n\n if ( !isObject( files ) )\n {\n return;\n }\n\n if ( !isFilesSupported() )\n {\n Rekord.trigger( Rekord.Events.FilesNotSupported );\n\n return;\n }\n\n for (var field in files)\n {\n var fieldOption = files[ field ];\n\n if ( isString( fieldOption ) )\n {\n fieldOption = {\n type: fieldOption\n };\n }\n\n db.decodings[ field ] = FileDecodings[ fieldOption.type ]( db, fieldOption );\n db.encodings[ field ] = FileEncoder;\n }\n});\n\n/**\nfiles: {\n field: {\n type: 'text', // base64, dataURL, resource\n processor: 'processor_name',\n capacity: 1024 * 1024, // maximum bytes\n types: ['image/png', 'image/jpg', 'image/gif'], // acceptable MIME types\n autoSave: true,\n store: true,\n save: true\n }\n}\n**/\n\nRekord.fileProcessors = {};\n\nRekord.Events.FilesNotSupported = 'files-not-supported';\nRekord.Events.FileTooLarge = 'file-too-large';\nRekord.Events.FileWrongType = 'file-wrong-type';\nRekord.Events.FileOffline = 'file-offline';\n\n// {\n// fileToValue(file, model, field, callback),\n// valueToUser(value, model, field, callback)\n// }\nRekord.addFileProcessor = function(name, methods)\n{\n Rekord.fileProcessors[ name ] = methods;\n};\n\nRekord.fileProperties =\n[\n 'lastModifiedDate', 'name', 'size', 'type'\n];\n\nfunction isFilesSupported()\n{\n return win.File && win.FileReader && win.FileList;\n}\n\nfunction toFile(input)\n{\n if ( input instanceof win.File )\n {\n return input;\n }\n else if ( input instanceof win.Blob )\n {\n return input;\n }\n else if ( input instanceof win.FileList && input.length > 0 )\n {\n return input[0];\n }\n\n return false;\n}\n\nfunction convertNone(x)\n{\n return x;\n}\n\nfunction convertBase64(x)\n{\n var i = isString( x ) ? x.indexOf(';base64,') : -1;\n\n return i === -1 ? x : x.substring( i + 8 );\n}\n\nfunction trySave(model, options)\n{\n if ( options.autoSave && model.$isSaved() )\n {\n model.$save();\n }\n}\n\nfunction putFileCache(model, property, value, file, options)\n{\n model.$files = model.$files || {};\n model.$files[ property ] = {\n value: value,\n user: value,\n file: file,\n options: options\n };\n}\n\nfunction setFilesValue(processor, value, model, property, options)\n{\n var result;\n var done = false;\n\n if ( processor && processor.valueToUser )\n {\n processor.valueToUser( value, model, property, function(user)\n {\n model.$files[ property ].user = user;\n\n if ( done )\n {\n model[ property ] = user;\n trySave( model, options );\n }\n else\n {\n result = user;\n }\n });\n }\n else\n {\n result = value;\n }\n\n done = true;\n\n return result;\n}\n\nfunction fileReader(method, converter, options)\n{\n var processor = Rekord.fileProcessors[ options.processor ];\n\n if ( !(method in win.FileReader.prototype) )\n {\n Rekord.trigger( Rekord.Events.FilesNotSupported );\n }\n\n return function(input, model, property)\n {\n var file = toFile( input );\n\n if ( file !== false )\n {\n var reader = new win.FileReader();\n var result;\n var done = false;\n\n reader.onload = function(e)\n {\n var value = converter( e.target.result );\n\n putFileCache( model, property, value, file, options );\n\n result = setFilesValue( processor, value, model, property, options );\n\n if ( done )\n {\n model[ property ] = result;\n trySave( model, options );\n }\n };\n\n reader[ method ]( file );\n\n done = true;\n\n return result;\n }\n else if ( isObject( input ) && input.FILE )\n {\n var result;\n\n var setter = function(value)\n {\n result = value;\n };\n\n Rekord.trigger( Rekord.Events.FileOffline, [input, model, property, setter] );\n\n return result;\n }\n else\n {\n putFileCache( model, property, input, null, options );\n\n return setFilesValue( processor, input, model, property, options );\n }\n };\n}\n\nvar FileDecodings =\n{\n text: function(db, options)\n {\n return fileReader( 'readAsText', convertNone, options );\n },\n dataURL: function(db, options)\n {\n return fileReader( 'readAsDataURL', convertNone, options );\n },\n base64: function(db, options)\n {\n return fileReader( 'readAsDataURL', convertBase64, options );\n },\n resource: function(db, options)\n {\n return function(input, model, property)\n {\n var file = toFile( input );\n var processor = Rekord.fileProcessors[ options.processor ];\n\n if ( !processor )\n {\n throw 'Processor required for resource files.';\n }\n\n if ( file !== false )\n {\n if ( isNumber( options.capacity ) && isNumber( file.size ) && file.size > options.capacity )\n {\n Rekord.trigger( Rekord.Events.FileTooLarge, [file, model, property] );\n\n return;\n }\n\n if ( isArray( options.types ) && isString( file.type ) && indexOf( options.types, file.type ) === false )\n {\n Rekord.trigger( Rekord.Events.FileWrongType, [file, model, property] );\n\n return;\n }\n\n var result;\n var done = false;\n\n processor.fileToValue( file, model, property, function(value)\n {\n putFileCache( model, property, value, file, options );\n\n result = setFilesValue( processor, value, model, property, options );\n\n if ( done )\n {\n model[ property ] = result;\n trySave( model, options );\n }\n });\n\n done = true;\n\n return result;\n }\n else if ( isObject( input ) && input.FILE )\n {\n Rekord.trigger( Rekord.Events.FileOffline, [input, model, property] );\n }\n else\n {\n putFileCache( model, property, input, null, options );\n\n return setFilesValue( processor, input, model, property, options );\n }\n };\n }\n};\n\nfunction FileEncoder(input, model, field, forSaving)\n{\n if ( model.$files && field in model.$files )\n {\n var cached = model.$files[ field ];\n\n if ( (forSaving && cached.save === false) || (!forSaving && cached.store === false) )\n {\n return;\n }\n\n if ( !forSaving && cached.file )\n {\n var props = grab( cached.file, Rekord.fileProperties, false );\n\n props.FILE = true;\n\n return props;\n }\n\n if ( input === cached.user )\n {\n if ( forSaving && cached.file )\n {\n model.$once( Model.Events.RemoteSave, function()\n {\n delete cached.file;\n\n model.$addOperation( SaveLocal, Cascade.Local );\n });\n }\n\n return cached.value;\n }\n }\n\n return input;\n}\n\naddPlugin(function(model, db, options)\n{\n\n model.filtered = function(whereProperties, whereValue, whereEquals)\n {\n return db.models.filtered( whereProperties, whereValue, whereEquals );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n model.first = model.find = function(whereProperties, whereValue, whereEquals)\n {\n return db.models.firstWhere( whereProperties, whereValue, whereEquals );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Finds or creates a model instance based on the given values. The key for\n * the model must be derivable from the given values - or this function will\n * always create a new model instance.\n *\n * ```javascript\n * var ListItem = Rekord({\n * key: ['list_id', 'iten_id'],\n * fields: ['quantity'],\n * belongsTo: {\n * list: { model: 'list' },\n * item: { model: 'item' }\n * }\n * });\n *\n * var listItem = ListItem.findOrCreate({\n * list: someList,\n * item: someItem,\n * quantity: 23\n * });\n * // do stuff with listItem\n * ```\n *\n * @method persist\n * @memberof Rekord.Model\n * @param {Object} [input] -\n * The values to set in the model instance found or created.\n * @param {Number} [cascade] -\n * Which operations should be performed out of: store, rest, & live.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @return {Rekord.Model} -\n * The saved model instance or undefined if the model database has not\n * finished loading.\n */\n model.findOrCreate = function( input, cascade, options, callback, context )\n {\n var callbackContext = context || this;\n var instance = db.get( input );\n var created = false;\n\n if ( !instance )\n {\n db.grabModel( input, function(grabbed)\n {\n if ( !grabbed )\n {\n instance = model.create( input, cascade, options );\n created = true;\n }\n else\n {\n instance = grabbed;\n instance.$set( input );\n\n // grab model created an instance that needs to be \"created\"\n if ( !instance.$isSaved() )\n {\n instance.$save( cascade, options );\n }\n }\n\n if ( callback )\n {\n callback.call( callbackContext, instance, created );\n }\n });\n }\n else\n {\n instance.$set( input );\n\n if ( callback )\n {\n callback.call( callbackContext, instance, created );\n }\n }\n\n return instance;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Returns the model instance identified with the given input. This includes\n * saved and unsaved models. If a `callback` is given the model will be passed\n * to the function. The `callback` method is useful for waiting for Rekord\n * to finish initializing (which includes loading models from local storage\n * followed by remote storage if configured) and returning a model instance.\n * If Rekord has finished initializing and the model doesn't exist locally\n * then it is fetched from the remoute source using {@link Rekord.rest}.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var t0 = Task.get( 34 ); // only looks at models currently loaded\n * var t1 = Task.get( 23, function(model) {\n * model; // local or remotely loaded if it didn't exist locally - could be null if it doesn't exist at all\n * })\n * ```\n *\n * @method get\n * @memberof Rekord.Model\n * @param {modelInput} input -\n * The model input used to determine the key and load the model.\n * @param {Function} [callback] -\n * The function to invoke passing the reference of the model when it's\n * successfully found.\n * @param {Object} [context] -\n * The context (this) for the callback.\n * @return {Rekord.Model} -\n * The model instance if `callback` is not given - or undefined if the\n * input doesn't resolve to a model or `callback` is given.\n */\n model.get = function( input, callback, context )\n {\n if ( isFunction( callback ) )\n {\n db.grabModel( input, callback, context );\n }\n else\n {\n return db.get( input );\n }\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Gets the model instance identified with the given input and passes it to the\n * `callback` function. If Rekord is not finished initializing this function\n * will wait until it is and check for the model. If it still doesn't exist\n * locally it is loaded from a remote source using {@link Rekord.rest}. If the\n * model doesn't exist at all a null value will be returned to the function.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var t1 = Task.grab( 23, function(model) {\n * model; // local or remotely loaded if it didn't exist locally - could be null if it doesn't exist at all\n * })\n * ```\n *\n * @method grab\n * @memberof Rekord.Model\n * @param {modelInput} input -\n * The model input used to determine the key and load the model.\n * @param {Function} callback -\n * The function to invoke passing the reference of the model when it's\n * successfully found.\n * @param {Object} [context] -\n * The context (this) for the callback.\n * @return {Rekord.Model} -\n * The model instance of it exists locally at the moment, or undefined\n * if the model hasn't been loaded yet.\n */\n model.grab = function( input, options, callback, context )\n {\n var callbackContext = context || this;\n var instance = db.get( input );\n\n if ( instance )\n {\n callback.call( callbackContext, instance );\n }\n else\n {\n db.grabModel( input, function(instance)\n {\n if ( instance )\n {\n callback.call( callbackContext, instance );\n }\n else\n {\n model.fetch( input, options, callback, context );\n }\n });\n }\n\n return instance;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Gets all model instances currently loaded, locally loaded, or remotely\n * loaded and passes it to the `callback` function.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var tasks = Task.grabAll( function(models) {\n * models; // local or remotely loaded if it didn't exist locally.\n * })\n * ```\n *\n * @method grabAll\n * @memberof Rekord.Model\n * @param {Function} callback -\n * The function to invoke passing the reference of the model collection\n * when it's loaded.\n * @param {Object} [context] -\n * The context (this) for the callback.\n * @return {Rekord.Model} -\n * The model collection of it exists locally at the moment, or undefined\n * if models haven't been loaded yet.\n */\n model.grabAll = function( callback, context )\n {\n var callbackContext = context || this;\n var models = db.models;\n\n if ( models.length )\n {\n callback.call( callbackContext, models );\n }\n else\n {\n db.ready(function()\n {\n if ( models.length )\n {\n callback.call( callbackContext, models );\n }\n else\n {\n db.refresh(function()\n {\n callback.call( callbackContext, models );\n });\n }\n });\n }\n\n return models;\n };\n});\n\n\naddPlugin( function(model, db, options)\n{\n\n model.hasTrait = function(trait, comparator)\n {\n return db.hasTrait(trait, comparator);\n };\n\n model.hasTraits = function(traits, comparator)\n {\n return db.hasTraits(traits, comparator);\n };\n\n});\n\n\naddPlugin( function(model, db, options)\n{\n if ( options.keyChanges )\n {\n enableKeyChanges();\n }\n});\n\nvar Map_put = Map.prototype.put;\nvar Map_remove = Map.prototype.remove;\n\nfunction mapKeyChangeListener(map)\n{\n return function onKeyChange(model, oldKey, newKey)\n {\n var index = map.indices[ oldKey ];\n\n if ( isNumber( index ) )\n {\n var listener = map.listeners[ oldKey ];\n\n delete map.indices[ oldKey ];\n delete map.listeners[ oldKey ];\n\n map.keys[ index ] = newKey;\n map.indices[ newKey ] = index;\n map.listeners[ newKey ] = listener;\n }\n };\n}\n\nfunction mapKeyChangePut(key, value)\n{\n Map_put.apply( this, arguments );\n\n if ( value instanceof Model && value.$db.keyChanges )\n {\n this.listeners = this.listeners || {};\n\n this.listeners[ key ] = value.$on( Model.Events.KeyChange, mapKeyChangeListener( this ) );\n }\n\n return this;\n}\n\nfunction mapKeyChangeRemove(key)\n{\n var index = this.indices[ key ];\n\n if ( isNumber( index ) )\n {\n if ( this.listeners )\n {\n evaluate( this.listeners[ key ] );\n\n delete this.listeners[ key ];\n }\n\n this.removeAt( index );\n }\n\n return this;\n}\n\nfunction enableKeyChanges()\n{\n Class.method( Map, 'put', mapKeyChangePut );\n Class.method( Map, 'remove', mapKeyChangeRemove );\n}\n\nfunction disableKeyChanges()\n{\n Class.method( Map, 'put', Map_put );\n Class.method( Map, 'remove', Map_remove );\n}\n\naddPlugin(function(model, db, options)\n{\n var methods = collapse( options.methods, Defaults.methods );\n\n if ( !isEmpty( methods ) )\n {\n Class.methods( model, methods );\n }\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Persists model values, creating a model instance if none exists already\n * (determined by the key derived from the input).\n *\n * ```javascript\n * var ListItem = Rekord({\n * key: ['list_id', 'iten_id'],\n * fields: ['quantity'],\n * belongsTo: {\n * list: { model: 'list' },\n * item: { model: 'item' }\n * }\n * });\n *\n * var listItem = ListItem.persist({ // creates relationship if it doesn't exist already - updates existing\n * list: someList,\n * item: someItem,\n * quantity: 23\n * });\n * ```\n *\n * @method persist\n * @memberof Rekord.Model\n * @param {Object} [input] -\n * The values to persist in the model instance found or created.\n * @return {Rekord.Model} -\n * The saved model instance or undefined if the model database has not\n * finished loading.\n */\n model.persist = function( input, cascade, options, callback, context )\n {\n var callbackContext = context || this;\n\n return model.findOrCreate( input, cascade, options, function(instance, created)\n {\n if ( !created )\n {\n instance.$save( cascade, options );\n }\n\n if ( callback )\n {\n callback.call( callbackContext, instance );\n }\n });\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n model.projection = function(projectionInput)\n {\n return Projection.parse( db, projectionInput );\n };\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Invokes a function when Rekord has loaded. It's considered loaded when\n * it's loaded locally, remotely, or neither (depending on the options\n * passed to the database). The `callback` can also be invoked `persistent`ly\n * on any load event - which includes {@link Rekord.Database#refresh}.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * Task.ready( function(db) {\n * // Tasks have been loaded, lets do something about it!\n * });\n * ```\n *\n * @method ready\n * @memberof Rekord.Model\n * @param {Function} callback -\n * The function to invoke passing the reference of the database when it's\n * loaded.\n * @param {Object} [context] -\n * The context (this) for the callback.\n * @param {Boolean} [persistent=false] -\n * Whether the `callback` function should be invoked multiple times.\n * Depending on the state of initializing, the callback can be invoked when\n * models are loaded locally (if the `cache` is not equal to `None`),\n * models are loaded remotely (if `load` is Rekord.Load.All), and every time\n * {@link Rekord.Database#refresh} is called manually OR if `autoRefresh`\n * is specified as true and the application changes from offline to online.\n */\n model.ready = function( callback, context, persistent )\n {\n db.ready( callback, context, persistent );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Refreshs the model database from the remote source by calling\n * {@link Rekord.Database#refresh}. A `callback` can be passed to be invoked\n * when the model database has refreshed (or failed to refresh) where all\n * models that have been loaded will be passed as the first argument.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * Task.refresh( function(models) {\n * models; // The collection of models loaded remotely (or current models if it failed to load them remotely.\n * });\n * ```\n *\n * @method refresh\n * @memberof Rekord.Model\n * @param {Function} callback -\n * The function to invoke passing the reference model collection.\n * @param {Object} [context] -\n * The context (this) for the callback.\n */\n model.refresh = function( callback, context )\n {\n return db.refresh( callback, context );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n model.reset = function(failOnPendingChanges, removeListeners)\n {\n return db.reset( failOnPendingChanges, removeListeners );\n };\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Creates a new search for model instances and returns the results collection.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var results = Task.results('/api/task/search', {name: 'like this', done: true});\n * // results populated when search finishes running.\n * // results.$search is the Search object.\n * // results.$search.$promise is the promise of the search.\n * ```\n *\n * @method results\n * @memberof Rekord.Model\n * @param {String} url -\n * A URL to send the search data to.\n * @param {Object} [props] -\n * Initial set of properties on the search.\n * @param {searchOptions} [options] -\n * Options for the search.\n * @return {Rekord.ModelCollection} -\n * A collection containing the results of the search.\n */\n model.results = function(url, props, options)\n {\n return new Search( db, url, options, props, true ).$results;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Creates a new search for model instances. A search is an object with\n * properties that are passed to a configurable {@link Rekord.rest} function\n * which expect an array of models to be returned from the remote call that\n * match the search parameters.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var search = Task.search('/api/task/search');\n * search.name = 'like this';\n * search.done = true;\n * search.anyProperty = [1, 3, 4];\n * var promise = search.$run();\n * promise.success( function(search) {\n * search.$results; // collection of returned results\n * });\n * ```\n *\n * @method search\n * @memberof Rekord.Model\n * @param {String} url -\n * A URL to send the search data to.\n * @param {searchOptions} [options] -\n * Options for the search.\n * @param {Object} [props] -\n * Initial set of properties on the search.\n * @param {Boolean} [run=false] -\n * Whether or not to run the search immediately.\n * @return {Rekord.Search} -\n * A new search for models.\n */\n model.search = function(url, options, props, run)\n {\n return new Search( db, url, options, props, run );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n model.searchAt = function(index, url, paging, options, props, success, failure)\n {\n var page = {page_index: index, page_size: 1};\n\n var search = paging ?\n new SearchPaged( db, url, collapse( options, page ), props ) :\n new Search( db, url, options, props );\n\n var promise = new Promise();\n\n promise.success( success );\n promise.failure( failure );\n\n search.$run().then(\n function onSuccess(search, response, results) {\n promise.resolve( results[ paging ? 0 : index ] );\n },\n function onFailure() {\n promise.reject();\n },\n function onOffline() {\n promise.noline();\n }\n );\n\n return promise;\n };\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Creates a new search with pagination for model instances. A paginated\n * search is an object with properties that are passed to a configurable\n * {@link Rekord.rest} function which expect an array of models to be returned\n * as well as paging information from the remote call. Special properties are\n * passed to the server (`page_index`, `page_size`) which dictate which\n * chunk of data should be returned. A special `total` property is expected to\n * be returned with `results` which tells the search how many records would've\n * been returned without the pagination.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var search = Task.searchPaged('/api/task/searchPaged');\n * search.name = 'like this';\n * search.done = true;\n * search.anyProperty = [1, 3, 4];\n * var promise = search.$run();\n * promise.success( function(search) {\n * search.$results; // collection of returned results\n * search.total; // number of results that would've been returned without pagination\n * search.page_index; // the zero-based page index\n * search.page_size; // the number of results to be returned\n * });\n * search.$next(); // increase page_index, get the next page\n * ```\n *\n * @method searchPaged\n * @memberof Rekord.Model\n * @param {String} url -\n * A URL to send the search data to.\n * @param {searchPageOptions} [options] -\n * Options for the search.\n * @param {Object} [props] -\n * Initial set of properties on the search.\n * @param {Boolean} [run=false] -\n * Whether or not to run the search immediately.\n * @return {Rekord.SearchPaged} -\n * A new paginated search for models.\n */\n model.searchPaged = function(url, options, props, run)\n {\n return new SearchPaged( db, url, options, props, run );\n };\n});\n\naddPlugin(function(options)\n{\n var shard = options.shard || Defaults.shard;\n\n if ( !isObject( shard ) )\n {\n return;\n }\n\n options.createRest = Rekord.shard( shard );\n \n}, true );\n\naddPlugin(function(model, db, options)\n{\n var staticMethods = collapse( options.staticMethods, Defaults.staticMethods );\n\n if ( !isEmpty( staticMethods ) )\n {\n Class.props( model, staticMethods );\n }\n});\n\naddPlugin(function(model, db, options)\n{\n var time = options.timestamps || Defaults.timestamps;\n var timeFormat = collapseOption( options.timestampFormat, Defaults.timestampFormat );\n var timeType = collapseOption( options.timestampType, Defaults.timestampType );\n var timeUTC = collapseOption( options.timestampUTC, Defaults.timestampUTC );\n var timeCurrent = options.timestampCurrent || Defaults.timestampCurrent;\n\n if ( !time )\n {\n return;\n }\n\n function collapseOption(option, defaultValue)\n {\n if ( isObject( option ) && isObject( defaultValue ) )\n {\n return collapse( option, defaultValue );\n }\n\n return option || defaultValue;\n }\n\n function hasDefault(field)\n {\n return timeCurrent === true || indexOf( timeCurrent, field ) !== false;\n }\n\n function fieldSpecific(field, map)\n {\n return isObject( map ) ? map[ field ] : map;\n }\n\n function currentTimestamp(field)\n {\n var to = fieldSpecific( field, timeType );\n\n return function()\n {\n return convertDate( new Date(), to );\n };\n }\n\n function encode(x, model, field, forSaving)\n {\n var to = fieldSpecific( field, timeFormat );\n var encoded = convertDate( x, to );\n\n return encoded || x;\n }\n\n function decode(x, rawData, field)\n {\n var to = fieldSpecific( field, timeType );\n var utc = fieldSpecific( field, timeUTC );\n var decoded = convertDate( x, to, utc );\n\n return decoded || x;\n }\n\n function addTimestamp(field)\n {\n var i = indexOf( db.fields, field );\n\n if ( i === false )\n {\n db.fields.push( field );\n db.saveFields.push( field );\n }\n\n if ( hasDefault( field ) && !(field in db.defaults) )\n {\n db.defaults[ field ] = currentTimestamp( field );\n }\n\n if ( timeFormat && !(field in db.encodings) )\n {\n db.encodings[ field ] = encode;\n }\n\n if ( timeType && !(field in db.decodings ) )\n {\n db.decodings[ field ] = decode;\n }\n }\n\n function addCreatedAt(field)\n {\n addTimestamp( field );\n\n db.ignoredFields[ field ] = true;\n }\n\n function addUpdatedAt(field)\n {\n addTimestamp( field );\n\n db.ignoredFields[ field ] = true;\n\n Class.replace( model, '$save', function($save)\n {\n return function()\n {\n this[ field ] = evaluate( db.defaults[ field ] );\n\n return $save.apply( this, arguments );\n };\n });\n }\n\n function addTimestampField(type, field)\n {\n switch (type) {\n case 'created_at':\n return addCreatedAt( field );\n case 'updated_at':\n return addUpdatedAt( field );\n default:\n return addTimestamp( field );\n }\n }\n\n if ( isString( time ) )\n {\n addTimestampField( time, time );\n }\n else if ( isArray( time ) )\n {\n for (var i = 0; i < time.length; i++)\n {\n addTimestampField( time[ i ], time[ i ] );\n }\n }\n else if ( isObject( time ) )\n {\n for (var prop in time)\n {\n addTimestampField( prop, time[ prop ] );\n }\n }\n else\n {\n addCreatedAt( 'created_at' );\n addUpdatedAt( 'updated_at' );\n }\n\n});\n\nvar Timestamp = {\n Date: 'date',\n Millis: 'millis',\n Seconds: 'seconds'\n};\n\nDefaults.timestampFormat = Timestamp.Millis;\nDefaults.timestampType = Timestamp.Date;\nDefaults.timestampUTC = false;\nDefaults.timestampCurrent = ['created_at', 'updated_at'];\n\nfunction convertDate(x, to, utc)\n{\n var date = parseDate( x, utc );\n\n if ( date === false )\n {\n return false;\n }\n\n if ( !to )\n {\n return date;\n }\n\n switch (to)\n {\n case Timestamp.Date:\n return date;\n case Timestamp.Millis:\n return date.getTime();\n case Timestamp.Seconds:\n return Math.floor( date.getTime() / 1000 );\n default:\n return Rekord.formatDate( date, to );\n }\n}\n\nRekord.Timestamp = Timestamp;\nRekord.formatDate = noop;\nRekord.convertDate = convertDate;\n\nvar IGNORE_TRAITS = { traits: true };\n\naddPlugin(function(options)\n{\n var traits = options.traits || Defaults.traits;\n\n if ( !isEmpty( traits ) )\n {\n if ( isFunction( traits ) )\n {\n traits = traits( options );\n }\n\n if ( isArray( traits ) )\n {\n for (var i = 0; i < traits.length; i++)\n {\n var trait = traits[ i ];\n\n if ( isFunction( trait ) )\n {\n trait = trait( options );\n }\n\n if ( isObject( trait ) )\n {\n merge( options, trait, IGNORE_TRAITS );\n }\n else\n {\n throw 'traits are expected to be an object or a function which returns an object of methods';\n }\n }\n }\n else\n {\n throw 'traits are expected to be an array or a function which returns an array';\n }\n }\n\n}, true );\n\naddPlugin(function(model, db, options)\n{\n\n model.where = function(whereProperties, whereValue, whereEquals, out)\n {\n return db.models.where(whereProperties, whereValue, whereEquals, out);\n };\n});\n\n\n /* Classes */\n Rekord.Model = Model;\n Rekord.Database = Database;\n Rekord.Defaults = Defaults;\n Rekord.Relation = Relation;\n Rekord.Operation = Operation;\n Rekord.Search = Search;\n Rekord.SearchPaged = SearchPaged;\n Rekord.Promise = Promise;\n\n /* Keys */\n Rekord.KeyHandler = KeyHandler;\n Rekord.KeySimple = KeySimple;\n Rekord.KeyComposite = KeyComposite;\n Rekord.enableKeyChanges = enableKeyChanges;\n Rekord.disableKeyChanges = disableKeyChanges;\n\n /* Enums */\n Rekord.Cascade = Cascade;\n Rekord.Cache = Cache;\n Rekord.Store = Store;\n Rekord.Save = Save;\n Rekord.Load = Load;\n\n /* Collections */\n Rekord.Map = Map;\n Rekord.Collection = Collection;\n Rekord.FilteredCollection = FilteredCollection;\n Rekord.ModelCollection = ModelCollection;\n Rekord.FilteredModelCollection = FilteredModelCollection;\n Rekord.Page = Page;\n Rekord.Context = Context;\n\n /* Relationships */\n Rekord.HasOne = HasOne;\n Rekord.BelongsTo = BelongsTo;\n Rekord.HasMany = HasMany;\n Rekord.HasManyThrough = HasManyThrough;\n Rekord.HasRemote = HasRemote;\n Rekord.HasList = HasList;\n\n /* Projections */\n Rekord.Filters = {};\n Rekord.Projection = Projection;\n\n /* Common Functions */\n Rekord.isRekord = isRekord;\n Rekord.isDefined = isDefined;\n Rekord.isFunction = isFunction;\n Rekord.isString = isString;\n Rekord.isNumber = isNumber;\n Rekord.isBoolean = isBoolean;\n Rekord.isDate = isDate;\n Rekord.isRegExp = isRegExp;\n Rekord.isArray = isArray;\n Rekord.isObject = isObject;\n Rekord.isValue = isValue;\n Rekord.noop = noop;\n Rekord.bind = bind;\n Rekord.uuid = uuid;\n Rekord.sizeof = sizeof;\n Rekord.isEmpty = isEmpty;\n Rekord.evaluate = evaluate;\n Rekord.addPlugin = addPlugin;\n Rekord.now = now;\n Rekord.merge = merge;\n\n /* Array Functions */\n Rekord.toArray = toArray;\n Rekord.indexOf = indexOf;\n Rekord.collect = collect;\n Rekord.array = collectArray;\n Rekord.swap = swap;\n Rekord.reverse = reverse;\n Rekord.isSorted = isSorted;\n Rekord.isPrimitiveArray = isPrimitiveArray;\n\n /* Class Functions */\n Rekord.Settings = Settings;\n Rekord.Class = Class;\n Rekord.extend = Class.extend;\n Rekord.extendArray = Class.extend;\n Rekord.addMethod = Rekord.setProperty = Class.prop;\n Rekord.addMethods = Rekord.setProperties = Class.props;\n Rekord.replaceMethod = Class.replace;\n Rekord.copyConstructor = Class.copyConstructor;\n Rekord.factory = Class.factory;\n\n /* Comparator Functions */\n Rekord.Comparators = Comparators;\n Rekord.saveComparator = saveComparator;\n Rekord.addComparator = addComparator;\n Rekord.createComparator = createComparator;\n\n /* Comparison Functions */\n Rekord.equalsStrict = equalsStrict;\n Rekord.equalsWeak = equalsWeak;\n Rekord.equalsCompare = equalsCompare;\n Rekord.equals = equals;\n Rekord.compareNumbers = compareNumbers;\n Rekord.compare = compare;\n\n /* Eventful Functions */\n Rekord.addEventFunction = addEventFunction;\n Rekord.addEventful = addEventful;\n\n /* Object Functions */\n Rekord.applyOptions = applyOptions;\n Rekord.propsMatch = propsMatch;\n Rekord.hasFields = hasFields;\n Rekord.updateFieldsReturnChanges = updateFieldsReturnChanges;\n Rekord.clearFieldsReturnChanges = clearFieldsReturnChanges;\n Rekord.grab = grab;\n Rekord.pull = pull;\n Rekord.transfer = transfer;\n Rekord.collapse = collapse;\n Rekord.clean = clean;\n Rekord.cleanFunctions = cleanFunctions;\n Rekord.copy = copy;\n Rekord.diff = diff;\n\n /* Parse Functions */\n Rekord.isParseInput = isParseInput;\n Rekord.parse = parse;\n Rekord.createParser = createParser;\n Rekord.isFormatInput = isFormatInput;\n Rekord.format = format;\n Rekord.createFormatter = createFormatter;\n Rekord.parseDate = parseDate;\n\n /* Resolver Functions */\n Rekord.NumberResolvers = NumberResolvers;\n Rekord.saveNumberResolver = saveNumberResolver;\n Rekord.createNumberResolver = createNumberResolver;\n Rekord.PropertyResolvers = PropertyResolvers;\n Rekord.savePropertyResolver = savePropertyResolver;\n Rekord.createPropertyResolver = createPropertyResolver;\n\n /* String Functions */\n Rekord.toCamelCase = toCamelCase;\n Rekord.split = split;\n\n /* Where Functions */\n Rekord.Wheres = Wheres;\n Rekord.saveWhere = saveWhere;\n Rekord.createWhere = createWhere;\n Rekord.expr = expr;\n Rekord.not = not;\n Rekord.oneOf = oneOf;\n Rekord.isExpr = isExpr;\n Rekord.exprEqualsTester = exprEqualsTester;\n Rekord.exprEquals = exprEquals;\n\n /* Services */\n Rekord.Stores = Stores;\n Rekord.Lives = Lives;\n Rekord.Rests = Rests;\n\n return Rekord;\n\n}));\n"],"sourceRoot":"/source/"} \ No newline at end of file +{"version":3,"sources":["rekord.min.js"],"names":["root","factory","define","amd","module","exports","global","Rekord","this","undefined","toArray","x","delimiter","Array","isString","split","isValue","indexOf","arr","comparator","cmp","equalsStrict","i","n","length","collect","a","values","arguments","isArray","prototype","slice","call","Collection","create","collectArray","swap","k","t","reverse","half","Math","floor","isSorted","array","isPrimitiveArray","item","isObject","isDefined","isFunction","constructor","apply","isRekord","Database","Model","isNumber","isNaN","isBoolean","isDate","Date","isRegExp","RegExp","noop","bind","context","func","uuid","S4","random","toString","substring","sizeof","properties","prop","isEmpty","getTime","evaluate","avoidCopy","copy","addPlugin","callback","beforeCreation","on","Events","Options","Plugins","saveComparator","name","comparatorInput","nullsFirst","createComparator","Comparators","addComparator","second","first","b","d","charAt","parsed","isFormatInput","formatter","createFormatter","af","bf","localeCompare","isParseInput","parser","createParser","ap","bp","compare","av","bv","parsedChain","equalsWeak","equalsCompare","equals","at","bt","ar","br","test","aa","ba","compareNumbers","addEventFunction","target","functionName","events","secret","off","eventFunction","listener","result","subject","unlistener","unlistened","$methods","Class","method","EventNode","before","type","group","next","prev","addEventful","onListeners","$this","eventsInput","callbackContext","listeners","$$on","nodes","eventName","eventListeners","push","triggerId","remove","Types","Persistent","once","Once","after","After","offListeners","event","node","deleteProperty","obj","triggerListeners","args","triggerGroup","trigger","ex","Error","methods","$on","$once","$after","$off","$trigger","props","merge","dst","src","ignoreMap","adding","existing","applyOptions","options","defaults","defaultProperty","defaultValue","option","valued","optionProperty","$options","propsMatch","testFields","expected","expectedFields","equality","testProp","expectedProp","hasFields","model","fields","exists","clearFieldsReturnChanges","targetFields","changes","targetField","updateFieldsReturnChanges","source","sourceFields","targetValue","sourceField","sourceValue","grab","copyValues","grabbed","p","pull","pulledValue","pulled","transfer","from","to","collapse","clean","cleanFunctions","copyHidden","c","diff","curr","old","parse","expr","base","regex","REGEX","match","exec","format","template","parts","formatted","parseDate","utc","getUTCFullYear","getUTCMonth","getUTCDate","getUTCHours","getUTCMinutes","getUTCSeconds","saveNumberResolver","numbers","invalidValue","resolver","createNumberResolver","NumberResolvers","createPropertyResolver","parseFloat","savePropertyResolver","PropertyResolvers","propsArray","propsResolver","resolved","camelCaseReplacer","toUpperCase","toCamelCase","replace","escape","regexDelimiter","splits","ae","joined","splice","saveWhere","where","createWhere","Wheres","value","tester","exprEqualsTester","expression","exprEquals","isExpr","not","oneOf","input","AP","promise","get","isComplete","results","database","dynamic","className","classes","autoload","loadBegin","success","loadFinish","unloaded","resolve","debug","Debugs","CREATION","canCascade","cascade","batch","namesInput","operationsInput","handler","names","operations","batchID","batchHandlers","batches","modelName","modelHandler","createModelHandler","earlyModelHandler","databaseName","modelClass","db","rest","op","batchOverwrites","all","failure","class","operation","encoded","update","query","url","batchRun","clear","batchStart","batchDepth","batchEnd","batchClear","batchExecute","Gate","opened","blocked","gate","open","Defaults","keyHandler","key","KeyComposite","KeySimple","addToFields","modelsCached","models","ModelCollection","allCached","loaded","initialized","pendingRefresh","localLoaded","remoteLoaded","firstRefresh","pendingOperations","afterOnline","saveFields","readyPromise","Promise","contextIndex","prepare","createRest","store","createStore","live","createLive","setComparator","comparatorNullsFirst","setRevision","revision","setSummarize","summarize","relations","relationNames","relationType","Relations","RelationClass","Relation","relationMap","relationOptions","relation","discriminator","init","save","projectionName","projections","Projection","defaultEncode","data","forSaving","encodings","defaultDecode","rawData","decodings","defaultSummarize","$key","defaultCreateRest","defaultRest","defaultCreateStore","defaultStore","defaultCreateLive","defaultLive","defaultResolveModel","response","defaultResolveModels","createModelPromise","restSuccess","restFailure","restOffline","localSuccess","localFailure","Cascade","Rest","off1","off2","off3","status","reject","noline","Local","Map","keys","indices","Dependents","map","addProjection","Context","databases","alls","add","KeyHandler","addAll","Page","collection","pageSize","pageIndex","onChanges","handleChanges","pageCount","setCollection","FilteredCollection","filter","remoteData","FilteredModelCollection","RelationCollection","relator","DiscriminateCollection","discriminatorsToModel","clone","buildKeyFromInput","parseModel","cloneEmpty","discriminatedValue","Search","run","$init","SearchPaged","executor","cancelable","Status","Pending","nexts","cancel","Operation","GetLocal","reset","GetRemote","RemoveCache","RemoveLocal","RemoveNow","RemoveRemote","SaveLocal","SaveNow","SaveRemote","RelationSingle","RelationMultiple","BelongsTo","HasOne","HasMany","HasManyThrough","HasRemote","HasList","HasReference","Shard","addDynamicProperty","modelPrototype","property","definition","set","Object","defineProperty","configurable","enumerable","lastCalculatedValue","handleChange","current","Changes","parseEventListeners","out","when","invoke","eventType","subcallback","applyEventListeners","l","isFilesSupported","win","File","FileReader","FileList","toFile","Blob","convertNone","convertBase64","trySave","autoSave","$isSaved","$save","putFileCache","file","$files","user","setFilesValue","processor","done","valueToUser","fileReader","converter","fileProcessors","FilesNotSupported","reader","onload","e","FILE","setter","FileOffline","FileEncoder","field","cached","fileProperties","RemoteSave","$addOperation","mapKeyChangeListener","oldKey","newKey","index","mapKeyChangePut","Map_put","$db","keyChanges","KeyChange","mapKeyChangeRemove","removeAt","enableKeyChanges","disableKeyChanges","Map_remove","convertDate","date","Timestamp","Millis","Seconds","formatDate","window","construct","build","extend","parent","override","parentCopy","copyConstructor","instanceFactory","nativeArray","Settings","parentInstance","code","DynamicClass","Function","propThis","methodThis","replaceThis","methodName","writable","propertyName","methodFactory","existingMethod","F","now","hasType","isAfter","RekordSettings","document","currentScript","script","getAttribute","loadPromise","load","onLoadFinish","loadedSuccess","loading","promises","removeListeners","failOnPendingChanges","hasPending","singularity","unload","check","Initialized","Online","Offline","Cache","None","All","NoLive","Live","NoRest","Remote","Load","Lazy","Both","RestStatus","Conflict","409","NotFound","404","410","0","Save","Key","Keys","Store","setDebug","overwrite","debugSet","REST","AUTO_REFRESH","MISSING_KEY","REMOTE_UPDATE","REMOTE_CREATE","REMOTE_REMOVE","REMOTE_LOAD","REMOTE_LOAD_OFFLINE","REMOTE_LOAD_ERROR","REMOTE_LOAD_REMOVE","REMOTE_LOAD_RESUME","LOCAL_LOAD","LOCAL_RESUME_DELETE","LOCAL_RESUME_SAVE","LOCAL_LOAD_SAVED","REALTIME_SAVE","REALTIME_REMOVE","SAVE_VALUES","SAVE_PUBLISH","SAVE_CONFLICT","SAVE_UPDATE_FAIL","SAVE_ERROR","SAVE_OFFLINE","SAVE_RESUME","SAVE_REMOTE","SAVE_DELETED","SAVE_OLD_REVISION","SAVE_LOCAL","SAVE_LOCAL_ERROR","SAVE_LOCAL_DELETED","SAVE_LOCAL_BLOCKED","SAVE_REMOTE_DELETED","SAVE_REMOTE_BLOCKED","REMOVE_PUBLISH","REMOVE_LOCAL","REMOVE_MISSING","REMOVE_ERROR","REMOVE_OFFLINE","REMOVE_RESUME","REMOVE_REMOTE","REMOVE_CANCEL_SAVE","REMOVE_LOCAL_ERROR","REMOVE_LOCAL_BLOCKED","REMOVE_LOCAL_NONE","REMOVE_LOCAL_UNSAVED","REMOVE_REMOTE_BLOCKED","GET_LOCAL_SKIPPED","GET_LOCAL","GET_LOCAL_ERROR","GET_REMOTE","GET_REMOTE_ERROR","ONLINE","OFFLINE","PUBSUB_CREATED","HASONE_INIT","HASONE_NINJA_REMOVE","HASONE_INITIAL_PULLED","HASONE_INITIAL","HASONE_CLEAR_MODEL","HASONE_SET_MODEL","HASONE_PRESAVE","HASONE_POSTREMOVE","HASONE_CLEAR_KEY","HASONE_UPDATE_KEY","HASONE_LOADED","HASONE_QUERY","HASONE_QUERY_RESULTS","BELONGSTO_INIT","BELONGSTO_NINJA_REMOVE","BELONGSTO_NINJA_SAVE","BELONGSTO_INITIAL_PULLED","BELONGSTO_INITIAL","BELONGSTO_CLEAR_MODEL","BELONGSTO_SET_MODEL","BELONGSTO_POSTREMOVE","BELONGSTO_CLEAR_KEY","BELONGSTO_UPDATE_KEY","BELONGSTO_LOADED","BELONGSTO_QUERY","BELONGSTO_QUERY_RESULTS","HASREFERENCE_INIT","HASREFERENCE_NINJA_REMOVE","HASREFERENCE_INITIAL_PULLED","HASREFERENCE_INITIAL","HASREFERENCE_CLEAR_MODEL","HASREFERENCE_SET_MODEL","HASREFERENCE_CLEAR_KEY","HASREFERENCE_UPDATE_KEY","HASREFERENCE_LOADED","HASREFERENCE_QUERY","HASREFERENCE_QUERY_RESULTS","HASMANY_INIT","HASMANY_NINJA_REMOVE","HASMANY_NINJA_SAVE","HASMANY_INITIAL","HASMANY_INITIAL_PULLED","HASMANY_REMOVE","HASMANY_SORT","HASMANY_ADD","HASMANY_LAZY_LOAD","HASMANY_INITIAL_GRABBED","HASMANY_NINJA_ADD","HASMANY_AUTO_SAVE","HASMANY_PREREMOVE","HASMANY_POSTSAVE","HASMANY_QUERY","HASMANY_QUERY_RESULTS","HASMANY_UPDATE_KEY","HASMANYTHRU_INIT","HASMANYTHRU_NINJA_REMOVE","HASMANYTHRU_NINJA_SAVE","HASMANYTHRU_NINJA_THRU_REMOVE","HASMANYTHRU_INITIAL","HASMANYTHRU_INITIAL_PULLED","HASMANYTHRU_REMOVE","HASMANYTHRU_SORT","HASMANYTHRU_ADD","HASMANYTHRU_LAZY_LOAD","HASMANYTHRU_INITIAL_GRABBED","HASMANYTHRU_NINJA_ADD","HASMANYTHRU_AUTO_SAVE","HASMANYTHRU_PREREMOVE","HASMANYTHRU_POSTSAVE","HASMANYTHRU_THRU_ADD","HASMANYTHRU_THRU_REMOVE","HASMANYTHRU_QUERY","HASMANYTHRU_QUERY_RESULTS","HASMANYTHRU_UPDATE_KEY","HASREMOTE_INIT","HASREMOTE_SORT","HASREMOTE_NINJA_REMOVE","HASREMOTE_NINJA_SAVE","HASREMOTE_QUERY","HASREMOTE_QUERY_RESULTS","HASLIST_INIT","HASLIST_SORT","HASLIST_NINJA_REMOVE","HASLIST_NINJA_SAVE","HASLIST_REMOVE","HASLIST_ADD","HASLIST_INITIAL","Lives","Default","setLive","liveSet","isOnline","navigator","onLine","online","forceOffline","setOnline","setOffline","listenToNetworkStatus","addEventListener","body","ononline","onoffline","checkNetworkStatus","Rests","setRest","restSet","Stores","put","record","records","setStore","storeSet","NoLoad","RemoteLoad","LocalLoad","Updated","ModelAdded","ModelUpdated","ModelRemoved","OperationsStarted","OperationsFinished","Loads","keySeparator","ignoredFields","publishAlways","saveAlways","traits","allComplete","loadRelations","autoRefresh","cache","fullSave","fullPublish","noReferences","allOptions","fetchOptions","getOptions","updateOptions","createOptions","saveOptions","removeOptions","queryOptions","prune","active","max","keepAlive","removeLocal","encode","decode","resolveModel","resolveModels","setStoreEnabled","enabled","storeDisabled","setRestEnabled","restDisabled","setLiveEnabled","liveDisabled","ready","persistent","clearAll","contains","$isPending","hasData","saving","grabModel","checkModel","hasLoad","missingModel","lazyLoad","buildObjectFromKey","RemoteGets","$set","$refresh","hasRemote","buildKeyFromRelations","putRemoteData","instantiate","updated","sort","revisionFunction","addReference","getKey","decoded","revisionRejected","hasKeyChange","$setKey","$saved","conflicts","conflicted","previous","saved","notReallySaved","compareTo","currentValue","savedValue","$local","PartialUpdate","FullUpdate","RemoteUpdate","has","saveReference","createModel","$toJSON","$status","$invalid","destroyModel","modelKey","pruneModel","RemoteAndRemove","removeReference","hasPruning","pruneModels","youngestAllowed","$remove","isTooYoung","$touched","youngest","minModel","eachWhere","destroyLocalUncachedModel","$hasChanges","removeKey","Detach","destroyLocalCachedModel","removedValue","destroyLocalModel","RemovePending","SavePending","refresh","onLoaded","onLocalLoad","Removed","triggerLoad","onLocalError","loadNone","onOnline","loadEvent","additionalParameters","concat","onOperationRest","handleRefreshSuccess","mapped","handleRefreshFailure","onRefreshOnline","executeRefresh","complete","isValid","filtered","hasTrait","trait","hasTraits","liveSave","liveRemove","delaySort","$isDeleted","UpdateAndSave","CreateAndSave","removeFromModels","Created","Saved","PreSave","PostSave","PreRemove","PostRemove","Change","KeyUpdate","RelationUpdate","LocalSave","LocalSaveFailure","LocalSaves","RemoteSaveFailure","RemoteSaveOffline","RemoteSaves","LocalRemove","LocalRemoveFailure","LocalRemoves","RemoteRemove","RemoteRemoveFailure","RemoteRemoveOffline","RemoteRemoves","LocalGet","LocalGetFailure","LocalGets","RemoteGet","RemoteGetFailure","RemoteGetOffline","SavedRemoteUpdate","Synced","Blocked","valueOf","$operation","$relations","$dependents","$savedState","$reset","databaseRelations","lazy","$getRelation","$load","def","keyFields","evaluatedValue","$$key","hasRelation","avoidChange","$hasRelation","$get","$decode","$sync","removeUnrelated","sync","$relate","relate","$unrelate","unrelated","unrelate","$isRelated","related","isRelated","initialValue","setProperties","setValue","$hasKey","$touch","$exists","$autoRefresh","callRefresh","$cancel","$clone","f","cloneKey","relationName","preClone","relationValues","postClone","$push","$pop","dontDiscard","$discard","OperationType","queue","execute","$changed","$updated","quietly","$keys","getKeys","$uid","skipApplication","applyKey","$remote","$isSynced","$isSaving","$isSavedLocally","$isNew","$project","projectionInput","projection","project","$getChanges","alreadyDecoded","local","ignore","$hasChange","decoder","$listenForOnline","$offline","$resume","$resumeCascade","$resumeOptions","JSON","stringify","lastValue","pop","lastKey","size","subtract","dest","v","rebuildIndex","partition","left","right","pivot","j","qsort","toObject","handleKeyChange","prefix","updateForeignKey","isSaved","callbackOnSaved","contextOnSaved","dependents","onDependentSave","uid","dependent","alias","aliasIndex","ALIAS_DELIMITER","word","words","tokens","types","resolvers","processWord","token","TOKEN_HANDLER","unshift","post","processToken","pre","TOKENS",".","?","|","#","(",")","[","]","{",":","}","@","=","sourceType","fieldIndex","filterName","Filters","resolveName","whereName","whereFrom","aggregateProperty","aggregateFunction","aggregateFrom","subEnd","pluckValueEnd","pluck","pluckObjectEnd","originalInput","start","getApplied","applied","each","applyDatabase","discard","discardDatabase","destroy","destroyDatabase","iterator","dbs","buildKey","join","otherFields","akey","bkey","copyFields","inKey","setKeyField","Add","Adds","Sort","Remove","Removes","Updates","Reset","Cleared","ignorePrimitive","page","whereProperties","whereValue","whereEquals","intersect","complement","insertAt","removed","shift","removing","element","removeAll","removedIndices","removeWhere","deleteCount","startingValue","min","maxModel","compareFunction","firstWhere","lastWhere","last","aggregate","validator","process","getResult","sum","avg","total","countWhere","met","count","valuesResolver","keysResolver","reduce","reducer","chunk","chunkSize","outer","outerIndex","inner","innerIndex","grouping","by","having","havingValue","havingEquals","select","groupings","grouped","groupArray","propName","aggregator","track","$group","$count","Filtering","onAdd","handleAdd","onAdds","handleAdds","onRemove","handleRemove","onRemoves","handleRemoves","onReset","handleReset","onUpdates","handleUpdates","onCleared","handleCleared","disconnect","connect","setFilter","matches","updates","setPageSize","setPageIndex","goto","actualIndex","jump","pages","ceil","can","canFirst","canPrev","canLast","canNext","forceApply","end","more","limit","offset","desiredEnd","actualEnd","cleared","inputs","rebuild","addingKeys","callRemove","avoidSave","updateWhere","pushWhere","pushIt","popWhere","popIt","discardWhere","discardIt","cancelWhere","cancelIt","refreshWhere","refreshIt","saveIt","hasChanges","getChanges","cloneModels","cloneProperties","onModelUpdated","handleModelUpdate","unrelateWhere","$getDefaults","$append","$url","$results","$search","$promise","$run","$unset","$encode","$handleSuccess","$handleFailure","isPending","offline","$clear","$change","change","page_size","page_index","$goto","dontRun","$getPageIndex","$getPageCount","desired","$setPageIndex","$more","$onMoreEnd","$first","$last","$prev","$next","$total","$getTotal","$pages","$page","$can","$canFirst","$canPrev","$canLast","$canNext","$updatePageSize","$updatePageIndex","$updateTotal","$decodeResults","$setPageSize","$getPageSize","$getPageOffset","$setTotal","Success","Failure","Canceled","Unsuccessful","Complete","iterable","handleSuccess","successes","goal","then","race","reason","promiseComplete","promiseCount","singularityResult","bindPromise","consuming","promiseOrContext","contextOrCallback","callbackOrNull","finish","canceled","addNext","unsuccessful","clearListeners","listenFor","immediate","handleEvents","isSuccess","isUnsuccessful","isFailure","catch","isOffline","isCanceled","finished","cascading","actual","notCascade","interrupts","tryNext","setNext","insertNext","onSuccess","handleFailure","onFailure","previousValue","finishRemove","$saving","$publish","notLive","markSaving","remote","grabAlways","publish","always","changesCopy","clearLocal","markSynced","tryAgain","handleData","saveNow","clearPending","auto","autoCascade","autoOptions","preserve","clearKey","discriminators","discriminatorToModel","debugQuery","debugQueryResults","getDefaults","discriminated","Polymorphic","setReferences","onInitialized","setModelReference","rekord","finishInitialization","mode","getStoredArray","getStored","listenToModelAdded","executeQuery","queryOption","queryData","search","handleExecuteQuery","createRelationCollection","createCollection","initial","grabInitial","grabModels","pending","setProperty","dynamicSet","lastRelated","isModelArray","relatedDatabase","relatedKey","clearFields","updateFields","getTargetFields","getSourceFields","targetKey","targetKeyHandler","debugUpdateKey","targetNewKey","clearForeignKey","debugClearKey","relateds","stored","debugInit","debugClearModel","debugSetModel","debugLoaded","clearModel","setRelated","clearRelated","setModel","dontClear","onSaved","onRemoved","dirty","isDependent","handleModel","ignoreLoaded","isRelatedFactory","modelDatabase","foreign","debugAutoSave","debugInitialGrabbed","debugSort","bulk","addModel","checkSave","delaySorting","delaySaving","given","removeModel","canRemoveRelated","saveParentCascade","saveParentOptions","belongsTo","cascadeRemoveOptions","postRemove","onKeyUpdate","relatedValue","modelFields","relatedFields","hasOne","saveCascade","child","preSave","populateInitial","relatedClone","hasMany","listenForRelated","loadRelated","cascadeRemove","cascadeSave","cascadeSaveOptions","onChange","postSave","preRemove","handleModelAdded","handleLazyLoad","onRelated","existingModel","relatedClones","hasManyThrough","through","cascadeSaveRelated","cascadeSaveRelatedOptions","cascadeRemoveThroughOptions","setThrough","throughDatabase","throughs","onThroughRemoved","removeModelFromThrough","existingThrough","HASMANYTHRU_PRESAVE","addModelFromThrough","finishAddModel","addThrough","throughKey","createThroughKey","onAddThrough","finishAddThrough","onAddModelFromThrough","added","actualRelated","removeThrough","finishRemoveRelated","keyObject","finishRemoveThrough","modelKeys","relatedKeys","onRefresh","hasList","hasReference","isRelatedDiscriminatedFactory","loadDiscriminators","getDiscriminatorForModel","discriminatorField","onLoad","handleLoaded","setDiscriminated","getDiscriminatorDatabase","discriminatorValue","ownsForeignKey","getDiscriminator","shard","initialize","STATUS_FAIL_ALL","STATUS_FAIL_GET","STATUS_FAIL_CREATE","STATUS_FAIL_UPDATE","STATUS_FAIL_REMOVE","STATUS_FAIL_QUERY","ATOMIC_ALL","ATOMIC_GET","ATOMIC_CREATE","ATOMIC_UPDATE","ATOMIC_REMOVE","ATOMIC_QUERY","getShards","forRead","getShardForModel","getShardsForModel","single","getShardsForQuery","onShardSuccess","onShardFailure","onComplete","successful","alreadyFailed","failedStatus","shards","multiplex","gotten","returned","atomic","onShardComplete","failureCalled","boot","instance","dynamics","modelEvents","databaseEvents","databaseEventString","modelEventString","tryOverwrite","edb","tryMerge","dbo","edbo","tryUnshift","sourceOptions","eoptions","relationCopy","fetch","fetchAll","files","fieldOption","FileDecodings","FileTooLarge","FileWrongType","addFileProcessor","text","dataURL","base64","resource","capacity","fileToValue","find","findOrCreate","created","grabAll","persist","searchAt","paging","searchPaged","staticMethods","collapseOption","hasDefault","timeCurrent","fieldSpecific","currentTimestamp","timeType","timeFormat","timeUTC","addTimestamp","addCreatedAt","addUpdatedAt","addTimestampField","time","timestamps","timestampFormat","timestampType","timestampUTC","timestampCurrent","IGNORE_TRAITS","extendArray","addMethod","addMethods","replaceMethod"],"mappings":"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","file":"rekord.min.js","sourcesContent":["/* rekord 1.5.8 - A javascript REST ORM that is offline and real-time capable http://rekord.github.io/rekord/ by Philip Diffenderfer */\n// UMD (Universal Module Definition)\n(function (root, factory)\n{\n if (typeof define === 'function' && define.amd) // jshint ignore:line\n {\n // AMD. Register as an anonymous module.\n define('rekord', [], function() { // jshint ignore:line\n return factory(root);\n });\n }\n else if (typeof module === 'object' && module.exports) // jshint ignore:line\n {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory(global); // jshint ignore:line\n }\n else\n {\n // Browser globals (root is window)\n root.Rekord = factory(root);\n }\n}(this, function(global, undefined)\n{\n\n var win = typeof window !== 'undefined' ? window : global; // jshint ignore:line\n\n\nvar AP = Array.prototype;\n\n/**\n * Converts the given variable to an array of strings. If the variable is a\n * string it is split based on the delimiter given. If the variable is an\n * array then it is returned. If the variable is any other type it may result\n * in an error.\n *\n * ```javascript\n * Rekord.toArray([1, 2, 3]); // [1, 2, 3]\n * Rekord.toArray('1,2,3', ','); // ['1', '2', '3']\n * Rekord.toArray(1); // [1]\n * Rekord.toArray(null); // []\n * ```\n *\n * @memberof Rekord\n * @param {String|String[]} x\n * The variable to convert to an Array.\n * @param {String} [delimiter]\n * The delimiter to split if the given variable is a string.\n * @return {String[]} -\n * The array of strings created.\n */\nfunction toArray(x, delimiter)\n{\n if ( x instanceof Array )\n {\n return x;\n }\n if ( isString( x ) )\n {\n return x.split( delimiter );\n }\n if ( isValue( x ) )\n {\n return [ x ];\n }\n\n return [];\n}\n\n/**\n * Finds the index of a variable in an array optionally using a custom\n * comparison function. If the variable is not found in the array then `false`\n * is returned.\n *\n * ```javascript\n * Rekord.indexOf([1, 2, 3], 1); // 0\n * Rekord.indexOf([1, 2, 3], 4); // false\n * Rekord.indexOf([1, 2, 2], 2); // 1\n * ```\n *\n *\n * @memberof Rekord\n * @param {Array} arr\n * The array to search through.\n * @param {Any} x\n * The variable to search for.\n * @param {Function} [comparator]\n * The function to use which compares two values and returns a truthy\n * value if they are considered equivalent. If a comparator is not given\n * then strict comparison is used to determine equivalence.\n * @return {Number|Boolean} -\n * The index in the array the variable exists at, otherwise false if\n * the variable wasn't found in the array.\n */\nfunction indexOf(arr, x, comparator)\n{\n var cmp = comparator || equalsStrict;\n\n for (var i = 0, n = arr.length; i < n; i++)\n {\n if ( cmp( arr[i], x ) )\n {\n return i;\n }\n }\n\n return false;\n}\n\n/**\n * Returns an instance of {@link Rekord.Collection} with the initial values\n * passed as arguments to this function.\n *\n * ```javascript\n * Rekord.collect(1, 2, 3, 4);\n * Rekord.collect([1, 2, 3, 4]); // same as above\n * Rekord.collect();\n * Rekord.collect([]); // same as above\n * ```\n *\n * @memberof Rekord\n * @param {Any[]|...Any} a\n * The initial values in the collection. You can pass an array of values\n * or any number of arguments.\n * @return {Rekord.Collection} -\n * A newly created instance containing the given values.\n */\nfunction collect(a)\n{\n var values = arguments.length > 1 || !isArray(a) ? Array.prototype.slice.call( arguments ) : a;\n\n return Collection.create( values );\n}\n\n/**\n * Returns an instance of {@link Rekord.Collection} with the initial values\n * passed as arguments to this function.\n *\n * ```javascript\n * Rekord.collectArray(1, 2, 3, 4);\n * Rekord.collectArray([1, 2, 3, 4]); // same as above\n * Rekord.collectArray();\n * Rekord.collectArray([]); // same as above\n * ```\n *\n * @memberof Rekord\n * @param {Any[]|...Any} a\n * The initial values in the collection. You can pass an array of values\n * or any number of arguments.\n * @return {Rekord.Collection} -\n * A newly created instance containing the given values.\n */\nfunction collectArray(a)\n{\n var values = arguments.length > 1 || !isArray(a) ? Array.prototype.slice.call( arguments ) : a;\n\n return Collection.native( values );\n}\n\nfunction swap(a, i, k)\n{\n var t = a[ i ];\n a[ i ] = a[ k ];\n a[ k ] = t;\n}\n\nfunction reverse(arr)\n{\n var n = arr.length;\n var half = Math.floor( n / 2 );\n\n for (var i = 0; i < half; i++)\n {\n swap( arr, n - i - 1, i );\n }\n\n return arr;\n}\n\nfunction isSorted(comparator, array)\n{\n if ( !comparator )\n {\n return true;\n }\n\n for (var i = 0, n = array.length - 1; i < n; i++)\n {\n if ( comparator( array[ i ], array[ i + 1 ] ) > 0 )\n {\n return false;\n }\n }\n\n return true;\n}\n\nfunction isPrimitiveArray(array)\n{\n for (var i = 0; i < array.length; i++)\n {\n var item = array[i];\n\n if ( isValue( item ) )\n {\n return !isObject( item );\n }\n }\n\n return true;\n}\n\n\n// Class.create( construct, methods )\n// Class.extend( parent, construct, override )\n// Class.prop( target, name, value )\n// Class.props( target, properties )\n// Class.method( construct, methodName, method )\n// Class.method( construct, methods )\n// Class.replace( construct, methodName, methodFactory(super) )\n\n// constructor.create( ... )\n// constructor.native( ... ) // for arrays\n// constructor.$constuctor\n// constructor.prototype.$super\n// constructor.$methods\n// constructor.$prop( name, value ) // add to prototype\n// constructor.$method( methodName, method ) // add to prototype\n// constructor.$replace( methodName, methodFactory(super) )\n\nvar Class =\n{\n\n create: function( construct, methods )\n {\n Class.prop( construct, 'create', Class.factory( construct ) );\n Class.build( construct, methods, noop );\n },\n\n extend: function( parent, construct, override )\n {\n var methods = collapse( override, parent.$methods );\n var parentCopy = Class.copyConstructor( parent );\n\n construct.prototype = new parentCopy();\n\n var instanceFactory = Class.factory( construct );\n\n if ( Class.isArray( parent ) )\n {\n var nativeArray = function()\n {\n var arr = [];\n Class.props( arr, methods );\n construct.apply( arr, arguments );\n return arr;\n };\n\n Class.prop( construct, 'native', nativeArray );\n Class.prop( construct, 'create', Settings.nativeArray ? nativeArray : instanceFactory );\n }\n else\n {\n Class.prop( construct, 'create', instanceFactory );\n }\n\n Class.build( construct, methods, parent );\n },\n\n dynamic: function(parent, parentInstance, className, code)\n {\n var DynamicClass = new Function('return function ' + className + code)(); // jshint ignore:line\n\n DynamicClass.prototype = parentInstance;\n\n Class.build( DynamicClass, {}, parent );\n\n return DynamicClass;\n },\n\n build: function(construct, methods, parent)\n {\n Class.prop( construct, '$methods', methods );\n Class.prop( construct, '$prop', Class.propThis );\n Class.prop( construct, '$method', Class.methodThis );\n Class.prop( construct, '$replace', Class.replaceThis );\n Class.prop( construct.prototype, '$super', parent );\n Class.prop( construct.prototype, 'constructor', construct );\n Class.props( construct.prototype, methods );\n },\n\n isArray: function( construct )\n {\n return Array === construct || construct.prototype instanceof Array;\n },\n\n method: function( construct, methodName, method )\n {\n if (construct.$methods)\n {\n construct.$methods[ methodName ] = method;\n }\n\n Class.prop( construct.prototype, methodName, method );\n },\n\n methodThis: function( methodName, method )\n {\n Class.method( this, methodName, method );\n },\n\n methods: function( construct, methods )\n {\n for (var methodName in methods)\n {\n Class.method( construct, methodName, methods[ methodName ] );\n }\n },\n\n prop: (function()\n {\n if (Object.defineProperty)\n {\n return function( target, property, value )\n {\n Object.defineProperty( target, property, {\n configurable: true,\n enumerable: false,\n writable: true,\n value: value\n });\n };\n }\n else\n {\n return function( target, property, value )\n {\n target[ property ] = value;\n };\n }\n })(),\n\n propThis: function( property, value )\n {\n Class.prop( this.prototype, property, value );\n },\n\n props: function( target, properties )\n {\n for (var propertyName in properties)\n {\n Class.prop( target, propertyName, properties[ propertyName ] );\n }\n },\n\n replace: function( target, methodName, methodFactory )\n {\n var existingMethod = target.prototype[ methodName ] || target[ methodName ] || noop;\n\n Class.method( target, methodName, methodFactory( existingMethod ) );\n },\n\n replaceThis: function( methodName, methodFactory )\n {\n Class.replace( this, methodName, methodFactory );\n },\n\n copyConstructor: function(construct)\n {\n function F()\n {\n\n }\n\n F.prototype = construct.prototype;\n\n return F;\n },\n\n factory: function(construct)\n {\n function F(args)\n {\n construct.apply( this, args );\n }\n\n F.prototype = construct.prototype;\n\n return function()\n {\n return new F( arguments );\n };\n }\n\n};\n\n\n/**\n * Determines whether the given variable is defined.\n *\n * ```javascript\n * Rekord.isDefined(); // false\n * Rekord.isDefined(0); // true\n * Rekord.isDefined(true); // true\n * Rekord.isDefined(void 0); // false\n * Rekord.isDefined(undefined); // false\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is defined, otherwise false.\n */\nfunction isDefined(x)\n{\n return x !== undefined;\n}\n\n/**\n * Determines whether the given variable is a function.\n *\n * ```javascript\n * Rekord.isFunction(); // false\n * Rekord.isFunction(parseInt); // true\n * Rekord.isFunction(2); // false\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is a function, otherwise false.\n */\nfunction isFunction(x)\n{\n return !!(x && x.constructor && x.call && x.apply);\n}\n\n/**\n * Determines whether the given variable is a Rekord object. A Rekord object is a\n * constructor for a model and also has a Database variable. A Rekord object is\n * strictly created by the Rekord function.\n *\n * ```javascript\n * var Task = Rekord({\n * name: 'task',\n * fields: ['name', 'done', 'finished_at', 'created_at', 'assigned_to']\n * });\n * Rekord.isRekord( Task ); // true\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is a Rekord object, otherwise false.\n */\nfunction isRekord(x)\n{\n return !!(x && x.Database && isFunction( x ) && x.prototype instanceof Model);\n}\n\n/**\n * Determines whether the given variable is a string.\n *\n * ```javascript\n * Rekord.isString(); // false\n * Rekord.isString('x'): // true\n * Rekord.isString(1); // false\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is a string, otherwise false.\n */\nfunction isString(x)\n{\n return typeof x === 'string';\n}\n\n/**\n * Determines whether the given variable is a valid number. NaN and Infinity are\n * not valid numbers.\n *\n * ```javascript\n * Rekord.isNumber(); // false\n * Rekord.isNumber('x'): // false\n * Rekord.isNumber(1); // true\n * Rekord.isNumber(NaN); // false\n * Rekord.isNumber(Infinity); // true\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is a valid number, otherwise false.\n */\nfunction isNumber(x)\n{\n return typeof x === 'number' && !isNaN(x);\n}\n\n/**\n * Determines whether the given variable is a boolean value.\n *\n * ```javascript\n * Rekord.isBoolean(); // false\n * Rekord.isBoolean('x'): // false\n * Rekord.isBoolean(1); // false\n * Rekord.isBoolean(true); // true\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is a boolean value, otherwise false.\n */\nfunction isBoolean(x)\n{\n return typeof x === 'boolean';\n}\n\n/**\n * Determines whether the given variable is an instance of Date.\n *\n * ```javascript\n * Rekord.isDate(); // false\n * Rekord.isDate('x'): // false\n * Rekord.isDate(1); // false\n * Rekord.isDate(true); // false\n * Rekord.isDate(new Date()); // true\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is an instance of Date, otherwise false.\n */\nfunction isDate(x)\n{\n return x instanceof Date;\n}\n\n/**\n * Determines whether the given variable is an instance of RegExp.\n *\n * ```javascript\n * Rekord.isRegExp(); // false\n * Rekord.isRegExp('x'): // false\n * Rekord.isRegExp(1); // false\n * Rekord.isRegExp(true); // false\n * Rekord.isRegExp(/[xyz]/); // true\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is an instance of RegExp, otherwise false.\n */\nfunction isRegExp(x)\n{\n return x instanceof RegExp;\n}\n\n/**\n * Determines whether the given variable is an instance of Array.\n *\n * ```javascript\n * Rekord.isArray(); // false\n * Rekord.isArray('x'): // false\n * Rekord.isArray(1); // false\n * Rekord.isArray([]); // true\n * Rekord.isArray(Rekord.collect(1, 2, 3)); // true\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is an instance of Array, otherwise false.\n */\nfunction isArray(x)\n{\n return x instanceof Array;\n}\n\n/**\n * Determines whether the given variable is a non-null object. As a note,\n * Arrays are considered objects.\n *\n * ```javascript\n * Rekord.isObject(); // false\n * Rekord.isObject('x'): // false\n * Rekord.isObject(1); // false\n * Rekord.isObject([]); // true\n * Rekord.isObject({}); // true\n * Rekord.isObject(null); // false\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is a non-null object, otherwise false.\n */\nfunction isObject(x)\n{\n return x !== null && typeof x === 'object';\n}\n\n/**\n * Determines whether the given variable is not null and is not undefined.\n *\n * ```javascript\n * Rekord.isValue(); // false\n * Rekord.isValue('x'): // true\n * Rekord.isValue(1); // true\n * Rekord.isValue([]); // true\n * Rekord.isValue({}); // true\n * Rekord.isValue(null); // false\n * Rekord.isValue(void 0); // false\n * Rekord.isValue(undefined); // false\n * ```\n *\n * @memberof Rekord\n * @param {Any} x\n * The variable to test.\n * @return {Boolean} -\n * True if the variable is non-null and not undefined.\n */\nfunction isValue(x)\n{\n return !!(x !== undefined && x !== null);\n}\n\n/**\n * A function that doesn't perform any operations.\n *\n * @memberof Rekord\n */\nfunction noop()\n{\n\n}\n\n/**\n * Returns the given function with the given context (`this`). This also has the\n * benefits of returning a \"copy\" of the function which makes it ideal for use\n * in listening on/once events and off events.\n *\n * ```javascript\n * var context = {};\n * var func = Rekord.bind( context, function(x) {\n * this.y = x * 2;\n * });\n * func( 4 );\n * context.y; // 8\n * ```\n *\n * @memberof Rekord\n * @param {Object} context\n * The value of `this` for the given function.\n * @param {Function}\n * The function to invoke with the given context.\n * @return {Function} -\n * A new function which is a copy of the given function with a new context.\n */\nfunction bind(context, func)\n{\n return function bindedFunction()\n {\n return func.apply( context, arguments );\n };\n}\n\n/**\n * Generates a UUID using the random number method.\n *\n * @memberof Rekord\n * @return {String} -\n * The generated UUID.\n */\nfunction uuid()\n{\n return (S4()+S4()+\"-\"+S4()+\"-\"+S4()+\"-\"+S4()+\"-\"+S4()+S4()+S4());\n}\n\nfunction S4()\n{\n return (((1+Math.random())*0x10000)|0).toString(16).substring(1);\n}\n\nvar now = Date.now || function()\n{\n return new Date().getTime();\n};\n\nfunction sizeof(x)\n{\n if ( isArray(x) || isString(x) )\n {\n return x.length;\n }\n else if ( isObject(x) )\n {\n var properties = 0;\n\n for (var prop in x) // jshint ignore:line\n {\n properties++;\n }\n\n return properties;\n }\n else if ( isNumber( x ) )\n {\n return x;\n }\n\n return 0;\n}\n\nfunction isEmpty(x)\n{\n if (x === null || x === undefined || x === 0)\n {\n return true;\n }\n if (isArray(x) || isString(x))\n {\n return x.length === 0;\n }\n if (isDate(x))\n {\n return x.getTime() === 0 || isNaN( x.getTime() );\n }\n if (isObject(x))\n {\n for (var prop in x) // jshint ignore:line\n {\n return false;\n }\n\n return true;\n }\n\n return false;\n}\n\nfunction evaluate(x, avoidCopy, context)\n{\n if ( !isValue( x ) )\n {\n return x;\n }\n\n if ( isRekord( x ) )\n {\n return new x();\n }\n if ( isFunction( x ) )\n {\n return context ? x.apply( context ) : x();\n }\n\n return avoidCopy ? x : copy( x );\n}\n\nfunction addPlugin( callback, beforeCreation )\n{\n if ( beforeCreation )\n {\n return Rekord.on( Rekord.Events.Options, callback ); // (options)\n }\n else\n {\n return Rekord.on( Rekord.Events.Plugins, callback ); // (model, db, options)\n }\n}\n\n\n /**\n * A string, a function, or an array of mixed values.\n *\n * ```javascript\n * 'age' // age property of an object\n * '-age' // age property of an object, ordering reversed\n * function(a, b) {} // a function which compares two values\n * ['age', 'done'] // age property of an object, and when equal, the done value\n * 'creator.name' // name sub-property of creator property\n * '{creator.name}, {age}' // formatted string\n * ```\n *\n * @typedef {String|comparisonCallback|Array} comparatorInput\n */\n\n\nvar Comparators = {};\n\nfunction saveComparator(name, comparatorInput, nullsFirst)\n{\n var comparator = createComparator( comparatorInput, nullsFirst );\n\n Comparators[ name ] = comparator;\n\n return comparator;\n}\n\nfunction addComparator(second, comparatorInput, nullsFirst)\n{\n var first = createComparator( comparatorInput, nullsFirst );\n\n if ( !isFunction( second ) )\n {\n return first;\n }\n\n return function compareCascading(a, b)\n {\n var d = first( a, b );\n\n return d !== 0 ? d : second( a, b );\n };\n}\n\n/**\n * Creates a function which compares two values.\n *\n * @memberof Rekord\n * @param {comparatorInput} comparator\n * The input which creates a comparison function.\n * @param {Boolean} [nullsFirst=false] -\n * True if null values should be sorted first.\n * @return {comparisonCallback}\n */\nfunction createComparator(comparator, nullsFirst)\n{\n if ( isFunction( comparator ) )\n {\n return comparator;\n }\n else if ( isString( comparator ) )\n {\n if ( comparator in Comparators )\n {\n return Comparators[ comparator ];\n }\n\n if ( comparator.charAt(0) === '-' )\n {\n var parsed = createComparator( comparator.substring( 1 ), !nullsFirst );\n\n return function compareObjectsReversed(a, b)\n {\n return -parsed( a, b );\n };\n }\n else if ( isFormatInput( comparator ) )\n {\n var formatter = createFormatter( comparator );\n\n return function compareFormatted(a, b)\n {\n var af = formatter( a );\n var bf = formatter( b );\n\n return af.localeCompare( bf );\n };\n }\n else if ( isParseInput( comparator ) )\n {\n var parser = createParser( comparator );\n\n return function compareExpression(a, b)\n {\n var ap = parser( a );\n var bp = parser( b );\n\n return compare( ap, bp, nullsFirst );\n };\n }\n else\n {\n return function compareObjects(a, b)\n {\n var av = isValue( a ) ? a[ comparator ] : a;\n var bv = isValue( b ) ? b[ comparator ] : b;\n\n return compare( av, bv, nullsFirst );\n };\n }\n }\n else if ( isArray( comparator ) )\n {\n var parsedChain = [];\n\n for (var i = 0; i < comparator.length; i++)\n {\n parsedChain[ i ] = createComparator( comparator[ i ], nullsFirst );\n }\n\n return function compareObjectsCascade(a, b)\n {\n var d = 0;\n\n for (var i = 0; i < parsedChain.length && d === 0; i++)\n {\n d = parsedChain[ i ]( a, b );\n }\n\n return d;\n };\n }\n\n return null;\n}\n\n\n/**\n * A function for comparing two values and determine whether they're considered\n * equal.\n *\n * @callback equalityCallback\n * @param {Any} a -\n * The first value to test.\n * @param {Any} b -\n * The second value to test.\n * @return {Boolean} -\n * Whether or not the two values are considered equivalent.\n * @see Rekord.equals\n * @see Rekord.equalsStrict\n * @see Rekord.equalsCompare\n */\n\n /**\n * A function for comparing two values to determine if one is greater or lesser\n * than the other or if they're equal.\n *\n * ```javascript\n * comparisonCallback( a, b ) < 0 // a < b\n * comparisonCallback( a, b ) > 0 // a > b\n * comparisonCallback( a, b ) == 0 // a == b\n * ```\n *\n * @callback comparisonCallback\n * @param {Any} a -\n * The first value to test.\n * @param {Any} b -\n * The second value to test.\n * @return {Number} -\n * 0 if the two values are considered equal, a negative value if `a` is\n * considered less than `b`, and a positive value if `a` is considered\n * greater than `b`.\n * @see Rekord.compare\n * @see Rekord.compareNumbers\n */\n\nfunction equalsStrict(a, b)\n{\n return a === b;\n}\n\nfunction equalsWeak(a, b)\n{\n return a == b; // jshint ignore:line\n}\n\nfunction equalsCompare(a, b)\n{\n return compare( a, b ) === 0;\n}\n\nfunction equals(a, b)\n{\n if (a === b)\n {\n return true;\n }\n if (a === null || b === null)\n {\n return false;\n }\n if (a !== a && b !== b)\n {\n return true; // NaN === NaN\n }\n\n var at = typeof a;\n var bt = typeof b;\n var ar = isRegExp(a);\n var br = isRegExp(b);\n\n if (at === 'string' && br)\n {\n return b.test(a);\n }\n if (bt === 'string' && ar)\n {\n return a.test(b);\n }\n\n if (at !== bt)\n {\n return false;\n }\n\n var aa = isArray(a);\n var ba = isArray(b);\n if (aa !== ba)\n {\n return false;\n }\n\n if (aa)\n {\n if (a.length !== b.length)\n {\n return false;\n }\n\n for (var i = 0; i < a.length; i++)\n {\n if (!equals(a[i], b[i]))\n {\n return false;\n }\n }\n\n return true;\n }\n\n if (isDate(a))\n {\n return isDate(b) && equals( a.getTime(), b.getTime() );\n }\n if (ar)\n {\n return br && a.toString() === b.toString();\n }\n\n if (at === 'object')\n {\n for (var ap in a)\n {\n if (ap.charAt(0) !== '$' && !isFunction(a[ap]))\n {\n if (!(ap in b) || !equals(a[ap], b[ap]))\n {\n return false;\n }\n }\n }\n\n for (var bp in b)\n {\n if (bp.charAt(0) !== '$' && !isFunction(b[bp]))\n {\n if (!(bp in a))\n {\n return false;\n }\n }\n }\n\n return true;\n }\n\n return false;\n}\n\nfunction compareNumbers(a, b)\n{\n return (a === b ? 0 : (a < b ? -1 : 1));\n}\n\nfunction compare(a, b, nullsFirst)\n{\n if (a == b) // jshint ignore:line\n {\n return 0;\n }\n\n var av = isValue( a );\n var bv = isValue( b );\n\n if (av !== bv)\n {\n return (av && !nullsFirst) || (bv && nullsFirst) ? -1 : 1;\n }\n\n if (isDate(a))\n {\n a = a.getTime();\n }\n if (isDate(b))\n {\n b = b.getTime();\n }\n if (isNumber(a) && isNumber(b))\n {\n return compareNumbers(a, b);\n }\n if (isArray(a) && isArray(b))\n {\n return compareNumbers(a.length, b.length);\n }\n if (isBoolean(a) && isBoolean(b))\n {\n return (a ? -1 : 1);\n }\n\n return (a + '').localeCompare(b + '');\n}\n\n\nfunction addEventFunction(target, functionName, events, secret)\n{\n var on = secret ? '$on' : 'on';\n var off = secret ? '$off' : 'off';\n\n var eventFunction = function(callback, context)\n {\n var subject = this;\n var unlistened = false;\n\n function listener()\n {\n var result = callback.apply( context || subject, arguments );\n\n if ( result === false )\n {\n unlistener();\n }\n }\n\n function unlistener()\n {\n if ( !unlistened )\n {\n subject[ off ]( events, listener );\n unlistened = true;\n }\n }\n\n subject[ on ]( events, listener );\n\n return unlistener;\n };\n\n if (target.$methods)\n {\n Class.method( target, functionName, eventFunction );\n }\n else\n {\n Class.prop( target, functionName, eventFunction );\n }\n}\n\nfunction EventNode(before, callback, context, type, group)\n{\n this.next = before ? before : this;\n this.prev = before ? before.prev : this;\n\n if ( before )\n {\n before.prev.next = this;\n before.prev = this;\n }\n\n this.callback = callback;\n this.context = context;\n this.type = type;\n this.group = group || 0;\n}\n\nEventNode.Types =\n{\n Persistent: 1,\n Once: 2,\n After: 4\n};\n\nClass.create( EventNode,\n{\n remove: function()\n {\n var next = this.next;\n var prev = this.prev;\n\n if ( next !== this )\n {\n prev.next = next;\n next.prev = prev;\n this.next = this.prev = this;\n }\n },\n\n hasType: function(type)\n {\n return (this.type & type) !== 0;\n },\n\n trigger: function(group, args, after)\n {\n var type = this.type;\n var isAfter = this.hasType( EventNode.Types.After );\n\n if ( this.group !== group )\n {\n if ( (after && isAfter) || !isAfter )\n {\n this.group = group;\n this.callback.apply( this.context, args );\n }\n\n if ( this.hasType( EventNode.Types.Once ) )\n {\n this.remove();\n }\n }\n }\n});\n\n/**\n * Adds functions to the given object (or prototype) so you can listen for any\n * number of events on the given object, optionally once. Listeners can be\n * removed later.\n *\n * The following methods will be added to the given target:\n *\n * ```\n * target.on( events, callback, [context] )\n * target.once( events, callback, [context] )\n * target.after( events, callback, [context] )\n * target.off( events, callback )\n * target.trigger( events, [a, b, c...] )\n * ```\n *\n * Where...\n * - `events` is a string of space delimited events.\n * - `callback` is a function to invoke when the event is triggered.\n * - `context` is an object that should be the `this` when the callback is\n * invoked. If no context is given the default value is the object which has\n * the trigger function that was invoked.\n *\n * @memberof Rekord\n * @param {Object} [target] -\n * The object to add `on`, `once`, `off`, and `trigger` functions to.\n * @param {Boolean} [secret=false] -\n * If true - the functions will be prefixed with `$`.\n */\nfunction addEventful(target, secret)\n{\n\n var triggerId = 0;\n\n /**\n * A mixin which adds `on`, `once`, `after`, and `trigger` functions to\n * another object.\n *\n * @class Eventful\n * @memberof Rekord\n * @see Rekord.addEventful\n */\n\n /**\n * A mixin which adds `$on`, `$once`, `$after`, and `$trigger` functions to\n * another object.\n *\n * @class Eventful$\n * @memberof Rekord\n * @see Rekord.addEventful\n */\n\n // Adds a listener to $this\n function onListeners($this, eventsInput, callback, context, type)\n {\n if ( !isFunction( callback ) )\n {\n return noop;\n }\n\n var callbackContext = context || $this;\n var events = toArray( eventsInput, ' ' );\n var listeners = $this.$$on;\n\n if ( !listeners )\n {\n Class.prop( $this, '$$on', listeners = {} );\n }\n\n var nodes = [];\n\n for (var i = 0; i < events.length; i++)\n {\n var eventName = events[ i ];\n var eventListeners = listeners[ eventName ];\n\n if ( !eventListeners )\n {\n eventListeners = listeners[ eventName ] = new EventNode();\n }\n\n nodes.push( new EventNode( eventListeners, callback, callbackContext, type, triggerId ) );\n }\n\n return function ignore()\n {\n for (var i = 0; i < nodes.length; i++)\n {\n nodes[ i ].remove();\n }\n\n nodes.length = 0;\n };\n }\n\n /**\n * Listens for every occurrence of the given events and invokes the callback\n * each time any of them are triggered.\n *\n * @method on\n * @memberof Rekord.Eventful#\n * @param {String|Array} events -\n * The event or events to listen to.\n * @param {Function} callback -\n * The function to invoke when any of the events are invoked.\n * @param {Object} [context] -\n * The value of `this` when the callback is invoked. If not specified, the\n * reference of the object this function exists on will be `this`.\n * @return {Function} -\n * A function to invoke to stop listening to all of the events given.\n */\n\n /**\n * Listens for every occurrence of the given events and invokes the callback\n * each time any of them are triggered.\n *\n * @method $on\n * @memberof Rekord.Eventful$#\n * @param {String|Array} events -\n * The event or events to listen to.\n * @param {Function} callback -\n * The function to invoke when any of the events are invoked.\n * @param {Object} [context] -\n * The value of `this` when the callback is invoked. If not specified, the\n * reference of the object this function exists on will be `this`.\n * @return {Function} -\n * A function to invoke to stop listening to all of the events given.\n */\n\n function on(events, callback, context)\n {\n return onListeners( this, events, callback, context, EventNode.Types.Persistent );\n }\n\n /**\n * Listens for the first of the given events to be triggered and invokes the\n * callback once.\n *\n * @method once\n * @memberof Rekord.Eventful#\n * @param {String|Array} events -\n * The event or events to listen to.\n * @param {Function} callback -\n * The function to invoke when any of the events are invoked.\n * @param {Object} [context] -\n * The value of `this` when the callback is invoked. If not specified, the\n * reference of the object this function exists on will be `this`.\n * @return {Function} -\n * A function to invoke to stop listening to all of the events given.\n */\n\n /**\n * Listens for the first of the given events to be triggered and invokes the\n * callback once.\n *\n * @method $once\n * @memberof Rekord.Eventful$#\n * @param {String|Array} events -\n * The event or events to listen to.\n * @param {Function} callback -\n * The function to invoke when any of the events are invoked.\n * @param {Object} [context] -\n * The value of `this` when the callback is invoked. If not specified, the\n * reference of the object this function exists on will be `this`.\n * @return {Function} -\n * A function to invoke to stop listening to all of the events given.\n */\n\n function once(events, callback, context)\n {\n return onListeners( this, events, callback, context, EventNode.Types.Once );\n }\n\n function after(events, callback, context)\n {\n return onListeners( this, events, callback, context, EventNode.Types.After );\n }\n\n // Removes a listener from an array of listeners.\n function offListeners(listeners, event, callback)\n {\n if (listeners && event in listeners)\n {\n var eventListeners = listeners[ event ];\n var next, node = eventListeners.next;\n\n while (node !== eventListeners)\n {\n next = node.next;\n\n if (node.callback === callback)\n {\n node.remove();\n }\n\n node = next;\n }\n }\n }\n\n // Deletes a property from the given object if it exists\n function deleteProperty(obj, prop)\n {\n if ( obj && prop in obj )\n {\n delete obj[ prop ];\n }\n }\n\n /**\n * Stops listening for a given callback for a given set of events.\n *\n * **Examples:**\n *\n * target.off(); // remove all listeners\n * target.off('a b'); // remove all listeners on events a & b\n * target.off(['a', 'b']); // remove all listeners on events a & b\n * target.off('a', x); // remove listener x from event a\n *\n * @method off\n * @for addEventful\n * @param {String|Array|Object} [eventsInput]\n * @param {Function} [callback]\n * @chainable\n */\n function off(eventsInput, callback)\n {\n // Remove ALL listeners\n if ( !isDefined( eventsInput ) )\n {\n deleteProperty( this, '$$on' );\n }\n else\n {\n var events = toArray( eventsInput, ' ' );\n\n // Remove listeners for given events\n if ( !isFunction( callback ) )\n {\n for (var i = 0; i < events.length; i++)\n {\n deleteProperty( this.$$on, events[i] );\n }\n }\n // Remove specific listener\n else\n {\n for (var i = 0; i < events.length; i++)\n {\n offListeners( this.$$on, events[i], callback );\n }\n }\n }\n\n return this;\n }\n\n // Triggers listeneers for the given event\n function triggerListeners(listeners, event, args)\n {\n if (listeners && event in listeners)\n {\n var eventListeners = listeners[ event ];\n var triggerGroup = ++triggerId;\n var next, node = eventListeners.next;\n\n while (node !== eventListeners)\n {\n next = node.next;\n node.trigger( triggerGroup, args, false );\n node = next;\n }\n\n node = eventListeners.next;\n\n while (node !== eventListeners)\n {\n next = node.next;\n node.trigger( triggerGroup, args, true );\n node = next;\n }\n }\n }\n\n /**\n * Triggers a single event optionally passing an argument to any listeners.\n *\n * @method trigger\n * @for addEventful\n * @param {String} eventsInput\n * @param {Array} args\n * @chainable\n */\n function trigger(eventsInput, args)\n {\n try\n {\n var events = toArray( eventsInput, ' ' );\n\n for (var i = 0; i < events.length; i++)\n {\n triggerListeners( this.$$on, events[ i ], args );\n }\n }\n catch (ex)\n {\n Rekord.trigger( Rekord.Events.Error, [ex] );\n }\n\n return this;\n }\n\n var methods = null;\n\n if ( secret )\n {\n methods = {\n $on: on,\n $once: once,\n $after: after,\n $off: off,\n $trigger: trigger\n };\n }\n else\n {\n methods = {\n on: on,\n once: once,\n after: after,\n off: off,\n trigger: trigger\n };\n }\n\n if ( target.$methods )\n {\n Class.methods( target, methods );\n }\n else\n {\n Class.props( target, methods );\n }\n}\n\n\n// Given two objects, merge src into dst.\n// - If a property in src has a truthy value in ignoreMap then skip merging it.\n// - If a property exists in src and not in dst, the property is added to dst.\n// - If an array property exists in src and in dst, the src elements are added to dst.\n// - If an array property exists in dst and a non array value exists in src, added the value to the dst array.\n// - If a property in dst is an object, try to merge the property from src into it.\n// - If a property exists in dst that is not an object or array, replace it with the value in src.\nfunction merge(dst, src, ignoreMap)\n{\n if (isObject( dst ) && isObject( src ))\n {\n for (var prop in src)\n {\n if (!ignoreMap || !ignoreMap[ prop ])\n {\n var adding = src[ prop ];\n\n if (prop in dst)\n {\n var existing = dst[ prop ];\n\n if (isArray( existing ))\n {\n if (isArray( adding ))\n {\n existing.push.apply( existing, adding );\n }\n else\n {\n existing.push( adding );\n }\n }\n else if (isObject( existing ))\n {\n merge( existing, adding, ignoreMap );\n }\n else\n {\n dst[ prop ] = copy( adding, true );\n }\n }\n else\n {\n dst[ prop ] = copy( adding, true );\n }\n }\n }\n }\n\n return dst;\n}\n\n\n\nfunction applyOptions( target, options, defaults, secret )\n{\n options = options || {};\n\n for (var defaultProperty in defaults)\n {\n var defaultValue = defaults[ defaultProperty ];\n var option = options[ defaultProperty ];\n var valued = isValue( option );\n\n if ( !valued && defaultValue === undefined )\n {\n throw defaultProperty + ' is a required option';\n }\n else if ( valued )\n {\n target[ defaultProperty ] = option;\n }\n else\n {\n target[ defaultProperty ] = copy( defaultValue );\n }\n }\n\n for (var optionProperty in options)\n {\n if ( !(optionProperty in defaults) )\n {\n target[ optionProperty ] = options[ optionProperty ];\n }\n }\n\n if ( secret )\n {\n target.$options = options;\n }\n else\n {\n target.options = options;\n }\n}\n\n/**\n * Determines whether the properties on one object equals the properties on\n * another object.\n *\n * @memberof Rekord\n * @param {Object} test -\n * The object to test for matching.\n * @param {String|String[]} testFields -\n * The property name or array of properties to test for equality on `test`.\n * @param {Object} expected -\n * The object with the expected values.\n * @param {String|String[]} expectedFields -\n * The property name or array of properties to test for equality on `expected`.\n * @param {equalityCallback} [equals] -\n * The equality function which compares two values and returns whether they\n * are considered equivalent.\n * @return {Boolean} -\n * True if the `testFields` properties on `test` are equivalent to the\n * `expectedFields` on `expected` according to the `equals` function.\n */\nfunction propsMatch(test, testFields, expected, expectedFields, equals)\n{\n var equality = equals || Rekord.equals;\n\n if ( isString( testFields ) ) // && isString( expectedFields )\n {\n return equality( test[ testFields ], expected[ expectedFields ] );\n }\n else // if ( isArray( testFields ) && isArray( expectedFields ) )\n {\n for (var i = 0; i < testFields.length; i++)\n {\n var testProp = testFields[ i ];\n var expectedProp = expectedFields[ i ];\n\n if ( !equality( test[ testProp ], expected[ expectedProp ] ) )\n {\n return false;\n }\n }\n\n return true;\n }\n\n return false;\n}\n\n// Determines whether the given model has the given fields\nfunction hasFields(model, fields, exists)\n{\n if ( isArray( fields ) )\n {\n for (var i = 0; i < fields.length; i++)\n {\n if ( !exists( model[ fields[ i ] ] ) )\n {\n return false;\n }\n }\n\n return true;\n }\n else // isString( fields )\n {\n return exists( model[ fields ] );\n }\n}\n\nfunction clearFieldsReturnChanges(target, targetFields)\n{\n var changes = false;\n\n if ( isArray( targetFields ) )\n {\n for (var i = 0; i < targetFields.length; i++)\n {\n var targetField = targetFields[ i ];\n\n if ( target[ targetField ] )\n {\n target[ targetField ] = null;\n changes = true;\n }\n }\n }\n else\n {\n if ( target[ targetFields ] )\n {\n target[ targetFields ] = null;\n changes = true;\n }\n }\n\n return changes;\n}\n\nfunction updateFieldsReturnChanges(target, targetFields, source, sourceFields)\n{\n var changes = false;\n\n if ( isArray( targetFields ) ) // && isArray( sourceFields )\n {\n for (var i = 0; i < targetFields.length; i++)\n {\n var targetField = targetFields[ i ];\n var targetValue = target[ targetField ];\n var sourceField = sourceFields[ i ];\n var sourceValue = source[ sourceField ];\n\n if ( !equals( targetValue, sourceValue ) )\n {\n target[ targetField ] = copy( sourceValue );\n changes = true;\n }\n }\n }\n else\n {\n var targetValue = target[ targetFields ];\n var sourceValue = source[ sourceFields ];\n\n if ( !equals( targetValue, sourceValue ) )\n {\n target[ targetFields ] = copy( sourceValue );\n changes = true;\n }\n }\n\n return changes;\n}\n\n\nfunction grab(obj, props, copyValues)\n{\n var grabbed = {};\n\n for (var i = 0; i < props.length; i++)\n {\n var p = props[ i ];\n\n if ( p in obj )\n {\n grabbed[ p ] = copyValues ? copy( obj[ p ] ) : obj[ p ];\n }\n }\n\n return grabbed;\n}\n\nfunction pull(obj, props, copyValues)\n{\n if ( isString( props ) )\n {\n var pulledValue = obj[ props ];\n\n return copyValues ? copy( pulledValue ) : pulledValue;\n }\n else // isArray( props )\n {\n var pulled = [];\n\n for (var i = 0; i < props.length; i++)\n {\n var p = props[ i ];\n var pulledValue = obj[ p ];\n\n pulled.push( copyValues ? copy( pulledValue ) : pulledValue );\n }\n\n return pulled;\n }\n}\n\nfunction transfer(from, to)\n{\n for (var prop in from)\n {\n to[ prop ] = from[ prop ];\n }\n\n return to;\n}\n\nfunction collapse()\n{\n var target = {};\n\n for (var i = 0; i < arguments.length; i++)\n {\n var a = arguments[ i ];\n\n if ( isObject( a ) )\n {\n for (var prop in a)\n {\n if ( !(prop in target) )\n {\n target[ prop ] = a[ prop ];\n }\n }\n }\n }\n\n return target;\n}\n\nfunction clean(x)\n{\n for (var prop in x)\n {\n if ( prop.charAt(0) === '$' )\n {\n delete x[ prop ];\n }\n }\n\n return x;\n}\n\nfunction cleanFunctions(x)\n{\n for (var prop in x)\n {\n if ( isFunction( x[prop] ) )\n {\n delete x[ prop ];\n }\n }\n\n return x;\n}\n\nfunction copy(x, copyHidden)\n{\n if (x === null || x === undefined || typeof x !== 'object' || isFunction(x) || isRegExp(x))\n {\n return x;\n }\n\n if (isArray(x))\n {\n var c = [];\n\n for (var i = 0; i < x.length; i++)\n {\n c.push( copy(x[i], copyHidden) );\n }\n\n return c;\n }\n\n if (isDate(x))\n {\n return new Date( x.getTime() );\n }\n\n var c = {};\n\n for (var prop in x)\n {\n if (copyHidden || prop.charAt(0) !== '$')\n {\n c[ prop ] = copy( x[prop], copyHidden );\n }\n }\n\n return c;\n}\n\nfunction diff(curr, old, props, comparator)\n{\n var d = {};\n\n for (var i = 0; i < props.length; i++)\n {\n var p = props[ i ];\n\n if (!comparator( curr[ p ], old[ p ] ) )\n {\n d[ p ] = copy( curr[ p ] );\n }\n }\n\n return d;\n}\n\n\nfunction isParseInput(x)\n{\n return x.indexOf('.') !== -1 || x.indexOf('[') !== -1 || x.indexOf('(') !== -1;\n}\n\nfunction parse(expr, base)\n{\n return createParser( expr )( base );\n}\n\nparse.REGEX = /([\\w$]+)/g;\n\nfunction createParser(expr)\n{\n var regex = parse.REGEX;\n var nodes = [];\n var match = null;\n\n while ((match = regex.exec( expr )) !== null)\n {\n nodes.push( match[ 1 ] );\n }\n\n return function(base)\n {\n for (var i = 0; i < nodes.length && base !== undefined; i++)\n {\n var n = nodes[ i ];\n\n if ( isObject( base ) )\n {\n base = evaluate( base[ n ], true, base );\n }\n }\n\n return base;\n };\n}\n\nfunction isFormatInput(x)\n{\n return x.indexOf('{') !== -1;\n}\n\nfunction format(template, base)\n{\n return createFormatter( template )( base );\n}\n\nformat.REGEX = /[\\{\\}]/;\n\nfunction createFormatter(template)\n{\n // Every odd element in parts is a parse expression\n var parts = template.split( format.REGEX );\n\n for (var i = 1; i < parts.length; i += 2 )\n {\n parts[ i ] = createParser( parts[ i ] );\n }\n\n return function formatter(base)\n {\n var formatted = '';\n\n for (var i = 0; i < parts.length; i++)\n {\n if ( (i & 1) === 0 )\n {\n formatted += parts[ i ];\n }\n else\n {\n var parsed = parts[ i ]( base );\n\n formatted += isValue( parsed ) ? parsed : '';\n }\n }\n\n return formatted;\n };\n}\n\nfunction parseDate(x, utc)\n{\n if ( isString( x ) )\n {\n if ( Date.parse )\n {\n x = Date.parse( x );\n }\n\n if ( !isNumber( x ) )\n {\n x = new Date( x );\n }\n }\n if ( isNumber( x ) )\n {\n x = new Date( x );\n }\n if ( isDate( x ) && isNumber( x.getTime() ) )\n {\n if ( utc )\n {\n x = new Date( x.getUTCFullYear(), x.getUTCMonth(), x.getUTCDate(), x.getUTCHours(), x.getUTCMinutes(), x.getUTCSeconds() );\n }\n\n return x;\n }\n\n return false;\n}\n\n\n\n/**\n * A function for resolving a value from a given value. Typically used to\n * transform an object into one of it's properties.\n *\n * @callback propertyResolverCallback\n * @param {Any} model -\n * The model to use to resolve a value.\n * @return {Any} -\n * The resolved value.\n * @see Rekord.createPropertyResolver\n */\n\n\n/**\n * An expression which resolves a value from another value.\n *\n * ```javascript\n * // {age: 6, name: 'x', user: {first: 'tom'}}\n * 'age' // age property of an object\n * 'user.first' // sub property\n * '{age}, {user.first}' // a formatted string built from object values\n * function(a) {} // a function which returns a value itself\n * ['age', 'name'] // multiple properties resolves to an array of values\n * {age:null, user:'first'} // multiple properties including a sub property returns an object of values\n * ```\n *\n * @typedef {String|Function|Array|Object} propertyResolverInput\n */\n\nvar NumberResolvers = {};\n\nfunction saveNumberResolver(name, numbers, invalidValue)\n{\n var resolver = createNumberResolver( numbers, invalidValue );\n\n NumberResolvers[ name ] = resolver;\n\n return resolver;\n}\n\nfunction createNumberResolver(numbers, invalidValue)\n{\n var resolver = createPropertyResolver( numbers );\n\n if ( isString( numbers ) && numbers in NumberResolvers )\n {\n return NumberResolvers[ numbers ];\n }\n\n return function resolveNumber(model)\n {\n var parsed = parseFloat( resolver( model ) );\n\n return isNaN( parsed ) ? invalidValue : parsed;\n };\n}\n\nvar PropertyResolvers = {};\n\nfunction savePropertyResolver(name, properties)\n{\n var resolver = createPropertyResolver( properties );\n\n PropertyResolvers[ name ] = resolver;\n\n return resolver;\n}\n\n/**\n * Creates a function which resolves a value from another value given an\n * expression. This is often used to get a property value of an object.\n *\n * ```javascript\n * // x = {age: 6, name: 'tom', user: {first: 'jack'}}\n * createPropertyResolver()( x ) // x\n * createPropertyResolver( 'age' )( x ) // 6\n * createPropertyResolver( 'user.first' )( x ) // 'jack'\n * createPropertyResolver( '{name} & {user.first}')( x ) // 'tom & jack'\n * createPropertyResolver( ['name', 'age'] )( x ) // ['tom', 6]\n * createPropertyResolver( {age:null, user:'first'})( x ) // {age: 6, user:'jack'}\n * ```\n *\n * @memberof Rekord\n * @param {propertyResolverInput} [properties] -\n * The expression which converts one value into another.\n * @return {propertyResolverCallback} -\n * A function to take values and resolve new ones.\n */\nfunction createPropertyResolver(properties)\n{\n if ( isFunction( properties ) )\n {\n return properties;\n }\n else if ( isString( properties ) )\n {\n if ( properties in PropertyResolvers )\n {\n return PropertyResolvers[ properties ];\n }\n\n if ( isFormatInput( properties ) )\n {\n return createFormatter( properties );\n }\n else if ( isParseInput( properties ) )\n {\n return createParser( properties );\n }\n else\n {\n return function resolveProperty(model)\n {\n return model ? model[ properties ] : undefined;\n };\n }\n }\n else if ( isArray( properties ) )\n {\n return function resolveProperties(model)\n {\n return pull( model, properties );\n };\n }\n else if ( isObject( properties ) )\n {\n var propsArray = [];\n var propsResolver = [];\n\n for (var prop in properties)\n {\n propsArray.push( prop );\n propsResolver.push( createPropertyResolver( properties[ prop ] ) );\n }\n\n return function resolvePropertyObject(model)\n {\n var resolved = {};\n\n for (var i = 0; i < propsArray.length; i++)\n {\n var prop = propsArray[ i ];\n\n resolved[ prop ] = propsResolver[ i ]( model[ prop ] );\n }\n\n return resolved;\n };\n }\n else\n {\n return function resolveNone(model)\n {\n return model;\n };\n }\n}\n\n\nvar Settings = global.RekordSettings || win.RekordSettings || {};\n\nif ( win.document && win.document.currentScript )\n{\n var script = win.document.currentScript;\n\n if (script.getAttribute('native-array') !== null)\n {\n Settings.nativeArray = true;\n }\n}\n\n\nfunction camelCaseReplacer(match)\n{\n return match.length === 1 ? match.toUpperCase() : match.charAt(1).toUpperCase();\n}\n\nfunction toCamelCase(name)\n{\n return name.replace( toCamelCase.REGEX, camelCaseReplacer );\n}\n\ntoCamelCase.REGEX = /(^.|_.)/g;\n\nfunction split(x, delimiter, escape)\n{\n var regexDelimiter = isRegExp( delimiter ) ? delimiter : new RegExp( '(' + delimiter + ')' );\n var splits = x.split( regexDelimiter );\n var i = 0;\n var n = splits.length - 2;\n\n while (i < n)\n {\n var a = splits[ i ];\n var ae = a.length - escape.length;\n\n if ( a.substring( ae ) === escape )\n {\n var b = splits[ i + 1 ];\n var c = splits[ i + 2 ];\n var joined = a.substring( 0, ae ) + b + c;\n\n splits.splice( i, 3, joined );\n n -= 2;\n }\n else\n {\n i += 1;\n splits.splice( i, 1 );\n n -= 1;\n }\n }\n\n return splits;\n}\n\n\n/**\n * A function which takes a value (typically an object) and returns a true or\n * false value.\n *\n * @callback whereCallback\n * @param {Any} value -\n * The value to test.\n * @return {Boolean} -\n * Whether or not the value passed the test.\n * @see Rekord.createWhere\n * @see Rekord.saveWhere\n */\n\n/**\n * An expression which can be used to generate a function for testing a value\n * and returning a boolean result. The following types can be given and will\n * result in the following tests:\n *\n * - `String`: If a string & value are given - the generated function will test\n * if the object has a property with the given value. If a string is given\n * and no value is given - the generated function will test if the object\n * has the property and a non-null value.\n * - `Object`: If an object is given - the generated function will test all\n * properties of the given object and return true only if the object being\n * tested has the same values.\n * - `Array`: If an array is given - each element in the array is passed as\n * arguments to generate a new function. The returned function will only\n * return true if all generated functions return true - otherwise false.\n * - `whereCallback`: A function can be given which is immediately returned as\n * the test function.\n *\n * @typedef {String|Object|Array|whereCallback} whereInput\n */\n\n\n/**\n * A map of saved {@link whereCallback} functions.\n *\n * @type {Object}\n */\nvar Wheres = {};\n\n/**\n * Saves a function created with {@link Rekord.createWhere} to a cache of\n * filter functions which can be created more quickly in subsequent calls. It's\n * advised to make use of saved where's even in simpler scenarios for several\n * reasons:\n *\n * - You can name a comparison which is self documenting\n * - When refactoring, you only need to modify a single place in the code\n * - It's slightly more efficient (time & memory) to cache filter functions\n *\n * ```javascript\n * Rekord.saveWhere('whereName', 'field', true);\n * Rekord.createWhere('whereName'); // returns the same function except quicker\n * ```\n *\n * @memberof Rekord\n * @param {String} name -\n * The name of the filter function to save for later use.\n * @param {String|Object|Array|whereCallback} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @see Rekord.createWhere\n */\nfunction saveWhere(name, properties, values, equals)\n{\n var where = createWhere( properties, values, equals );\n\n Wheres[ name ] = where;\n\n return where;\n}\n\n/**\n * Creates a function which returns a true or false value given a test value.\n * This is also known as a filter function.\n *\n * ```javascript\n * Rekord.createWhere('field', true); // when an object has property where field=true\n * Rekord.createWhere('field'); // when an object has the property named field\n * Rekord.createWhere(function(){}); // a function can be given which is immediately returned\n * Rekord.createWhere(['field', function(){}, ['field', true]]); // when an object meets all of the above criteria\n * Rekord.createWhere({foo: 1, bar: 2}); // when an object has foo=1 and bar=2\n * Rekord.createWhere('field', true, myEquals); // A custom comparison function can be given.\n * Rekord.createWhere(); // always returns true\n * ```\n *\n * @memberof Rekord\n * @param {whereInput} [properties] -\n * The first expression used to generate a filter function.\n * @param {Any} [value] -\n * When the first argument is a string this argument will be treated as a\n * value to compare to the value of the named property on the object passed\n * through the filter function.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * An alternative function can be used to compare to values.\n * @return {whereCallback} -\n * A function which takes a value (typically an object) and returns a true\n * or false value.\n * @see Rekord.saveWhere\n */\nfunction createWhere(properties, value, equals)\n{\n var equality = equals || equalsStrict;\n\n if ( isFunction( properties ) )\n {\n return properties;\n }\n else if ( isArray( properties ) )\n {\n var parsed = [];\n\n for (var i = 0; i < properties.length; i++)\n {\n var where = properties[ i ];\n\n parsed.push( isArray( where ) ? createWhere.apply( this, where ) : createWhere( where ) );\n }\n\n return function whereMultiple(model)\n {\n for (var i = 0; i < parsed.length; i++)\n {\n if ( !parsed[ i ]( model ) )\n {\n return false;\n }\n }\n\n return true;\n };\n }\n else if ( isObject( properties ) )\n {\n var props = [];\n\n for (var prop in properties)\n {\n props.push({\n tester: exprEqualsTester( properties[ prop ], equality ),\n resolver: createPropertyResolver( prop )\n });\n }\n\n return function whereEqualsObject(model)\n {\n for (var i = 0; i < props.length; i++)\n {\n var prop = props[ i ];\n\n if ( !prop.tester( prop.resolver( model ) ) )\n {\n return false;\n }\n }\n\n return true;\n };\n }\n else if ( isString( properties ) )\n {\n if ( properties in Wheres )\n {\n return Wheres[ properties ];\n }\n\n var resolver = createPropertyResolver( properties );\n\n if ( isValue( value ) )\n {\n var tester = exprEqualsTester( value, equality );\n\n return function whereEqualsValue(model)\n {\n return tester( resolver( model ) );\n };\n }\n else\n {\n return function whereHasValue(model)\n {\n return isValue( resolver( model ) );\n };\n }\n }\n else\n {\n return function whereAll(model)\n {\n return true;\n };\n }\n}\n\nfunction expr(func)\n{\n func.expression = true;\n\n return func;\n}\n\nfunction exprEquals(value, test, equals)\n{\n return isExpr( value ) ? value( test, equals ) : equals( value, test );\n}\n\nfunction exprEqualsTester(value, equals)\n{\n if ( isExpr( value ) )\n {\n return function tester(test)\n {\n return value( test, equals );\n };\n }\n\n return function tester(test)\n {\n return equals( value, test );\n };\n}\n\nfunction isExpr(x)\n{\n return isFunction( x ) && x.expression;\n}\n\nfunction not(x)\n{\n if ( isExpr( x ) )\n {\n return expr(function notExpr(value, equals)\n {\n return !x( value, equals );\n });\n }\n\n if ( isFunction( x ) )\n {\n return function notWhere(value)\n {\n return !x( value );\n };\n }\n\n return expr(function notValue(value, equals)\n {\n return !equals( value, x );\n });\n}\n\nfunction oneOf(input)\n{\n var values = isArray( input ) ? input : AP.slice.call( arguments );\n\n return expr(function oneOfValue(value, equals)\n {\n for (var i = 0; i < values.length; i++)\n {\n if (exprEquals( values[ i ], value, equals ) )\n {\n return true;\n }\n }\n\n return false;\n });\n}\n\n\n/**\n * Creates a Rekord object given a set of options. A Rekord object is also the\n * constructor for creating instances of the Rekord object defined.\n *\n * @namespace\n * @param {Object} options\n * The options of\n */\nfunction Rekord(options)\n{\n var promise = Rekord.get( options.name );\n\n if ( promise.isComplete() )\n {\n return promise.results[0];\n }\n\n Rekord.trigger( Rekord.Events.Options, [options] );\n\n var database = new Database( options );\n\n var model = Class.dynamic(\n Model,\n new Model( database ),\n database.className,\n '(props, remoteData) { this.$init( props, remoteData ) }'\n );\n\n database.Model = model;\n model.Database = database;\n\n Rekord.classes[ database.name ] = model;\n\n Rekord.trigger( Rekord.Events.Plugins, [model, database, options] );\n\n if ( Rekord.autoload )\n {\n database.loadBegin(function onLoadFinish(success)\n {\n if ( success )\n {\n database.loadFinish();\n }\n });\n }\n else\n {\n Rekord.unloaded.push( database );\n }\n\n Rekord.get( database.name ).resolve( model );\n Rekord.get( database.className ).resolve( model );\n\n Rekord.debug( Rekord.Debugs.CREATION, database, options );\n\n return model;\n}\n\nRekord.classes = {};\n\nRekord.autoload = false;\n\nRekord.unloaded = [];\n\nRekord.loadPromise = null;\n\nRekord.load = function(callback, context)\n{\n var promise = Rekord.loadPromise = Rekord.loadPromise || new Promise( null, false );\n var loading = Rekord.unloaded.slice();\n var loaded = [];\n var loadedSuccess = [];\n\n promise.success( callback, context || this );\n\n Rekord.unloaded.length = 0;\n\n function onLoadFinish(success, db)\n {\n loadedSuccess.push( success );\n loaded.push( db );\n\n if ( loaded.length === loading.length )\n {\n for (var k = 0; k < loaded.length; k++)\n {\n var db = loaded[ k ];\n var success = loadedSuccess[ k ];\n\n if ( success )\n {\n db.loadFinish();\n }\n }\n\n promise.reset().resolve();\n }\n }\n\n for (var i = 0; i < loading.length; i++)\n {\n loading[ i ].loadBegin( onLoadFinish );\n }\n\n return promise;\n};\n\nRekord.promises = {};\n\nRekord.get = function(name)\n{\n var existing = Rekord.promises[ name ];\n\n if ( !existing )\n {\n existing = Rekord.promises[ name ] = new Promise( null, false );\n }\n\n return existing;\n};\n\nRekord.export = function()\n{\n var classes = Rekord.classes;\n\n for (var className in classes)\n {\n win[ className ] = classes[ className ];\n }\n};\n\nRekord.clear = function(removeListeners)\n{\n var classes = Rekord.classes;\n\n for (var className in classes)\n {\n classes[ className ].clear( removeListeners );\n }\n};\n\nRekord.reset = function(failOnPendingChanges, removeListeners)\n{\n var classes = Rekord.classes;\n\n if ( failOnPendingChanges )\n {\n for (var className in classes)\n {\n var db = classes[ className ].Database;\n\n if ( db.hasPending() )\n {\n return Promise.reject( db );\n }\n }\n }\n\n return Promise.singularity(this, function()\n {\n for (var className in classes)\n {\n var db = classes[ className ].Database;\n\n db.reset( false, removeListeners );\n }\n });\n};\n\nRekord.unload = function(names, reset, failOnPendingChanges, removeListeners)\n{\n var classes = Rekord.classes;\n var promises = Rekord.promises;\n\n if ( failOnPendingChanges )\n {\n for (var className in classes)\n {\n var db = classes[ className ].Database;\n var check = ( !isArray( names ) || indexOf( names, className ) !== false );\n\n if ( check && db.hasPending() )\n {\n return Promise.reject( db );\n }\n }\n }\n\n return Promise.singularity(this, function()\n {\n for (var className in classes)\n {\n var db = classes[ className ].Database;\n var check = ( !isArray( names ) || indexOf( names, className ) !== false );\n\n if ( check )\n {\n if ( reset )\n {\n db.reset( false, removeListeners );\n }\n\n delete classes[ className ];\n delete promises[ db.name ];\n delete promises[ db.className ];\n }\n }\n });\n};\n\n/**\n * A value which identifies a model instance. This can be the key of the model,\n * an array of values (if the model has composite keys), an object which at\n * least contains fields which identify the model, an instance of a model, the\n * reference to a Rekord instance, or a function.\n *\n * If a plain object is given and it shares the same key as an existing model -\n * the other fields on the object will be applied to the existing instance. If\n * a plain object is given and it's key doesn't map to an existing model - a new\n * one is created.\n *\n * If a reference to a Rekord instance is given - a new model instance is created\n * with default values.\n *\n * If a function is given - it's invoked and the returning value is used as the\n * value to identify the model instance.\n *\n * @typedef {String|Number|String[]|Number[]|Object|Rekord|Rekord.Model|Function} modelInput\n */\n\n /**\n * A key to a model instance.\n *\n * @typedef {String|Number} modelKey\n */\n\naddEventful( Rekord );\n\nRekord.Events =\n{\n Initialized: 'initialized',\n Plugins: 'plugins',\n Options: 'options',\n Online: 'online',\n Offline: 'offline',\n Error: 'error'\n};\n\n\nvar Cache =\n{\n None: 'none',\n Pending: 'pending',\n All: 'all'\n};\n\n\nvar Cascade =\n{\n None: 0,\n Local: 1,\n Rest: 2,\n NoLive: 3,\n Live: 4,\n NoRest: 5,\n Remote: 6,\n All: 7\n};\n\nfunction canCascade(cascade, type)\n{\n return !isNumber( cascade ) || (cascade & type) === type;\n}\n\nvar Load =\n{\n None: 0,\n All: 1,\n Lazy: 2,\n Both: 3\n};\n\n\n\nvar RestStatus =\n{\n Conflict: {409: true},\n NotFound: {404: true, 410: true},\n Offline: {0: true}\n};\n\nvar Save =\n{\n None: 0,\n Model: 4,\n Key: 5,\n Keys: 6\n};\n\nvar Store =\n{\n None: 0,\n Model: 1,\n Key: 2,\n Keys: 3\n};\n\n\nvar batchDepth = 0;\nvar batches = [];\nvar batchHandlers = [];\nvar batchOverwrites = [];\n\nfunction batch(namesInput, operationsInput, handler)\n{\n var names = toArray( namesInput, /\\s*,\\s/ );\n var operations = toArray( operationsInput, /\\s*,\\s/ );\n var batchID = batchHandlers.push( handler ) - 1;\n var batch = batches[ batchID ] = new Collection();\n\n for (var i = 0; i < names.length; i++)\n {\n var modelName = names[ i ];\n var modelHandler = createModelHandler( operations, batch );\n\n if ( isString( modelName ) )\n {\n if ( modelName in Rekord.classes )\n {\n modelHandler( Rekord.classes[ modelName ] );\n }\n else\n {\n earlyModelHandler( modelName, modelHandler );\n }\n }\n else if ( isRekord( modelName ) )\n {\n modelHandler( modelName );\n }\n else if ( modelName === true )\n {\n for (var databaseName in Rekord.classes)\n {\n modelHandler( Rekord.classes[ databaseName ] );\n }\n\n Rekord.on( Rekord.Events.Plugins, modelHandler );\n }\n else\n {\n throw modelName + ' is not a valid input for batching';\n }\n }\n}\n\nfunction earlyModelHandler(name, modelHandler)\n{\n var off = Rekord.on( Rekord.Events.Plugins, function(model, database)\n {\n if ( database.name === name )\n {\n modelHandler( model );\n\n off();\n }\n });\n}\n\nfunction createModelHandler(operations, batch)\n{\n return function(modelClass)\n {\n var db = modelClass.Database;\n var rest = db.rest;\n\n for (var i = 0; i < operations.length; i++)\n {\n var op = operations[ i ];\n\n batchOverwrites.push( rest, op, rest[ op ] );\n\n switch (op)\n {\n case 'all':\n rest.all = function(options, success, failure) // jshint ignore:line\n {\n batch.push({\n database: db,\n class: modelClass,\n operation: 'all',\n options: options,\n success: success,\n failure: failure\n });\n };\n break;\n case 'get':\n rest.get = function(model, options, success, failure) // jshint ignore:line\n {\n batch.push({\n database: db,\n class: modelClass,\n operation: 'get',\n options: options,\n success: success,\n failure: failure,\n model: model\n });\n };\n break;\n case 'create':\n rest.create = function(model, encoded, options, success, failure) // jshint ignore:line\n {\n batch.push({\n database: db,\n class: modelClass,\n operation: 'create',\n options: options,\n success: success,\n failure: failure,\n model: model,\n encoded: encoded\n });\n };\n break;\n case 'update':\n rest.update = function(model, encoded, options, success, failure) // jshint ignore:line\n {\n batch.push({\n database: db,\n class: modelClass,\n operation: 'update',\n options: options,\n success: success,\n failure: failure,\n model: model,\n encoded: encoded\n });\n };\n break;\n case 'remove':\n rest.remove = function(model, options, success, failure) // jshint ignore:line\n {\n batch.push({\n database: db,\n class: modelClass,\n operation: 'remove',\n options: options,\n success: success,\n failure: failure,\n model: model\n });\n };\n break;\n case 'query':\n rest.query = function(url, query, options, success, failure) // jshint ignore:line\n {\n batch.push({\n database: db,\n class: modelClass,\n operation: 'query',\n options: options,\n success: success,\n failure: failure,\n url: url,\n encoded: query\n });\n };\n break;\n default:\n throw op + ' is not a valid operation you can batch';\n }\n }\n };\n}\n\nfunction batchRun()\n{\n for (var i = 0; i < batches.length; i++)\n {\n var batch = batches[ i ];\n var handler = batchHandlers[ i ];\n\n if ( batch.length )\n {\n handler( batch );\n\n batch.clear();\n }\n }\n}\n\nfunction batchStart()\n{\n batchDepth++;\n}\n\nfunction batchEnd()\n{\n batchDepth--;\n\n if ( batchDepth === 0 )\n {\n batchRun();\n }\n}\n\nfunction batchClear()\n{\n for (var i = 0; i < batchOverwrites.length; i += 3)\n {\n var rest = batchOverwrites[ i + 0 ];\n var prop = batchOverwrites[ i + 1 ];\n var func = batchOverwrites[ i + 2 ];\n\n rest[ prop ] = func;\n }\n\n batches.length = 0;\n batchHandlers.length = 0;\n batchOverwrites.length = 0;\n}\n\nfunction batchExecute(func, context)\n{\n try\n {\n batchStart();\n\n func.apply( context );\n }\n catch (ex)\n {\n Rekord.trigger( Rekord.Events.Error, [ex] );\n\n throw ex;\n }\n finally\n {\n batchEnd();\n }\n}\n\nRekord.batch = batch;\nRekord.batchRun = batchRun;\nRekord.batchStart = batchStart;\nRekord.batchEnd = batchEnd;\nRekord.batchClear = batchClear;\nRekord.batchExecute = batchExecute;\nRekord.batchDepth = function() { return batchDepth; };\n\n\nRekord.debug = function(event, source) /*, data.. */\n{\n // up to the user\n};\n\n/**\n * Sets the debug implementation provided the factory function. This function\n * can only be called once - all subsequent calls will be ignored unless\n * `overwrite` is given as a truthy value.\n *\n * @memberof Rekord\n * @param {Function} factory -\n * The factory which provides debug implementations.\n * @param {Boolean} [overwrite=false] -\n * True if existing implementations are to be ignored and the given factory\n * should be the implementation.\n */\nRekord.setDebug = function(factory, overwrite)\n{\n if ( !Rekord.debugSet || overwrite )\n {\n Rekord.debug = factory;\n Rekord.debugSet = true;\n }\n};\n\nRekord.Debugs = {\n\n CREATION: 0, // options\n\n REST: 1, // options\n AUTO_REFRESH: 73, //\n\n MISSING_KEY: 33, // encoded\n\n REMOTE_UPDATE: 2, // encoded, Model\n REMOTE_CREATE: 3, // encoded, Model\n REMOTE_REMOVE: 4, // Model\n REMOTE_LOAD: 5, // encoded[]\n REMOTE_LOAD_OFFLINE: 6, //\n REMOTE_LOAD_ERROR: 7, // status\n REMOTE_LOAD_REMOVE: 8, // key\n REMOTE_LOAD_RESUME: 22, //\n\n LOCAL_LOAD: 9, // encoded[]\n LOCAL_RESUME_DELETE: 10, // Model\n LOCAL_RESUME_SAVE: 11, // Model\n LOCAL_LOAD_SAVED: 12, // Model\n\n REALTIME_SAVE: 13, // encoded, key\n REALTIME_REMOVE: 14, // key\n\n SAVE_VALUES: 15, // encoded, Model\n SAVE_PUBLISH: 16, // encoded, Model\n SAVE_CONFLICT: 17, // encoded, Model\n SAVE_UPDATE_FAIL: 18, // Model\n SAVE_ERROR: 19, // Model, status\n SAVE_OFFLINE: 20, // Model\n SAVE_RESUME: 21, // Model\n SAVE_REMOTE: 25, // Model\n SAVE_DELETED: 40, // Model\n\n SAVE_OLD_REVISION: 48, // Model, encoded\n\n SAVE_LOCAL: 23, // Model\n SAVE_LOCAL_ERROR: 24, // Model, error\n SAVE_LOCAL_DELETED: 38, // Model\n SAVE_LOCAL_BLOCKED: 39, // Model\n\n SAVE_REMOTE_DELETED: 41, // Model, [encoded]\n SAVE_REMOTE_BLOCKED: 42, // Model\n\n REMOVE_PUBLISH: 26, // key, Model\n REMOVE_LOCAL: 27, // key, Model\n REMOVE_MISSING: 28, // key, Model\n REMOVE_ERROR: 29, // status, key, Model\n REMOVE_OFFLINE: 30, // Model\n REMOVE_RESUME: 31, // Model\n REMOVE_REMOTE: 32, // Model\n REMOVE_CANCEL_SAVE: 47, // Model\n\n REMOVE_LOCAL_ERROR: 34, // Model, error\n REMOVE_LOCAL_BLOCKED: 44, // Model\n REMOVE_LOCAL_NONE: 45, // Model\n REMOVE_LOCAL_UNSAVED: 46, // Model\n\n REMOVE_REMOTE_BLOCKED: 43, // Model\n\n GET_LOCAL_SKIPPED: 104, // Model\n GET_LOCAL: 105, // Model, encoded\n GET_LOCAL_ERROR: 106, // Model, e\n GET_REMOTE: 107, // Model, data\n GET_REMOTE_ERROR: 108, // Model, data, status\n\n ONLINE: 35, //\n OFFLINE: 36, //\n\n PUBSUB_CREATED: 37, // PubSub\n\n HASONE_INIT: 53, // HasOne\n HASONE_NINJA_REMOVE: 49, // Model, relation\n HASONE_INITIAL_PULLED: 51, // Model, initial\n HASONE_INITIAL: 52, // Model, initial\n HASONE_CLEAR_MODEL: 54, // relation\n HASONE_SET_MODEL: 55, // relation\n HASONE_PRESAVE: 56, // Model, relation\n HASONE_POSTREMOVE: 57, // Model, relation\n HASONE_CLEAR_KEY: 58, // Model, local\n HASONE_UPDATE_KEY: 59, // Model, targetFields, Model, sourceFields\n HASONE_LOADED: 60, // Model, relation, [Model]\n HASONE_QUERY: 111, // Model, RemoteQuery, queryOption, query\n HASONE_QUERY_RESULTS: 112, // Model, RemoteQuery\n\n BELONGSTO_INIT: 61, // HasOne\n BELONGSTO_NINJA_REMOVE: 62, // Model, relation\n BELONGSTO_NINJA_SAVE: 63, // Model, relation\n BELONGSTO_INITIAL_PULLED: 64,// Model, initial\n BELONGSTO_INITIAL: 65, // Model, initial\n BELONGSTO_CLEAR_MODEL: 66, // relation\n BELONGSTO_SET_MODEL: 67, // relation\n BELONGSTO_POSTREMOVE: 69, // Model, relation\n BELONGSTO_CLEAR_KEY: 70, // Model, local\n BELONGSTO_UPDATE_KEY: 71, // Model, targetFields, Model, sourceFields\n BELONGSTO_LOADED: 72, // Model, relation, [Model]\n BELONGSTO_QUERY: 113, // Model, RemoteQuery, queryOption, query\n BELONGSTO_QUERY_RESULTS: 114,// Model, RemoteQuery\n\n HASREFERENCE_INIT: 131, // HasOne\n HASREFERENCE_NINJA_REMOVE: 132, // Model, relation\n HASREFERENCE_INITIAL_PULLED: 133, // Model, initial\n HASREFERENCE_INITIAL: 134, // Model, initial\n HASREFERENCE_CLEAR_MODEL: 135, // relation\n HASREFERENCE_SET_MODEL: 136, // relation\n HASREFERENCE_CLEAR_KEY: 137, // Model, local\n HASREFERENCE_UPDATE_KEY: 138, // Model, targetFields, Model, sourceFields\n HASREFERENCE_LOADED: 139, // Model, relation, [Model]\n HASREFERENCE_QUERY: 140, // Model, RemoteQuery, queryOption, query\n HASREFERENCE_QUERY_RESULTS: 141, // Model, RemoteQuery\n\n HASMANY_INIT: 74, // HasMany\n HASMANY_NINJA_REMOVE: 75, // Model, Model, relation\n HASMANY_NINJA_SAVE: 76, // Model, Model, relation\n HASMANY_INITIAL: 77, // Model, relation, initial\n HASMANY_INITIAL_PULLED: 78, // Model, relation\n HASMANY_REMOVE: 79, // relation, Model\n HASMANY_SORT: 80, // relation\n HASMANY_ADD: 81, // relation, Model\n HASMANY_LAZY_LOAD: 82, // relation, Model[]\n HASMANY_INITIAL_GRABBED: 83, // relation, Model\n HASMANY_NINJA_ADD: 84, // relation, Model\n HASMANY_AUTO_SAVE: 85, // relation\n HASMANY_PREREMOVE: 86, // Model, relation\n HASMANY_POSTSAVE: 87, // Model, relation\n HASMANY_QUERY: 115, // Model, RemoteQuery, queryOption, query\n HASMANY_QUERY_RESULTS: 116, // Model, RemoteQuery\n HASMANY_UPDATE_KEY: 129, // Model, targetFields, Model, sourceFields\n\n HASMANYTHRU_INIT: 88, // HasMany\n HASMANYTHRU_NINJA_REMOVE: 89, // Model, Model, relation\n HASMANYTHRU_NINJA_SAVE: 90, // Model, Model, relation\n HASMANYTHRU_NINJA_THRU_REMOVE: 91,// Model, Model, relation\n HASMANYTHRU_INITIAL: 92, // Model, relation, initial\n HASMANYTHRU_INITIAL_PULLED: 93, // Model, relation\n HASMANYTHRU_REMOVE: 94, // relation, Model\n HASMANYTHRU_SORT: 95, // relation\n HASMANYTHRU_ADD: 96, // relation, Model\n HASMANYTHRU_LAZY_LOAD: 97, // relation, Model[]\n HASMANYTHRU_INITIAL_GRABBED: 98, // relation, Model\n HASMANYTHRU_NINJA_ADD: 99, // relation, Model\n HASMANYTHRU_AUTO_SAVE: 100, // relation\n HASMANYTHRU_PREREMOVE: 101, // Model, relation\n HASMANYTHRU_POSTSAVE: 102, // Model, relation\n HASMANYTHRU_THRU_ADD: 103, // relation, Model\n HASMANYTHRU_THRU_REMOVE: 68, // relation, Model, Model\n HASMANYTHRU_QUERY: 117, // Model, RemoteQuery, queryOption, query\n HASMANYTHRU_QUERY_RESULTS: 118, // Model, RemoteQuery\n HASMANYTHRU_UPDATE_KEY: 130, // Model, targetFields, Model, sourceFields\n\n HASREMOTE_INIT: 50, // HasRemote\n HASREMOTE_SORT: 121, // relation\n HASREMOTE_NINJA_REMOVE: 109, // Model, Model, relation\n HASREMOTE_NINJA_SAVE: 110, // Model, Model, relation\n HASREMOTE_QUERY: 119, // Model, RemoteQuery, queryOption, query\n HASREMOTE_QUERY_RESULTS: 120, // Model, RemoteQuery\n\n HASLIST_INIT: 122, // HasList\n HASLIST_SORT: 123, // relation\n HASLIST_NINJA_REMOVE: 124, // Model, Model, relation\n HASLIST_NINJA_SAVE: 125, // Model, Model, relation\n HASLIST_REMOVE: 126, // HasList, relation, Model\n HASLIST_ADD: 127, // HasList, relation, Model\n HASLIST_INITIAL: 128 // HasList, Model, relation, initial\n};\n\n\nvar Lives = {};\n\n/**\n * The factory responsible for creating a service which publishes operations\n * and receives operations that have occurred. The first argument is a reference\n * to the Database and the second argument is a function to invoke when a\n * live operation occurs. This function must return a function that can be passed\n * an operation to be delegated to other clients.\n *\n * @param {Database} database\n * The database this live function is for.\n * @return {function} -\n * The function which sends operations.\n */\nLives.Default =\nRekord.defaultLive =\nRekord.live = function(database)\n{\n return {\n\n save: function(model, data)\n {\n // ignore save\n },\n\n remove: function(model)\n {\n // ignore remove\n }\n\n };\n};\n\n/**\n * Sets the live implementation provided the factory function. This function\n * can only be called once - all subsequent calls will be ignored unless\n * `overwrite` is given as a truthy value.\n *\n * @memberof Rekord\n * @param {Function} factory -\n * The factory which provides live implementations.\n * @param {Boolean} [overwrite=false] -\n * True if existing implementations are to be ignored and the given factory\n * should be the implementation.\n */\nRekord.setLive = function(factory, overwrite)\n{\n if ( !Rekord.liveSet || overwrite )\n {\n Rekord.live = factory;\n Rekord.liveSet = true;\n }\n};\n\n\n// Initial online\n\nRekord.isOnline = function()\n{\n return !win.navigator || win.navigator.onLine !== false;\n};\n\nRekord.online = Rekord.isOnline();\n\nRekord.forceOffline = false;\n\n// Set network status to online and notify all listeners\nRekord.setOnline = function()\n{\n Rekord.online = true;\n Rekord.debug( Rekord.Debugs.ONLINE );\n\n batchExecute(function()\n {\n Rekord.trigger( Rekord.Events.Online );\n });\n};\n\n// Set network status to offline and notify all listeners\nRekord.setOffline = function()\n{\n Rekord.online = false;\n Rekord.debug( Rekord.Debugs.OFFLINE );\n Rekord.trigger( Rekord.Events.Offline );\n};\n\n// This must be called manually - this will try to use built in support for\n// online/offline detection instead of solely using status codes of 0.\nRekord.listenToNetworkStatus = function()\n{\n if (win.addEventListener)\n {\n win.addEventListener( Rekord.Events.Online, Rekord.setOnline, false );\n win.addEventListener( Rekord.Events.Offline, Rekord.setOffline, false );\n }\n else\n {\n win.document.body.ononline = Rekord.setOnline;\n win.document.body.onoffline = Rekord.setOffline;\n }\n};\n\n// Check to see if the network status has changed.\nRekord.checkNetworkStatus = function()\n{\n var online = Rekord.isOnline();\n\n if ( Rekord.forceOffline )\n {\n online = false;\n }\n\n if (online === true && Rekord.online === false)\n {\n Rekord.setOnline();\n }\n\n else if (online === false && Rekord.online === true)\n {\n Rekord.setOffline();\n }\n};\n\n\nvar Rests = {};\n\n// Rekord.rest = function(options, success(data), failure(data, status))\n\nRests.Default =\nRekord.defaultRest =\nRekord.rest = function(database)\n{\n\n return {\n\n // success ( data[] )\n // failure ( data[], status )\n all: function( options, success, failure )\n {\n success( [] );\n },\n\n // success( data )\n // failure( data, status )\n get: function( model, options, success, failure )\n {\n failure( null, -1 );\n },\n\n // success ( data )\n // failure ( data, status )\n create: function( model, encoded, options, success, failure )\n {\n success( {} );\n },\n\n // success ( data )\n // failure ( data, status )\n update: function( model, encoded, options, success, failure )\n {\n success( {} );\n },\n\n // success ( data )\n // failure ( data, status )\n remove: function( model,options, success, failure )\n {\n success( {} );\n },\n\n // success ( data[] )\n // failure ( data[], status )\n query: function( url, query, options, success, failure )\n {\n success( [] );\n }\n\n };\n\n};\n\n/**\n * Sets the rest implementation provided the factory function. This function\n * can only be called once - all subsequent calls will be ignored unless\n * `overwrite` is given as a truthy value.\n *\n * @memberof Rekord\n * @param {Function} factory -\n * The factory which provides rest implementations.\n * @param {Boolean} [overwrite=false] -\n * True if existing implementations are to be ignored and the given factory\n * should be the implementation.\n */\nRekord.setRest = function(factory, overwrite)\n{\n if ( !Rekord.restSet || overwrite )\n {\n Rekord.rest = factory;\n Rekord.restSet = true;\n }\n};\n\n\nvar Stores = {};\n\n/**\n * A factory function for returning an object capable of storing objects for\n * retrieval later by the application.\n *\n * @param {Database} database\n * The database this store is for.\n * @return {Object} -\n * An object with put, remove, and all functions.\n */\nStores.Default =\nRekord.defaultStore =\nRekord.store = function(database)\n{\n return {\n\n /**\n * Places a record in the store with the given key.\n *\n * @param {String|Number} key\n * The key to store the record as.\n * @param {Object} record\n * The record to store.\n * @param {function} success\n * A function to invoke when the record is successfully stored with\n * the key. The arguments of the function should be the key and\n * record passed to this function.\n * @param {function} failure\n * A function to invoke when the record failed to be stored with the\n * key. The arguments of the function should be the key, record, and\n * an error that occurred if available.\n */\n put: function(key, record, success, failure)\n {\n success( key, record );\n },\n\n // TODO\n get: function(key, success, failure)\n {\n failure( key, undefined );\n },\n\n /**\n * Removes a record from the store with the given key.\n *\n * @param {String|Number} key\n * The key to remove from the store.\n * @param {function} success\n * A function to invoke when the record doesn't exist in the store.\n * The arguments of the function are the removedValue (if any) and\n * the key passed to this function.\n * @param {function} failure\n * A function to invoke when there was an issue removing the key\n * from the store. The arguments of the function are the key given\n * to this function and an error that occurred if available.\n */\n remove: function(key, success, failure)\n {\n success( key );\n },\n\n /**\n * Returns all records and their keys to the given success callback.\n *\n * @param {function} success\n * The function to invoke with the array of records and an array\n * of keys.\n * @param {function} failure\n * The function to invoke with the error that occurred if available.\n */\n all: function(success, failure)\n {\n success( [], [] );\n },\n\n\n /**\n * Resets the store so it contains ONLY the given keys & record pairs.\n *\n * @param {String[]} keys -\n * The array of keys.\n * @param {Object[]} records -\n * The array of records to save.\n * @param {function} success\n * The function to invoke with the array of records and an array\n * of keys.\n * @param {function} failure\n * The function to invoke with the error that occurred if available.\n */\n reset: function(keys, records, success, failure)\n {\n success( keys, records );\n }\n\n };\n\n};\n\n/**\n * Sets the store implementation provided the factory function. This function\n * can only be called once - all subsequent calls will be ignored unless\n * `overwrite` is given as a truthy value.\n *\n * @memberof Rekord\n * @param {Function} factory -\n * The factory which provides store implementations.\n * @param {Boolean} [overwrite=false] -\n * True if existing implementations are to be ignored and the given factory\n * should be the implementation.\n */\nRekord.setStore = function(factory, overwrite)\n{\n if ( !Rekord.storeSet || overwrite )\n {\n Rekord.store = factory;\n Rekord.storeSet = true;\n }\n};\n\n\nfunction Gate(callback)\n{\n var opened = false;\n var blocked = [];\n\n var gate = function()\n {\n if ( opened )\n {\n callback.apply( this, arguments );\n }\n else\n {\n blocked.push( this, AP.slice.apply( arguments ) );\n }\n };\n\n gate.open = function()\n {\n if ( !opened )\n {\n for (var i = 0; i < blocked.length; i += 2)\n {\n var context = blocked[ i ];\n var args = blocked[ i + 1 ];\n\n callback.apply( context, args );\n }\n\n blocked.length = 0;\n opened = true;\n }\n };\n\n return gate;\n}\n\n\n\n/**\n *\n * @constructor\n * @memberof Rekord\n * @augments Rekord.Eventful\n */\nfunction Database(options)\n{\n // Apply the options to this database!\n applyOptions( this, options, Defaults );\n\n // Create the key handler based on the given key\n this.keyHandler = isArray( this.key ) ?\n new KeyComposite( this ) : new KeySimple( this );\n\n // If key fields aren't in fields array, add them in\n this.keyHandler.addToFields( this.fields );\n\n // Properties\n this.modelsCached = this.models = ModelCollection.create( this );\n this.allCached = this.all = {};\n this.loaded = {};\n this.className = this.className || toCamelCase( this.name );\n this.initialized = false;\n this.pendingRefresh = false;\n this.localLoaded = false;\n this.remoteLoaded = false;\n this.firstRefresh = false;\n this.pendingOperations = 0;\n this.afterOnline = false;\n this.saveFields = copy( this.fields );\n this.readyPromise = new Promise( null, false );\n this.context = null;\n this.contextIndex = -1;\n\n // Prepare\n this.prepare( this, options );\n\n // Services\n this.rest = this.createRest( this );\n this.store = this.createStore( this );\n this.live = this.createLive( this );\n\n // Functions\n this.setComparator( this.comparator, this.comparatorNullsFirst );\n this.setRevision( this.revision );\n this.setSummarize( this.summarize );\n\n // Relations\n this.relations = {};\n this.relationNames = [];\n\n for (var relationType in options)\n {\n if ( !(relationType in Rekord.Relations) )\n {\n continue;\n }\n\n var RelationClass = Rekord.Relations[ relationType ];\n\n if ( !(RelationClass.prototype instanceof Relation ) )\n {\n continue;\n }\n\n var relationMap = options[ relationType ];\n\n for ( var name in relationMap )\n {\n var relationOptions = relationMap[ name ];\n var relation = new RelationClass();\n\n if ( isString( relationOptions ) )\n {\n relationOptions = {\n model: relationOptions\n };\n }\n else if ( !isObject( relationOptions ) )\n {\n relationOptions = {};\n }\n\n if ( !relationOptions.model && !relationOptions.discriminator )\n {\n relationOptions.model = name;\n }\n\n relation.init( this, name, relationOptions );\n\n if ( relation.save )\n {\n this.saveFields.push( name );\n }\n\n this.relations[ name ] = relation;\n this.relationNames.push( name );\n }\n }\n\n // Projections\n for (var projectionName in this.projections)\n {\n this.projections[ projectionName ] = Projection.parse( this, projectionName );\n }\n}\n\nfunction defaultEncode(model, data, forSaving)\n{\n var encodings = this.encodings;\n\n for (var prop in data)\n {\n if ( prop in encodings )\n {\n data[ prop ] = encodings[ prop ]( data[ prop ], model, prop, forSaving );\n }\n }\n\n return data;\n}\n\nfunction defaultDecode(rawData, data)\n{\n var decodings = this.decodings;\n var target = data || rawData;\n\n for (var prop in rawData)\n {\n if ( prop in decodings )\n {\n target[ prop ] = decodings[ prop ]( rawData[ prop ], rawData, prop );\n }\n else\n {\n target[ prop ] = rawData[ prop ];\n }\n }\n\n return target;\n}\n\nfunction defaultSummarize(model)\n{\n return model.$key();\n}\n\nfunction defaultCreateRest(database)\n{\n return database.rest === false ? Rekord.defaultRest( database ) : Rekord.rest( database );\n}\n\nfunction defaultCreateStore(database)\n{\n return database.store === false ? Rekord.defaultStore( database ) : Rekord.store( database );\n}\n\nfunction defaultCreateLive( database )\n{\n return database.live === false ? Rekord.defaultLive( database ) : Rekord.live( database );\n}\n\nfunction defaultResolveModel( response )\n{\n return response;\n}\n\nfunction defaultResolveModels( response )\n{\n return response;\n}\n\nDatabase.Events =\n{\n NoLoad: 'no-load',\n RemoteLoad: 'remote-load',\n LocalLoad: 'local-load',\n Updated: 'updated',\n ModelAdded: 'model-added',\n ModelUpdated: 'model-updated',\n ModelRemoved: 'model-removed',\n OperationsStarted: 'operations-started',\n OperationsFinished: 'operations-finished',\n Loads: 'no-load remote-load local-load',\n Changes: 'updated'\n};\n\nvar Defaults = Database.Defaults =\n{\n name: undefined, // required\n className: null, // defaults to toCamelCase( name )\n key: 'id',\n keySeparator: '/',\n fields: [],\n ignoredFields: {},\n defaults: {},\n publishAlways: [],\n saveAlways: [],\n comparator: null,\n comparatorNullsFirst: null,\n revision: null,\n traits: [],\n cascade: Cascade.All,\n load: Load.None,\n allComplete: false,\n loadRelations: true,\n autoRefresh: true,\n cache: Cache.All,\n fullSave: false,\n fullPublish: false,\n noReferences: false,\n encodings: {},\n decodings: {},\n projections: {},\n allOptions: null,\n fetchOptions: null,\n getOptions: null,\n updateOptions: null,\n createOptions: null,\n saveOptions: null,\n removeOptions: null,\n queryOptions: null,\n prune: {active: false, max: 0, keepAlive: 0, removeLocal: false},\n prepare: noop,\n encode: defaultEncode,\n decode: defaultDecode,\n resolveModel: defaultResolveModel,\n resolveModels: defaultResolveModels,\n summarize: defaultSummarize,\n createRest: defaultCreateRest,\n createStore: defaultCreateStore,\n createLive: defaultCreateLive\n};\n\nClass.create( Database,\n{\n\n setStoreEnabled: function(enabled)\n {\n if ( enabled )\n {\n if ( this.storeDisabled )\n {\n this.store = this.storeDisabled;\n this.storeDisabled = false;\n }\n }\n else if ( !this.storeDisabled )\n {\n this.storeDisabled = this.store;\n this.store = Rekord.defaultStore( this );\n }\n },\n\n setRestEnabled: function(enabled)\n {\n if ( enabled )\n {\n if ( this.restDisabled )\n {\n this.rest = this.restDisabled;\n this.restDisabled = false;\n }\n }\n else if ( !this.restDisabled )\n {\n this.restDisabled = this.rest;\n this.rest = Rekord.defaultRest( this );\n }\n },\n\n setLiveEnabled: function(enabled)\n {\n if ( enabled )\n {\n if ( this.liveDisabled )\n {\n this.live = this.liveDisabled;\n this.liveDisabled = false;\n }\n }\n else if ( !this.liveDisabled )\n {\n this.liveDisabled = this.live;\n this.live = Rekord.defaultLive( this );\n }\n },\n\n // Notifies a callback when the database has loaded (either locally or remotely).\n ready: function(callback, context, persistent)\n {\n return this.readyPromise.success( callback, context, persistent );\n },\n\n clearAll: function()\n {\n var db = this;\n\n if (db.context)\n {\n db.context.clear( this );\n }\n else\n {\n db.allCached = db.all = {};\n }\n },\n\n clear: function(removeListeners)\n {\n var db = this;\n\n db.clearAll();\n db.models.clear();\n\n if ( removeListeners )\n {\n db.off();\n }\n\n return db;\n },\n\n hasPending: function()\n {\n return this.models.contains(function(model)\n {\n return model.$isPending();\n });\n },\n\n reset: function(failOnPendingChanges, removeListeners)\n {\n var db = this;\n var promise = new Promise();\n\n if ( failOnPendingChanges && db.hasPending() )\n {\n promise.reject( db );\n }\n else\n {\n db.clear( removeListeners );\n\n db.store.reset( [], [],\n function()\n {\n promise.resolve( db );\n },\n function()\n {\n promise.reject( db );\n }\n );\n }\n\n return promise;\n },\n\n // Determines whether the given object has data to save\n hasData: function(saving)\n {\n if ( !isObject( saving ) )\n {\n return false;\n }\n\n for (var prop in saving)\n {\n if ( !this.ignoredFields[ prop ] )\n {\n return true;\n }\n }\n\n return false;\n },\n\n // Grab a model with the given input and notify the callback\n grabModel: function(input, callback, context, remoteData)\n {\n var db = this;\n var promise = new Promise();\n\n promise.success( callback, context || db );\n\n function checkModel()\n {\n var result = db.parseModel( input, remoteData );\n\n if ( result !== false && !promise.isComplete() && db.initialized )\n {\n var remoteLoaded = db.remoteLoaded || !db.hasLoad( Load.All );\n var missingModel = (result === null || !result.$isSaved());\n var lazyLoad = db.hasLoad( Load.Lazy );\n\n if ( lazyLoad && remoteLoaded && missingModel )\n {\n if ( !result )\n {\n result = db.keyHandler.buildObjectFromKey( db.keyHandler.buildKeyFromInput( input ) );\n }\n\n result.$once( Model.Events.RemoteGets, function()\n {\n if ( !promise.isComplete() )\n {\n if ( isObject( input ) )\n {\n result.$set( input );\n }\n\n promise.resolve( result.$isSaved() ? result : null );\n }\n });\n\n result.$refresh( Cascade.All, db.fetchOptions );\n }\n else\n {\n promise.resolve( result );\n }\n }\n\n return promise.isComplete() ? false : true;\n }\n\n if ( checkModel() )\n {\n db.ready( checkModel, db, true );\n }\n\n return promise;\n },\n\n // Parses the model from the given input\n //\n // Returns false if the input doesn't resolve to a model at the moment\n // Returns null if the input doesn't resolve to a model and all models have been remotely loaded\n //\n // parseModel( Rekord )\n // parseModel( Rekord.Model )\n // parseModel( 'uuid' )\n // parseModel( ['uuid'] )\n // parseModel( modelInstance )\n // parseModel( {name:'new model'} )\n // parseModel( {id:4, name:'new or existing model'} )\n //\n parseModel: function(input, remoteData)\n {\n var db = this;\n var keyHandler = db.keyHandler;\n var hasRemote = db.remoteLoaded || !db.hasLoad( Load.All );\n\n if ( !isValue( input ) )\n {\n return hasRemote ? null : false;\n }\n\n if ( isRekord( input ) )\n {\n input = new input();\n }\n if ( isFunction( input ) )\n {\n input = input();\n }\n\n var key = keyHandler.buildKeyFromInput( input );\n\n if ( input instanceof db.Model )\n {\n return input;\n }\n else if ( key in db.all )\n {\n var model = db.all[ key ];\n\n if ( isObject( input ) )\n {\n keyHandler.buildKeyFromRelations( input );\n\n if ( remoteData )\n {\n db.putRemoteData( input, key, model );\n }\n else\n {\n model.$set( input );\n }\n }\n\n return model;\n }\n else if ( isObject( input ) )\n {\n keyHandler.buildKeyFromRelations( input );\n\n if ( remoteData )\n {\n return db.putRemoteData( input );\n }\n else\n {\n return db.instantiate( db.decode( input ) );\n }\n }\n else if ( hasRemote )\n {\n return null;\n }\n\n return false;\n },\n\n // Sorts the models & notifies listeners that the database has been updated.\n updated: function()\n {\n this.sort(); // TODO remove\n this.trigger( Database.Events.Updated );\n },\n\n // Sets a revision comparision function for this database. It can be a field\n // name or a function. This is used to avoid updating model data that is older\n // than the model's current data.\n setRevision: function(revision)\n {\n if ( isFunction( revision ) )\n {\n this.revisionFunction = revision;\n }\n else if ( isString( revision ) )\n {\n this.revisionFunction = function(a, b)\n {\n var ar = isObject( a ) && revision in a ? a[ revision ] : undefined;\n var br = isObject( b ) && revision in b ? b[ revision ] : undefined;\n\n return ar === undefined || br === undefined ? false : compare( ar, br ) > 0;\n };\n }\n else\n {\n this.revisionFunction = function(a, b)\n {\n return false;\n };\n }\n },\n\n // Sets a comparator for this database. It can be a field name, a field name\n // with a minus in the front to sort in reverse, or a comparator function.\n setComparator: function(comparator, nullsFirst)\n {\n this.models.setComparator( comparator, nullsFirst );\n },\n\n addComparator: function(comparator, nullsFirst)\n {\n this.models.addComparator( comparator, nullsFirst );\n },\n\n setSummarize: function(summarize)\n {\n if ( isFunction( summarize ) )\n {\n this.summarize = summarize;\n }\n else if ( isString( summarize ) )\n {\n if ( indexOf( this.fields, summarize ) !== false )\n {\n this.summarize = function(model)\n {\n return isValue( model ) ? model[ summarize ] : model;\n };\n }\n else\n {\n this.summarize = createFormatter( summarize );\n }\n }\n else\n {\n this.summarize = function(model)\n {\n return model.$key();\n };\n }\n },\n\n // Sorts the database if it isn't sorted.\n sort: function()\n {\n this.models.sort();\n },\n\n // Determines whether this database is sorted.\n isSorted: function()\n {\n return this.models.isSorted();\n },\n\n clean: function()\n {\n var db = this;\n var keys = db.models.keys;\n var models = db.models;\n\n db.clearAll();\n\n for (var i = 0; i < keys.length; i++)\n {\n db.addReference( models[ i ], keys[ i ] );\n }\n },\n\n // Handles when we receive data from the server - either from\n // a publish, refresh, or values being returned on a save.\n putRemoteData: function(encoded, key, model, overwrite)\n {\n if ( !isObject( encoded ) )\n {\n return model;\n }\n\n var db = this;\n var key = key || db.keyHandler.getKey( encoded, true );\n\n // The remote source might be crazy, if the key isn't there then log it and ignore it\n if ( !isValue( key ) )\n {\n Rekord.debug( Rekord.Debugs.MISSING_KEY, db, encoded );\n\n return;\n }\n\n var model = model || db.all[ key ];\n var decoded = db.decode( copy( encoded ) );\n\n // Reject the data if it's a lower revision\n if ( model )\n {\n var revisionRejected = this.revisionFunction( model, encoded );\n\n if ( revisionRejected )\n {\n Rekord.debug( Rekord.Debugs.SAVE_OLD_REVISION, db, model, encoded );\n\n return model;\n }\n }\n\n // If the model already exists, update it.\n if ( model )\n {\n if ( db.keyHandler.hasKeyChange( model, decoded ) )\n {\n key = model.$setKey( db.keyHandler.getKey( decoded, true ) );\n }\n\n db.addReference( model, key );\n\n if ( !model.$saved )\n {\n model.$saved = {};\n }\n\n var current = model; // model.$toJSON( true );\n var conflicts = {};\n var conflicted = false;\n var updated = {};\n var previous = {};\n var saved = {};\n var notReallySaved = isEmpty( model.$saved );\n var relations = db.relations;\n var compareTo = db.decode( model.$saved ); // model.$saved\n\n for (var prop in encoded)\n {\n if ( prop.charAt(0) === '$' )\n {\n continue;\n }\n\n if ( prop in relations )\n {\n model.$set( prop, encoded[ prop ], true );\n\n continue;\n }\n\n var currentValue = current[ prop ];\n var savedValue = compareTo[ prop ];\n\n previous[ prop ] = model[ prop ];\n saved[ prop ] = savedValue;\n\n if ( notReallySaved || overwrite || equals( currentValue, savedValue ) )\n {\n model[ prop ] = decoded[ prop ];\n updated[ prop ] = encoded[ prop ];\n\n if ( model.$local )\n {\n model.$local[ prop ] = encoded[ prop ];\n }\n }\n else\n {\n conflicts[ prop ] = encoded[ prop ];\n conflicted = true;\n }\n\n model.$saved[ prop ] = copy( encoded[ prop ] );\n }\n\n if ( conflicted )\n {\n model.$trigger( Model.Events.PartialUpdate, [encoded, updated, previous, saved, conflicts] );\n }\n else\n {\n model.$trigger( Model.Events.FullUpdate, [encoded, updated, previous, saved, conflicts] );\n }\n\n model.$trigger( Model.Events.RemoteUpdate, [encoded, updated, previous, saved, conflicts] );\n\n model.$addOperation( SaveNow );\n\n if ( !db.models.has( key ) )\n {\n db.saveReference( model, key );\n db.trigger( Database.Events.ModelAdded, [model, true] );\n }\n }\n // The model doesn't exist, create it.\n else\n {\n model = db.createModel( decoded, true );\n\n if ( model )\n {\n if ( db.cache === Cache.All )\n {\n model.$local = model.$toJSON( false );\n model.$local.$status = model.$status;\n model.$saved = model.$local.$saved = model.$toJSON( true );\n\n model.$addOperation( SaveNow );\n }\n else\n {\n model.$saved = model.$toJSON( true );\n }\n }\n }\n\n return model;\n },\n\n createModel: function(decoded, remoteData)\n {\n var db = this;\n var model = db.instantiate( decoded, remoteData );\n\n if ( model.$invalid === true )\n {\n Rekord.debug( Rekord.Debugs.MISSING_KEY, db, decoded );\n\n return;\n }\n\n var key = model.$key();\n\n if ( !db.models.has( key ) )\n {\n db.saveReference( model, key );\n db.trigger( Database.Events.ModelAdded, [model, remoteData] );\n }\n\n return model;\n },\n\n destroyModel: function(model, modelKey)\n {\n this.pruneModel( model, modelKey );\n\n model.$trigger( Model.Events.RemoteAndRemove );\n\n Rekord.debug( Rekord.Debugs.REMOTE_REMOVE, this, model );\n },\n\n pruneModel: function(model, modelKey)\n {\n var db = this;\n var key = modelKey || model.$key();\n\n db.removeReference( key );\n db.models.remove( key );\n db.trigger( Database.Events.ModelRemoved, [model] );\n },\n\n removeReference: function(key)\n {\n delete this.all[ key ];\n },\n\n hasPruning: function()\n {\n return this.prune.max || this.prune.keepAlive;\n },\n\n pruneModels: function()\n {\n var db = this;\n var prune = db.prune;\n var models = db.models;\n\n if (prune.max || prune.keepAlive)\n {\n if (prune.active)\n {\n var youngestAllowed = now() - prune.keepAlive;\n\n var pruneModel = function(model)\n {\n if (prune.removeLocal)\n {\n model.$remove( Cascade.Local );\n }\n else\n {\n db.pruneModel( model );\n }\n };\n\n var isTooYoung = function(model)\n {\n return model.$touched <= youngestAllowed;\n };\n\n while ( prune.max && models.length > prune.max )\n {\n var youngest = models.minModel('$touched');\n\n if (youngest)\n {\n pruneModel( youngest );\n }\n }\n\n if ( prune.keepAlive )\n {\n models.eachWhere( pruneModel, isTooYoung );\n }\n }\n }\n },\n\n destroyLocalUncachedModel: function(model, key)\n {\n var db = this;\n\n if ( model )\n {\n if ( model.$hasChanges() )\n {\n delete model.$saved;\n\n db.keyHandler.removeKey( model );\n\n model.$trigger( Model.Events.Detach );\n\n return false;\n }\n\n db.destroyModel( model, key );\n\n return true;\n }\n\n return false;\n },\n\n destroyLocalCachedModel: function(model, key)\n {\n var db = this;\n\n if ( model )\n {\n // If a model was removed remotely but the model has changes - don't remove it.\n if ( model.$hasChanges() )\n {\n // Removed saved history and the current ID\n delete model.$saved;\n\n db.keyHandler.removeKey( model );\n\n if ( model.$local )\n {\n delete model.$local.$saved;\n\n db.keyHandler.removeKey( model.$local );\n }\n\n model.$trigger( Model.Events.Detach );\n\n model.$addOperation( SaveNow );\n\n return false;\n }\n\n model.$addOperation( RemoveNow );\n\n db.destroyModel( model, key );\n }\n else\n {\n db.store.remove( key, function(removedValue)\n {\n if (removedValue)\n {\n Rekord.debug( Rekord.Debugs.REMOTE_REMOVE, db, removedValue );\n }\n });\n\n // The model didn't exist\n return false;\n }\n\n return true;\n },\n\n // Destroys a model locally because it doesn't exist remotely\n destroyLocalModel: function(key)\n {\n var db = this;\n var model = db.all[ key ];\n\n if ( db.cache === Cache.All )\n {\n return db.destroyLocalCachedModel( model, key );\n }\n else\n {\n return db.destroyLocalUncachedModel( model, key );\n }\n },\n\n loadFinish: function()\n {\n var db = this;\n\n batchExecute(function()\n {\n for (var key in db.loaded)\n {\n var model = db.loaded[ key ];\n\n if ( model.$status === Model.Status.RemovePending )\n {\n Rekord.debug( Rekord.Debugs.LOCAL_RESUME_DELETE, db, model );\n\n model.$addOperation( RemoveRemote );\n }\n else\n {\n if ( model.$status === Model.Status.SavePending )\n {\n Rekord.debug( Rekord.Debugs.LOCAL_RESUME_SAVE, db, model );\n\n model.$addOperation( SaveRemote );\n }\n else\n {\n Rekord.debug( Rekord.Debugs.LOCAL_LOAD_SAVED, db, model );\n }\n\n db.saveReference( model, key, true );\n }\n }\n });\n\n db.loaded = {};\n db.updated();\n\n if ( db.hasLoad( Load.All ) )\n {\n if ( db.pendingOperations === 0 )\n {\n db.refresh();\n }\n else\n {\n db.firstRefresh = true;\n }\n }\n },\n\n hasLoad: function(load)\n {\n return (this.load & load) !== 0;\n },\n\n loadBegin: function(onLoaded)\n {\n var db = this;\n\n function onLocalLoad(records, keys)\n {\n Rekord.debug( Rekord.Debugs.LOCAL_LOAD, db, records );\n\n for (var i = 0; i < records.length; i++)\n {\n var encoded = records[ i ];\n var key = keys[ i ];\n var decoded = db.decode( copy( encoded, true ) );\n var model = db.instantiate( decoded, true );\n\n if ( model.$invalid === true )\n {\n Rekord.debug( Rekord.Debugs.MISSING_KEY, db, encoded );\n\n break;\n }\n\n model.$local = encoded;\n model.$saved = encoded.$saved;\n\n if ( model.$status !== Model.Status.Removed )\n {\n db.loaded[ key ] = model;\n db.addReference( model, key );\n }\n }\n\n db.localLoaded = true;\n db.triggerLoad( Database.Events.LocalLoad );\n\n onLoaded( true, db );\n }\n\n function onLocalError()\n {\n db.loadNone();\n\n onLoaded( false, db );\n }\n\n if ( db.hasLoad( Load.All ) && db.autoRefresh )\n {\n Rekord.after( Rekord.Events.Online, db.onOnline, db );\n }\n\n if ( db.cache === Cache.None )\n {\n db.loadNone();\n\n onLoaded( false, db );\n }\n else\n {\n db.store.all( onLocalLoad, onLocalError );\n }\n },\n\n triggerLoad: function(loadEvent, additionalParameters)\n {\n var db = this;\n\n db.initialized = true;\n db.trigger( loadEvent, [ db ].concat( additionalParameters || [] ) );\n db.readyPromise.reset().resolve( db );\n },\n\n loadNone: function()\n {\n var db = this;\n\n if ( db.hasLoad( Load.All ) )\n {\n db.refresh();\n }\n else\n {\n db.triggerLoad( Database.Events.NoLoad );\n }\n },\n\n onOnline: function()\n {\n var db = this;\n\n db.afterOnline = true;\n\n if ( db.pendingOperations === 0 )\n {\n db.onOperationRest();\n }\n },\n\n onOperationRest: function()\n {\n var db = this;\n\n if ( ( db.autoRefresh && db.remoteLoaded && db.afterOnline ) || db.firstRefresh )\n {\n db.afterOnline = false;\n db.firstRefresh = false;\n\n Rekord.debug( Rekord.Debugs.AUTO_REFRESH, db );\n\n db.refresh();\n }\n },\n\n handleRefreshSuccess: function(promise)\n {\n var db = this;\n\n return function onRefreshSuccess(response)\n {\n var models = db.resolveModels( response );\n var mapped = {};\n\n for (var i = 0; i < models.length; i++)\n {\n var model = db.putRemoteData( models[ i ] );\n\n if ( model )\n {\n var key = model.$key();\n\n mapped[ key ] = model;\n }\n }\n\n if ( db.allComplete )\n {\n var keys = db.models.keys().slice();\n\n for (var i = 0; i < keys.length; i++)\n {\n var k = keys[ i ];\n\n if ( !(k in mapped) )\n {\n var old = db.models.get( k );\n\n if ( old.$saved )\n {\n Rekord.debug( Rekord.Debugs.REMOTE_LOAD_REMOVE, db, k );\n\n db.destroyLocalModel( k );\n }\n }\n }\n }\n\n db.remoteLoaded = true;\n db.triggerLoad( Database.Events.RemoteLoad );\n\n db.updated();\n\n Rekord.debug( Rekord.Debugs.REMOTE_LOAD, db, models );\n\n promise.resolve( db.models );\n };\n },\n\n handleRefreshFailure: function(promise)\n {\n var db = this;\n\n return function onRefreshFailure(response, status)\n {\n if ( status === 0 )\n {\n Rekord.checkNetworkStatus();\n\n if ( !Rekord.online )\n {\n db.pendingRefresh = true;\n\n Rekord.once( Rekord.Events.Online, db.onRefreshOnline, db );\n }\n\n Rekord.debug( Rekord.Debugs.REMOTE_LOAD_OFFLINE, db );\n }\n else\n {\n Rekord.debug( Rekord.Debugs.REMOTE_LOAD_ERROR, db, status );\n\n db.triggerLoad( Database.Events.NoLoad, [response] );\n }\n\n promise.reject( db.models );\n };\n },\n\n executeRefresh: function(success, failure)\n {\n this.rest.all( this.allOptions, success, failure );\n },\n\n // Loads all data remotely\n refresh: function(callback, context)\n {\n var db = this;\n var promise = new Promise();\n var success = this.handleRefreshSuccess( promise );\n var failure = this.handleRefreshFailure( promise );\n\n promise.complete( callback, context || db );\n\n batchExecute(function()\n {\n db.executeRefresh( success, failure );\n });\n\n return promise;\n },\n\n onRefreshOnline: function()\n {\n var db = this;\n\n Rekord.debug( Rekord.Debugs.REMOTE_LOAD_RESUME, db );\n\n if ( db.pendingRefresh )\n {\n db.pendingRefresh = false;\n\n db.refresh();\n }\n },\n\n // Returns a model\n get: function(key)\n {\n return this.all[ this.keyHandler.buildKeyFromInput( key ) ];\n },\n\n filter: function(isValid)\n {\n var all = this.all;\n var filtered = [];\n\n for (var key in all)\n {\n var model = all[ key ];\n\n if ( isValid( model ) )\n {\n filtered.push( model );\n }\n }\n\n return filtered;\n },\n\n hasTrait: function(trait, comparator)\n {\n var cmp = comparator || equals;\n\n return isArray( this.traits ) && indexOf( this.traits, trait, cmp ) !== false;\n },\n\n hasTraits: function(traits, comparator)\n {\n for (var i = 0; i < traits.length; i++)\n {\n if ( !this.hasTrait( traits[ i ], comparator ) )\n {\n return false;\n }\n }\n\n return true;\n },\n\n liveSave: function(key, encoded)\n {\n this.putRemoteData( encoded, key );\n this.updated();\n\n Rekord.debug( Rekord.Debugs.REALTIME_SAVE, this, encoded, key );\n },\n\n liveRemove: function(key)\n {\n if ( this.destroyLocalModel( key ) )\n {\n this.updated();\n }\n\n Rekord.debug( Rekord.Debugs.REALTIME_REMOVE, this, key );\n },\n\n // Return an instance of the model with the data as initial values\n instantiate: function(data, remoteData)\n {\n return new this.Model( data, remoteData );\n },\n\n addReference: function(model, key)\n {\n if (!this.noReferences)\n {\n this.all[ key || model.$key() ] = model;\n }\n },\n\n saveReference: function(model, key, delaySort)\n {\n if ( !this.noReferences )\n {\n this.models.put( key || model.$key(), model, delaySort );\n }\n },\n\n // Save the model\n save: function(model, cascade, options)\n {\n var db = this;\n\n if ( model.$isDeleted() )\n {\n Rekord.debug( Rekord.Debugs.SAVE_DELETED, db, model );\n\n return;\n }\n\n var key = model.$key();\n var existing = db.models.has( key );\n\n if ( existing )\n {\n db.trigger( Database.Events.ModelUpdated, [model] );\n\n model.$trigger( Model.Events.UpdateAndSave );\n }\n else\n {\n db.saveReference( model, key );\n db.trigger( Database.Events.ModelAdded, [model] );\n db.updated();\n\n model.$trigger( Model.Events.CreateAndSave );\n }\n\n model.$addOperation( SaveLocal, cascade, options );\n },\n\n // Remove the model\n remove: function(model, cascade, options)\n {\n var db = this;\n\n // If we have it in the models, remove it!\n this.removeFromModels( model );\n\n // If we're offline and we have a pending save - cancel the pending save.\n if ( model.$status === Model.Status.SavePending )\n {\n Rekord.debug( Rekord.Debugs.REMOVE_CANCEL_SAVE, db, model );\n }\n\n model.$status = Model.Status.RemovePending;\n\n model.$addOperation( RemoveLocal, cascade, options );\n },\n\n removeFromModels: function(model)\n {\n var db = this;\n var key = model.$key();\n\n if ( db.models.has( key ) )\n {\n db.models.remove( key );\n db.trigger( Database.Events.ModelRemoved, [model] );\n db.updated();\n\n model.$trigger( Model.Events.Removed );\n }\n }\n\n});\n\naddEventful( Database );\n\naddEventFunction( Database, 'change', Database.Events.Changes );\n\n\n/**\n * An instance\n *\n * @constructor\n * @memberof Rekord\n * @augments Rekord.Eventful$\n * @param {Rekord.Database} db\n * The database instance used in model instances.\n */\nfunction Model(db)\n{\n Class.prop( this, '$db', db );\n\n /**\n * @property {Database} $db\n * The reference to the database this model is stored in.\n */\n\n /**\n * @property {Object} [$saved]\n * An object of encoded data representing the values saved remotely.\n * If this object does not exist - the model hasn't been created\n * yet.\n */\n\n /**\n * @property {Object} [$local]\n * The object of encoded data that is stored locally. It's $saved\n * property is the same object as this $saved property.\n */\n\n /**\n * @property {Boolean} $status\n * Whether there is a pending save for this model.\n */\n}\n\nModel.Events =\n{\n Created: 'created',\n Saved: 'saved',\n PreSave: 'pre-save',\n PostSave: 'post-save',\n PreRemove: 'pre-remove',\n PostRemove: 'post-remove',\n PartialUpdate: 'partial-update',\n FullUpdate: 'full-update',\n Updated: 'updated',\n Detach: 'detach',\n Change: 'change',\n CreateAndSave: 'created saved',\n UpdateAndSave: 'updated saved',\n KeyUpdate: 'key-update',\n RelationUpdate: 'relation-update',\n Removed: 'removed',\n RemoteUpdate: 'remote-update',\n LocalSave: 'local-save',\n LocalSaveFailure: 'local-save-failure',\n LocalSaves: 'local-save local-save-failure',\n RemoteSave: 'remote-save',\n RemoteSaveFailure: 'remote-save-failure',\n RemoteSaveOffline: 'remote-save-offline',\n RemoteSaves: 'remote-save remote-save-failure remote-save-offline',\n LocalRemove: 'local-remove',\n LocalRemoveFailure: 'local-remove-failure',\n LocalRemoves: 'local-remove local-remove-failure',\n RemoteRemove: 'remote-remove',\n RemoteRemoveFailure: 'remote-remove-failure',\n RemoteRemoveOffline: 'remote-remove-offline',\n RemoteRemoves: 'remote-remove remote-remove-failure remote-remove-offline',\n LocalGet: 'local-get',\n LocalGetFailure: 'local-get-failure',\n LocalGets: 'local-get local-get-failure',\n RemoteGet: 'remote-get',\n RemoteGetFailure: 'remote-get-failure',\n RemoteGetOffline: 'remote-get-offline',\n RemoteGets: 'remote-get remote-get-failure remote-get-offline',\n RemoteAndRemove: 'remote-remove removed',\n SavedRemoteUpdate: 'saved remote-update',\n OperationsStarted: 'operations-started',\n OperationsFinished: 'operations-finished',\n KeyChange: 'key-change',\n Changes: 'saved remote-update key-update relation-update removed key-change change'\n};\n\nModel.Status =\n{\n Synced: 0,\n SavePending: 1,\n RemovePending: 2,\n Removed: 3\n};\n\nModel.Blocked =\n{\n toString: true,\n valueOf: true\n};\n\nClass.create( Model,\n{\n\n $init: function(props, remoteData)\n {\n this.$status = Model.Status.Synced;\n\n Class.props(this, {\n $operation: null,\n $relations: {},\n $dependents: new Dependents( this ),\n $savedState: false,\n $saved: false,\n $local: false,\n $touched: now()\n });\n\n if ( remoteData )\n {\n var key = this.$db.keyHandler.getKey( props, true );\n\n if ( !isValue( key ) )\n {\n Class.prop( this, '$invalid', true );\n\n return;\n }\n\n this.$db.addReference( this, key );\n this.$set( props, undefined, remoteData );\n }\n else\n {\n this.$reset( props );\n }\n\n if ( this.$db.loadRelations )\n {\n var databaseRelations = this.$db.relations;\n\n for (var name in databaseRelations)\n {\n var relation = databaseRelations[ name ];\n\n if ( !relation.lazy )\n {\n this.$getRelation( name, undefined, remoteData );\n }\n }\n }\n },\n\n $load: function(relations)\n {\n if ( isArray( relations ) )\n {\n for (var i = 0; i < relations.length; i++)\n {\n this.$getRelation( relations[ i ] );\n }\n }\n else if ( isString( relations ) )\n {\n this.$getRelation( relations );\n }\n else\n {\n var databaseRelations = this.$db.relations;\n\n for (var name in databaseRelations)\n {\n this.$getRelation( name );\n }\n }\n },\n\n $reset: function(props)\n {\n var def = this.$db.defaults;\n var fields = this.$db.fields;\n var relations = this.$db.relations;\n var keyHandler = this.$db.keyHandler;\n var keyFields = this.$db.key;\n\n if ( !isEmpty( def ) )\n {\n for (var i = 0; i < fields.length; i++)\n {\n var prop = fields[ i ];\n var defaultValue = def[ prop ];\n var evaluatedValue = evaluate( defaultValue );\n\n this[ prop ] = evaluatedValue;\n }\n }\n else\n {\n for (var i = 0; i < fields.length; i++)\n {\n var prop = fields[ i ];\n\n this[ prop ] = undefined;\n }\n }\n\n var key = null;\n\n // First try pulling key from properties (only if it hasn't been\n // initialized through defaults)\n if ( props )\n {\n key = keyHandler.getKey( props, true );\n }\n\n // If the key wasn't specified, try generating it on this model\n if ( !isValue( key ) )\n {\n key = keyHandler.getKey( this );\n }\n // The key was specified in the properties, apply it to this model\n else\n {\n updateFieldsReturnChanges( this, keyFields, props, keyFields );\n }\n\n // The key exists on this model - place the reference of this model\n // in the all map and set the cached key.\n if ( isValue( key ) )\n {\n this.$db.addReference( this, key );\n this.$$key = key;\n }\n\n // Apply the default relation values now that this key is most likely populated\n if ( !isEmpty( def ) )\n {\n for (var prop in relations)\n {\n if ( prop in def )\n {\n var defaultValue = def[ prop ];\n var evaluatedValue = evaluate( defaultValue );\n var hasRelation = !!this.$relations[ prop ];\n var relation = this.$getRelation( prop, evaluatedValue );\n\n if ( hasRelation )\n {\n relation.set( this, evaluatedValue );\n }\n }\n }\n }\n\n // Set the remaing properties\n this.$set( props );\n },\n\n $set: function(props, value, remoteData, avoidChange)\n {\n if ( isObject( props ) )\n {\n for (var prop in props)\n {\n this.$set( prop, props[ prop ], remoteData, true );\n }\n }\n else if ( isString( props ) )\n {\n if ( Model.Blocked[ props ] )\n {\n return;\n }\n\n var exists = this.$hasRelation( props );\n var relation = this.$getRelation( props, value, remoteData );\n\n if ( relation )\n {\n if ( exists )\n {\n relation.set( this, value, remoteData );\n }\n }\n else\n {\n this[ props ] = value;\n }\n }\n\n if ( !avoidChange && isValue( props ) )\n {\n this.$trigger( Model.Events.Change, [props, value] );\n }\n },\n\n $get: function(props, copyValues)\n {\n if ( isArray( props ) )\n {\n return grab( this, props, copyValues );\n }\n else if ( isObject( props ) )\n {\n for (var p in props)\n {\n props[ p ] = copyValues ? copy( this[ p ] ) : this[ p ];\n }\n\n return props;\n }\n else if ( isString( props ) )\n {\n if ( Model.Blocked[ props ] )\n {\n return;\n }\n\n var relation = this.$getRelation( props );\n\n if ( relation )\n {\n var values = relation.get( this );\n\n return copyValues ? copy( values ) : values;\n }\n else\n {\n return copyValues ? copy( this[ props ] ) : this[ props ];\n }\n }\n },\n\n $decode: function()\n {\n this.$db.decode( this );\n },\n\n $sync: function(prop, removeUnrelated)\n {\n var relation = this.$getRelation( prop );\n\n if ( relation )\n {\n relation.sync( this, removeUnrelated );\n }\n },\n\n $relate: function(prop, relate, remoteData)\n {\n var relation = this.$getRelation( prop );\n\n if ( relation )\n {\n relation.relate( this, relate, remoteData );\n }\n },\n\n $unrelate: function(prop, unrelated, remoteData)\n {\n var relation = this.$getRelation( prop );\n\n if ( relation )\n {\n relation.unrelate( this, unrelated, remoteData );\n }\n },\n\n $isRelated: function(prop, related)\n {\n var relation = this.$getRelation( prop );\n\n return relation && relation.isRelated( this, related );\n },\n\n $hasRelation: function(prop)\n {\n return prop in this.$relations;\n },\n\n $getRelation: function(prop, initialValue, remoteData)\n {\n var databaseRelations = this.$db.relations;\n var relation = databaseRelations[ prop ];\n\n if ( relation )\n {\n if ( !(prop in this.$relations) )\n {\n relation.load( this, initialValue, remoteData );\n }\n\n return relation;\n }\n\n return false;\n },\n\n $save: function(setProperties, setValue, cascade, options)\n {\n if ( isObject( setProperties ) )\n {\n options = cascade;\n cascade = setValue;\n setValue = undefined;\n }\n else if ( isNumber( setProperties ) )\n {\n options = setValue;\n cascade = setProperties;\n setValue = undefined;\n setProperties = undefined;\n }\n\n if ( !isNumber( cascade ) )\n {\n cascade = this.$db.cascade;\n }\n\n if ( this.$isDeleted() )\n {\n Rekord.debug( Rekord.Debugs.SAVE_DELETED, this.$db, this );\n\n return Promise.resolve( this );\n }\n\n if ( !this.$hasKey() )\n {\n throw 'Key missing from model';\n }\n\n var promise = createModelPromise( this, cascade,\n Model.Events.RemoteSave,\n Model.Events.RemoteSaveFailure,\n Model.Events.RemoteSaveOffline,\n Model.Events.LocalSave,\n Model.Events.LocalSaveFailure\n );\n\n return Promise.singularity( promise, this, function(singularity)\n {\n batchExecute(function()\n {\n this.$touch();\n\n this.$db.addReference( this );\n\n if ( setProperties !== undefined )\n {\n this.$set( setProperties, setValue );\n }\n\n this.$trigger( Model.Events.PreSave, [this] );\n\n this.$db.save( this, cascade, options );\n\n this.$db.pruneModels();\n\n this.$trigger( Model.Events.PostSave, [this] );\n\n }, this );\n });\n },\n\n $remove: function(cascade, options)\n {\n var cascade = isNumber( cascade ) ? cascade : this.$db.cascade;\n\n if ( !this.$exists() )\n {\n return Promise.resolve( this );\n }\n\n var promise = createModelPromise( this, cascade,\n Model.Events.RemoteRemove,\n Model.Events.RemoteRemoveFailure,\n Model.Events.RemoteRemoveOffline,\n Model.Events.LocalRemove,\n Model.Events.LocalRemoveFailure\n );\n\n return Promise.singularity( promise, this, function(singularity)\n {\n batchExecute(function()\n {\n this.$trigger( Model.Events.PreRemove, [this] );\n\n this.$db.remove( this, cascade, options );\n\n this.$trigger( Model.Events.PostRemove, [this] );\n\n }, this );\n });\n },\n\n $refresh: function(cascade, options)\n {\n var promise = createModelPromise( this, cascade,\n Model.Events.RemoteGet,\n Model.Events.RemoteGetFailure,\n Model.Events.RemoteGetOffline,\n Model.Events.LocalGet,\n Model.Events.LocalGetFailure\n );\n\n if ( canCascade( cascade, Cascade.Rest ) )\n {\n this.$addOperation( GetRemote, cascade, options );\n }\n else if ( canCascade( cascade, Cascade.Local ) )\n {\n this.$addOperation( GetLocal, cascade, options );\n }\n else\n {\n promise.resolve( this );\n }\n\n return promise;\n },\n\n $autoRefresh: function(cascade, options)\n {\n var callRefresh = function()\n {\n this.$refresh( cascade, options );\n };\n\n Rekord.on( Rekord.Events.Online, callRefresh, this );\n\n return this;\n },\n\n $cancel: function(reset, options)\n {\n if ( this.$saved )\n {\n this.$save( this.$saved, this.$db.cascade, options );\n }\n else if ( reset )\n {\n this.$reset();\n }\n },\n\n $clone: function(properties)\n {\n // If field is given, evaluate the value and use it instead of value on this object\n // If relation is given, call clone on relation\n\n var db = this.$db;\n var key = db.key;\n var fields = db.fields;\n var relations = db.relations;\n var values = {};\n\n for (var i = 0; i < fields.length; i++)\n {\n var f = fields[ i ];\n\n if ( properties && f in properties )\n {\n values[ f ] = evaluate( properties[ f ] );\n }\n else if ( f in this )\n {\n values[ f ] = copy( this[ f ] );\n }\n }\n\n if ( isString( key ) )\n {\n delete values[ key ];\n }\n\n var cloneKey = db.keyHandler.getKey( values );\n var modelKey = this.$key();\n\n if ( cloneKey === modelKey )\n {\n throw 'A clone cannot have the same key as the original model.';\n }\n\n for (var relationName in relations)\n {\n if ( properties && relationName in properties )\n {\n relations[ relationName ].preClone( this, values, properties[ relationName ] );\n }\n }\n\n var clone = db.instantiate( values );\n var relationValues = {};\n\n for (var relationName in relations)\n {\n if ( properties && relationName in properties )\n {\n relations[ relationName ].postClone( this, relationValues, properties[ relationName ] );\n }\n }\n\n clone.$set( relationValues );\n\n return clone;\n },\n\n $push: function(fields)\n {\n this.$savedState = this.$db.encode( this, grab( this, fields || this.$db.fields, true ), false );\n },\n\n $pop: function(dontDiscard)\n {\n if ( isObject( this.$savedState ) )\n {\n this.$set( this.$savedState );\n\n if ( !dontDiscard )\n {\n this.$discard();\n }\n }\n },\n\n $discard: function()\n {\n this.$savedState = false;\n },\n\n $exists: function()\n {\n return !this.$isDeleted() && this.$db.models.has( this.$key() );\n },\n\n $addOperation: function(OperationType, cascade, options)\n {\n var operation = new OperationType( this, cascade, options );\n\n if ( !this.$operation )\n {\n this.$operation = operation;\n this.$operation.execute();\n }\n else\n {\n this.$operation.queue( operation );\n }\n },\n\n $toJSON: function( forSaving )\n {\n var encoded = this.$db.encode( this, grab( this, this.$db.fields, true ), forSaving );\n\n var databaseRelations = this.$db.relations;\n var relations = this.$relations;\n\n for (var name in relations)\n {\n databaseRelations[ name ].encode( this, encoded, forSaving );\n }\n\n return encoded;\n },\n\n $changed: function()\n {\n this.$trigger( Model.Events.Change );\n },\n\n $updated: function()\n {\n this.$changed();\n this.$db.trigger( Database.Events.ModelUpdated, [this] );\n },\n\n $key: function(quietly)\n {\n if ( !this.$$key )\n {\n this.$$key = this.$db.keyHandler.getKey( this, quietly );\n }\n\n return this.$$key;\n },\n\n $keys: function()\n {\n return this.$db.keyHandler.getKeys( this );\n },\n\n $uid: function()\n {\n return this.$db.name + '$' + this.$key();\n },\n\n $hasKey: function()\n {\n return hasFields( this, this.$db.key, isValue );\n },\n\n $setKey: function(key, skipApplication)\n {\n var db = this.$db;\n var newKey = db.keyHandler.buildKeyFromInput(key);\n var oldKey = this.$$key;\n\n if (newKey !== oldKey)\n {\n if (!db.keyChanges)\n {\n throw 'Key changes are not supported, see the documentation on how to enable key changes.';\n }\n\n db.removeReference( oldKey );\n db.addReference( this, newKey );\n\n this.$$key = newKey;\n\n if ( !skipApplication )\n {\n db.keyHandler.applyKey( newKey, this );\n }\n\n this.$trigger( Model.Events.KeyChange, [this, oldKey, newKey] );\n }\n\n return newKey;\n },\n\n $remote: function(encoded, overwrite)\n {\n this.$db.putRemoteData( encoded, this.$key(), this, overwrite );\n },\n\n $isSynced: function()\n {\n return this.$status === Model.Status.Synced;\n },\n\n $isSaving: function()\n {\n return this.$status === Model.Status.SavePending;\n },\n\n $isPending: function()\n {\n return this.$status === Model.Status.SavePending || this.$status === Model.Status.RemovePending;\n },\n\n $isDeleted: function()\n {\n return this.$status >= Model.Status.RemovePending;\n },\n\n $isSaved: function()\n {\n return !!this.$saved;\n },\n\n $isSavedLocally: function()\n {\n return !!this.$local;\n },\n\n $isNew: function()\n {\n return !(this.$saved || this.$local);\n },\n\n $touch: function()\n {\n if ( this.$db.hasPruning() )\n {\n this.$touched = now();\n }\n },\n\n $project: function(projectionInput)\n {\n var projection = Projection.parse( this.$db, projectionInput );\n\n return projection.project( this );\n },\n\n $getChanges: function(alreadyDecoded)\n {\n var db = this.$db;\n var saved = db.decode( this.$saved, {} );\n var encoded = alreadyDecoded || this;\n var fields = db.saveFields;\n\n return saved ? diff( encoded, saved, fields, equals ) : encoded;\n },\n\n $hasChanges: function(local)\n {\n var compareTo = local ? this.$local : this.$saved;\n\n if (!compareTo)\n {\n return true;\n }\n\n var db = this.$db;\n var ignore = db.ignoredFields;\n var saved = db.decode( compareTo, {} );\n var fields = db.saveFields;\n\n for (var i = 0; i < fields.length; i++)\n {\n var prop = fields[ i ];\n var currentValue = this[ prop ];\n var savedValue = saved[ prop ];\n\n if ( ignore[ prop ] )\n {\n continue;\n }\n\n if ( !equals( currentValue, savedValue ) )\n {\n return true;\n }\n }\n\n return false;\n },\n\n $hasChange: function(prop, local)\n {\n var compareTo = local ? this.$local : this.$saved;\n\n if (!compareTo)\n {\n return true;\n }\n\n var db = this.$db;\n var decoder = db.decodings[ prop ];\n var currentValue = this[ prop ];\n var savedValue = decoder ? decoder( compareTo[ prop ], compareTo, prop ) : compareTo[ prop ];\n\n return !equals( currentValue, savedValue );\n },\n\n $listenForOnline: function(cascade, options)\n {\n if (!this.$offline)\n {\n this.$offline = true;\n\n Rekord.once( Rekord.Events.Online, this.$resume, this );\n }\n\n Class.props(this,\n {\n $resumeCascade: cascade,\n $resumeOptions: options\n });\n },\n\n $resume: function()\n {\n if (this.$status === Model.Status.RemovePending)\n {\n Rekord.debug( Rekord.Debugs.REMOVE_RESUME, this );\n\n this.$addOperation( RemoveRemote, this.$resumeCascade, this.$resumeOptions );\n }\n else if (this.$status === Model.Status.SavePending)\n {\n Rekord.debug( Rekord.Debugs.SAVE_RESUME, this );\n\n this.$addOperation( SaveRemote, this.$resumeCascade, this.$resumeOptions );\n }\n\n this.$offline = false;\n },\n\n toString: function()\n {\n return this.$db.className + ' ' + JSON.stringify( this.$toJSON() );\n }\n\n});\n\naddEventful( Model, true );\n\naddEventFunction( Model, '$change', Model.Events.Changes, true );\n\nfunction createModelPromise(model, cascade, restSuccess, restFailure, restOffline, localSuccess, localFailure)\n{\n var promise = new Promise( null, false );\n\n if ( canCascade( cascade, Cascade.Rest ) )\n {\n var off1 = model.$once( restSuccess, function(data) {\n off2();\n off3();\n promise.resolve( model, data );\n });\n var off2 = model.$once( restFailure, function(data, status) {\n off1();\n off3();\n promise.reject( model, status, data );\n });\n var off3 = model.$once( restOffline, function() {\n off1();\n off2();\n promise.noline( model );\n });\n }\n else if ( canCascade( cascade, Cascade.Local ) )\n {\n var off1 = model.$once( localSuccess, function(data)\n {\n off2();\n promise.resolve( model, data );\n });\n var off2 = model.$once( localFailure, function(data, status)\n {\n off1();\n promise.reject( model, data );\n });\n }\n else\n {\n promise.resolve( model );\n }\n\n return promise;\n}\n\n\n/**\n * A Map has the key-to-value benefits of a map and iteration benefits of an\n * array. This is especially beneficial when most of the time the contents of\n * the structure need to be iterated and order doesn't matter (since removal\n * performs a swap which breaks insertion order).\n *\n * @constructor\n * @memberof Rekord\n */\nfunction Map()\n{\n /**\n * An array of the values in this map.\n * @member {Array}\n */\n this.values = [];\n\n /**\n * An array of the keys in this map.\n * @type {Array}\n */\n this.keys = [];\n\n /**\n * An object of key to index mappings.\n * @type {Object}\n */\n this.indices = {};\n}\n\nClass.create( Map,\n{\n\n /**\n * Resets the map by initializing the values, keys, and indexes.\n *\n * @return {Rekord.Map} -\n * The reference to this map.\n */\n reset: function()\n {\n this.values.length = 0;\n this.keys.length = 0;\n this.indices = {};\n\n return this;\n },\n\n /**\n * Puts the value in the map by the given key.\n *\n * @param {String} key\n * @param {V} value\n * @return {Rekord.Map} -\n * The reference to this map.\n */\n put: function(key, value)\n {\n if ( key in this.indices )\n {\n this.values[ this.indices[ key ] ] = value;\n }\n else\n {\n this.indices[ key ] = this.values.length;\n AP.push.call( this.values, value );\n AP.push.call( this.keys, key );\n }\n\n return this;\n },\n\n /**\n * Returns the value mapped by the given key.\n *\n * @param {String} key\n * @return {V}\n */\n get: function(key)\n {\n return this.values[ this.indices[ key ] ];\n },\n\n /**\n * Removes the value by a given key\n *\n * @param {String} key\n * @return {Rekord.Map} -\n * The reference to this map.\n */\n remove: function(key)\n {\n var index = this.indices[ key ];\n\n if ( isNumber( index ) )\n {\n this.removeAt( index );\n }\n\n return this;\n },\n\n /**\n * Removes the value & key at the given index.\n *\n * @param {Number} index\n * @return {Rekord.Map} -\n * The reference to this map.\n */\n removeAt: function(index)\n {\n var key = this.keys[ index ];\n var lastValue = AP.pop.apply( this.values );\n var lastKey = AP.pop.apply( this.keys );\n\n if ( index < this.values.length )\n {\n this.values[ index ] = lastValue;\n this.keys[ index ] = lastKey;\n this.indices[ lastKey ] = index;\n }\n\n delete this.indices[ key ];\n\n return this;\n },\n\n /**\n * Returns whether this map has a value for the given key.\n *\n * @param {String} key\n * @return {Boolean}\n */\n has: function(key)\n {\n return key in this.indices;\n },\n\n /**\n * Returns the number of elements in the map.\n *\n * @return {Number}\n */\n size: function()\n {\n return this.values.length;\n },\n\n subtract: function(map, dest)\n {\n var out = dest || new Map();\n var n = this.size();\n var values = this.values;\n var keys = this.keys;\n\n for (var i = 0; i < n; i++)\n {\n var v = values[ i ];\n var k = keys[ i ];\n\n if ( !map.has( k ) )\n {\n out.put( k, v );\n }\n }\n\n return out;\n },\n\n /**\n * Passes all values & keys in this map to a callback and if it returns a\n * truthy value then the key and value are placed in the destination map.\n *\n * @param {Function} callback [description]\n * @param {Rekord.Map} [dest] [description]\n * @return {Rekord.Map} [description]\n */\n filter: function(callback, dest)\n {\n var out = dest || new Map();\n var n = this.size();\n var values = this.values;\n var keys = this.keys;\n\n for (var i = 0; i < n; i++)\n {\n var v = values[ i ];\n var k = keys[ i ];\n\n if ( callback( v, k ) )\n {\n out.put( k, v );\n }\n }\n\n return out;\n },\n\n /**\n * Reverses the order of the underlying values & keys.\n *\n * @return {Rekord.Map} -\n * The referense to this map.\n */\n reverse: function()\n {\n reverse( this.values );\n reverse( this.keys );\n\n this.rebuildIndex();\n\n return this;\n },\n\n /**\n *\n * @param {function} comparator [description]\n * @return {Boolean} [description]\n */\n isSorted: function(comparator)\n {\n return isSorted( comparator, this.values );\n },\n\n /**\n * Sorts the underlying values & keys given a value compare function.\n *\n * @param {function} comparator\n * A function which accepts two values and returns a number used for\n * sorting. If the first argument is less than the second argument, a\n * negative number should be returned. If the arguments are equivalent\n * then 0 should be returned, otherwise a positive number should be\n * returned.\n * @return {Map} -\n * The reference to this map.\n */\n sort: function(comparator)\n {\n var map = this;\n\n // Sort this partition!\n function partition(left, right)\n {\n var pivot = map.values[ Math.floor((right + left) / 2) ];\n var i = left;\n var j = right;\n\n while (i <= j)\n {\n while (comparator( map.values[i], pivot ) < 0)\n {\n i++;\n }\n while (comparator( map.values[j], pivot ) > 0)\n {\n j--;\n }\n\n if (i <= j)\n {\n swap( map.values, i, j );\n swap( map.keys, i, j );\n i++;\n j--;\n }\n }\n\n return i;\n }\n\n // Quicksort\n function qsort(left, right)\n {\n var index = partition( left, right );\n\n if (left < index - 1)\n {\n qsort( left, index - 1 );\n }\n\n if (index < right)\n {\n qsort( index, right );\n }\n }\n\n var right = this.size() - 1;\n\n // Are there elements to sort?\n if ( right > 0 )\n {\n qsort( 0, right );\n\n this.rebuildIndex();\n }\n\n return this;\n },\n\n /**\n * Rebuilds the index based on the keys.\n *\n * @return {Rekord.Map} -\n * The reference to this map.\n */\n rebuildIndex: function()\n {\n this.indices = {};\n\n for (var i = 0, l = this.keys.length; i < l; i++)\n {\n this.indices[ this.keys[ i ] ] = i;\n }\n\n return this;\n },\n\n /**\n * Builds an object contain the keys and values in this map.\n *\n * @return {Object} -\n * The built object.\n */\n toObject: function(out)\n {\n var target = out || {};\n var keys = this.keys;\n var values = this.values;\n\n for (var i = 0; i < keys.length; i++)\n {\n target[ keys[ i ] ] = values[ i ];\n }\n\n return target;\n }\n\n});\n\n\nfunction Dependents(subject)\n{\n this.map = {};\n this.listeners = {};\n\n this.subject = subject;\n}\n\nClass.create( Dependents,\n{\n\n add: function(model, relator)\n {\n var key = model.$uid();\n\n this.map[ key ] = model;\n\n if ( model.$db.keyChanges && !this.listeners[ key ] )\n {\n var listener = this.handleKeyChange( relator );\n\n this.listeners[ key ] = model.$on( Model.Events.KeyChange, listener, this );\n }\n },\n\n remove: function(model)\n {\n var key = model.$uid();\n\n evaluate( this.listeners[ key ] );\n\n delete this.listeners[ key ];\n delete this.map[ key ];\n },\n\n handleKeyChange: function(relator)\n {\n return function(model, oldKey, newKey)\n {\n var prefix = model.$db.name + '$';\n\n oldKey = prefix + oldKey;\n newKey = prefix + newKey;\n\n this.listeners[ newKey ] = this.listeners[ oldKey ];\n this.map[ newKey ] = this.map[ oldKey ];\n\n delete this.listeners[ oldKey ];\n delete this.map[ oldKey ];\n\n relator.updateForeignKey( this.subject, model, true );\n };\n },\n\n isSaved: function(callbackOnSaved, contextOnSaved)\n {\n var dependents = this.map;\n var off = noop;\n\n var onDependentSave = function()\n {\n callbackOnSaved.apply( contextOnSaved || this, arguments );\n\n off();\n };\n\n for (var uid in dependents)\n {\n var dependent = dependents[ uid ];\n\n if ( !dependent.$isSaved() )\n {\n off = dependent.$once( Model.Events.RemoteSaves, onDependentSave );\n\n return false;\n }\n }\n\n return true;\n }\n\n});\n\n\n// field\n// relation.field\n// relations[pluckValue]\n// relations?savedWhere[pluckValue]\n// relations{pluckKey: pluckValue}\n// relation(subprojection)\n// relations(subprojection)\n// relations?savedWhere(subprojection)\n// expression|filter\n// expression?savedWhere\n// alias:expression\n// expression#resolve\n// relations@sum=field\n\nfunction Projection(database, input)\n{\n this.database = database;\n this.input = input;\n this.projections = {};\n\n for (var i = 0; i < input.length; i++)\n {\n this.addProjection( input[ i ] );\n }\n}\n\nClass.create( Projection,\n{\n\n addProjection: function(input)\n {\n var projection = this;\n var alias = input;\n var aliasIndex = input.indexOf( Projection.ALIAS_DELIMITER );\n\n if (aliasIndex > 0)\n {\n alias = input.substring( 0, aliasIndex );\n input = input.substring( aliasIndex + 1 );\n }\n\n var word = '';\n var words = [];\n var tokens = ['property'];\n var types = [this.database];\n var i = 0;\n var resolvers = [];\n\n var processWord = function(word)\n {\n if (!word)\n {\n return;\n }\n\n var token = tokens[0];\n var handler = Projection.TOKEN_HANDLER[ token ];\n\n words.unshift( word );\n\n if (handler && handler.post)\n {\n resolvers.push( handler.post( words, tokens, types, projection ) );\n }\n };\n\n var processToken = function(token)\n {\n var handler = Projection.TOKEN_HANDLER[ token ];\n\n tokens.unshift( token );\n\n if (handler && handler.pre)\n {\n resolvers.push( handler.pre( words, tokens, types, projection ) );\n }\n };\n\n for (var i = 0; i < input.length; i++)\n {\n var c = input.charAt( i );\n var token = Projection.TOKENS[ c ];\n\n if (token)\n {\n processWord( word );\n processToken( token );\n\n word = '';\n }\n else\n {\n word += c;\n }\n }\n\n processWord( word );\n\n var resolver = function(value) {\n return value;\n };\n\n for (var i = resolvers.length - 1; i >= 0; i--) {\n resolver = resolvers[ i ]( resolver );\n }\n\n this.projections[ alias ] = resolver;\n },\n\n project: function(model)\n {\n var out = {};\n\n for (var alias in this.projections)\n {\n out[ alias ] = this.projections[ alias ]( model );\n }\n\n return out;\n }\n\n});\n\nProjection.TOKENS =\n{\n '.': 'property',\n '?': 'where',\n '|': 'filter',\n '#': 'resolve',\n '(': 'subStart',\n ')': 'subEnd',\n '[': 'pluckValueStart',\n ']': 'pluckValueEnd',\n '{': 'pluckObjectStart',\n ':': 'pluckObjectDelimiter',\n '}': 'pluckObjectEnd',\n '@': 'aggregateStart',\n '=': 'aggregateProperty'\n};\n\nProjection.TOKEN_HANDLER =\n{\n\n property:\n {\n post: function(words, tokens, types, projection)\n {\n var propertyName = words[0];\n var sourceType = types[0];\n\n if (!(sourceType instanceof Database))\n {\n throw ('The property ' + propertyName + ' can only be taken from a Model');\n }\n\n var relation = sourceType.relations[ propertyName ];\n\n if (relation)\n {\n if (relation instanceof RelationSingle)\n {\n types.unshift( relation.model.Database );\n }\n else\n {\n types.unshift( relation );\n }\n }\n\n var fieldIndex = indexOf( sourceType.fields, propertyName );\n\n if (fieldIndex === false && !relation)\n {\n throw ('The property ' + propertyName + ' does not exist as a field or relation on the Model ' + sourceType.name );\n }\n\n return function(resolver)\n {\n return function(model)\n {\n if ( !isValue( model ) )\n {\n return null;\n }\n\n return resolver( model.$get( propertyName ) );\n };\n };\n }\n },\n\n filter:\n {\n post: function(words, tokens, types, projection)\n {\n var filterName = words[0];\n var filter = Rekord.Filters[ filterName ];\n\n if (!filter)\n {\n throw (filterName + ' is not a valid filter function');\n }\n\n return function(resolver)\n {\n return function(value)\n {\n if ( !isValue( value ) )\n {\n return null;\n }\n\n return resolver( filter( value ) );\n };\n };\n }\n },\n\n resolve:\n {\n post: function(words, tokens, types, projection)\n {\n var resolveName = words[0];\n\n return function(resolver)\n {\n return function(source)\n {\n if ( !isValue( source ) )\n {\n return null;\n }\n\n var value = source[ resolveName ];\n\n if ( isFunction( value ) )\n {\n value = value.apply( source );\n }\n\n return resolver( value );\n };\n };\n }\n },\n\n where:\n {\n post: function(words, tokens, types, projection)\n {\n var whereName = words[0];\n var whereFrom = types[0];\n var where = Rekord.Wheres[ whereName ];\n\n if (!where)\n {\n throw (whereName + ' is not a valid where expression');\n }\n\n if (!(whereFrom instanceof RelationMultiple))\n {\n throw (whereName + ' where expressions can only be used on relations');\n }\n\n return function(resolver)\n {\n return function(relation)\n {\n if ( !isValue( relation ) )\n {\n return null;\n }\n\n return resolver( relation.where( where ) );\n };\n };\n }\n },\n\n aggregateProperty:\n {\n post: function(words, tokens, types, projection)\n {\n var property = words[0];\n var aggregateFunction = words[1];\n var aggregateFrom = types[0];\n\n if (tokens[1] !== 'aggregateStart')\n {\n throw ('Aggregate function syntax error, a = must follow a @');\n }\n\n if (!(aggregateFrom instanceof Relation))\n {\n throw ('Aggregate functions like ' + aggregateFunction + ' from ' + aggregateFrom + ' can only be used on relations');\n }\n\n return function (resolver)\n {\n return function (relation)\n {\n if ( !isValue( relation ) )\n {\n return null;\n }\n\n return resolver( relation[ aggregateFunction ]( property ) );\n };\n };\n }\n },\n\n subEnd:\n {\n pre: function(words, tokens, types, projection)\n {\n var projectionName = words[0];\n var whereFrom = types[0];\n\n if (tokens[1] !== 'subStart')\n {\n throw ('Sub projection syntax error, an ending ) requires a starting (');\n }\n\n if (!(whereFrom instanceof Relation))\n {\n throw ('Sub projections like ' + projectionName + ' from ' + words[1] + ' can only be used on relations');\n }\n\n if (!whereFrom.model.Database.projections[ projectionName ])\n {\n throw ('The projection ' + projectionName + ' does not exist on ' + whereFrom.model.Database.name);\n }\n\n if (whereFrom instanceof RelationSingle)\n {\n return function(resolver)\n {\n return function (relation)\n {\n if ( !isValue( relation ) )\n {\n return null;\n }\n\n return resolver( relation.$project( projectionName ) );\n };\n };\n }\n else\n {\n return function(resolver)\n {\n return function(relations)\n {\n if ( !isValue( relations ) )\n {\n return null;\n }\n\n return resolver( relations.project( projectionName ) );\n };\n };\n }\n }\n },\n\n pluckValueEnd:\n {\n pre: function(words, tokens, types, projection)\n {\n var properties = words[0];\n var whereFrom = types[0];\n\n if (tokens[1] !== 'pluckValueStart')\n {\n throw ('Pluck value syntax error, an ending ] requires a starting [');\n }\n\n if (!(whereFrom instanceof RelationMultiple))\n {\n throw ('Pluck values like ' + properties + ' from ' + words[1] + ' can only be used on relations');\n }\n\n return function (resolver)\n {\n return function (relations)\n {\n if ( !isValue( relations ) )\n {\n return null;\n }\n\n return resolver( relations.pluck( properties ) );\n };\n };\n }\n },\n\n pluckObjectEnd:\n {\n pre: function(words, tokens, types, projection)\n {\n var properties = words[0];\n var keys = words[1];\n var whereFrom = types[0];\n\n if (tokens[1] !== 'pluckObjectDelimiter' || tokens[2] !== 'pluckObjectStart')\n {\n throw ('Pluck object syntax error, must be {key: value}');\n }\n\n if (!(whereFrom instanceof RelationMultiple))\n {\n throw ('Pluck values like ' + properties + ' from ' + words[1] + ' can only be used on relations');\n }\n\n return function (resolver)\n {\n return function (relations)\n {\n if ( !isValue( relations ) )\n {\n return null;\n }\n\n return resolver( relations.pluck( properties, keys ) );\n };\n };\n }\n }\n};\n\nProjection.ALIAS_DELIMITER = ':';\n\nProjection.parse = function(database, input)\n{\n var originalInput = input;\n\n if ( isString( input ) )\n {\n input = database.projections[ input ];\n }\n\n if ( isArray( input ) )\n {\n input = new Projection( database, input );\n }\n\n if (!(input instanceof Projection))\n {\n throw (originalInput + ' is not a valid projection');\n }\n\n return input;\n};\n\n\nfunction Context(models)\n{\n this.databases = [];\n this.alls = [];\n this.models = [];\n\n if ( isEmpty( models ) )\n {\n for (var name in Rekord.classes)\n {\n this.add( name );\n }\n }\n else if ( isArray( models ) )\n {\n for (var i = 0; i < models.length; i++)\n {\n this.add( models[ i ] );\n }\n }\n}\n\nContext.start = function(models)\n{\n var context = new Context( models );\n\n context.apply();\n\n return context;\n};\n\nClass.create( Context,\n{\n\n add: function(type)\n {\n if ( isString( type ) )\n {\n type = Rekord.classes[ type ];\n }\n\n if ( isRekord( type ) )\n {\n type = type.Database;\n }\n\n if ( type instanceof Database )\n {\n this.databases.push( type );\n this.alls.push( {} );\n this.models.push( new ModelCollection( type ) );\n }\n },\n\n getApplied: function()\n {\n var applied = 0;\n\n this.each(function(db)\n {\n if (db.context === this)\n {\n applied++;\n }\n });\n\n return applied / this.databases.length;\n },\n\n apply: function()\n {\n this.each( this.applyDatabase );\n },\n\n applyDatabase: function(db, all, models, i)\n {\n db.all = all;\n db.models = models;\n db.context = this;\n db.contextIndex = i;\n },\n\n discard: function()\n {\n this.each( this.discardDatabase );\n },\n\n discardDatabase: function(db)\n {\n if (db.context === this)\n {\n db.all = db.allCached;\n db.models = db.modelsCached;\n db.context = null;\n db.contextIndex = -1;\n }\n },\n\n destroy: function()\n {\n this.each( this.destroyDatabase );\n\n this.databases.length = 0;\n this.alls.length = 0;\n this.models.length = 0;\n },\n\n destroyDatabase: function(db, alls, models, i)\n {\n this.discardDatabase( db );\n\n this.databases[ i ] = null;\n this.alls[ i ] = null;\n this.models[ i ].clear();\n this.models[ i ] = null;\n },\n\n clear: function(db)\n {\n this.alls[ db.contextIndex ] = {};\n },\n\n each: function(iterator)\n {\n var dbs = this.databases;\n var alls = this.alls;\n var models = this.models;\n\n for (var i = 0; i < dbs.length; i++)\n {\n iterator.call( this, dbs[ i ], alls[ i ], models[ i ], i );\n }\n }\n\n});\n\n\nfunction KeyHandler()\n{\n\n}\n\nClass.create( KeyHandler,\n{\n\n init: function(database)\n {\n this.key = database.key;\n this.keySeparator = database.keySeparator;\n this.database = database;\n },\n\n getKey: function(model, quietly)\n {\n var field = this.key;\n var modelKey = this.buildKey( model, field );\n\n if ( hasFields( model, field, isValue ) )\n {\n return modelKey;\n }\n else if ( !quietly )\n {\n throw 'Composite key not supplied.';\n }\n\n return null;\n },\n\n buildKeyFromRelations: function(input)\n {\n if ( isObject( input ) )\n {\n var relations = this.database.relations;\n\n for (var relationName in relations)\n {\n if ( relationName in input )\n {\n relations[ relationName ].buildKey( input );\n }\n }\n }\n },\n\n buildKeyFromInput: function(input)\n {\n if ( input instanceof this.database.Model )\n {\n return input.$key();\n }\n else if ( isArray( input ) ) // && isArray( this.key )\n {\n return input.join( this.keySeparator );\n }\n else if ( isObject( input ) )\n {\n return this.buildKey( input );\n }\n\n return input;\n }\n\n});\n\n\nfunction KeySimple(database)\n{\n this.init( database );\n}\n\nClass.extend( KeyHandler, KeySimple,\n{\n getKeys: function(model)\n {\n return this.buildKey( model );\n },\n\n removeKey: function(model)\n {\n var field = this.key;\n\n delete model[ field ];\n },\n\n buildKey: function(input, otherFields)\n {\n this.buildKeyFromRelations( input );\n\n var field = otherFields || this.key;\n var key = input[ field ];\n\n if ( !isValue( key ) )\n {\n key = input[ field ] = uuid();\n }\n\n return key;\n },\n\n buildObjectFromKey: function(key)\n {\n var field = this.key;\n var props = {};\n\n props[ field ] = key;\n\n return this.database.instantiate( props );\n },\n\n hasKeyChange: function(a, b)\n {\n var field = this.key;\n var akey = a[ field ];\n var bkey = b[ field ];\n\n return isValue( akey ) && isValue( bkey ) && akey !== bkey;\n },\n\n addToFields: function(out)\n {\n var field = this.key;\n\n if ( indexOf( out, field ) === false )\n {\n out.unshift( field );\n }\n },\n\n isValid: function(key)\n {\n return isValue( key );\n },\n\n copyFields: function(target, targetFields, source, sourceFields)\n {\n var targetValue = target[ targetFields ];\n var sourceValue = source[ sourceFields ];\n\n if ( !isValue( targetValue ) && isValue( sourceValue ) )\n {\n target[ targetFields ] = copy( sourceValue );\n }\n },\n\n inKey: function(field)\n {\n if ( isArray( field ) )\n {\n for (var i = 0; i < field.length; i++)\n {\n if ( field[ i ] === this.key )\n {\n return true;\n }\n }\n\n return false;\n }\n\n return field === this.key;\n },\n\n setKeyField: function(key, field, source, target)\n {\n if ( field === target )\n {\n key[ field ] = source[ this.key ];\n }\n },\n\n applyKey: function(input, target)\n {\n target[ this.key ] = input;\n }\n\n});\n\n\nfunction KeyComposite(database)\n{\n this.init( database );\n}\n\nClass.extend( KeyHandler, KeyComposite,\n{\n getKeys: function(input, otherFields)\n {\n this.buildKeyFromRelations( input );\n\n return pull( input, otherFields || this.key );\n },\n\n removeKey: function(model)\n {\n var fields = this.key;\n\n for (var i = 0; i < fields.length; i++)\n {\n delete model[ fields[ i ] ];\n }\n },\n\n buildKey: function(input, otherFields)\n {\n return this.getKeys( input, otherFields ).join( this.keySeparator );\n },\n\n buildObjectFromKey: function(key)\n {\n var fields = this.key;\n var props = {};\n\n if ( isString( key ) )\n {\n key = key.split( this.keySeparator );\n }\n\n for (var i = 0; i < fields.length; i++)\n {\n props[ fields[ i ] ] = key[ i ];\n }\n\n return this.database.instantiate( props );\n },\n\n hasKeyChange: function(a, b)\n {\n var fields = this.key;\n\n for (var i = 0; i < fields.length; i++)\n {\n var akey = a[ fields[ i ] ];\n var bkey = b[ fields[ i ] ];\n\n if ( isValue( akey ) && isValue( bkey ) && akey !== bkey )\n {\n return true;\n }\n }\n\n return false;\n },\n\n addToFields: function(out)\n {\n var fields = this.key;\n\n for (var i = fields.length - 1; i >= 0; i--)\n {\n if ( indexOf( out, fields[ i ] ) === false )\n {\n out.unshift( fields[ i ] );\n }\n }\n },\n\n isValid: function(key)\n {\n return isValue( key );\n },\n\n copyFields: function(target, targetFields, source, sourceFields)\n {\n for (var i = 0; i < targetFields.length; i++)\n {\n var targetValue = target[ targetFields[ i ] ];\n var sourceValue = source[ sourceFields[ i ] ];\n\n if ( !isValue( targetValue ) && isValue( sourceValue ) )\n {\n target[ targetFields[ i ] ] = copy( sourceValue );\n }\n }\n },\n\n inKey: function(field)\n {\n if ( isArray( field ) )\n {\n for (var i = 0; i < field.length; i++)\n {\n if ( indexOf( this.key, field[ i ] ) !== false )\n {\n return true;\n }\n }\n\n return false;\n }\n\n return indexOf( this.key, field ) !== false;\n },\n\n setKeyField: function(key, field, source, target)\n {\n var index = indexOf( target );\n\n if ( index !== false )\n {\n key[ field ] = source[ this.key[ index ] ];\n }\n },\n\n applyKey: function(input, target)\n {\n var fields = this.key;\n\n if ( isString( input ) )\n {\n input = input.split( this.keySeparator );\n }\n\n for (var i = 0; i < fields.length; i++)\n {\n target[ fields[ i ] ] = input[ i ];\n }\n }\n\n});\n\n\n/**\n * An extension of the Array class adding many useful functions and events. This\n * is the base collection class in Rekord.\n *\n * A collection of any type can be created via {@link Rekord.collect}.\n *\n * ```\n * var nc = new Rekord.Collection([1, 2, 3, 4]);\n * ```\n *\n * @constructor\n * @memberof Rekord\n * @augments Rekord.Eventful\n * @extends Array\n * @param {Array} [values] 0\n * The initial set of values in this collection.\n * @see Rekord.collect\n */\nfunction Collection(values)\n{\n this.addAll( values, true );\n}\n\n/**\n* A comparator to keep the collection sorted with.\n*\n* @memberof Rekord.Collection#\n* @member {comparisonCallback} [comparator]\n*/\n\n/**\n * The events a collection can emit.\n *\n * {@link Rekord.Collection#event:add Add}\n * {@link Rekord.Collection#event:adds Adds}\n * {@link Rekord.Collection#event:sort Sort}\n * {@link Rekord.Collection#event:remove Remove}\n * {@link Rekord.Collection#event:removes Removes}\n * {@link Rekord.Collection#event:updates Updates}\n * {@link Rekord.Collection#event:reset Reset}\n * {@link Rekord.Collection#event:cleared Cleared}\n * {@link Rekord.Collection#event:changes Changes}\n *\n * @static\n */\nCollection.Events =\n{\n /**\n * An event triggered when a single value is added to a collection.\n *\n * @event Rekord.Collection#add\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @argument {T} value -\n * The value added.\n * @argument {number} index -\n * The index where the value was added.\n * @see Rekord.Collection#add\n * @see Rekord.Collection#insertAt\n * @see Rekord.ModelCollection#add\n * @see Rekord.ModelCollection#push\n */\n Add: 'add',\n\n /**\n * An event triggered when multiple values are added to a collection.\n *\n * @event Rekord.Collection#adds\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @argument {T[]} value -\n * The values added.\n * @argument {number|number[]} indices -\n * The index or indices where the values were added.\n * @see Rekord.Collection#addAll\n * @see Rekord.ModelCollection#addAll\n */\n Adds: 'adds',\n\n /**\n * An event triggered when a collection is sorted. This may automatically\n * be triggered by any method that modifies the collection.\n *\n * @event Rekord.Collection#sort\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @see Rekord.Collection#sort\n * @see Rekord.ModelCollection#sort\n */\n Sort: 'sort',\n\n /**\n * An event triggered when a collection has an element removed at a given index.\n *\n * @event Rekord.Collection#remove\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @argument {Any} removing -\n * The element that was removed.\n * @argument {Number} index -\n * The index where the element was removed at.\n * @see Rekord.Collection#remove\n * @see Rekord.Collection#removeAt\n * @see Rekord.ModelCollection#remove\n */\n Remove: 'remove',\n\n /**\n * An event triggered when a collection has multiple elements removed.\n *\n * @event Rekord.Collection#removes\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @argument {Any[]} removed -\n * The array of elements removed from the collection.\n * @argument {number|number[]} indices -\n * The index where the values were removed OR the array of indices.\n * @argument {number} [removeCount] -\n * The number of values removed at the given index (undefined if array specified).\n * @see Rekord.Collection#removeAll\n * @see Rekord.Collection#removeWhere\n */\n Removes: 'removes',\n\n /**\n * An event triggered when a collection has elements modified.\n *\n * @event Rekord.Collection#updates\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @argument {Array} updated -\n * The array of elements modified.\n * @see Rekord.ModelCollection#update\n * @see Rekord.ModelCollection#updateWhere\n */\n Updates: 'updates',\n\n /**\n * An event triggered when a collection's elements are entirely replaced by\n * a new set of elements.\n *\n * @event Rekord.Collection#reset\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @argument {Array} updated -\n * The array of elements modified.\n * @see Rekord.FilteredCollection#sync\n * @see Rekord.ModelCollection#reset\n */\n Reset: 'reset',\n\n /**\n * An event triggered when a collection is cleared of all elements.\n *\n * @event Rekord.Collection#cleared\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n * @see Rekord.Collection#clear\n */\n Cleared: 'cleared',\n\n /**\n * All events triggered by a collection when the contents of the collection changes.\n *\n * @event Rekord.Collection#changes\n * @argument {Rekord.Collection} collection -\n * The collection that triggered the event.\n */\n Changes: 'add adds sort remove removes updates reset cleared'\n\n};\n\nClass.extend( Array, Collection,\n{\n\n /**\n * Sets the comparator for this collection and performs a sort.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {ComparatorInput} comparator -\n * The comparator input to convert to a comparison function.\n * @param {Boolean} [nullsFirst=false] -\n * When a comparison is done involving a null/undefined value this can\n * determine which is ordered before the other.\n * @emits Rekord.Collection#sort\n * @see Rekord.createComparator\n * @return {Rekord.Collection}\n */\n setComparator: function(comparator, nullsFirst)\n {\n this.comparator = createComparator( comparator, nullsFirst );\n this.sort();\n\n return this;\n },\n\n /**\n * Adds a comparator to the existing comparator. This added comparator is ran\n * after the current comparator when it finds two elements equal. If no\n * comparator exists on this collection then it's set to the given comparator.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {ComparatorInput} comparator -\n * The comparator input to convert to a comparison function.\n * @param {Boolean} [nullsFirst=false] -\n * When a comparison is done involving a null/undefined value this can\n * determine which is ordered before the other.\n * @emits Rekord.Collection#sort\n * @see Rekord.createComparator\n * @return {Rekord.Collection}\n */\n addComparator: function(comparator, nullsFirst)\n {\n this.comparator = addComparator( this.comparator, comparator, nullsFirst );\n this.sort();\n\n return this;\n },\n\n /**\n * Determines if the collection is currently sorted based on the current\n * comparator of the collection unless a comparator is given\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {ComparatorInput} [comparator] -\n * The comparator input to convert to a comparison function.\n * @param {Boolean} [nullsFirst=false] -\n * When a comparison is done involving a null/undefined value this can\n * determine which is ordered before the other.\n * @see Rekord.createComparator\n * @return {Boolean}\n */\n isSorted: function(comparator, nullsFirst)\n {\n var cmp = comparator ? createComparator( comparator, nullsFirst ) : this.comparator;\n\n return isSorted( cmp, this );\n },\n\n /**\n * Sorts the elements in this collection based on the current comparator\n * unless a comparator is given. If a comparator is given it will not override\n * the current comparator, subsequent operations to the collection may trigger\n * a sort if the collection has a comparator.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {ComparatorInput} [comparator] -\n * The comparator input to convert to a comparison function.\n * @param {Boolean} [nullsFirst=false] -\n * When a comparison is done involving a null/undefined value this can\n * determine which is ordered before the other.\n * @param {Boolean} [ignorePrimitive=false] -\n * Sorting is automatically done for non-primitive collections if a\n * comparator exists. This flag ensures primitive collections aren't sorted\n * after every operation.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#sort\n * @see Rekord.createComparator\n */\n sort: function(comparator, nullsFirst, ignorePrimitive)\n {\n var cmp = comparator ? createComparator( comparator, nullsFirst ) : this.comparator;\n\n if ( !isSorted( cmp, this ) || ( !ignorePrimitive && !cmp && isPrimitiveArray( this ) ) )\n {\n AP.sort.call( this, cmp );\n\n this.trigger( Collection.Events.Sort, [this] );\n }\n\n return this;\n },\n\n /**\n * Resets the values in this collection with a new collection of values.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Any[]} [values] -\n * The new array of values in this collection.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#reset\n */\n reset: function(values)\n {\n this.length = 0;\n\n if ( isArray( values ) )\n {\n AP.push.apply( this, values );\n }\n else if ( isValue( values ) )\n {\n AP.push.call( this, values );\n }\n\n this.trigger( Collection.Events.Reset, [this] );\n this.sort( undefined, undefined, true );\n\n return this;\n },\n\n /**\n * Creates a limited view of this collection known as a page. The resulting\n * page object changes when this collection changes. At the very least the\n * page size is required, and a starting page index can be specified.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Number} pageSize -\n * The maximum number of elements allowed in the page at once.\n * @param {Number} [pageIndex=0]\n * The starting page offset. This isn't an element offset, but the element\n * offset can be calculated by multiplying the page index by the page size.\n * @return {Rekord.Page} -\n * The newly created Page.\n */\n page: function(pageSize, pageIndex)\n {\n return new Page( this, pageSize, pageIndex );\n },\n\n /**\n * Creates a sub view of this collection known as a filtered collection. The\n * resulting collection changes when this collection changes. Any time an\n * element is added or removed to this collection it may be added or removed\n * from the filtered collection if it fits the filter function. The filter\n * function is created by passing the arguments of this function to\n * {@link Rekord.createWhere}.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.FilteredCollection} -\n * The newly created live filtered view of this collection.\n * @see Rekord.createWhere\n */\n filtered: function(whereProperties, whereValue, whereEquals)\n {\n var filter = createWhere( whereProperties, whereValue, whereEquals );\n\n return FilteredCollection.create( this, filter );\n },\n\n /**\n * Creates a copy of this collection with elements that match the supplied\n * parameters. The parameters are passed to the {@link Rekord.createWhere}\n * to generate a function which tests each element of this collection for\n * inclusion in the newly created collection.\n *\n * ```javascript\n * var isEven = function() { return x % 2 == 0; };\n * var c = Rekord.collect(1, 2, 3, 4, 5);\n * var w = c.where(isEven); // [2, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @param {Array} [out=this.cloneEmpty()] -\n * The array to place the elements that match.\n * @return {Rekord.Collection} -\n * The copy of this collection ran through a filtering function.\n * @see Rekord.createWhere\n */\n where: function(whereProperties, whereValue, whereEquals, out)\n {\n var where = createWhere( whereProperties, whereValue, whereEquals );\n var target = out || this.cloneEmpty();\n\n for (var i = 0; i < this.length; i++)\n {\n var a = this[ i ];\n\n if ( where( a ) )\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Returns a collection with elements that exist in this collection but does\n * not exist in the given collection.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * var b = Rekord.collect(1, 3, 5);\n * var c = a.subtract( b ); // [2, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Array} collection -\n * The array of elements that shouldn't exist in the resulting collection.\n * @param {Array} [out=this.cloneEmpty()] -\n * The array to place the elements that exist in this collection but not in\n * the given collection. If this is not given - a collection of this type\n * will be created.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to an element that exists in the given\n * collection.\n * @return {Array} -\n * The collection of elements that exist in this collection and not the\n * given collection.\n */\n subtract: function(collection, out, equals)\n {\n var target = out || this.cloneEmpty();\n var equality = equals || equalsStrict;\n\n for (var i = 0; i < this.length; i++)\n {\n var a = this[ i ];\n var exists = false;\n\n for (var j = 0; j < collection.length && !exists; j++)\n {\n exists = equality( a, collection[ j ] );\n }\n\n if (!exists)\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Returns a collection of elements that are shared between this collection\n * and the given collection.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * var b = Rekord.collect(1, 3, 5);\n * var c = a.intersect( b ); // [1, 3]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Array} collection -\n * The collection of elements to intersect with this collection.\n * @param {Array} [out=this.cloneEmpty()] -\n * The array to place the elements that exist in both this collection and\n * the given collection. If this is not given - a collection of this type\n * will be created.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to an element that exists in the given\n * collection.\n * @return {Array} -\n * The collection of elements that exist in both collections.\n */\n intersect: function(collection, out, equals)\n {\n var target = out || this.cloneEmpty();\n var equality = equals || equalsStrict;\n\n for (var i = 0; i < collection.length; i++)\n {\n var a = collection[ i ];\n var exists = false;\n\n for (var j = 0; j < this.length && !exists; j++)\n {\n exists = equality( a, this[ j ] );\n }\n\n if (exists)\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Returns a collection of elements that exist in the given collection but\n * not in this collection.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * var b = Rekord.collect(1, 3, 5);\n * var c = a.complement( b ); // [5]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Array} collection -\n * The array of elements that could exist in the resulting collection.\n * @param {Array} [out=this.cloneEmpty()] -\n * The array to place the elements that exist in given collection but not\n * in this collection. If this is not given - a collection of this type\n * will be created.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to an element that exists in the given\n * collection.\n * @return {Array} -\n * The collection of elements that exist in the given collection and not\n * this collection.\n */\n complement: function(collection, out, equals)\n {\n var target = out || this.cloneEmpty();\n var equality = equals || equalsStrict;\n\n for (var i = 0; i < collection.length; i++)\n {\n var a = collection[ i ];\n var exists = false;\n\n for (var j = 0; j < this.length && !exists; j++)\n {\n exists = equality( a, this[ j ] );\n }\n\n if (!exists)\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Clears all elements from this collection.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * a.clear(); // []\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#sort\n */\n clear: function()\n {\n this.length = 0;\n this.trigger( Collection.Events.Cleared, [this] );\n\n return this;\n },\n\n\n /**\n * Adds an element to this collection - sorting the collection if a\n * comparator is set on this collection and `delaySort` is not a specified or\n * a true value.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * a.add( 5 ); // [1, 2, 3, 4, 5]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Any} value -\n * The value to add to this collection.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#add\n * @emits Rekord.Collection#sort\n */\n add: function(value, delaySort)\n {\n var i = this.length;\n\n AP.push.call( this, value );\n\n this.trigger( Collection.Events.Add, [this, value, i] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n\n return this;\n },\n\n /**\n * Adds one or more elements to the end of this collection - sorting the\n * collection if a comparator is set on this collection.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * a.push( 5, 6, 7 ); // 7\n * a // [1, 2, 3, 4, 5, 6, 7]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {...Any} value -\n * The values to add to this collection.\n * @return {Number} -\n * The new length of this collection.\n * @emits Rekord.Collection#add\n * @emits Rekord.Collection#sort\n */\n push: function()\n {\n var values = AP.slice.apply(arguments);\n var i = this.length;\n\n AP.push.apply( this, values );\n\n this.trigger( Collection.Events.Adds, [this, values, i] );\n\n this.sort( undefined, undefined, true );\n\n return this.length;\n },\n\n /**\n * Adds one or more elements to the beginning of this collection - sorting the\n * collection if a comparator is set on this collection.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * a.unshift( 5, 6, 7 ); // 7\n * a // [5, 6, 7, 1, 2, 3, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {...Any} value -\n * The values to add to this collection.\n * @return {Number} -\n * The new length of this collection.\n * @emits Rekord.Collection#adds\n * @emits Rekord.Collection#sort\n */\n unshift: function()\n {\n var values = arguments;\n\n AP.unshift.apply( this, values );\n\n this.trigger( Collection.Events.Adds, [this, AP.slice.apply(values), 0] );\n\n this.sort( undefined, undefined, true );\n\n return this.length;\n },\n\n /**\n * Adds all elements in the given array to this collection - sorting the\n * collection if a comparator is set on this collection and `delaySort` is\n * not specified or a true value.\n *\n * ```javascript\n * var a = Rekord.collect(1, 2, 3, 4);\n * a.addAll( [5, 6] ); // [1, 2, 3, 4, 5, 6]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Any[]} values -\n * The values to add to this collection.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#adds\n * @emits Rekord.Collection#sort\n */\n addAll: function(values, delaySort)\n {\n if ( isArray( values ) && values.length )\n {\n var i = this.length;\n\n AP.push.apply( this, values );\n\n this.trigger( Collection.Events.Adds, [this, values, i] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n }\n\n return this;\n },\n\n /**\n * Inserts an element into this collection at the given index - sorting the\n * collection if a comparator is set on this collection and `delaySort` is not\n * specified or a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.insertAt( 0, 0 ); // [0, 1, 2, 3, 4]\n * c.insertAt( 2, 1.5 ); // [0, 1, 1.5, 2, 3, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Number} i -\n * The index to insert the element at.\n * @param {Any} value -\n * The value to insert into the collection.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#add\n * @emits Rekord.Collection#sort\n */\n insertAt: function(i, value, delaySort)\n {\n AP.splice.call( this, i, 0, value );\n\n this.trigger( Collection.Events.Add, [this, value, i] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n\n return this;\n },\n\n /**\n * Removes the last element in this collection and returns it - sorting the\n * collection if a comparator is set on this collection and `delaySort` is\n * no specified or a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.pop(); // 4\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Any} -\n * The element removed from the end of the collection.\n * @emits Rekord.Collection#remove\n * @emits Rekord.Collection#sort\n */\n pop: function(delaySort)\n {\n var removed = AP.pop.apply( this );\n var i = this.length;\n\n this.trigger( Collection.Events.Remove, [this, removed, i] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n\n return removed;\n },\n\n /**\n * Removes the first element in this collection and returns it - sorting the\n * collection if a comparator is set on this collection and `delaySort` is\n * no specified or a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.shift(); // 1\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Any} -\n * The element removed from the beginning of the collection.\n * @emits Rekord.Collection#remove\n * @emits Rekord.Collection#sort\n */\n shift: function(delaySort)\n {\n var removed = AP.shift.apply( this );\n\n this.trigger( Collection.Events.Remove, [this, removed, 0] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n\n return removed;\n },\n\n /**\n * Removes the element in this collection at the given index `i` - sorting\n * the collection if a comparator is set on this collection and `delaySort` is\n * not specified or a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.removeAt( 1 ); // 2\n * c.removeAt( 5 ); // undefined\n * c // [1, 3, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Number} i -\n * The index of the element to remove.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Any} -\n * The element removed, or undefined if the index was invalid.\n * @emits Rekord.Collection#remove\n * @emits Rekord.Collection#sort\n */\n removeAt: function(i, delaySort)\n {\n var removing;\n\n if (i >= 0 && i < this.length)\n {\n removing = this[ i ];\n\n AP.splice.call( this, i, 1 );\n this.trigger( Collection.Events.Remove, [this, removing, i] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n }\n\n return removing;\n },\n\n /**\n * Removes the given value from this collection if it exists - sorting the\n * collection if a comparator is set on this collection and `delaySort` is not\n * specified or a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.remove( 1 ); // 1\n * c.remove( 5 ); // undefined\n * c // [2, 3, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Any} value -\n * The value to remove from this collection if it exists.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to the given value.\n * @return {Any} -\n * The element removed from this collection.\n * @emits Rekord.Collection#remove\n * @emits Rekord.Collection#sort\n */\n remove: function(value, delaySort, equals)\n {\n var i = this.indexOf( value, equals );\n var element = this[ i ];\n\n if ( i !== -1 )\n {\n this.removeAt( i, delaySort );\n }\n\n return element;\n },\n\n /**\n * Removes the given values from this collection - sorting the collection if\n * a comparator is set on this collection and `delaySort` is not specified or\n * a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.removeAll( [1, 5] ); // [1]\n * c // [2, 3, 4]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Any[]} values -\n * The values to remove from this collection if they exist.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to any of the given values.\n * @return {Any[]} -\n * The elements removed from this collection.\n * @emits Rekord.Collection#removes\n * @emits Rekord.Collection#sort\n */\n removeAll: function(values, delaySort, equals)\n {\n var removed = [];\n var removedIndices = [];\n\n if ( isArray( values ) && values.length )\n {\n for (var i = 0; i < values.length; i++)\n {\n var value = values[ i ];\n var k = this.indexOf( value, equals );\n\n if ( k !== -1 )\n {\n removedIndices.push( k );\n removed.push( value );\n }\n }\n\n removedIndices.sort();\n\n for (var i = removedIndices.length - 1; i >= 0; i--)\n {\n AP.splice.call( this, removedIndices[ i ], 1 );\n }\n\n this.trigger( Collection.Events.Removes, [this, removed, removedIndices] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n }\n\n return removed;\n },\n\n /**\n * Removes elements from this collection that meet the specified criteria. The\n * given criteria are passed to {@link Rekord.createWhere} to create a filter\n * function. All elements removed are returned\n *\n * ```javascript\n * var isEven = function(x) { return x % 2 === 0; };\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.removeWhere( isEven ); // [2, 4];\n * c // [1, 3]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @param {Array} [out=this.cloneEmpty()] -\n * The array to place the elements that match.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#removes\n * @emits Rekord.Collection#sort\n * @see Rekord.createWhere\n */\n removeWhere: function(whereProperties, whereValue, whereEquals, out, delaySort)\n {\n var where = createWhere( whereProperties, whereValue, whereEquals );\n var removed = out || this.cloneEmpty();\n var removedIndices = [];\n\n for (var i = 0; i < this.length; i++)\n {\n var value = this[ i ];\n\n if ( where( value ) )\n {\n removedIndices.push( i );\n removed.push( value );\n }\n }\n\n for (var i = removedIndices.length - 1; i >= 0; i--)\n {\n AP.splice.call( this, removedIndices[ i ], 1 );\n }\n\n this.trigger( Collection.Events.Removes, [this, removed, removedIndices] );\n\n if ( !delaySort )\n {\n this.sort( undefined, undefined, true );\n }\n\n return removed;\n },\n\n /**\n * Splices elements out of and into this collection - sorting the collection\n * if a comparator is set on this collection.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Number} start -\n * Index at which to start changing the array (with origin 0). If greater\n * than the length of the array, actual starting index will be set to the\n * length of the array. If negative, will begin that many elements from the end.\n * @param {Number} deleteCount -\n * An integer indicating the number of old array elements to remove. If\n * deleteCount is 0, no elements are removed. In this case, you should\n * specify at least one new element. If deleteCount is greater than the\n * number of elements left in the array starting at start, then all of the\n * elements through the end of the array will be deleted.\n * If deleteCount is omitted, deleteCount will be equal to (arr.length - start).\n * @param {...Any} values -\n * The elements to add to the array, beginning at the start index. If you\n * don't specify any elements, splice() will only remove elements from the array.\n * @return {Any[]} -\n * The array of deleted elements.\n * @emits Rekord.Collection#removes\n * @emits Rekord.Collection#adds\n * @emits Rekord.Collection#sort\n */\n splice: function(start, deleteCount)\n {\n var adding = AP.slice.call( arguments, 2 );\n var removed = AP.splice.apply( this, arguments );\n\n if ( deleteCount )\n {\n this.trigger( Collection.Events.Removes, [this, removed, start, deleteCount] );\n }\n\n if ( adding.length )\n {\n this.trigger( Collection.Events.Adds, [this, adding, start] );\n }\n\n this.sort( undefined, undefined, true );\n\n return removed;\n },\n\n /**\n * Reverses the order of elements in this collection.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.reverse(); // [4, 3, 2, 1]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @emits Rekord.Collection#updates\n */\n reverse: function()\n {\n if ( AP.reverse )\n {\n AP.reverse.apply( this );\n }\n else\n {\n reverse( this );\n }\n\n this.trigger( Collection.Events.Updates, [this] );\n\n return this;\n },\n\n /**\n * Returns the index of the given element in this collection or returns -1\n * if the element doesn't exist in this collection.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.indexOf( 1 ); // 0\n * c.indexOf( 2 ); // 1\n * c.indexOf( 5 ); // -1\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Any} value -\n * The value to search for.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to the given value.\n * @return {Number} -\n * The index of the element in this collection or -1 if it was not found.\n * @see Rekord.equals\n * @see Rekord.equalsStrict\n */\n indexOf: function(value, equals)\n {\n var equality = equals || equalsStrict;\n\n for (var i = 0; i < this.length; i++)\n {\n if ( equality( value, this[ i ] ) )\n {\n return i;\n }\n }\n\n return -1;\n },\n\n /**\n * Returns the element with the minimum value given a comparator.\n *\n * ```javascript\n * var c = Rekord.collect({age: 4}, {age: 5}, {age: 6}, {age: 3});\n * c.minModel('age'); // {age: 3}\n * c.minModel('-age'); // {age: 6}\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {comparatorInput} comparator -\n * The comparator which calculates the minimum model.\n * @param {Any} [startingValue]\n * The initial minimum value. If a value is specified, it's compared\n * against all elements in this collection until the comparator function\n * finds a more minimal value. If it doesn't - this is the value returned.\n * @return {Any} -\n * The minimum element in the collection given the comparator function.\n * @see Rekord.createComparator\n */\n minModel: function(comparator, startingValue)\n {\n var cmp = createComparator( comparator || this.comparator, false );\n var min = startingValue;\n\n for (var i = 0; i < this.length; i++)\n {\n if ( cmp( min, this[i] ) > 0 )\n {\n min = this[i];\n }\n }\n\n return min;\n },\n\n /**\n * Returns the element with the maximum value given a comparator.\n *\n * ```javascript\n * var c = Rekord.collect({age: 4}, {age: 5}, {age: 6}, {age: 3});\n * c.maxModel('age'); // {age: 6}\n * c.maxModel('-age'); // {age: 3}\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {comparatorInput} comparator -\n * The comparator which calculates the maximum model.\n * @param {Any} [startingValue] -\n * The initial maximum value. If a value is specified, it's compared\n * against all elements in this collection until the comparator function\n * finds a more maximal value. If it doesn't - this is the value returned.\n * @return {Any} -\n * The maximum element in the collection given the comparator function.\n * @see Rekord.createComparator\n */\n maxModel: function(comparator, startingValue)\n {\n var cmp = createComparator( comparator || this.comparator, true );\n var max = startingValue;\n\n for (var i = 0; i < this.length; i++)\n {\n if ( cmp( max, this[i] ) < 0 )\n {\n max = this[i];\n }\n }\n\n return max;\n },\n\n /**\n * Returns the minimum value for the given property expression out of all the\n * elements this collection.\n *\n * ```javascript\n * var c = Rekord.collect({age: 6}, {age: 5}, {notage: 5});\n * c.min('age'); // 5\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [properties] -\n * The expression which takes an element in this container and resolves a\n * value that can be compared to the current minimum.\n * @param {Any} [startingValue] -\n * The initial minimum value. If a value is specified, it's compared\n * against all elements in this collection until the comparator function\n * finds a more minimal value. If it doesn't - this is the value returned.\n * @param {compareCallback} [compareFunction=Rekord.compare] -\n * A comparison function to use.\n * @return {Any} -\n * The minimum value found.\n * @see Rekord.createPropertyResolver\n * @see Rekord.compare\n */\n min: function(properties, startingValue, compareFunction)\n {\n var comparator = compareFunction || compare;\n var resolver = createPropertyResolver( properties );\n var min = startingValue;\n\n for (var i = 0; i < this.length; i++)\n {\n var resolved = resolver( this[ i ] );\n\n if ( comparator( min, resolved, false ) > 0 )\n {\n min = resolved;\n }\n }\n\n return min;\n },\n\n /**\n * Returns the maximum value for the given property expression out of all the\n * elements this collection.\n *\n * ```javascript\n * var c = Rekord.collect({age: 6}, {age: 5}, {notage: 5});\n * c.max('age'); // 6\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [properties] -\n * The expression which takes an element in this container and resolves a\n * value that can be compared to the current maximum.\n * @param {Any} [startingValue] -\n * The initial maximum value. If a value is specified, it's compared\n * against all elements in this collection until the comparator function\n * finds a more maximal value. If it doesn't - this is the value returned.\n * @param {compareCallback} [compareFunction=Rekord.compare] -\n * A comparison function to use.\n * @return {Any} -\n * The maximum value found.\n * @see Rekord.createPropertyResolver\n * @see Rekord.compare\n */\n max: function(properties, startingValue, compareFunction)\n {\n var comparator = compareFunction || compare;\n var resolver = createPropertyResolver( properties );\n var max = startingValue;\n\n for (var i = 0; i < this.length; i++)\n {\n var resolved = resolver( this[ i ] );\n\n if ( comparator( max, resolved, true ) < 0 )\n {\n max = resolved;\n }\n }\n\n return max;\n },\n\n /**\n * Returns the first element where the given expression is true.\n *\n * ```javascript\n * var c = Rekord.collect([{x: 5}, {y: 6}, {y: 6, age: 8}, {z: 7}]);\n * c.firstWhere('y', 6); // {x: 6}\n * c.firstWhere(); // {x: 5}\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [whereProperties] -\n * The expression used to create a function to test the elements in this\n * collection.\n * @param {Any} [whereValue] -\n * When the first argument is a string this argument will be treated as a\n * value to compare to the value of the named property on the object passed\n * through the filter function.\n * @param {equalityCallback} [whereEquals=Rekord.equalsStrict] -\n * An alternative function can be used to compare to values.\n * @return {Any} -\n * The first element in this collection that matches the given expression.\n * @see Rekord.createWhere\n */\n firstWhere: function(whereProperties, whereValue, whereEquals)\n {\n var where = createWhere( whereProperties, whereValue, whereEquals );\n\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n\n if ( where( model ) )\n {\n return model;\n }\n }\n\n return null;\n },\n\n /**\n * Returns the first non-null value in this collection given a property\n * expression. If no non-null values exist for the given property expression,\n * then undefined will be returned.\n *\n * ```javascript\n * var c = Rekord.collect([{x: 5}, {y: 6}, {y: 4}, {z: 7}]);\n * c.first('y'); // 6\n * c.first(); // {x: 5}\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [properties] -\n * The expression which converts one value into another.\n * @return {Any} -\n * @see Rekord.createPropertyResolver\n * @see Rekord.isValue\n */\n first: function(properties)\n {\n var resolver = createPropertyResolver( properties );\n\n for (var i = 0; i < this.length; i++)\n {\n var resolved = resolver( this[ i ] );\n\n if ( isValue( resolved ) )\n {\n return resolved;\n }\n }\n },\n\n /**\n * Returns the last element where the given expression is true.\n *\n * ```javascript\n * var c = Rekord.collect([{x: 5}, {y: 6}, {y: 6, age: 8}, {z: 7}]);\n * c.lastWhere('y', 6); // {x: 6, age: 8}\n * c.lastWhere(); // {z: 7}\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [properties] -\n * The expression used to create a function to test the elements in this\n * collection.\n * @param {Any} [value] -\n * When the first argument is a string this argument will be treated as a\n * value to compare to the value of the named property on the object passed\n * through the filter function.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * An alternative function can be used to compare to values.\n * @return {Any} -\n * The last element in this collection that matches the given expression.\n * @see Rekord.createWhere\n */\n lastWhere: function(properties, value, equals)\n {\n var where = createWhere( properties, value, equals );\n\n for (var i = this.length - 1; i >= 0; i--)\n {\n var model = this[ i ];\n\n if ( where( model ) )\n {\n return model;\n }\n }\n\n return null;\n },\n\n /**\n * Returns the last non-null value in this collection given a property\n * expression. If no non-null values exist for the given property expression,\n * then undefined will be returned.\n *\n * ```javascript\n * var c = Rekord.collect([{x: 5}, {y: 6}, {y: 4}, {z: 7}]);\n * c.last('y'); // 4\n * c.last(); // {z: 7}\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [properties] -\n * The expression which converts one value into another.\n * @return {Any} -\n * @see Rekord.createPropertyResolver\n * @see Rekord.isValue\n */\n last: function(properties)\n {\n var resolver = createPropertyResolver( properties );\n\n for (var i = this.length - 1; i >= 0; i--)\n {\n var resolved = resolver( this[ i ] );\n\n if ( isValue( resolved ) )\n {\n return resolved;\n }\n }\n },\n\n /**\n * Iterates over all elements in this collection and passes them through the\n * `resolver` function. The returned value is passed through the `validator`\n * function and if that returns true the resolved value is passed through the\n * `process` function. After iteration, the `getResult` function is executed\n * and the returned value is returned by this function.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Function} resolver -\n * The function which takes an element in this collection and returns a\n * value based on that element.\n * @param {Function} validator -\n * The function which takes the resolved value and determines whether it\n * passes some test.\n * @param {Function} process -\n * The function which is given the resolved value if it passes the test.\n * @param {Function} getResult -\n * The function which is executed at the end of iteration and the result is\n * is returned by this function.\n * @return {Any} -\n * The value returned by `getResult`.\n */\n aggregate: function(resolver, validator, process, getResult)\n {\n for (var i = 0; i < this.length; i++)\n {\n var resolved = resolver( this[ i ] );\n\n if ( validator( resolved ) )\n {\n process( resolved );\n }\n }\n\n return getResult();\n },\n\n /**\n * Sums all numbers resolved from the given property expression and returns\n * the result.\n *\n * ```javascript\n * var c = Rekord.collect([2, 3, 4]);\n * c.sum(); // 9\n * var d = Rekord.collect([{age: 5}, {age: 4}, {age: 2}]);\n * d.sum('age'); // 11\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [numbers]\n * The expression which converts an element in this collection to a number.\n * @return {Number} -\n * The sum of all valid numbers found in this collection.\n * @see Rekord.createNumberResolver\n */\n sum: function(numbers)\n {\n var resolver = createNumberResolver( numbers );\n var result = 0;\n\n function process(x)\n {\n result += x;\n }\n\n function getResult()\n {\n return result;\n }\n\n return this.aggregate( resolver, isNumber, process, getResult );\n },\n\n /**\n * Averages all numbers resolved from the given property expression and\n * returns the result.\n *\n * ```javascript\n * var c = Rekord.collect([2, 3, 4]);\n * c.avg(); // 3\n * var d = Rekord.collect([{age: 5}, {age: 4}, {age: 2}]);\n * d.avg('age'); // 3.66666\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [numbers]\n * The expression which converts an element in this collection to a number.\n * @return {Number} -\n * The average of all valid numbers found in this collection.\n * @see Rekord.createNumberResolver\n */\n avg: function(numbers)\n {\n var resolver = createNumberResolver( numbers );\n var result = 0;\n var total = 0;\n\n function process(x)\n {\n result += x;\n total++;\n }\n\n function getResult()\n {\n return total === 0 ? 0 : result / total;\n }\n\n return this.aggregate( resolver, isNumber, process, getResult );\n },\n\n /**\n * Counts the number of elements in this collection that past the test\n * function generated by {@link Rekord.createWhere}.\n *\n * ```javascript\n * var c = Rekord.collect([{name: 't1', done: 1}, {name: 't2', done: 0}, {name: 't3', done: 1}, {name: 't4'}]);\n * c.countWhere('done'); // 3\n * c.countWhere('done', 0); // 1\n * c.countWhere('done', 1); // 2\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [properties] -\n * The expression used to create a function to test the elements in this\n * collection.\n * @param {Any} [value] -\n * When the first argument is a string this argument will be treated as a\n * value to compare to the value of the named property on the object passed\n * through the filter function.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * An alternative function can be used to compare to values.\n * @return {Number} -\n * The number of elements in the collection that passed the test.\n * @see Rekord.createWhere\n */\n countWhere: function(properties, value, equals)\n {\n var where = createWhere( properties, value, equals );\n var met = 0;\n\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n\n if ( where( model ) )\n {\n met++;\n }\n }\n\n return met;\n },\n\n /**\n * Counts the number of elements in this collection that has a value for the\n * given property expression.\n *\n * ```javascript\n * var c = Rekord.collect([{age: 2}, {age: 3}, {taco: 4}]);\n * c.count('age'); // 2\n * c.count('taco'); // 1\n * c.count(); // 3\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [properties] -\n * The expression which converts one value into another.\n * @return {Number} -\n * The number of elements that had values for the property expression.\n * @see Rekord.createPropertyResolver\n * @see Rekord.isValue\n */\n count: function(properties)\n {\n if ( !isValue( properties ) )\n {\n return this.length;\n }\n\n var resolver = createPropertyResolver( properties );\n var result = 0;\n\n for (var i = 0; i < this.length; i++)\n {\n var resolved = resolver( this[ i ] );\n\n if ( isValue( resolved ) )\n {\n result++;\n }\n }\n\n return result;\n },\n\n /**\n * Plucks values from elements in the collection. If only a `values` property\n * expression is given the result will be an array of resolved values. If the\n * `keys` property expression is given, the result will be an object where the\n * property of the object is determined by the key expression.\n *\n * ```javascript\n * var c = Rekord.collect([{age: 2, nm: 'T'}, {age: 4, nm: 'R'}, {age: 5, nm: 'G'}]);\n * c.pluck(); // c\n * c.pluck('age'); // [2, 4, 5]\n * c.pluck('age', 'nm'); // {T: e, R: 4, G: 5}\n * c.pluck(null, 'nm'); // {T: {age: 2, nm: 'T'}, R: {age: 4, nm: 'R'}, G: {age: 5, nm: 'G'}}\n * c.pluck('{age}-{nm}'); // ['2-T', '4-R', '5-G']\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {propertyResolverInput} [values] -\n * The expression which converts an element into a value to pluck.\n * @param {propertyResolverInput} [keys] -\n * The expression which converts an element into an object property (key).\n * @return {Array|Object} -\n * The plucked values.\n * @see Rekord.createPropertyResolver\n */\n pluck: function(values, keys)\n {\n var valuesResolver = createPropertyResolver( values );\n\n if ( keys )\n {\n var keysResolver = createPropertyResolver( keys );\n var result = {};\n\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n var value = valuesResolver( model );\n var key = keysResolver( model );\n\n result[ key ] = value;\n }\n\n return result;\n }\n else\n {\n var result = [];\n\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n var value = valuesResolver( model );\n\n result.push( value );\n }\n\n return result;\n }\n },\n\n /**\n * Iterates over each element in this collection and passes the element and\n * it's index to the given function. An optional function context can be given.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Function} callback -\n * The function to invoke for each element of this collection passing the\n * element and the index where it exists.\n * @param {Object} [context] -\n * The context to the callback function.\n * @return {Rekord.Collection} -\n * The reference to this collection.\n */\n each: function(callback, context)\n {\n var callbackContext = context || this;\n\n for (var i = 0; i < this.length; i++)\n {\n var item = this[ i ];\n\n callback.call( callbackContext, item, i );\n\n if ( this[ i ] !== item )\n {\n i--;\n }\n }\n\n return this;\n },\n\n /**\n * Iterates over each element in this collection that matches the where\n * expression and passes the element and it's index to the given function.\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Function} callback -\n * The function to invoke for each element of this collection passing the\n * element and the index where it exists.\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.Collection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n */\n eachWhere: function(callback, properties, values, equals)\n {\n var where = createWhere( properties, values, equals );\n\n for (var i = 0; i < this.length; i++)\n {\n var item = this[ i ];\n\n if ( where( item ) )\n {\n callback.call( this, item, i );\n\n if ( this[ i ] !== item )\n {\n i--;\n }\n }\n }\n\n return this;\n },\n\n /**\n * Reduces all the elements of this collection to a single value. All elements\n * are passed to a function which accepts the currently reduced value and the\n * current element and returns the new reduced value.\n *\n * ```javascript\n * var reduceIt = function(curr, elem) {\n * return curr + ( elem[0] * elem[1] );\n * };\n * var c = Rekord.collect([[2, 1], [3, 2], [5, 6]]);\n * c.reduce( reduceIt, 0 ); // 38\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Function} reducer -\n * A function which accepts the current reduced value and an element and\n * returns the new reduced value.\n * @param {Any} [initialValue] -\n * The first value to pass to the reducer function.\n * @return {Any} -\n * The reduced value.\n */\n reduce: function(reducer, initialValue)\n {\n for (var i = 0; i < this.length; i++)\n {\n initialValue = reducer( initialValue, this[ i ] );\n }\n\n return initialValue;\n },\n\n /**\n * Returns a random element in this collection.\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Any} -\n * The randomly chosen element from this collection.\n */\n random: function()\n {\n var i = Math.floor( Math.random() * this.length );\n\n return this[ i ];\n },\n\n /**\n * Breaks up the collection into an array of arrays of a maximum size (chunks).\n * A destination array can be used to avoid re-allocating arrays.\n *\n * ```javascript\n * var c = Rekord.collect([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n * c.chunk(4); // [[1, 2, 3, 4], [5, 6, 7, 8], [9]]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Number} chunkSize -\n * The maximum number of elements that can exist in a chunk.\n * @param {Array} [out] -\n * The destination array to place the chunks.\n * @return {Array} -\n * The array of chunks of elements taken from this collection.\n */\n chunk: function(chunkSize, out)\n {\n var outer = out || [];\n var outerIndex = 0;\n var inner = outer[ outerIndex ] = outer[ outerIndex ] || [];\n var innerIndex = 0;\n\n for (var i = 0; i < this.length; i++)\n {\n inner[ innerIndex ] = this[ i ];\n\n if ( ++innerIndex >= chunkSize )\n {\n innerIndex = 0;\n outerIndex++;\n inner.length = chunkSize;\n inner = outer[ outerIndex ] = outer[ outerIndex ] || [];\n }\n }\n\n if ( innerIndex !== 0 )\n {\n outerIndex++;\n }\n\n inner.length = innerIndex;\n outer.length = outerIndex;\n\n return outer;\n },\n\n /**\n * Determines whether at least one element in this collection matches the\n * given criteria.\n *\n * ```javascript\n * var c = Rekord.collect([{age: 2}, {age: 6}]);\n * c.contains('age', 2); // true\n * c.contains('age', 3); // false\n * c.contains('age'); // true\n * c.contains('name'); // false\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {whereInput} [properties] -\n * The expression used to create a function to test the elements in this\n * collection.\n * @param {Any} [value] -\n * When the first argument is a string this argument will be treated as a\n * value to compare to the value of the named property on the object passed\n * through the filter function.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * An alternative function can be used to compare to values.\n * @return {Boolean} -\n * True if any of the elements passed the test function, otherwise false.\n * @see Rekord.createWhere\n */\n contains: function(properties, value, equals)\n {\n var where = createWhere( properties, value, equals );\n\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n\n if ( where( model ) )\n {\n return true;\n }\n }\n\n return false;\n },\n\n /**\n * Groups the elements into sub collections given some property expression to\n * use as the value to group by.\n *\n * ```javascript\n * var c = Rekord.collect([\n * { name: 'Tom', age: 6, group: 'X' },\n * { name: 'Jon', age: 7, group: 'X' },\n * { name: 'Rob', age: 8, group: 'X' },\n * { name: 'Bon', age: 9, group: 'Y' },\n * { name: 'Ran', age: 10, group: 'Y' },\n * { name: 'Man', age: 11, group: 'Y' },\n * { name: 'Tac', age: 12, group: 'Z' }\n * ]);\n *\n * c.group({by: 'group'});\n * // [{group: 'X', $count: 3, $group: [...]},\n * // {group: 'Y', $count: 3, $group: [...]},\n * // {group: 'Z', $count: 1, $group: [.]}]\n *\n * c.group({by: 'group', select: {age: 'avg', name: 'first'}});\n * // [{group: 'X', age: 7, name: 'Tom', $count: 3, $group: [...]},\n * // {group: 'Y', age: 9, name: 'Bon', $count: 3, $group: [...]},\n * // {group: 'Z', age: 12, name: 'Tac', $count: 1, $group: [.]}]\n *\n * c.group({by: 'group', track: false, count: false});\n * // [{group: 'X'}, {group: 'Y'}, {group: 'Z'}]\n *\n * var havingMoreThanOne = function(grouping, groupElements) {\n * return groupElements.length > 0;\n * };\n * c.group({by: 'group', select: {age: 'avg'}, comparator: '-age', having: havingMoreThanOne, track: false, count: false});\n * // [{group: 'Y', age: 9},\n * // {group: 'X', age: 7}]\n * ```\n *\n * @method\n * @memberof Rekord.Collection#\n * @param {Object} grouping -\n * An object specifying how elements in this collection are to be grouped\n * and what properties from the elements should be aggregated in the\n * resulting groupings.\n * - `by`: A property expression that resolves how elements will be grouped.\n * - `select`: An object which contains properties that should be aggregated where the value is the aggregate collection function to call (sum, avg, count, first, last, etc).\n * - `having`: A having expression which takes a grouping and the grouped elements and determines whether the grouping should be in the final result.\n * - `comparator`: A comparator for sorting the resulting collection of groupings.\n * - `comparatorNullsFirst`: Whether nulls should be sorted to the top.\n * - `track`: Whether all elements in the group should exist in a collection in the `$group` property of each grouping.\n * - `count`: Whether the number of elements in the group should be placed in the `$count` property of each grouping.\n * @return {Rekord.Collection} -\n * A collection of groupings.\n */\n group: function(grouping)\n {\n var by = createPropertyResolver( grouping.by );\n var having = createWhere( grouping.having, grouping.havingValue, grouping.havingEquals );\n var select = grouping.select || {};\n var map = {};\n\n if ( isString( grouping.by ) )\n {\n if ( !(grouping.by in select) )\n {\n select[ grouping.by ] = 'first';\n }\n }\n else if ( isArray( grouping.by ) )\n {\n for (var prop in grouping.by)\n {\n if ( !(prop in select) )\n {\n select[ prop ] = 'first';\n }\n }\n }\n\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n var key = by( model );\n var group = map[ key ];\n\n if ( !group )\n {\n group = map[ key ] = this.cloneEmpty();\n }\n\n group.add( model, true );\n }\n\n var groupings = this.cloneEmpty();\n\n groupings.setComparator( grouping.comparator, grouping.comparatorNullsFirst );\n\n for (var key in map)\n {\n var grouped = {};\n var groupArray = map[ key ];\n\n for (var propName in select)\n {\n var aggregator = select[ propName ];\n\n if ( isString( aggregator ) )\n {\n grouped[ propName ] = groupArray[ aggregator ]( propName );\n }\n else if ( isFunction( aggregator ) )\n {\n grouped[ propName ] = aggregator( groupArray, propName );\n }\n }\n\n if ( grouping.track !== false )\n {\n grouped.$group = groupArray;\n }\n\n if ( grouping.count !== false )\n {\n grouped.$count = groupArray.length;\n }\n\n if ( having( grouped, groupArray ) )\n {\n groupings.push( grouped );\n }\n }\n\n groupings.sort();\n\n return groupings;\n },\n\n /**\n * Returns a copy of this collection as a plain Array.\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Array} -\n * The copy of this collection as a plain array.\n */\n toArray: function()\n {\n return this.slice();\n },\n\n /**\n * Returns a clone of this collection.\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Rekord.Collection} -\n * The reference to a clone collection.\n */\n clone: function()\n {\n return this.constructor.create( this );\n },\n\n /**\n * Returns an empty clone of this collection.\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Rekord.Collection} -\n * The reference to a clone collection.\n */\n cloneEmpty: function()\n {\n return this.constructor.create();\n }\n\n});\n\naddEventful( Collection );\n\n/**\n * Adds a listener for change events on this collection.\n *\n * @method change\n * @memberof Rekord.Collection#\n * @param {Function} callback -\n * A function to call every time a change occurs in this collection.\n * @param {Object} [context] -\n * The desired context (this) for the given callback function.\n * @return {Function} -\n * A function to call to stop listening for change events.\n * @see Rekord.Collection#event:changes\n */\naddEventFunction( Collection, 'change', Collection.Events.Changes );\n\n\n// The methods necessary for a filtered collection.\nvar Filtering = {\n\n bind: function()\n {\n Class.props(this, {\n onAdd: bind( this, Filtering.handleAdd ),\n onAdds: bind( this, Filtering.handleAdds ),\n onRemove: bind( this, Filtering.handleRemove ),\n onRemoves: bind( this, Filtering.handleRemoves ),\n onReset: bind( this, Filtering.handleReset ),\n onUpdates: bind( this, Filtering.handleUpdates ),\n onCleared: bind( this, Filtering.handleCleared )\n });\n },\n\n init: function(base, filter)\n {\n if ( this.base !== base )\n {\n if ( this.base )\n {\n this.disconnect();\n }\n\n Class.prop( this, 'base', base );\n\n this.connect();\n }\n\n Class.prop( this, 'filter', filter );\n\n this.sync();\n\n return this;\n },\n\n setFilter: function(whereProperties, whereValue, whereEquals)\n {\n this.filter = createWhere( whereProperties, whereValue, whereEquals );\n this.sync();\n\n return this;\n },\n\n connect: function()\n {\n this.base.on( Collection.Events.Add, this.onAdd );\n this.base.on( Collection.Events.Adds, this.onAdds );\n this.base.on( Collection.Events.Remove, this.onRemove );\n this.base.on( Collection.Events.Removes, this.onRemoves );\n this.base.on( Collection.Events.Reset, this.onReset );\n this.base.on( Collection.Events.Updates, this.onUpdates );\n this.base.on( Collection.Events.Cleared, this.onCleared );\n\n return this;\n },\n\n disconnect: function()\n {\n this.base.off( Collection.Events.Add, this.onAdd );\n this.base.off( Collection.Events.Adds, this.onAdds );\n this.base.off( Collection.Events.Remove, this.onRemove );\n this.base.off( Collection.Events.Removes, this.onRemoves );\n this.base.off( Collection.Events.Reset, this.onReset );\n this.base.off( Collection.Events.Updates, this.onUpdates );\n this.base.off( Collection.Events.Cleared, this.onCleared );\n\n return this;\n },\n\n sync: function()\n {\n var base = this.base;\n var filter = this.filter;\n var matches = [];\n\n for (var i = 0; i < base.length; i++)\n {\n var value = base[ i ];\n\n if ( filter( value ) )\n {\n matches.push( value );\n }\n }\n\n return this.reset( matches );\n },\n\n handleAdd: function(collection, value)\n {\n var filter = this.filter;\n\n if ( filter( value ) )\n {\n this.add( value );\n }\n },\n\n handleAdds: function(collection, values)\n {\n var filter = this.filter;\n var filtered = [];\n\n for (var i = 0; i < values.length; i++)\n {\n var value = values[ i ];\n\n if ( filter( value ) )\n {\n filtered.push( value );\n }\n }\n\n this.addAll( filtered );\n },\n\n handleRemove: function(collection, value)\n {\n this.remove( value );\n },\n\n handleRemoves: function(collection, values)\n {\n this.removeAll( values );\n },\n\n handleReset: function(collection)\n {\n this.sync();\n },\n\n handleUpdates: function(collection, updates)\n {\n var filter = this.filter;\n\n for (var i = 0; i < updates.length; i++)\n {\n var value = updates[ i ];\n\n if ( filter( value ) )\n {\n this.add( value, true );\n }\n else\n {\n this.remove( value, true );\n }\n }\n\n this.sort();\n },\n\n handleCleared: function(collection)\n {\n this.clear();\n },\n\n clone: function()\n {\n return this.constructor.create( this.base, this.filter );\n },\n\n cloneEmpty: function()\n {\n return this.constructor.create( this.base, this.filter );\n }\n\n};\n\n\n/**\n *\n * @constructor\n * @memberof Rekord\n * @augments Rekord.Eventful\n */\nfunction Page(collection, pageSize, pageIndex)\n{\n this.onChanges = bind( this, this.handleChanges );\n this.pageSize = pageSize;\n this.pageIndex = pageIndex || 0;\n this.pageCount = 0;\n this.setCollection( collection );\n}\n\nPage.Events =\n{\n Change: 'change',\n Changes: 'change'\n};\n\nClass.extend( Array, Page,\n{\n\n setPageSize: function(pageSize)\n {\n this.pageSize = pageSize;\n this.handleChanges();\n },\n\n setPageIndex: function(pageIndex)\n {\n this.goto( pageIndex );\n },\n\n setCollection: function(collection)\n {\n if ( collection !== this.collection )\n {\n if ( this.collection )\n {\n this.disconnect();\n }\n\n this.collection = collection;\n this.connect();\n this.handleChanges( true );\n }\n },\n\n connect: function()\n {\n this.collection.on( Collection.Events.Changes, this.onChanges );\n },\n\n disconnect: function()\n {\n this.collection.off( Collection.Events.Changes, this.onChanges );\n },\n\n goto: function(pageIndex)\n {\n var actualIndex = this.page( pageIndex );\n\n if ( actualIndex !== this.pageIndex )\n {\n this.pageIndex = actualIndex;\n this.update();\n this.trigger( Page.Events.Change, [ this ] );\n }\n },\n\n next: function()\n {\n this.goto( this.pageIndex + 1 );\n },\n\n prev: function()\n {\n this.goto( this.pageIndex - 1 );\n },\n\n jump: function(to)\n {\n this.goto( to );\n },\n\n first: function()\n {\n this.goto( 0 );\n },\n\n last: function()\n {\n this.goto( this.pageCount - 1 );\n },\n\n total: function()\n {\n return this.collection.length;\n },\n\n pages: function()\n {\n return Math.ceil( this.total() / this.pageSize );\n },\n\n page: function(index)\n {\n return Math.max( 0, Math.min( index, this.pages() - 1 ) );\n },\n\n can: function(index)\n {\n return this.total() && index >= 0 && index < this.pageCount;\n },\n\n canFirst: function()\n {\n return this.canPrev();\n },\n\n canLast: function()\n {\n return this.canNext();\n },\n\n canPrev: function()\n {\n return this.total() && this.pageIndex > 0;\n },\n\n canNext: function()\n {\n return this.total() && this.pageIndex < this.pageCount - 1;\n },\n\n handleChanges: function(forceApply)\n {\n var pageCount = this.pages();\n var pageIndex = this.page( this.pageIndex );\n var apply = forceApply || this.pageIndex !== pageIndex || this.length !== this.pageSize;\n var changes = apply || this.pageCount !== pageCount;\n\n this.pageIndex = pageIndex;\n this.pageCount = pageCount;\n\n if ( apply )\n {\n this.update();\n }\n if ( changes )\n {\n this.trigger( Page.Events.Change, [ this ] );\n }\n },\n\n update: function()\n {\n var source = this.collection;\n var n = source.length;\n var start = this.pageIndex * this.pageSize;\n var end = Math.min( start + this.pageSize, n );\n var length = end - start;\n\n this.length = 0;\n\n for (var i = 0; i < length; i++)\n {\n this.push( source[ start++ ] );\n }\n },\n\n more: function(pages)\n {\n var source = this.collection;\n var limit = source.length;\n var pageCount = pages || 1;\n var offset = this.pageIndex * this.pageSize;\n var start = offset + this.length;\n var adding = this.pageSize * pageCount;\n var desiredEnd = start + adding;\n var actualEnd = Math.min( limit, desiredEnd );\n\n while (start < actualEnd)\n {\n this.push( source[ start++ ] );\n }\n },\n\n toArray: function()\n {\n return this.slice();\n }\n\n});\n\naddEventful( Page );\n\naddEventFunction( Page, 'change', Page.Events.Changes );\n\n\n/**\n * An extension of the {@link Rekord.Collection} class which is a filtered view\n * of another collection.\n *\n * ```javascript\n * var isEven = function(x) { return x % 2 === 0; };\n * var c = Rekord.collect([1, 2, 3, 4, 5, 6, 7]);\n * var f = c.filtered( isEven );\n * f; // [2, 4, 6]\n * c.add( 8 );\n * c.remove( 2 );\n * f; // [4, 6, 8]\n * ```\n *\n * @constructor\n * @memberof Rekord\n * @extends Rekord.Collection\n * @param {Rekord.Collection} base -\n * The collection to listen to for changes to update this collection.\n * @param {whereCallback} filter -\n * The function which determines whether an element in the base collection\n * should exist in this collection.\n * @see Rekord.Collection#filtered\n */\nfunction FilteredCollection(base, filter)\n{\n this.bind();\n this.init( base, filter );\n}\n\n/**\n * The collection to listen to for changes to update this collection.\n *\n * @memberof Rekord.FilteredCollection#\n * @member {Rekord.Collection} base\n */\n\n /**\n * The function which determines whether an element in the base collection\n * should exist in this collection.\n *\n * @memberof Rekord.FilteredCollection#\n * @member {whereCallback} filter\n */\n\nClass.extend( Collection, FilteredCollection,\n{\n\n /**\n * Generates the handlers which are passed to the base collection when this\n * filtered collection is connected or disconnected - which happens on\n * initialization and subsequent calls to {@link FilteredCollection#init}.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n */\n bind: Filtering.bind,\n\n /**\n * Initializes the filtered collection by setting the base collection and the\n * filtering function.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @param {Rekord.Collection} base -\n * The collection to listen to for changes to update this collection.\n * @param {whereCallback} filter -\n * The function which determines whether an element in the base collection\n * should exist in this collection.\n * @return {Rekord.FilteredCollection} -\n * The reference to this collection.\n * @emits Rekord.Collection#reset\n */\n init: Filtering.init,\n\n /**\n * Sets the filter function of this collection and re-sychronizes it with the\n * base collection.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.FilteredCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @emits Rekord.Collection#reset\n */\n setFilter: Filtering.setFilter,\n\n /**\n * Registers callbacks with events of the base collection.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @return {Rekord.FilteredCollection} -\n * The reference to this collection.\n */\n connect: Filtering.connect,\n\n /**\n * Unregisters callbacks with events from the base collection.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @return {Rekord.FilteredCollection} -\n * The reference to this collection.\n */\n disconnect: Filtering.disconnect,\n\n /**\n * Synchronizes this collection with the base collection. Synchronizing\n * involves iterating over the base collection and passing each element into\n * the filter function and if it returns a truthy value it's added to this\n * collection.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @return {Rekord.FilteredCollection} -\n * The reference to this collection.\n * @emits Rekord.Collection#reset\n */\n sync: Filtering.sync,\n\n /**\n * Returns a clone of this collection.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @return {Rekord.FilteredCollection} -\n * The reference to a clone collection.\n */\n clone: Filtering.clone,\n\n /**\n * Returns an empty clone of this collection.\n *\n * @method\n * @memberof Rekord.FilteredCollection#\n * @return {Rekord.FilteredCollection} -\n * The reference to a clone collection.\n */\n cloneEmpty: Filtering.cloneEmpty\n\n});\n\n\n/**\n * An extension of the {@link Rekord.Collection} class for {@link Rekord.Model}\n * instances.\n *\n * @constructor\n * @memberof Rekord\n * @extends Rekord.Collection\n * @param {Rekord.Database} database -\n * The database for the models in this collection.\n * @param {modelInput[]} [models] -\n * The initial array of models in this collection.\n * @param {Boolean} [remoteData=false] -\n * If the models array is from a remote source. Remote sources place the\n * model directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @see Rekord.Models.boot\n * @see Rekord.Models.collect\n */\nfunction ModelCollection(database, models, remoteData)\n{\n this.init( database, models, remoteData );\n}\n\n/**\n * The map of models which keeps an index (by model key) of the models.\n *\n * @memberof Rekord.ModelCollection#\n * @member {Rekord.Map} map\n */\n\n/**\n * The database for the models in this collection.\n *\n * @memberof Rekord.ModelCollection#\n * @member {Rekord.Database} database\n */\n\nClass.extend( Collection, ModelCollection,\n{\n\n /**\n * Initializes the model collection by setting the database, the initial set\n * of models, and whether the initial set of models is from a remote source.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Rekord.Database} database -\n * The database for the models in this collection.\n * @param {modelInput[]} [models] -\n * The initial array of models in this collection.\n * @param {Boolean} [remoteData=false] -\n * If the models array is from a remote source. Remote sources place the\n * model directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#reset\n */\n init: function(database, models, remoteData)\n {\n Class.props(this, {\n database: database,\n map: new Map()\n });\n\n this.map.values = this;\n this.reset( models, remoteData );\n\n return this;\n },\n\n /**\n * Documented in Collection.js\n */\n sort: function(comparator, comparatorNullsFirst)\n {\n var cmp = comparator ? createComparator( comparator, comparatorNullsFirst ) : this.comparator;\n\n if ( !isSorted( cmp, this ) )\n {\n this.map.sort( cmp );\n\n this.trigger( Collection.Events.Sort, [this] );\n }\n\n return this;\n },\n\n /**\n * Takes input provided to the collection for adding, removing, or querying\n * and generates the key which uniquely identifies a model.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput} input -\n * The input to convert to a key.\n * @return {modelKey} -\n * The key built from the input.\n */\n buildKeyFromInput: function(input)\n {\n return this.database.keyHandler.buildKeyFromInput( input );\n },\n\n /**\n * Takes input provided to this collection for adding, removing, or querying\n * and returns a model instance. An existing model can be referenced or a new\n * model can be created on the spot.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput} input -\n * The input to convert to a model instance.\n * @param {Boolean} [remoteData=false] -\n * If the model is from a remote source. Remote sources place the model\n * directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @return {Rekord.Model} -\n * A model instance parsed from the input.\n */\n parseModel: function(input, remoteData)\n {\n return this.database.parseModel( input, remoteData );\n },\n\n /**\n * Creates a sub view of this collection known as a filtered collection. The\n * resulting collection changes when this collection changes. Any time an\n * element is added or removed to this collection it may be added or removed\n * from the filtered collection if it fits the filter function. The filter\n * function is created by passing the arguments of this function to\n * {@link Rekord.createWhere}.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.FilteredModelCollection} -\n * The newly created live filtered view of this collection.\n * @see Rekord.createWhere\n */\n filtered: function(whereProperties, whereValue, whereEquals)\n {\n var filter = createWhere( whereProperties, whereValue, whereEquals );\n\n return FilteredModelCollection.create( this, filter );\n },\n\n /**\n * Documented in Collection.js\n *\n * @see Rekord.ModelCollection#buildKeyFromInput\n */\n subtract: function(models, out)\n {\n var target = out || this.cloneEmpty();\n\n for (var i = 0; i < this.length; i++)\n {\n var a = this[ i ];\n var key = a.$key();\n var exists = false;\n\n if ( models instanceof ModelCollection )\n {\n exists = models.has( key );\n }\n else\n {\n for (var k = 0; k < models.length && !exists; k++)\n {\n var modelKey = this.buildKeyFromInput( models[ k ] );\n\n exists = (key === modelKey);\n }\n }\n\n if (!exists)\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Documented in Collection.js\n */\n intersect: function(models, out)\n {\n var target = out || this.cloneEmpty();\n\n for (var i = 0; i < models.length; i++)\n {\n var a = models[ i ];\n var key = this.buildKeyFromInput( a );\n\n if ( this.has( key ) )\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Documented in Collection.js\n */\n complement: function(models, out)\n {\n var target = out || this.cloneEmpty();\n\n for (var i = 0; i < models.length; i++)\n {\n var a = models[ i ];\n var key = this.buildKeyFromInput( a );\n\n if ( !this.has( key ) )\n {\n target.push( a );\n }\n }\n\n return target;\n },\n\n /**\n * Documented in Collection.js\n */\n clear: function()\n {\n var cleared = this.map.reset();\n\n this.trigger( Collection.Events.Cleared, [this] );\n\n return cleared;\n },\n\n /**\n * Resets the models in this collection with a new collection of models.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput[]} [models] -\n * The initial array of models in this collection.\n * @param {Boolean} [remoteData=false] -\n * If the models array is from a remote source. Remote sources place the\n * model directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.ModelCollection#parseModel\n * @emits Rekord.ModelCollection#reset\n */\n reset: function(models, remoteData)\n {\n var map = this.map;\n\n map.reset();\n\n if ( isArray( models ) )\n {\n for (var i = 0; i < models.length; i++)\n {\n var model = models[ i ];\n var parsed = this.parseModel( model, remoteData );\n\n if ( parsed )\n {\n map.put( parsed.$key(), parsed );\n }\n }\n }\n else if ( isObject( models ) )\n {\n var parsed = this.parseModel( models, remoteData );\n\n if ( parsed )\n {\n map.put( parsed.$key(), parsed );\n }\n }\n\n this.trigger( Collection.Events.Reset, [this] );\n this.sort();\n\n return this;\n },\n\n /**\n * Returns whether this collection contains a model with the given key.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelKey} key -\n * The key of the model to check for existence.\n * @return {Boolean} -\n * True if a model with the given key exists in this collection, otherwise\n * false.\n */\n has: function(key)\n {\n return this.map.has( key );\n },\n\n /**\n * Returns the model in this collection with the given key.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelKey} key -\n * The key of the model to return.\n * @return {Rekord.Model} -\n * The model instance for the given key, or undefined if a model wasn't\n * found.\n */\n get: function(key)\n {\n return this.map.get( key );\n },\n\n /**\n * Places a model in this collection providing a key to use.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelKey} key -\n * The key of the model.\n * @param {Rekord.Model} model -\n * The model instance to place in the collection.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#add\n * @emits Rekord.ModelCollection#sort\n */\n put: function(key, model, delaySort)\n {\n this.map.put( key, model );\n this.trigger( Collection.Events.Add, [this, model, this.map.indices[ key ]] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n },\n\n /**\n * Adds a model to this collection - sorting the collection if a comparator\n * is set on this collection and `delaySort` is not a specified or a true\n * value.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput} input -\n * The model to add to this collection.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @param {Boolean} [remoteData=false] -\n * If the model is from a remote source. Remote sources place the model\n * directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#add\n * @emits Rekord.ModelCollection#sort\n */\n add: function(input, delaySort, remoteData)\n {\n var model = this.parseModel( input, remoteData );\n var key = model.$key();\n\n this.map.put( key, model );\n this.trigger( Collection.Events.Add, [this, model, this.map.indices[ key ]] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n\n return this;\n },\n\n /**\n * Adds one or more models to the end of this collection - sorting the\n * collection if a comparator is set on this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {...modelInput} value -\n * The models to add to this collection.\n * @return {Number} -\n * The new length of this collection.\n * @emits Rekord.ModelCollection#add\n * @emits Rekord.ModelCollection#sort\n */\n push: function()\n {\n var values = AP.slice.apply( arguments );\n var indices = [];\n\n for (var i = 0; i < values.length; i++)\n {\n var model = this.parseModel( values[ i ] );\n var key = model.$key();\n\n this.map.put( key, model );\n indices.push( this.map.indices[ key ] );\n }\n\n this.trigger( Collection.Events.Adds, [this, values, indices] );\n this.sort();\n\n return this.length;\n },\n\n /**\n * @method\n * @memberof Rekord.ModelCollection#\n * @see Rekord.ModelCollection#push\n * @param {...modelInput} value -\n * The values to add to this collection.\n * @return {Number} -\n * The new length of this collection.\n * @emits Rekord.ModelCollection#adds\n * @emits Rekord.ModelCollection#sort\n */\n unshift: function()\n {\n return this.push.apply( this, arguments );\n },\n\n /**\n * Adds all models in the given array to this collection - sorting the\n * collection if a comparator is set on this collection and `delaySort` is\n * not specified or a true value.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput[]} models -\n * The models to add to this collection.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @param {Boolean} [remoteData=false] -\n * If the model is from a remote source. Remote sources place the model\n * directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#adds\n * @emits Rekord.ModelCollection#sort\n */\n addAll: function(models, delaySort, remoteData)\n {\n if ( isArray( models ) )\n {\n var indices = [];\n\n for (var i = 0; i < models.length; i++)\n {\n var model = this.parseModel( models[ i ], remoteData );\n var key = model.$key();\n\n this.map.put( key, model );\n indices.push( this.map.indices[ key ] );\n }\n\n this.trigger( Collection.Events.Adds, [this, models, indices] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n }\n },\n\n /**\n * @method\n * @memberof Rekord.ModelCollection#\n * @see Rekord.ModelCollection#add\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#add\n * @emits Rekord.ModelCollection#sort\n */\n insertAt: function(i, value, delaySort)\n {\n return this.add( value, delaySort );\n },\n\n /**\n * Removes the last model in this collection and returns it - sorting the\n * collection if a comparator is set on this collection and `delaySort` is\n * no specified or a true value.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @return {Rekord.Model} -\n * The model removed from the end of the collection.\n * @emits Rekord.ModelCollection#remove\n * @emits Rekord.ModelCollection#sort\n */\n pop: function(delaySort)\n {\n var i = this.length - 1;\n var removed = this[ i ];\n\n this.map.removeAt( i );\n this.trigger( Collection.Events.Remove, [this, removed, i] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n\n return removed;\n },\n\n /**\n * Removes the first model in this collection and returns it - sorting the\n * collection if a comparator is set on this collection and `delaySort` is\n * no specified or a true value.\n *\n * ```javascript\n * var c = Rekord.collect(1, 2, 3, 4);\n * c.shift(); // 1\n * ```\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @return {Rekord.Model} -\n * The model removed from the beginning of the collection.\n * @emits Rekord.ModelCollection#remove\n * @emits Rekord.ModelCollection#sort\n */\n shift: function(delaySort)\n {\n var removed = this[ 0 ];\n\n this.map.removeAt( 0 );\n this.trigger( Collection.Events.Remove, [this, removed, 0] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n\n return removed;\n },\n\n /**\n * Removes the model in this collection at the given index `i` - sorting\n * the collection if a comparator is set on this collection and `delaySort` is\n * not specified or a true value.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Number} i -\n * The index of the model to remove.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @return {Rekord.Model} -\n * The model removed, or undefined if the index was invalid.\n * @emits Rekord.ModelCollection#remove\n * @emits Rekord.ModelCollection#sort\n */\n removeAt: function(i, delaySort)\n {\n var removing;\n\n if (i >= 0 && i < this.length)\n {\n removing = this[ i ];\n\n this.map.removeAt( i );\n this.trigger( Collection.Events.Remove, [this, removing, i] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n }\n\n return removing;\n },\n\n /**\n * Removes the given model from this collection if it exists - sorting the\n * collection if a comparator is set on this collection and `delaySort` is not\n * specified or a true value.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput} input -\n * The model to remove from this collection if it exists.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * The function which determines whether one of the elements that exist in\n * this collection are equivalent to the given value.\n * @return {Rekord.Model} -\n * The element removed from this collection.\n * @emits Rekord.ModelCollection#remove\n * @emits Rekord.ModelCollection#sort\n */\n remove: function(input, delaySort)\n {\n var key = this.buildKeyFromInput( input );\n var removing = this.map.get( key );\n\n if ( removing )\n {\n var i = this.map.indices[ key ];\n\n this.map.remove( key );\n this.trigger( Collection.Events.Remove, [this, removing, i] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n }\n\n return removing;\n },\n\n /**\n * Removes the given models from this collection - sorting the collection if\n * a comparator is set on this collection and `delaySort` is not specified or\n * a true value.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput[]} inputs -\n * The models to remove from this collection if they exist.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.ModelCollection#sort sort}.\n * @return {Rekord.Model[]} -\n * The models removed from this collection.\n * @emits Rekord.ModelCollection#removes\n * @emits Rekord.ModelCollection#sort\n */\n removeAll: function(inputs, delaySort)\n {\n var map = this.map;\n var removed = [];\n var removedIndices = [];\n\n for (var i = 0; i < inputs.length; i++)\n {\n var key = this.buildKeyFromInput( inputs[ i ] );\n var removing = map.get( key );\n\n if ( removing )\n {\n removedIndices.push( map.indices[ key ] );\n removed.push( removing );\n }\n }\n\n removedIndices.sort();\n\n for (var i = removedIndices.length - 1; i >= 0; i--)\n {\n map.removeAt( removedIndices[ i ] );\n }\n\n this.trigger( Collection.Events.Removes, [this, removed, removedIndices] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n\n return removed;\n },\n\n /**\n * Returns the index of the given model in this collection or returns -1\n * if the model doesn't exist in this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {modelInput} input -\n * The model to search for.\n * @return {Number} -\n * The index of the model in this collection or -1 if it was not found.\n */\n indexOf: function(input)\n {\n var key = this.buildKeyFromInput( input );\n var index = this.map.indices[ key ];\n\n return index === undefined ? -1 : index;\n },\n\n /**\n * Rebuilds the internal index which maps keys to the index of the model in\n * this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n */\n rebuild: function()\n {\n this.map.rebuildIndex();\n },\n\n /**\n * Returns the array of keys that correspond to the models in this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @return {modelKey[]} -\n * The array of model keys.\n */\n keys: function()\n {\n return this.map.keys;\n },\n\n /**\n * Reverses the order of models in this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#updates\n */\n reverse: function()\n {\n this.map.reverse();\n\n this.trigger( Collection.Events.Updates, [this] );\n\n return this;\n },\n\n /**\n * Splices elements out of and into this collection - sorting the collection\n * if a comparator is set on this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Number} start -\n * Index at which to start changing the array (with origin 0). If greater\n * than the length of the array, actual starting index will be set to the\n * length of the array. If negative, will begin that many elements from the end.\n * @param {Number} deleteCount -\n * An integer indicating the number of old array elements to remove. If\n * deleteCount is 0, no elements are removed. In this case, you should\n * specify at least one new element. If deleteCount is greater than the\n * number of elements left in the array starting at start, then all of the\n * elements through the end of the array will be deleted.\n * If deleteCount is omitted, deleteCount will be equal to (arr.length - start).\n * @param {...Any} values -\n * The elements to add to the array, beginning at the start index. If you\n * don't specify any elements, splice() will only remove elements from the array.\n * @return {Any[]} -\n * The array of deleted elements.\n * @emits Rekord.ModelCollection#removes\n * @emits Rekord.ModelCollection#adds\n * @emits Rekord.ModelCollection#sort\n */\n splice: function(start, deleteCount)\n {\n var adding = AP.slice.call( arguments, 2 );\n\n var addingKeys = [start, deleteCount];\n for (var i = 0; i < adding.length; i++)\n {\n addingKeys.push( this.buildKeyFromInput( adding[ i ] ) );\n }\n\n var removed = AP.splice.apply( this, arguments );\n\n AP.splice.apply( this.map.keys, addingKeys );\n\n if ( deleteCount )\n {\n this.trigger( Collection.Events.Removes, [this, removed, start, deleteCount] );\n }\n\n if ( adding.length )\n {\n this.trigger( Collection.Events.Adds, [this, adding, start] );\n }\n\n this.sort();\n\n return removed;\n },\n\n /**\n * Removes the models from this collection where the given expression is true.\n * The first argument, if `true`, can call {@link Rekord.Model#$remove} on each\n * model removed from this colleciton.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Boolean} [callRemove=false] -\n * Whether {@link Rekord.Model#$remove} should be called on each removed model.\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @param {Array} [out=this.cloneEmpty()] -\n * The array to place the elements that match.\n * @param {Boolean} [delaySort=false] -\n * Whether automatic sorting should be delayed until the user manually\n * calls {@link Rekord.Collection#sort sort}.\n * @return {Rekord.Model[]} -\n * An array of models removed from this collection.\n * @emits Rekord.ModelCollection#removes\n * @emits Rekord.ModelCollection#sort\n */\n removeWhere: function(callRemove, whereProperties, whereValue, whereEquals, out, delaySort, cascade, options)\n {\n var where = createWhere( whereProperties, whereValue, whereEquals );\n var removed = out || this.cloneEmpty();\n var removedIndices = [];\n\n batchExecute(function()\n {\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n\n if ( where( model ) )\n {\n removedIndices.push( i );\n removed.push( model );\n }\n }\n\n for (var i = 0; i < removed.length; i++)\n {\n var model = removed[ i ];\n var key = model.$key();\n\n this.map.remove( key );\n\n if ( callRemove )\n {\n model.$remove( cascade, options );\n }\n }\n\n }, this );\n\n this.trigger( Collection.Events.Removes, [this, removed, removedIndices] );\n\n if ( !delaySort )\n {\n this.sort();\n }\n\n return removed;\n },\n\n /**\n * Updates the given property(s) in all models in this collection with the\n * given value. If `avoidSave` is not a truthy value then\n * {@link Rekord.Model#$save} is called on every model in this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {String|Object} props -\n * The property or properties to update.\n * @param {Any} [value] -\n * The value to set if a String `props` is given.\n * @param {Boolean} [remoteData=false] -\n * If the properties are from a remote source. Remote sources place the\n * model directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @param {Boolean} [avoidSave=false] -\n * True for NOT calling {@link Rekord.Model#$save}, otherwise false.\n * @param {Number} [cascade] -\n * Which operations should be performed out of: store, rest, & live.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @emits Rekord.ModelCollection#updates\n * @emits Rekord.ModelCollection#sort\n */\n update: function(props, value, remoteData, avoidSave, cascade, options)\n {\n batchExecute(function()\n {\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n\n model.$set( props, value, remoteData );\n\n if ( !avoidSave )\n {\n model.$save( cascade, options );\n }\n }\n\n }, this );\n\n this.trigger( Collection.Events.Updates, [this, this] );\n this.sort();\n\n return this;\n },\n\n /**\n * Updates the given property(s) in models in this collection which pass the\n * `where` function with the given value. If `avoidSave` is not a truthy value\n * then {@link Rekord.Model#$save} is called on every model in this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereCallback} where -\n * The function which determines whether a model should be updated.\n * @param {String|Object} props -\n * The property or properties to update.\n * @param {*} [value] -\n * The value to set if a String `props` is given.\n * @param {Boolean} [remoteData=false] -\n * If the properties are from a remote source. Remote sources place the\n * model directly into the database while local sources aren't stored in the\n * database until they're saved.\n * @param {Boolean} [avoidSave=false] -\n * True for NOT calling {@link Rekord.Model#$save}, otherwise false.\n * @param {Number} [cascade] -\n * Which operations should be performed out of: store, rest, & live.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @return {Rekord.Model[]} -\n * An array of models updated.\n * @emits Rekord.ModelCollection#updates\n * @emits Rekord.ModelCollection#sort\n */\n updateWhere: function(where, props, value, remoteData, avoidSave, cascade, options)\n {\n var updated = [];\n\n batchExecute(function()\n {\n for (var i = 0; i < this.length; i++)\n {\n var model = this[ i ];\n\n if ( where( model ) )\n {\n model.$set( props, value, remoteData );\n\n if ( !avoidSave )\n {\n model.$save( cascade, options );\n }\n\n updated.push( model );\n }\n }\n\n }, this );\n\n this.trigger( Collection.Events.Updates, [this, updated] );\n this.sort();\n\n return updated;\n },\n\n /**\n * Calls {@link Rekord.Model#$push} on models in this collection that meet\n * the given where expression.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {String[]} [fields] -\n * The set of fields to save for later popping or discarding. If not\n * specified, all model fields will be saved.\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$push\n */\n pushWhere: function(fields, properties, value, equals)\n {\n function pushIt(model)\n {\n model.$push( fields );\n }\n\n return this.eachWhere( pushIt, properties, value, equals );\n },\n\n /**\n * Calls {@link Rekord.Model#$pop} on models in this collection that meet\n * the given where expression.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Boolean} [dontDiscard=false] -\n * Whether to remove the saved state after the saved state has been applied\n * back to the model. A falsy value will result in\n * {@link Rekord.Model#$discard} being called.\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$pop\n */\n popWhere: function(dontDiscard, properties, value, equals)\n {\n function popIt(model)\n {\n model.$pop( dontDiscard );\n }\n\n return this.eachWhere( popIt, properties, value, equals );\n },\n\n /**\n * Calls {@link Rekord.Model#$discard} on models in this collection that meet\n * the given where expression.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$discard\n */\n discardWhere: function(properties, value, equals)\n {\n function discardIt(model)\n {\n model.$discard();\n }\n\n return this.eachWhere( discardIt, properties, value, equals );\n },\n\n /**\n * Calls {@link Rekord.Model#$cancel} on models in this collection that meet\n * the given where expression.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Boolean} [reset=false] -\n * If reset is true and the model doesn't have a saved state -\n * {@link Rekord.Model#$reset} will be called.\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$cancel\n */\n cancelWhere: function(reset, properties, value, equals)\n {\n function cancelIt(model)\n {\n model.$cancel( reset );\n }\n\n batchExecute(function()\n {\n this.eachWhere( cancelIt, properties, value, equals );\n\n }, this );\n\n return this;\n },\n\n /**\n * Calls {@link Rekord.Model#$refresh} on models in this collection that meet\n * the given where expression.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @param {Number} [cascade] -\n * Which operations should be performed out of: store, rest, & live.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$refresh\n */\n refreshWhere: function(properties, value, equals, cascade, options)\n {\n function refreshIt(model)\n {\n model.$refresh( cascade, options );\n }\n\n batchExecute(function()\n {\n this.eachWhere( refreshIt, properties, value, equals );\n\n }, this );\n\n return this;\n },\n\n /**\n * Calls {@link Rekord.Model#$save} on models in this collection that meet\n * the given where expression.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @param {Object} [props={}] -\n * Properties to apply to each model in the collection that pass the where\n * expression.\n * @param {Number} [cascade] -\n * Which operations should be performed out of: store, rest, & live.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @return {Rekord.ModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$refresh\n */\n saveWhere: function(properties, value, equals, props, cascade, options)\n {\n function saveIt(model)\n {\n model.$save( props, cascade, options );\n }\n\n batchExecute(function()\n {\n this.eachWhere( saveIt, properties, value, equals );\n\n }, this );\n\n return this;\n },\n\n /**\n * Returns whether this collection has at least one model with changes. An\n * additional where expression can be given to only check certain models.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @return {Boolean} -\n * True if at least one model has changes, otherwise false.\n * @see Rekord.createWhere\n * @see Rekord.Model#$hasChanges\n */\n hasChanges: function(properties, value, equals)\n {\n var where = createWhere( properties, value, equals );\n\n var hasChanges = function( model )\n {\n return where( model ) && model.$hasChanges();\n };\n\n return this.contains( hasChanges );\n },\n\n /**\n * Returns a collection of all changes for each model. The changes are keyed\n * into the collection by the models key. An additional where expression can\n * be given to only check certain models.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals=Rekord.equalsStrict] -\n * See {@link Rekord.createWhere}\n * @param {Rekord.ModelCollection} [out] -\n * The collection to add the changes to.\n * @return {Rekord.ModelCollection} -\n * The collection with all changes to models in this collection.\n * @see Rekord.createWhere\n * @see Rekord.Model#$hasChanges\n * @see Rekord.Model#$getChanges\n */\n getChanges: function(properties, value, equals, out)\n {\n var where = createWhere( properties, value, equals );\n var changes = out && out instanceof ModelCollection ? out : this.cloneEmpty();\n\n this.each(function(model)\n {\n if ( where( model ) && model.$hasChanges() )\n {\n changes.put( model.$key(), model.$getChanges() );\n }\n });\n\n return changes;\n },\n\n // TODO\n project: function(projectionInput, out)\n {\n var target = out || [];\n var projection = Projection.parse( this.database, projectionInput );\n\n for (var i = 0; i < this.length; i++)\n {\n target.push( projection.project( this[ i ] ) );\n }\n\n return target;\n },\n\n /**\n * Converts this collection into an object where the keys of the models are\n * the object properties and the models are the values.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Object} [out] -\n * The object to place the models in.\n * @return {Object} -\n * The object containing the models in this collection.\n */\n toObject: function(out)\n {\n return this.map.toObject( out );\n },\n\n /**\n * Returns a clone of this collection. Optionally the models in this\n * collection can also be cloned.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @param {Boolean} [cloneModels=false] -\n * Whether or not the models should be cloned as well.\n * @param {Boolean} [cloneProperties] -\n * The properties object which defines what fields should be given a\n * different (non-cloned) value and which relations need to be cloned.\n * @return {Rekord.ModelCollection} -\n * The reference to a clone collection.\n * @see Rekord.Model#$clone\n */\n clone: function(cloneModels, cloneProperties)\n {\n var source = this;\n\n if ( cloneModels )\n {\n source = [];\n\n for (var i = 0; i < this.length; i++)\n {\n source[ i ] = this[ i ].$clone( cloneProperties );\n }\n }\n\n return ModelCollection.create( this.database, source, true );\n },\n\n /**\n * Returns an empty clone of this collection.\n *\n * @method\n * @memberof Rekord.ModelCollection#\n * @return {Rekord.ModelCollection} -\n * The reference to a clone collection.\n */\n cloneEmpty: function()\n {\n return ModelCollection.create( this.database );\n }\n\n});\n\n\n/**\n * An extension of the {@link Rekord.ModelCollection} class which is a filtered\n * view of another model collection. Changes made to the base collection are\n * reflected in the filtered collection - possibly resulting in additions and\n * removals from the filtered collection.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var finished = Task.filtered('done', true);\n * finished; // will always contain tasks that are done\n * ```\n *\n * @constructor\n * @memberof Rekord\n * @extends Rekord.ModelCollection\n * @param {Rekord.ModelCollection} base -\n * The model collection to listen to for changes to update this collection.\n * @param {whereCallback} filter -\n * The function which determines whether a model in the base collection\n * should exist in this collection.\n * @see Rekord.Collection#filtered\n */\nfunction FilteredModelCollection(base, filter)\n{\n this.bind();\n this.init( base, filter );\n}\n\n/**\n * The collection to listen to for changes to update this collection.\n *\n * @memberof Rekord.FilteredModelCollection#\n * @member {Rekord.ModelCollection} base\n */\n\n /**\n * The function which determines whether an element in the base collection\n * should exist in this collection.\n *\n * @memberof Rekord.FilteredModelCollection#\n * @member {whereCallback} filter\n */\n\nClass.extend( ModelCollection, FilteredModelCollection,\n{\n\n /**\n * Generates the handlers which are passed to the base collection when this\n * filtered collection is connected or disconnected - which happens on\n * initialization and subsequent calls to {@link FilteredModelCollection#init}.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n */\n bind: function()\n {\n Filtering.bind.apply( this );\n\n Class.props(this, {\n onModelUpdated: bind( this, this.handleModelUpdate )\n });\n },\n\n /**\n * Initializes the filtered collection by setting the base collection and the\n * filtering function.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @param {Rekord.ModelCollection} base -\n * The model collection to listen to for changes to update this collection.\n * @param {whereCallback} filter -\n * The function which determines whether a model in the base collection\n * should exist in this collection.\n * @return {Rekord.FilteredModelCollection} -\n * The reference to this collection.\n * @emits Rekord.Collection#reset\n */\n init: function(base, filter)\n {\n if ( this.base )\n {\n this.base.database.off( Database.Events.ModelUpdated, this.onModelUpdated );\n }\n\n ModelCollection.prototype.init.call( this, base.database );\n\n Filtering.init.call( this, base, filter );\n\n base.database.on( Database.Events.ModelUpdated, this.onModelUpdated );\n\n return this;\n },\n\n /**\n * Sets the filter function of this collection and re-sychronizes it with the\n * base collection.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @param {whereInput} [whereProperties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [whereValue] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [whereEquals] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.FilteredModelCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @emits Rekord.Collection#reset\n */\n setFilter: Filtering.setFilter,\n\n /**\n * Registers callbacks with events of the base collection.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @return {Rekord.FilteredModelCollection} -\n * The reference to this collection.\n */\n connect: Filtering.connect,\n\n /**\n * Unregisters callbacks with events from the base collection.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @return {Rekord.FilteredModelCollection} -\n * The reference to this collection.\n */\n disconnect: Filtering.disconnect,\n\n /**\n * Synchronizes this collection with the base collection. Synchronizing\n * involves iterating over the base collection and passing each element into\n * the filter function and if it returns a truthy value it's added to this\n * collection.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @return {Rekord.FilteredModelCollection} -\n * The reference to this collection.\n * @emits Rekord.Collection#reset\n */\n sync: Filtering.sync,\n\n /**\n * Handles the ModelUpdated event from the database.\n */\n handleModelUpdate: function(model)\n {\n var exists = this.has( model.$key() );\n var matches = this.filter( model );\n\n if ( exists && !matches )\n {\n this.remove( model );\n }\n if ( !exists && matches )\n {\n this.add( model );\n }\n },\n\n /**\n * Returns a clone of this collection.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @return {Rekord.FilteredModelCollection} -\n * The reference to a clone collection.\n */\n clone: Filtering.clone,\n\n /**\n * Returns an empty clone of this collection.\n *\n * @method\n * @memberof Rekord.FilteredModelCollection#\n * @return {Rekord.FilteredModelCollection} -\n * The reference to a clone collection.\n */\n cloneEmpty: Filtering.cloneEmpty\n\n});\n\n\n/**\n * An extension of the {@link Rekord.ModelCollection} class for relationships.\n *\n * @constructor\n * @memberof Rekord\n * @extends Rekord.ModelCollection\n * @param {Rekord.Database} database -\n * The database for the models in this collection.\n * @param {Rekord.Model} model -\n * The model instance all models in this collection are related to.\n * @param {Rekord.Relation} relator -\n * The relation instance responsible for relating/unrelating models.\n * @param {modelInput[]} [models] -\n * The initial array of models in this collection.\n * @param {Boolean} [remoteData=false] -\n * If the models array is from a remote source. Remote sources place the\n * model directly into the database while local sources aren't stored in the\n * database until they're saved.\n */\nfunction RelationCollection(database, model, relator, models, remoteData)\n{\n Class.props(this, {\n model: model,\n relator: relator\n });\n\n this.init( database, models, remoteData );\n}\n\n/**\n * The model instance all models in this collection are related to.\n *\n * @memberof Rekord.RelationCollection#\n * @member {Rekord.Model} model\n */\n\n /**\n * The relation instance responsible for relating/unrelating models.\n *\n * @memberof Rekord.RelationCollection#\n * @member {Rekord.Relation} relator\n */\n\nClass.extend( ModelCollection, RelationCollection,\n{\n\n /**\n * Sets the entire set of models which are related. If a model is specified\n * that doesn't exist in this collection a relationship is added. If a model\n * in this collection is not specified in the `input` the relationship is\n * removed. Depending on the relationship, adding and removing relationships\n * may result in the saving or deleting of models.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @param {modelInput|modelInput[]} [input] -\n * The model or array of models to relate. If input isn't specified, all\n * models currently related are unrelated.\n * @param {boolean} [remoteData=false] -\n * Whether this change is due to remote changes or changes that should not\n * trigger removes or saves.\n * @return {Rekord.RelationCollection} -\n * The reference to this collection.\n */\n set: function(input, remoteData)\n {\n this.relator.set( this.model, input, remoteData );\n\n return this;\n },\n\n /**\n * Relates one or more models to this collection's model. If a model is\n * specified that is already related then it has no effect.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @param {modelInput|modelInput[]} input -\n * The model or array of models to relate.\n * @param {boolean} [remoteData=false] -\n * Whether this change is due to remote changes or changes that should not\n * trigger removes or saves.\n * @return {Rekord.RelationCollection} -\n * The reference to this collection.\n */\n relate: function(input, remoteData)\n {\n this.relator.relate( this.model, input, remoteData );\n\n return this;\n },\n\n /**\n * Unrelates one or more models from this collection's model. If a model is\n * specified that is not related then it has no effect. If no models are\n * specified then all models in this collection are unrelated.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @param {modelInput|modelInput[]} input -\n * The model or array of models to relate.\n * @param {boolean} [remoteData=false] -\n * Whether this change is due to remote changes or changes that should not\n * trigger removes or saves.\n * @return {Rekord.RelationCollection} -\n * The reference to this collection.\n */\n unrelate: function(input, remoteData)\n {\n this.relator.unrelate( this.model, input, remoteData );\n\n return this;\n },\n\n /**\n * Syncrhonizes the related models in this collection by re-evaluating all\n * models for a relationship.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @param {boolean} [removeUnrelated=false] -\n * Whether to remove models that are no longer related. The $remove\n * function is not called on these models.\n * @return {Rekord.RelationCollection} -\n * The reference to this collection.\n */\n sync: function(removeUnrelated)\n {\n this.relator.sync( this.model, removeUnrelated );\n\n return this;\n },\n\n /**\n * Unrelates any models in this collection which meet the where expression.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @param {whereInput} [properties] -\n * See {@link Rekord.createWhere}\n * @param {Any} [value] -\n * See {@link Rekord.createWhere}\n * @param {equalityCallback} [equals] -\n * See {@link Rekord.createWhere}\n * @return {Rekord.RelationCollection} -\n * The reference to this collection.\n * @see Rekord.createWhere\n * @see Rekord.RelationCollection.unrelate\n * @see Rekord.RelationCollection.where\n */\n unrelateWhere: function(properties, value, equals)\n {\n return this.unrelate( this.where( properties, value, equals, [] ) );\n },\n\n /**\n * Determines whether one or more models all exist in this collection.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @param {modelInput|modelInput[]} input -\n * The model or array of models to check for existence.\n * @return {Boolean} -\n * True if all models are related - otherwise false.\n */\n isRelated: function(input)\n {\n return this.relator.isRelated( this.model, input );\n },\n\n /**\n * Returns a clone of this collection.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @return {Rekord.RelationCollection} -\n * The reference to a clone collection.\n */\n clone: function()\n {\n return RelationCollection.create( this.database, this.model, this.relator, this, true );\n },\n\n /**\n * Returns an empty clone of this collection.\n *\n * @method\n * @memberof Rekord.RelationCollection#\n * @return {Rekord.RelationCollection} -\n * The reference to a clone collection.\n */\n cloneEmpty: function()\n {\n return RelationCollection.create( this.database, this.model, this.relator );\n }\n\n});\n\n\n/**\n * Overrides functions in the given model collection to turn it into a collection\n * which contains models with a discriminator field.\n *\n * @param {Rekord.ModelCollection} collection -\n * The collection instance with discriminated models.\n * @param {String} discriminator -\n * The name of the field which contains the discriminator.\n * @param {Object} discriminatorsToModel -\n * A map of discriminators to the Rekord instances.\n * @return {Rekord.ModelCollection} -\n * The reference to the given collection.\n */\nfunction DiscriminateCollection(collection, discriminator, discriminatorsToModel)\n{\n Class.props( collection,\n {\n discriminator: discriminator,\n discriminatorsToModel: discriminatorsToModel\n });\n\n // Original Functions\n var buildKeyFromInput = collection.buildKeyFromInput;\n var parseModel = collection.parseModel;\n var clone = collection.clone;\n var cloneEmpty = collection.cloneEmpty;\n\n Class.props( collection,\n {\n\n /**\n * Builds a key from input. Discriminated collections only accept objects as\n * input - otherwise there's no way to determine the discriminator. If the\n * discriminator on the input doesn't map to a Rekord instance OR the input\n * is not an object the input will be returned instead of a model instance.\n *\n * @param {modelInput} input -\n * The input to create a key for.\n * @return {Any} -\n * The built key or the given input if a key could not be built.\n */\n buildKeyFromInput: function(input)\n {\n if ( isObject( input ) )\n {\n var discriminatedValue = input[ this.discriminator ];\n var model = this.discriminatorsToModel[ discriminatedValue ];\n\n if ( model )\n {\n return model.Database.keyHandler.buildKeyFromInput( input );\n }\n }\n\n return input;\n },\n\n /**\n * Takes input and returns a model instance. The input is expected to be an\n * object, any other type will return null.\n *\n * @param {modelInput} input -\n * The input to parse to a model instance.\n * @param {Boolean} [remoteData=false] -\n * Whether or not the input is coming from a remote source.\n * @return {Rekord.Model} -\n * The model instance parsed or null if none was found.\n */\n parseModel: function(input, remoteData)\n {\n if ( input instanceof Model )\n {\n return input;\n }\n\n var discriminatedValue = isValue( input ) ? input[ this.discriminator ] : null;\n var model = this.discriminatorsToModel[ discriminatedValue ];\n\n return model ? model.Database.parseModel( input, remoteData ) : null;\n },\n\n /**\n * Returns a clone of this collection.\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Rekord.Collection} -\n * The reference to a clone collection.\n */\n clone: function()\n {\n return DiscriminateCollection( clone.apply( this ), discriminator, discriminatorsToModel );\n },\n\n /**\n * Returns an empty clone of this collection.\n *\n * @method\n * @memberof Rekord.Collection#\n * @return {Rekord.Collection} -\n * The reference to a clone collection.\n */\n cloneEmpty: function()\n {\n return DiscriminateCollection( cloneEmpty.apply( this ), discriminator, discriminatorsToModel );\n }\n\n });\n\n return collection;\n}\n\n\n/**\n * Options you can pass to {@link Rekord.Search} or {@link Rekord.Model.search}.\n *\n * @typedef {Object} searchOptions\n * @property {Function} [$encode] -\n * A function which converts the search into an object to pass to the\n * specified methods.\n * @property {Function} [$decode] -\n * A function which takes the data returned from the server and returns\n * The array of models which are to be placed in the\n * {@link Rekord.Search#$results} property.\n */\n\n/**\n *\n * @constructor\n * @memberof Rekord\n */\nfunction Search(database, url, options, props, run)\n{\n this.$init( database, url, options, props, run );\n}\n\nSearch.Defaults =\n{\n};\n\nClass.create( Search,\n{\n\n $getDefaults: function()\n {\n return Search.Defaults;\n },\n\n $init: function(database, url, options, props, run)\n {\n applyOptions( this, options, this.$getDefaults(), true );\n\n Class.prop( this, '$db', database );\n\n this.$append = false;\n this.$url = url;\n this.$set( props );\n this.$results = ModelCollection.create( database );\n this.$results.$search = this;\n this.$promise = Promise.resolve( this );\n\n if ( run )\n {\n this.$run();\n }\n },\n\n $set: function(props)\n {\n if ( isObject( props ) )\n {\n transfer( props, this );\n }\n\n return this;\n },\n\n $unset: function()\n {\n for (var prop in this)\n {\n if ( prop.charAt(0) !== '$' )\n {\n delete this[ prop ];\n }\n }\n\n return this;\n },\n\n $run: function(url, props)\n {\n this.$url = url || this.$url;\n this.$set( props );\n\n var promise = new Promise();\n var encoded = this.$encode();\n var success = bind( this, this.$handleSuccess( promise ) );\n var failure = bind( this, this.$handleFailure( promise ) );\n var options = this.$options || this.$db.queryOptions;\n\n batchExecute(function()\n {\n this.$cancel();\n this.$promise = promise;\n this.$db.rest.query( this.$url, encoded, options, success, failure );\n\n }, this );\n\n return this.$promise;\n },\n\n $handleSuccess: function(promise)\n {\n return function(response)\n {\n if ( !this.$promise.isPending() || promise !== this.$promise )\n {\n return;\n }\n\n var models = this.$decode.apply( this, arguments );\n\n if ( this.$append )\n {\n this.$results.addAll( models, false, true );\n }\n else\n {\n this.$results.reset( models, true );\n }\n\n this.$promise.resolve( this, response, this.$results );\n };\n },\n\n $handleFailure: function(promise)\n {\n return function(response, status)\n {\n if ( !this.$promise.isPending() || promise !== this.$promise )\n {\n return;\n }\n\n var offline = RestStatus.Offline[ status ];\n\n if ( offline )\n {\n Rekord.checkNetworkStatus();\n\n offline = !Rekord.online;\n }\n\n if ( offline )\n {\n this.$promise.noline( this, response, status );\n }\n else\n {\n this.$promise.reject( this, response, status );\n }\n };\n },\n\n $cancel: function()\n {\n this.$promise.cancel();\n },\n\n $clear: function()\n {\n this.$results.clear();\n },\n\n $encode: function()\n {\n return cleanFunctions( copy( this ) );\n },\n\n $decode: function(models)\n {\n return models;\n },\n\n $key: function()\n {\n return '';\n },\n\n $change: function(callback, context)\n {\n return this.$results.change( callback, context );\n }\n\n});\n\n\n/**\n * Options you can pass to {@link Rekord.SearchPaged} or\n * {@link Rekord.Model.searchPaged}.\n *\n * @typedef {Object} searchPageOptions\n * @property {Number} [page_size=10] -\n * The size of the pages.\n * @property {Number} [page_index=0] -\n * The index of the search page.\n * @property {Number} [total=0] -\n * The total number of models that exist in the search without pagination\n * - this is expected to be provided by the remote search response.\n * @property {Function} [$encode] -\n * A function which converts the search into an object to pass to the\n * specified methods.\n * @property {Function} [$decode] -\n * A function which takes the data returned from the server and updates\n * this search with the results and paging information.\n * @property {Function} [$decodeResults] -\n * A function which takes the data returned from the server and returns the\n * array of models which are to be placed in the\n * {@link Rekord.Search#$results} property.\n * @property {Function} [$updatePageSize] -\n * A function which takes the data returned from the server and sets an\n * updated page size of the search.\n * @property {Function} [$updatePageIndex] -\n * A function which takes the data returned from the server and sets an\n * updated page index of the search.\n * @property {Function} [$updateTotal] -\n * A function which takes the data returned from the server and sets an\n * updated total of the search.\n */\n\nfunction SearchPaged(database, url, options, props, run)\n{\n this.$init( database, url, options, props, run );\n}\n\nSearchPaged.Defaults =\n{\n page_size: 10,\n page_index: 0,\n total: 0\n};\n\nClass.extend( Search, SearchPaged,\n{\n\n $getDefaults: function()\n {\n return SearchPaged.Defaults;\n },\n\n $goto: function(index, dontRun)\n {\n var pageIndex = this.$getPageIndex();\n var pageCount = this.$getPageCount();\n var desired = Math.max( 0, Math.min( index, pageCount - 1 ) );\n\n if ( pageIndex !== desired )\n {\n this.$setPageIndex( desired );\n\n if ( !dontRun )\n {\n this.$append = false;\n this.$run();\n }\n }\n\n return this.$promise;\n },\n\n $more: function()\n {\n var next = this.$getPageIndex() + 1;\n\n if ( next < this.$getPageCount() )\n {\n this.$setPageIndex( next );\n this.$append = true;\n this.$run();\n this.$promise.complete( this.$onMoreEnd, this );\n }\n\n return this.$promise;\n },\n\n $onMoreEnd: function()\n {\n this.$append = false;\n },\n\n $first: function(dontRun)\n {\n return this.$goto( 0, dontRun );\n },\n\n $last: function(dontRun)\n {\n return this.$goto( this.$getPageCount() - 1, dontRun );\n },\n\n $prev: function(dontRun)\n {\n return this.$goto( this.$getPageIndex() - 1, dontRun );\n },\n\n $next: function(dontRun)\n {\n return this.$goto( this.$getPageIndex() + 1, dontRun );\n },\n\n $total: function()\n {\n return this.$getTotal();\n },\n\n $pages: function()\n {\n return this.$getPageCount();\n },\n\n $page: function(index)\n {\n return Math.max( 0, Math.min( index, this.$pages() - 1 ) );\n },\n\n $can: function(index)\n {\n return this.$getTotal() && index >= 0 && index < this.$getPageCount();\n },\n\n $canFirst: function()\n {\n return this.$canPrev();\n },\n\n $canLast: function()\n {\n return this.$canNext();\n },\n\n $canPrev: function()\n {\n return this.$getTotal() && this.$getPageIndex() > 0;\n },\n\n $canNext: function()\n {\n return this.$getTotal() && this.$getPageIndex() < this.$getPageCount() - 1;\n },\n\n $decode: function(response)\n {\n this.$updatePageSize( response );\n this.$updatePageIndex( response );\n this.$updateTotal( response );\n\n return this.$decodeResults( response );\n },\n\n $decodeResults: function(response)\n {\n return response.results;\n },\n\n $updatePageSize: function(response)\n {\n if ( isNumber( response.page_size ) )\n {\n this.page_size = response.page_size;\n }\n },\n\n $setPageSize: function(page_size)\n {\n this.page_size = page_size;\n },\n\n $getPageSize: function()\n {\n return this.page_size;\n },\n\n $updatePageIndex: function(response)\n {\n if ( isNumber( response.page_index ) )\n {\n this.page_index = response.page_index;\n }\n },\n\n $setPageIndex: function(page_index)\n {\n this.page_index = page_index || 0;\n },\n\n $getPageIndex: function()\n {\n return this.page_index;\n },\n\n $getPageOffset: function()\n {\n return this.page_index * this.page_size;\n },\n\n $updateTotal: function(response)\n {\n if ( isNumber( response.total ) )\n {\n this.total = response.total;\n }\n },\n\n $setTotal: function(total)\n {\n this.total = total || 0;\n },\n\n $getTotal: function()\n {\n return this.total;\n },\n\n $getPageCount: function()\n {\n return Math.ceil( this.$getTotal() / this.$getPageSize() );\n }\n\n});\n\n\nfunction Promise(executor, cancelable)\n{\n this.status = Promise.Status.Pending;\n this.cancelable = cancelable !== false;\n this.nexts = [];\n\n Class.prop( this, 'results', null );\n\n if ( isFunction( executor ) )\n {\n executor(\n bind(this, this.resolve),\n bind(this, this.reject),\n bind(this, this.noline),\n bind(this, this.cancel)\n );\n }\n}\n\nPromise.Status =\n{\n Pending: 'pending',\n Success: 'success',\n Failure: 'failure',\n Offline: 'offline',\n Canceled: 'canceled'\n};\n\nPromise.Events =\n{\n Success: 'success',\n Failure: 'failure',\n Offline: 'offline',\n Canceled: 'canceled',\n Unsuccessful: 'failure offline canceled',\n Complete: 'success failure offline canceled'\n};\n\nPromise.all = function(iterable)\n{\n var all = new Promise();\n var successes = 0;\n var goal = iterable.length;\n var results = [];\n\n function handleSuccess()\n {\n results.push( AP.slice.apply( arguments ) );\n\n if ( ++successes === goal )\n {\n all.resolve( results );\n }\n }\n\n for (var i = 0; i < iterable.length; i++)\n {\n var p = iterable[ i ];\n\n if ( p instanceof Promise )\n {\n p.then( handleSuccess, all.reject, all.noline, all.cancel, all );\n }\n else\n {\n goal--;\n }\n }\n\n return all;\n};\n\nPromise.race = function(iterable)\n{\n var race = new Promise();\n\n for (var i = 0; i < iterable.length; i++)\n {\n var p = iterable[ i ];\n\n if ( p instanceof Promise )\n {\n p.bind( race );\n }\n }\n\n return race;\n};\n\nPromise.reject = function(reason)\n{\n var p = new Promise();\n p.reject.apply( p, arguments );\n return p;\n};\n\nPromise.resolve = function()\n{\n var p = new Promise();\n p.resolve.apply( p, arguments );\n return p;\n};\n\nPromise.noline = function(reason)\n{\n var p = new Promise();\n p.noline.apply( p, arguments );\n return p;\n};\n\nPromise.cancel = function()\n{\n var p = new Promise();\n p.cancel.apply( p, arguments );\n return p;\n};\n\nPromise.then = function()\n{\n var p = new Promise();\n p.resolve();\n return p.then.apply( p, arguments );\n};\n\nPromise.singularity = (function()\n{\n var singularity = null;\n var singularityResult = null;\n var consuming = false;\n var promiseCount = 0;\n var promiseComplete = 0;\n\n function handleSuccess()\n {\n if ( ++promiseComplete === promiseCount )\n {\n singularity.resolve( singularityResult );\n }\n }\n\n function bindPromise(promise)\n {\n promiseCount++;\n promise.then( handleSuccess, singularity.reject, singularity.noline, null, singularity );\n }\n\n return function(promiseOrContext, contextOrCallback, callbackOrNull)\n {\n var promise = promiseOrContext;\n var context = contextOrCallback;\n var callback = callbackOrNull;\n\n if (!(promise instanceof Promise))\n {\n promise = false;\n context = promiseOrContext;\n callback = contextOrCallback;\n }\n\n if ( !consuming )\n {\n consuming = true;\n singularity = new Promise( null, false );\n singularityResult = context;\n promiseCount = 0;\n promiseComplete = 0;\n\n if (promise)\n {\n bindPromise( promise );\n }\n\n try\n {\n callback.call( context, singularity );\n }\n catch (ex)\n {\n Rekord.trigger( Rekord.Events.Error, [ex] );\n\n throw ex;\n }\n finally\n {\n consuming = false;\n }\n }\n else\n {\n if (promise)\n {\n bindPromise( promise );\n }\n\n callback.call( context, singularity );\n }\n\n if (promiseCount === 0)\n {\n singularity.resolve();\n }\n\n return singularity;\n };\n\n})();\n\nClass.create( Promise,\n{\n resolve: function()\n {\n this.finish( Promise.Status.Success, Promise.Events.Success, arguments );\n },\n\n reject: function()\n {\n this.finish( Promise.Status.Failure, Promise.Events.Failure, arguments );\n },\n\n noline: function()\n {\n this.finish( Promise.Status.Offline, Promise.Events.Offline, arguments );\n },\n\n cancel: function()\n {\n if ( this.cancelable )\n {\n this.finish( Promise.Status.Canceled, Promise.Events.Canceled, arguments );\n }\n },\n\n bind: function(promise)\n {\n this.success( promise.resolve, promise );\n this.failure( promise.reject, promise );\n this.offline( promise.noline, promise );\n this.canceled( promise.cancel, promise );\n },\n\n then: function(success, failure, offline, canceled, context, persistent )\n {\n // The promise which can be resolved if any of the callbacks return\n // a Promise which is resolved.\n var next = new Promise();\n\n this.success( success, context, persistent, next );\n this.failure( failure, context, persistent, next );\n this.offline( offline, context, persistent, next );\n this.canceled( canceled, context, persistent, next );\n this.addNext( next );\n \n return next;\n },\n\n addNext: function(next)\n {\n var nexts = this.nexts;\n\n if (nexts.length === 0)\n {\n // If this promise is not successful, let all chained promises know.\n this.unsuccessful(function()\n {\n for (var i = 0; i < nexts.length; i++)\n {\n nexts[ i ].finish( this.status, this.status, arguments );\n }\n });\n }\n\n nexts.push( next );\n },\n\n reset: function(clearListeners)\n {\n this.status = Promise.Status.Pending;\n\n if ( clearListeners )\n {\n this.off();\n }\n\n return this;\n },\n\n finish: function(status, events, results)\n {\n if ( this.status === Promise.Status.Pending )\n {\n this.results = AP.slice.apply( results );\n this.status = status;\n this.trigger( events, results );\n }\n },\n\n listenFor: function(immediate, events, callback, context, persistent, next)\n {\n if ( isFunction( callback ) )\n {\n var handleEvents = function()\n {\n var result = callback.apply( context || this, this.results );\n\n if ( result instanceof Promise &&\n next instanceof Promise &&\n next.isPending() )\n {\n result.bind( next );\n }\n };\n\n if ( this.status === Promise.Status.Pending )\n {\n if ( persistent )\n {\n this.on( events, handleEvents, this );\n }\n else\n {\n this.once( events, handleEvents, this );\n }\n }\n else if ( immediate )\n {\n handleEvents.apply( this );\n }\n }\n\n return this;\n },\n\n success: function(callback, context, persistent, next)\n {\n return this.listenFor( this.isSuccess(), Promise.Events.Success, callback, context, persistent, next );\n },\n\n unsuccessful: function(callback, context, persistent, next)\n {\n return this.listenFor( this.isUnsuccessful(), Promise.Events.Unsuccessful, callback, context, persistent, next );\n },\n\n failure: function(callback, context, persistent, next)\n {\n return this.listenFor( this.isFailure(), Promise.Events.Failure, callback, context, persistent, next );\n },\n\n catch: function(callback, context, persistent, next)\n {\n return this.listenFor( this.isFailure(), Promise.Events.Failure, callback, context, persistent, next );\n },\n\n offline: function(callback, context, persistent, next)\n {\n return this.listenFor( this.isOffline(), Promise.Events.Offline, callback, context, persistent, next );\n },\n\n canceled: function(callback, context, persistent, next)\n {\n return this.listenFor( this.isCanceled(), Promise.Events.Canceled, callback, context, persistent, next );\n },\n\n complete: function(callback, context, persistent, next)\n {\n return this.listenFor( true, Promise.Events.Complete, callback, context, persistent, next );\n },\n\n isSuccess: function()\n {\n return this.status === Promise.Status.Success;\n },\n\n isUnsuccessful: function()\n {\n return this.status !== Promise.Status.Success && this.status !== Promise.Status.Pending;\n },\n\n isFailure: function()\n {\n return this.status === Promise.Status.Failure;\n },\n\n isOffline: function()\n {\n return this.status === Promise.Status.Offline;\n },\n\n isCanceled: function()\n {\n return this.status === Promise.Status.Canceled;\n },\n\n isPending: function()\n {\n return this.status === Promise.Status.Pending;\n },\n\n isComplete: function()\n {\n return this.status !== Promise.Status.Pending;\n }\n\n});\n\naddEventful( Promise );\n\n\nfunction Operation()\n{\n}\n\nClass.create( Operation,\n{\n\n reset: function(model, cascade, options)\n {\n this.model = model;\n this.cascade = isNumber( cascade ) ? cascade : Cascade.All;\n this.options = options;\n this.db = model.$db;\n this.next = null;\n this.finished = false;\n },\n\n canCascade: function(cascade)\n {\n var expected = cascade || this.cascading;\n var actual = this.cascade;\n\n return (expected & actual) !== 0;\n },\n\n notCascade: function(expected)\n {\n var actual = this.cascade;\n\n return (expected & actual) === 0;\n },\n\n queue: function(operation)\n {\n if ( this.next && !operation.interrupts )\n {\n this.next.queue( operation );\n }\n else\n {\n this.next = operation;\n this.model.$trigger( Model.Events.OperationsStarted );\n }\n },\n\n tryNext: function(OperationType)\n {\n var setNext = !this.next;\n\n if ( setNext )\n {\n this.next = new OperationType( this.model, this.cascade, this.options );\n }\n\n return setNext;\n },\n\n insertNext: function(OperationType)\n {\n var op = new OperationType( this.model, this.cascade, this.options );\n\n op.next = this.next;\n this.next = op;\n },\n\n execute: function()\n {\n if ( this.db.pendingOperations === 0 )\n {\n this.db.trigger( Database.Events.OperationsStarted );\n }\n\n this.db.pendingOperations++;\n\n try\n {\n this.run( this.db, this.model );\n }\n catch (ex)\n {\n this.finish();\n\n Rekord.trigger( Rekord.Events.Error, [ex] );\n\n throw ex;\n }\n },\n\n run: function(db, model)\n {\n throw 'Operation.run Not implemented';\n },\n\n finish: function()\n {\n if ( !this.finished )\n {\n this.finished = true;\n this.model.$operation = this.next;\n\n if ( this.next )\n {\n this.next.execute();\n }\n\n this.db.pendingOperations--;\n\n if ( !this.next )\n {\n this.model.$trigger( Model.Events.OperationsFinished );\n }\n\n if ( this.db.pendingOperations === 0 )\n {\n this.db.onOperationRest();\n this.db.trigger( Database.Events.OperationsFinished );\n }\n }\n\n return this;\n },\n\n success: function()\n {\n return bind( this, this.handleSuccess );\n },\n\n handleSuccess: function()\n {\n try\n {\n this.onSuccess.apply( this, arguments );\n }\n catch (ex)\n {\n Rekord.trigger( Rekord.Events.Error, [ex] );\n\n throw ex;\n }\n finally\n {\n this.finish();\n }\n },\n\n onSuccess: function()\n {\n\n },\n\n failure: function()\n {\n return bind( this, this.handleFailure );\n },\n\n handleFailure: function()\n {\n try\n {\n this.onFailure.apply( this, arguments );\n }\n catch (ex)\n {\n Rekord.trigger( Rekord.Events.Error, [ex] );\n\n throw ex;\n }\n finally\n {\n this.finish();\n }\n },\n\n onFailure: function()\n {\n\n }\n\n});\n\nfunction GetLocal(model, cascade, options)\n{\n this.reset( model, cascade, options );\n}\n\nClass.extend( Operation, GetLocal,\n{\n\n cascading: Cascade.Local,\n\n interrupts: false,\n\n type: 'GetLocal',\n\n run: function(db, model)\n {\n if ( model.$isDeleted() )\n {\n model.$trigger( Model.Events.LocalGetFailure, [model] );\n\n this.finish();\n }\n else if ( this.canCascade() && db.cache === Cache.All )\n {\n db.store.get( model.$key(), this.success(), this.failure() );\n }\n else\n {\n Rekord.debug( Rekord.Debugs.GET_LOCAL_SKIPPED, model );\n\n model.$trigger( Model.Events.LocalGet, [model] );\n\n this.insertNext( GetRemote );\n this.finish();\n }\n },\n\n onSuccess: function(key, encoded)\n {\n var model = this.model;\n\n if ( isObject( encoded ) )\n {\n model.$set( encoded );\n }\n\n Rekord.debug( Rekord.Debugs.GET_LOCAL, model, encoded );\n\n model.$trigger( Model.Events.LocalGet, [model] );\n\n if ( this.canCascade( Cascade.Rest ) && !model.$isDeleted() )\n {\n this.insertNext( GetRemote );\n }\n },\n\n onFailure: function(e)\n {\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.GET_LOCAL, model, e );\n\n model.$trigger( Model.Events.LocalGetFailure, [model] );\n\n if ( this.canCascade( Cascade.Rest ) && !model.$isDeleted() )\n {\n this.insertNext( GetRemote );\n }\n }\n\n});\n\nfunction GetRemote(model, cascade, options)\n{\n this.reset( model, cascade, options );\n}\n\nClass.extend( Operation, GetRemote,\n{\n\n cascading: Cascade.Rest,\n\n interrupts: false,\n\n type: 'GetRemote',\n\n run: function(db, model)\n {\n if ( model.$isDeleted() )\n {\n model.$trigger( Model.Events.RemoteGetFailure, [model] );\n\n this.finish();\n }\n else if ( this.canCascade() )\n {\n batchExecute(function()\n {\n db.rest.get( model, this.options || db.getOptions, this.success(), this.failure() );\n\n }, this );\n }\n else\n {\n model.$trigger( Model.Events.RemoteGet, [model] );\n\n this.finish();\n }\n },\n\n onSuccess: function(response)\n {\n var db = this.db;\n var data = db.resolveModel( response );\n var model = this.model;\n\n if ( isObject( data ) )\n {\n db.putRemoteData( data, model.$key(), model, true );\n }\n\n Rekord.debug( Rekord.Debugs.GET_REMOTE, model, data );\n\n model.$trigger( Model.Events.RemoteGet, [model] );\n },\n\n onFailure: function(response, status)\n {\n var db = this.db;\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.GET_REMOTE_ERROR, model, response, status );\n\n if ( RestStatus.NotFound[ status ] )\n {\n this.insertNext( RemoveNow );\n\n db.destroyModel( model );\n\n model.$trigger( Model.Events.RemoteGetFailure, [model, response] );\n }\n else if ( RestStatus.Offline[ status ] )\n {\n model.$trigger( Model.Events.RemoteGetOffline, [model, response] );\n }\n else\n {\n model.$trigger( Model.Events.RemoteGetFailure, [model, response] );\n }\n }\n\n});\n\nfunction RemoveCache(model, cascade)\n{\n this.reset( model, cascade );\n}\n\nClass.extend( Operation, RemoveCache,\n{\n\n cascading: Cascade.None,\n\n interrupts: true,\n\n type: 'RemoveCache',\n\n run: function(db, model)\n {\n if ( db.cache === Cache.None )\n {\n this.finish();\n }\n else\n {\n db.store.remove( model.$key(), this.success(), this.failure() );\n }\n }\n\n});\n\nfunction RemoveLocal(model, cascade)\n{\n this.reset( model, cascade );\n}\n\nClass.extend( Operation, RemoveLocal,\n{\n\n cascading: Cascade.Local,\n\n interrupts: true,\n\n type: 'RemoveLocal',\n\n run: function(db, model)\n {\n model.$status = Model.Status.RemovePending;\n\n if ( db.cache === Cache.None || !model.$local || !this.canCascade() )\n {\n Rekord.debug( Rekord.Debugs.REMOVE_LOCAL_NONE, model );\n\n model.$trigger( Model.Events.LocalRemove, [model] );\n\n this.insertNext( RemoveRemote );\n this.finish();\n }\n else if ( model.$saved && this.canCascade( Cascade.Rest ) )\n {\n model.$local.$status = model.$status;\n\n db.store.put( model.$key(), model.$local, this.success(), this.failure() );\n }\n else\n {\n Rekord.debug( Rekord.Debugs.REMOVE_LOCAL_UNSAVED, model );\n\n db.store.remove( model.$key(), this.success(), this.failure() );\n }\n },\n\n onSuccess: function(key, encoded, previousValue)\n {\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.REMOVE_LOCAL, model );\n\n model.$trigger( Model.Events.LocalRemove, [model] );\n\n if ( model.$saved && this.canCascade( Cascade.Remote ) )\n {\n model.$addOperation( RemoveRemote, this.cascade, this.options );\n }\n },\n\n onFailure: function(e)\n {\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.REMOVE_LOCAL_ERROR, model, e );\n\n model.$trigger( Model.Events.LocalRemoveFailure, [model] );\n\n if ( model.$saved && this.canCascade( Cascade.Remote ) )\n {\n model.$addOperation( RemoveRemote, this.cascade, this.options );\n }\n }\n\n});\n\nfunction RemoveNow(model, cascade)\n{\n this.reset( model, cascade );\n}\n\nClass.extend( Operation, RemoveNow,\n{\n\n cascading: Cascade.Local,\n\n interrupts: true,\n\n type: 'RemoveNow',\n\n run: function(db, model)\n {\n var key = model.$key();\n\n model.$status = Model.Status.RemovePending;\n\n db.removeFromModels( model );\n\n if ( db.cache === Cache.None || !this.canCascade() )\n {\n this.finishRemove();\n this.finish();\n }\n else\n {\n db.store.remove( key, this.success(), this.failure() );\n }\n },\n\n onSuccess: function()\n {\n this.finishRemove();\n },\n\n onFailure: function()\n {\n this.finishRemove();\n },\n\n finishRemove: function()\n {\n var model = this.model;\n\n model.$status = Model.Status.Removed;\n\n delete model.$local;\n delete model.$saving;\n delete model.$publish;\n delete model.$saved;\n }\n\n});\n\nfunction RemoveRemote(model, cascade, options)\n{\n this.reset( model, cascade, options );\n}\n\nClass.extend( Operation, RemoveRemote,\n{\n\n cascading: Cascade.Remote,\n\n interrupts: true,\n\n type: 'RemoveRemote',\n\n run: function(db, model)\n {\n if ( this.notCascade( Cascade.Rest ) )\n {\n this.liveRemove();\n\n model.$trigger( Model.Events.RemoteRemove, [model] );\n\n this.finish();\n }\n else\n {\n model.$status = Model.Status.RemovePending;\n\n batchExecute(function()\n {\n db.rest.remove( model, this.options || this.removeOptions, this.success(), this.failure() );\n\n }, this );\n }\n },\n\n onSuccess: function(data)\n {\n this.finishRemove();\n },\n\n onFailure: function(response, status)\n {\n var model = this.model;\n var key = model.$key();\n\n if ( RestStatus.NotFound[ status ] )\n {\n Rekord.debug( Rekord.Debugs.REMOVE_MISSING, model, key );\n\n this.finishRemove( true );\n }\n else if ( RestStatus.Offline[ status ] )\n {\n // Looks like we're offline!\n Rekord.checkNetworkStatus();\n\n // If we are offline, wait until we're online again to resume the delete\n if (!Rekord.online)\n {\n model.$listenForOnline( this.cascade );\n\n model.$trigger( Model.Events.RemoteRemoveOffline, [model, response] );\n }\n else\n {\n model.$trigger( Model.Events.RemoteRemoveFailure, [model, response] );\n }\n\n Rekord.debug( Rekord.Debugs.REMOVE_OFFLINE, model, response );\n }\n else\n {\n Rekord.debug( Rekord.Debugs.REMOVE_ERROR, model, status, key, response );\n\n model.$trigger( Model.Events.RemoteRemoveFailure, [model, response] );\n }\n },\n\n finishRemove: function(notLive)\n {\n var db = this.db;\n var model = this.model;\n var key = model.$key();\n\n Rekord.debug( Rekord.Debugs.REMOVE_REMOTE, model, key );\n\n // Successfully removed!\n model.$status = Model.Status.Removed;\n\n // Successfully Removed!\n model.$trigger( Model.Events.RemoteRemove, [model] );\n\n // Remove from local storage now\n this.insertNext( RemoveNow );\n\n // Remove it live!\n if ( !notLive )\n {\n this.liveRemove();\n }\n\n // Remove the model reference for good!\n db.removeReference( key );\n },\n\n liveRemove: function()\n {\n if ( this.canCascade( Cascade.Live ) )\n {\n var db = this.db;\n var model = this.model;\n var key = model.$key();\n\n // Publish REMOVE\n Rekord.debug( Rekord.Debugs.REMOVE_PUBLISH, model, key );\n\n db.live.remove( model );\n }\n }\n\n});\n\nfunction SaveLocal(model, cascade, options)\n{\n this.reset( model, cascade, options );\n}\n\nClass.extend( Operation, SaveLocal,\n{\n\n cascading: Cascade.Local,\n\n interrupts: false,\n\n type: 'SaveLocal',\n\n run: function(db, model)\n {\n if ( model.$isDeleted() )\n {\n Rekord.debug( Rekord.Debugs.SAVE_LOCAL_DELETED, model );\n\n model.$trigger( Model.Events.LocalSaveFailure, [model] );\n\n this.finish();\n }\n else if ( db.cache === Cache.None || !this.canCascade() )\n {\n if ( this.canCascade( Cascade.Remote ) )\n {\n if ( this.tryNext( SaveRemote ) )\n {\n this.markSaving( db, model );\n }\n }\n\n model.$trigger( Model.Events.LocalSave, [model] );\n\n this.finish();\n }\n else\n {\n var key = model.$key();\n var local = model.$toJSON( false );\n\n this.markSaving( db, model );\n\n if ( model.$local )\n {\n transfer( local, model.$local );\n }\n else\n {\n model.$local = local;\n\n if ( model.$saved )\n {\n model.$local.$saved = model.$saved;\n }\n }\n\n model.$local.$status = model.$status;\n model.$local.$saving = model.$saving;\n model.$local.$publish = model.$publish;\n\n db.store.put( key, model.$local, this.success(), this.failure() );\n }\n },\n\n markSaving: function(db, model)\n {\n var remote = model.$toJSON( true );\n var changes = model.$getChanges( remote );\n\n var saving = db.fullSave ? remote : this.grabAlways( db.saveAlways, changes, remote );\n var publish = db.fullPublish ? remote : this.grabAlways( db.publishAlways, changes, remote );\n\n model.$status = Model.Status.SavePending;\n model.$saving = saving;\n model.$publish = publish;\n },\n\n grabAlways: function(always, changes, encoded)\n {\n var changesCopy = null;\n\n if ( always.length )\n {\n for (var i = 0; i < always.length; i++)\n {\n var prop = always[ i ];\n\n if ( !(prop in changes) )\n {\n if ( !changesCopy )\n {\n changesCopy = copy( changes );\n }\n\n changesCopy[ prop ] = encoded[ prop ];\n }\n }\n }\n\n return changesCopy || changes;\n },\n\n clearLocal: function(model)\n {\n model.$status = Model.Status.Synced;\n\n model.$local.$status = model.$status;\n\n delete model.$local.$saving;\n delete model.$local.$publish;\n\n this.insertNext( SaveNow );\n },\n\n onSuccess: function(key, encoded, previousValue)\n {\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.SAVE_LOCAL, model );\n\n if ( this.cascade )\n {\n this.tryNext( SaveRemote );\n }\n else\n {\n this.clearLocal( model );\n }\n\n model.$trigger( Model.Events.LocalSave, [model] );\n },\n\n onFailure: function(e)\n {\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.SAVE_LOCAL_ERROR, model, e );\n\n if ( this.cascade )\n {\n this.tryNext( SaveRemote );\n }\n else\n {\n this.clearLocal( model );\n }\n\n model.$trigger( Model.Events.LocalSaveFailure, [model] );\n }\n\n});\n\nfunction SaveNow(model, cascade)\n{\n this.reset( model, cascade );\n}\n\nClass.extend( Operation, SaveNow,\n{\n\n cascading: Cascade.Local,\n\n interrupts: false,\n\n type: 'SaveNow',\n\n run: function(db, model)\n {\n var key = model.$key();\n var local = model.$local;\n\n if ( db.cache === Cache.All && key && local && this.canCascade() )\n {\n db.store.put( key, local, this.success(), this.failure() );\n }\n else\n {\n this.finish();\n }\n }\n\n});\n\nfunction SaveRemote(model, cascade, options)\n{\n this.reset( model, cascade, options );\n}\n\nClass.extend( Operation, SaveRemote,\n{\n\n cascading: Cascade.Remote,\n\n interrupts: false,\n\n type: 'SaveRemote',\n\n run: function(db, model)\n {\n if ( model.$isDeleted() )\n {\n Rekord.debug( Rekord.Debugs.SAVE_REMOTE_DELETED, model );\n\n this.markSynced( model, true, Model.Events.RemoteSaveFailure, null );\n this.finish();\n }\n else if ( !model.$dependents.isSaved( this.tryAgain, this ) )\n {\n this.finish();\n }\n else if ( !db.hasData( model.$saving ) || this.notCascade( Cascade.Rest ) )\n {\n this.liveSave();\n this.markSynced( model, true, Model.Events.RemoteSave, null );\n this.finish();\n }\n else\n {\n model.$status = Model.Status.SavePending;\n\n batchExecute(function()\n {\n if ( model.$saved )\n {\n db.rest.update( model, model.$saving, this.options || db.updateOptions || db.saveOptions, this.success(), this.failure() );\n }\n else\n {\n db.rest.create( model, model.$saving, this.options || db.createOptions || db.saveOptions, this.success(), this.failure() );\n }\n\n }, this );\n }\n },\n\n onSuccess: function(response)\n {\n var db = this.db;\n var data = db.resolveModel( response );\n var model = this.model;\n\n Rekord.debug( Rekord.Debugs.SAVE_REMOTE, model );\n\n this.handleData( data );\n },\n\n onFailure: function(response, status)\n {\n var operation = this;\n var db = this.db;\n var data = db.resolveModel( response );\n var model = this.model;\n\n // A non-zero status means a real problem occurred\n if ( RestStatus.Conflict[ status ] ) // 409 Conflict\n {\n Rekord.debug( Rekord.Debugs.SAVE_CONFLICT, model, data );\n\n this.handleData( data );\n }\n else if ( RestStatus.NotFound[ status ] )\n {\n Rekord.debug( Rekord.Debugs.SAVE_UPDATE_FAIL, model );\n\n this.insertNext( RemoveNow );\n\n db.destroyModel( model );\n\n model.$trigger( Model.Events.RemoteSaveFailure, [model, response] );\n }\n else if ( RestStatus.Offline[ status ] )\n {\n // Check the network status right now\n Rekord.checkNetworkStatus();\n\n // If not online for sure, try saving once online again\n if (!Rekord.online)\n {\n model.$listenForOnline( this.cascade );\n\n model.$trigger( Model.Events.RemoteSaveOffline, [model, response] );\n }\n else\n {\n this.markSynced( model, true, Model.Events.RemoteSaveFailure, response );\n }\n\n Rekord.debug( Rekord.Debugs.SAVE_OFFLINE, model, response );\n }\n else\n {\n Rekord.debug( Rekord.Debugs.SAVE_ERROR, model, status );\n\n this.markSynced( model, true, Model.Events.RemoteSaveFailure, response );\n }\n },\n\n markSynced: function(model, saveNow, eventType, response)\n {\n model.$status = Model.Status.Synced;\n\n this.clearPending( model );\n\n if ( saveNow )\n {\n this.insertNext( SaveNow );\n }\n\n if ( eventType )\n {\n model.$trigger( eventType, [model, response] );\n }\n },\n\n clearPending: function(model)\n {\n delete model.$saving;\n delete model.$publish;\n\n if ( model.$local )\n {\n model.$local.$status = model.$status;\n\n delete model.$local.$saving;\n delete model.$local.$publish;\n }\n },\n\n handleData: function(data)\n {\n var db = this.db;\n var model = this.model;\n var saving = model.$saving;\n\n // Check deleted one more time before updating model.\n if ( model.$isDeleted() )\n {\n Rekord.debug( Rekord.Debugs.SAVE_REMOTE_DELETED, model, data );\n\n return this.clearPending( model );\n }\n\n Rekord.debug( Rekord.Debugs.SAVE_VALUES, model, saving );\n\n // If the model hasn't been saved before - create the record where the\n // local and model point to the same object.\n if ( !model.$saved )\n {\n model.$saved = model.$local ? (model.$local.$saved = {}) : {};\n }\n\n // Tranfer all saved fields into the saved object\n transfer( saving, model.$saved );\n\n // Update the model with the return data\n if ( !isEmpty( data ) )\n {\n db.putRemoteData( data, model.$key(), model );\n }\n\n this.liveSave( data );\n this.markSynced( model, false, Model.Events.RemoteSave, null );\n\n if ( db.cache === Cache.Pending )\n {\n this.insertNext( RemoveCache );\n }\n else\n {\n this.insertNext( SaveNow );\n }\n },\n\n liveSave: function(data)\n {\n var db = this.db;\n var model = this.model;\n\n if ( isObject(data) )\n {\n transfer( data, model.$publish );\n }\n\n if ( this.canCascade( Cascade.Live ) && db.hasData( model.$publish ) )\n {\n // Publish saved data to everyone else\n Rekord.debug( Rekord.Debugs.SAVE_PUBLISH, model, model.$publish );\n\n db.live.save( model, model.$publish );\n }\n },\n\n tryAgain: function()\n {\n var model = this.model;\n\n model.$addOperation( SaveLocal, this.cascade, this.options );\n }\n\n});\n\n\nfunction Relation()\n{\n\n}\n\nRekord.Relations = {};\n\nRelation.Defaults =\n{\n model: null,\n lazy: false,\n store: Store.None,\n save: Save.None,\n auto: true,\n autoCascade: Cascade.All,\n autoOptions: null,\n property: true,\n preserve: true,\n clearKey: true,\n dynamic: false,\n discriminator: 'discriminator',\n discriminators: {},\n discriminatorToModel: {}\n};\n\nClass.create( Relation,\n{\n\n debugQuery: null,\n debugQueryResults: null,\n\n getDefaults: function(database, field, options)\n {\n return Relation.Defaults;\n },\n\n /**\n * Initializes this relation with the given database, field, and options.\n *\n * @param {Rekord.Database} database [description]\n * @param {String} field [description]\n * @param {Object} options [description]\n */\n init: function(database, field, options)\n {\n applyOptions( this, options, this.getDefaults( database, field, options ) );\n\n this.database = database;\n this.name = field;\n this.options = options;\n this.initialized = false;\n this.property = this.property || (indexOf( database.fields, this.name ) !== false);\n this.discriminated = !isEmpty( this.discriminators );\n\n if ( this.discriminated )\n {\n if ( !Polymorphic )\n {\n throw 'Polymorphic feature is required to use the discriminated option.';\n }\n\n Class.props( this, Polymorphic );\n }\n\n this.setReferences( database, field, options );\n },\n\n setReferences: function(database, field, options)\n {\n if ( !isRekord( this.model ) )\n {\n Rekord.get( this.model ).complete( this.setModelReference( database, field, options ), this );\n }\n else\n {\n this.onInitialized( database, field, options );\n }\n },\n\n /**\n *\n */\n setModelReference: function(database, field, options)\n {\n return function(rekord)\n {\n this.model = rekord;\n\n this.onInitialized( database, field, options );\n };\n },\n\n /**\n *\n */\n onInitialized: function(database, fields, options)\n {\n\n },\n\n finishInitialization: function()\n {\n this.initialized = true;\n this.load.open();\n },\n\n /**\n * Loads the model.$relation variable with what is necessary to get, set,\n * relate, and unrelate models. If property is true, look at model[ name ]\n * to load models/keys. If it contains values that don't exist or aren't\n * actually related\n *\n * @param {Rekord.Model} model [description]\n */\n\n load: Gate(function(model, initialValue, remoteData)\n {\n\n }),\n\n set: function(model, input, remoteData)\n {\n\n },\n\n relate: function(model, input, remoteData)\n {\n\n },\n\n unrelate: function(model, input, remoteData)\n {\n\n },\n\n sync: function(model, removeUnrelated)\n {\n\n },\n\n isRelated: function(model, input)\n {\n\n },\n\n preClone: function(model, clone, properties)\n {\n\n },\n\n postClone: function(model, clone, properties)\n {\n\n },\n\n get: function(model)\n {\n return model.$relations[ this.name ].related;\n },\n\n encode: function(model, out, forSaving)\n {\n var relation = model.$relations[ this.name ];\n var mode = forSaving ? this.save : this.store;\n\n if ( relation && mode )\n {\n var related = relation.related;\n\n if ( isArray( related ) )\n {\n out[ this.name ] = this.getStoredArray( related, mode );\n }\n else // if ( isObject( related ) )\n {\n out[ this.name ] = this.getStored( related, mode );\n }\n }\n },\n\n ready: function(callback)\n {\n this.model.Database.ready( callback, this );\n },\n\n listenToModelAdded: function(callback)\n {\n this.model.Database.on( Database.Events.ModelAdded, callback, this );\n },\n\n executeQuery: function(model)\n {\n if ( !Search )\n {\n throw 'Search feature is required to use the query option.';\n }\n\n var queryOption = this.query;\n var queryOptions = this.queryOptions;\n var queryData = this.queryData;\n var query = isString( queryOption ) ? format( queryOption, model ) : queryOption;\n var search = this.model.search( query, queryOptions, queryData );\n\n Rekord.debug( this.debugQuery, this, model, search, queryOption, query, queryData );\n\n var promise = search.$run();\n\n promise.complete( this.handleExecuteQuery( model ), this );\n\n return search;\n },\n\n handleExecuteQuery: function(model)\n {\n return function onExecuteQuery(search)\n {\n var results = search.$results;\n\n Rekord.debug( this.debugQueryResults, this, model, search );\n\n for (var i = 0; i < results.length; i++)\n {\n this.relate( model, results[ i ], true );\n }\n };\n },\n\n createRelationCollection: function(model)\n {\n return RelationCollection.create( this.model.Database, model, this );\n },\n\n createCollection: function(initial)\n {\n return ModelCollection.create( this.model.Database, initial );\n },\n\n parseModel: function(input, remoteData)\n {\n return this.model.Database.parseModel( input, remoteData );\n },\n\n grabInitial: function( model, fields )\n {\n if ( hasFields( model, fields, isValue ) )\n {\n return pull( model, fields );\n }\n },\n\n grabModel: function(input, callback, remoteData)\n {\n this.model.Database.grabModel( input, callback, this, remoteData );\n },\n\n grabModels: function(relation, initial, callback, remoteData)\n {\n var db = this.model.Database;\n\n for (var i = 0; i < initial.length; i++)\n {\n var input = initial[ i ];\n var key = db.keyHandler.buildKeyFromInput( input );\n\n relation.pending[ key ] = true;\n\n if ( input instanceof Model )\n {\n callback.call( this, input );\n }\n else\n {\n db.grabModel( input, callback, this, remoteData );\n }\n }\n },\n\n buildKey: function(input)\n {\n\n },\n\n setProperty: function(relation)\n {\n if ( this.property )\n {\n var model = relation.parent;\n var propertyName = this.name;\n var applied = !!relation.dynamicSet;\n\n if ( !applied && this.dynamic && Object.defineProperty )\n {\n var relator = this;\n\n Object.defineProperty( model, propertyName,\n {\n enumerable: true,\n\n set: function(input)\n {\n relator.set( model, input );\n },\n get: function()\n {\n return relation.related;\n }\n });\n\n applied = relation.dynamicSet = true;\n }\n\n if ( !applied )\n {\n model[ propertyName ] = relation.related;\n }\n\n if ( relation.lastRelated !== relation.related )\n {\n model.$trigger( Model.Events.RelationUpdate, [this, relation] );\n\n relation.lastRelated = relation.related;\n }\n }\n },\n\n isModelArray: function(input)\n {\n if ( !isArray( input ) )\n {\n return false;\n }\n\n var relatedDatabase = this.model.Database;\n var relatedKey = relatedDatabase.key;\n\n if ( !isArray( relatedKey ) )\n {\n return true;\n }\n\n if ( relatedKey.length !== input.length )\n {\n return true;\n }\n\n for ( var i = 0; i < input.length; i++ )\n {\n if ( !isNumber( input[ i ] ) && !isString( input[ i ] ) )\n {\n return true;\n }\n }\n\n return false;\n },\n\n clearFields: function(target, targetFields, remoteData, cascade)\n {\n var changes = clearFieldsReturnChanges( target, targetFields );\n\n if ( changes && !remoteData && this.auto && !target.$isNew() )\n {\n target.$save( cascade || this.autoCascade, this.autoOptions );\n }\n\n return changes;\n },\n\n updateFields: function(target, targetFields, source, sourceFields, remoteData)\n {\n var changes = updateFieldsReturnChanges( target, targetFields, source, sourceFields );\n\n if ( changes )\n {\n if ( this.auto && !target.$isNew() && !remoteData )\n {\n target.$save( this.autoCascade, this.autoOptions );\n }\n\n target.$trigger( Model.Events.KeyUpdate, [target, source, targetFields, sourceFields] );\n }\n\n return changes;\n },\n\n updateForeignKey: function(target, source, remoteData)\n {\n var targetFields = this.getTargetFields( target );\n var sourceFields = this.getSourceFields( source );\n var targetKey = target.$key();\n var targetKeyHandler = target.$db.keyHandler;\n var keyChanges = target.$db.keyChanges;\n\n Rekord.debug( this.debugUpdateKey, this, target, targetFields, source, sourceFields );\n\n this.updateFields( target, targetFields, source, sourceFields, remoteData );\n\n if ( keyChanges && remoteData )\n {\n var targetNewKey = targetKeyHandler.getKey( target, true );\n\n if ( targetKeyHandler.inKey( targetFields ) && targetNewKey !== targetKey )\n {\n target.$setKey( targetNewKey, true );\n }\n }\n },\n\n clearForeignKey: function(related, remoteData)\n {\n var key = this.getTargetFields( related );\n\n Rekord.debug( this.debugClearKey, this, related, key );\n\n this.clearFields( related, key, remoteData );\n },\n\n getTargetFields: function(target)\n {\n return target.$db.key;\n },\n\n getSourceFields: function(source)\n {\n return source.$db.key;\n },\n\n getStoredArray: function(relateds, mode)\n {\n if ( !mode )\n {\n return null;\n }\n\n var stored = [];\n\n for (var i = 0; i < relateds.length; i++)\n {\n var related = this.getStored( relateds[ i ], mode );\n\n if ( related !== null )\n {\n stored.push( related );\n }\n }\n\n return stored;\n },\n\n getStored: function(related, mode)\n {\n if ( related )\n {\n switch (mode)\n {\n case Save.Model:\n return related.$toJSON( true );\n\n case Store.Model:\n if ( related.$local )\n {\n return related.$local;\n }\n\n var local = related.$toJSON( false );\n\n if ( related.$saved )\n {\n local.$saved = related.$saved;\n }\n\n return local;\n\n case Save.Key:\n case Store.Key:\n return related.$key();\n\n case Save.Keys:\n case Store.Keys:\n return related.$keys();\n\n }\n }\n\n return null;\n }\n\n});\n\nfunction RelationSingle()\n{\n}\n\nClass.extend( Relation, RelationSingle,\n{\n\n debugInit: null,\n debugClearModel: null,\n debugSetModel: null,\n debugLoaded: null,\n debugClearKey: null,\n debugUpdateKey: null,\n\n onInitialized: function(database, field, options)\n {\n if ( !this.discriminated )\n {\n var relatedDatabase = this.model.Database;\n\n this.local = this.local || ( relatedDatabase.name + '_' + relatedDatabase.key );\n }\n\n Rekord.debug( this.debugInit, this );\n\n this.finishInitialization();\n },\n\n set: function(model, input, remoteData)\n {\n if ( isEmpty( input ) )\n {\n this.unrelate( model, undefined, remoteData );\n }\n else\n {\n var relation = model.$relations[ this.name ];\n var related = this.parseModel( input, remoteData );\n\n if ( related && relation.related !== related )\n {\n this.clearModel( relation, remoteData );\n this.setRelated( relation, related, remoteData );\n }\n }\n },\n\n relate: function(model, input, remoteData)\n {\n var relation = model.$relations[ this.name ];\n var related = this.parseModel( input, remoteData );\n\n if ( related && relation.related !== related )\n {\n this.clearModel( relation, remoteData );\n this.setRelated( relation, related, remoteData );\n }\n },\n\n unrelate: function(model, input, remoteData)\n {\n var relation = model.$relations[ this.name ];\n var related = this.parseModel( input );\n\n if ( !related || relation.related === related )\n {\n this.clearRelated( relation, remoteData );\n }\n },\n\n isRelated: function(model, input)\n {\n var relation = model.$relations[ this.name ];\n var related = this.parseModel( input );\n\n return related === relation.related;\n },\n\n setRelated: function(relation, related, remoteData)\n {\n if ( !related.$isDeleted() )\n {\n this.setModel( relation, related );\n this.updateForeignKey( relation.parent, related, remoteData );\n this.setProperty( relation );\n }\n },\n\n clearRelated: function(relation, remoteData, dontClear)\n {\n if ( remoteData )\n {\n var related = relation.related;\n\n if ( related && related.$isSaving() )\n {\n return;\n }\n }\n\n this.clearModel( relation, remoteData, dontClear );\n this.setProperty( relation );\n },\n\n clearModel: function(relation, remoteData, dontClear)\n {\n var related = relation.related;\n\n if ( related )\n {\n Rekord.debug( this.debugClearModel, this, relation );\n\n if (relation.onSaved)\n {\n related.$off( Model.Events.Saved, relation.onSaved );\n }\n if (relation.onRemoved)\n {\n related.$off( Model.Events.Removed, relation.onRemoved );\n }\n\n relation.related = null;\n relation.dirty = true;\n relation.loaded = true;\n\n relation.parent.$dependents.remove( related );\n\n if ( !dontClear && !remoteData )\n {\n if ( this.clearKey )\n {\n this.clearForeignKey( relation.parent, remoteData );\n }\n }\n }\n },\n\n setModel: function(relation, related)\n {\n if (relation.onSaved)\n {\n related.$on( Model.Events.Saved, relation.onSaved, this );\n }\n\n if (relation.onRemoved)\n {\n related.$on( Model.Events.Removed, relation.onRemoved, this );\n }\n\n relation.related = related;\n relation.dirty = true;\n relation.loaded = true;\n\n if ( this.isDependent( relation, related ) )\n {\n relation.parent.$dependents.add( related, this );\n }\n\n Rekord.debug( this.debugSetModel, this, relation );\n },\n\n isDependent: function(relation, related)\n {\n return true;\n },\n\n handleModel: function(relation, remoteData, ignoreLoaded)\n {\n return function(related)\n {\n var model = relation.parent;\n\n Rekord.debug( this.debugLoaded, this, model, relation, related );\n\n if ( relation.loaded === false || ignoreLoaded )\n {\n if ( related && !related.$isDeleted() )\n {\n this.setModel( relation, related, remoteData );\n this.updateForeignKey( model, related, remoteData );\n }\n else\n {\n if ( this.query )\n {\n relation.query = this.executeQuery( model );\n }\n else if ( !this.preserve )\n {\n this.clearForeignKey( model, remoteData );\n }\n }\n\n relation.loaded = true;\n\n this.setProperty( relation );\n }\n };\n },\n\n isRelatedFactory: function(model)\n {\n var local = this.local;\n\n return function hasForeignKey(related)\n {\n return propsMatch( model, local, related, related.$db.key );\n };\n },\n\n getTargetFields: function(target)\n {\n return this.local;\n },\n\n buildKey: function(input)\n {\n var related = input[ this.name ];\n var key = this.local;\n\n if ( isObject( related ) && this.model )\n {\n var modelDatabase = this.model.Database;\n var foreign = modelDatabase.key;\n\n modelDatabase.keyHandler.copyFields( input, key, related, foreign );\n }\n }\n\n});\n\nfunction RelationMultiple()\n{\n}\n\nClass.extend( Relation, RelationMultiple,\n{\n\n debugAutoSave: null,\n debugInitialGrabbed: null,\n debugSort: null,\n\n handleExecuteQuery: function(model)\n {\n return function onExecuteQuery(search)\n {\n var relation = model.$relations[ this.name ];\n var results = search.$results;\n\n Rekord.debug( this.debugQueryResults, this, model, search );\n\n this.bulk( relation, function()\n {\n for (var i = 0; i < results.length; i++)\n {\n this.addModel( relation, results[ i ], true );\n }\n });\n\n this.sort( relation );\n this.checkSave( relation, true );\n };\n },\n\n bulk: function(relation, callback, remoteData)\n {\n relation.delaySorting = true;\n relation.delaySaving = true;\n\n callback.apply( this );\n\n relation.delaySorting = false;\n relation.delaySaving = false;\n\n this.sort( relation );\n this.checkSave( relation, remoteData );\n },\n\n set: function(model, input, remoteData)\n {\n if ( isEmpty( input ) )\n {\n this.unrelate( model, undefined, remoteData );\n }\n else\n {\n var relation = model.$relations[ this.name ];\n var existing = relation.related;\n var given = this.createCollection();\n\n if ( this.isModelArray( input ) )\n {\n for (var i = 0; i < input.length; i++)\n {\n var related = this.parseModel( input[ i ], remoteData );\n\n if ( related )\n {\n given.add( related );\n }\n }\n }\n else\n {\n var related = this.parseModel( input, remoteData );\n\n if ( related )\n {\n given.add( related );\n }\n }\n\n var removing = existing.subtract( given );\n var adding = given.subtract( existing );\n\n this.bulk( relation, function()\n {\n for (var i = 0; i < adding.length; i++)\n {\n this.addModel( relation, adding[ i ], remoteData );\n }\n\n for (var i = 0; i < removing.length; i++)\n {\n this.removeModel( relation, removing[ i ], remoteData );\n }\n\n }, remoteData);\n }\n },\n\n relate: function(model, input, remoteData)\n {\n var relation = model.$relations[ this.name ];\n\n if ( this.isModelArray( input ) )\n {\n this.bulk( relation, function()\n {\n for (var i = 0; i < input.length; i++)\n {\n var related = this.parseModel( input[ i ], remoteData );\n\n if ( related )\n {\n this.addModel( relation, related, remoteData );\n }\n }\n });\n }\n else if ( isValue( input ) )\n {\n var related = this.parseModel( input, remoteData );\n\n if ( related )\n {\n this.addModel( relation, related, remoteData );\n }\n }\n },\n\n unrelate: function(model, input, remoteData)\n {\n var relation = model.$relations[ this.name ];\n\n if ( this.isModelArray( input ) )\n {\n this.bulk( relation, function()\n {\n for (var i = 0; i < input.length; i++)\n {\n var related = this.parseModel( input[ i ] );\n\n if ( related )\n {\n this.removeModel( relation, related, remoteData );\n }\n }\n });\n }\n else if ( isValue( input ) )\n {\n var related = this.parseModel( input );\n\n if ( related )\n {\n this.removeModel( relation, related, remoteData );\n }\n }\n else\n {\n var all = relation.related;\n\n this.bulk( relation, function()\n {\n for (var i = all.length - 1; i >= 0; i--)\n {\n this.removeModel( relation, all[ i ], remoteData );\n }\n });\n }\n },\n\n isRelated: function(model, input)\n {\n var relation = model.$relations[ this.name ];\n var existing = relation.related;\n\n if ( this.isModelArray( input ) )\n {\n for (var i = 0; i < input.length; i++)\n {\n var related = this.parseModel( input[ i ] );\n\n if ( related && !existing.has( related.$key() ) )\n {\n return false;\n }\n }\n\n return input.length > 0;\n }\n else if ( isValue( input ) )\n {\n var related = this.parseModel( input );\n\n return related && existing.has( related.$key() );\n }\n\n return false;\n },\n\n canRemoveRelated: function(related, remoteData)\n {\n return !remoteData || !related.$isSaving();\n },\n\n checkSave: function(relation, remoteData)\n {\n if ( !relation.delaySaving && !remoteData && relation.parent.$exists() )\n {\n if ( this.store === Store.Model || this.save === Save.Model )\n {\n Rekord.debug( this.debugAutoSave, this, relation );\n\n relation.parent.$save( this.saveParentCascade, this.saveParentOptions );\n }\n }\n },\n\n handleModel: function(relation, remoteData, ignoreLoaded)\n {\n return function (related)\n {\n var pending = relation.pending;\n var key = related.$key();\n\n if ( key in pending || ignoreLoaded )\n {\n Rekord.debug( this.debugInitialGrabbed, this, relation, related );\n\n this.addModel( relation, related, remoteData );\n\n delete pending[ key ];\n }\n };\n },\n\n sort: function(relation)\n {\n var related = relation.related;\n\n if ( !relation.delaySorting )\n {\n Rekord.debug( this.debugSort, this, relation );\n\n related.sort( this.comparator );\n\n relation.parent.$trigger( Model.Events.RelationUpdate, [this, relation] );\n }\n }\n\n});\n\nfunction BelongsTo()\n{\n}\n\nRekord.Relations.belongsTo = BelongsTo;\n\nBelongsTo.Defaults =\n{\n model: null,\n lazy: false,\n query: false,\n store: Store.None,\n save: Save.None,\n auto: true,\n autoCascade: Cascade.All,\n autoOptions: null,\n property: true,\n preserve: true,\n clearKey: true,\n dynamic: false,\n local: null,\n cascade: Cascade.Local,\n cascadeRemoveOptions: null,\n discriminator: 'discriminator',\n discriminators: {},\n discriminatorToModel: {}\n};\n\nClass.extend( RelationSingle, BelongsTo,\n{\n\n type: 'belongsTo',\n\n debugInit: Rekord.Debugs.BELONGSTO_INIT,\n debugClearModel: Rekord.Debugs.BELONGSTO_CLEAR_MODEL,\n debugSetModel: Rekord.Debugs.BELONGSTO_SET_MODEL,\n debugLoaded: Rekord.Debugs.BELONGSTO_LOADED,\n debugClearKey: Rekord.Debugs.BELONGSTO_CLEAR_KEY,\n debugUpdateKey: Rekord.Debugs.BELONGSTO_UPDATE_KEY,\n debugQuery: Rekord.Debugs.BELONGSTO_QUERY,\n debugQueryResults: Rekord.Debugs.BELONGSTO_QUERY_RESULTS,\n\n getDefaults: function(database, field, options)\n {\n return BelongsTo.Defaults;\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n isRelated: this.isRelatedFactory( model ),\n related: null,\n loaded: false,\n\n onRemoved: function()\n {\n Rekord.debug( Rekord.Debugs.BELONGSTO_NINJA_REMOVE, this, model, relation );\n\n model.$remove( this.cascade, this.cascadeRemoveOptions );\n this.clearRelated( relation, false, true );\n },\n\n onSaved: function()\n {\n Rekord.debug( Rekord.Debugs.BELONGSTO_NINJA_SAVE, this, model, relation );\n\n if ( !relation.isRelated( relation.related ) )\n {\n this.clearRelated( relation, false, true );\n }\n }\n };\n\n model.$on( Model.Events.PostRemove, this.postRemove, this );\n model.$on( Model.Events.KeyUpdate, this.onKeyUpdate, this );\n\n if ( isEmpty( initialValue ) )\n {\n initialValue = this.grabInitial( model, this.local );\n\n if ( initialValue )\n {\n Rekord.debug( Rekord.Debugs.BELONGSTO_INITIAL_PULLED, this, model, initialValue );\n }\n }\n\n if ( !isEmpty( initialValue ) )\n {\n Rekord.debug( Rekord.Debugs.BELONGSTO_INITIAL, this, model, initialValue );\n\n this.grabModel( initialValue, this.handleModel( relation, remoteData ), remoteData );\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( model );\n }\n }),\n\n sync: function(model, removeUnrelated)\n {\n var relation = model.$relations[ this.name ];\n var relatedValue = this.grabInitial( model, this.local );\n var remoteData = true;\n var ignoreLoaded = true;\n var dontClear = true;\n\n if ( relation )\n {\n if ( !isEmpty( relatedValue ) )\n {\n this.grabModel( relatedValue, this.handleModel( relation, remoteData, ignoreLoaded ), remoteData );\n }\n else if ( removeUnrelated )\n {\n this.clearRelated( relation, remoteData, dontClear );\n }\n }\n },\n\n postRemove: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation )\n {\n Rekord.debug( Rekord.Debugs.BELONGSTO_POSTREMOVE, this, model, relation );\n\n this.clearModel( relation );\n this.setProperty( relation );\n }\n },\n\n onKeyUpdate: function(model, related, modelFields, relatedFields)\n {\n if ( this.local === modelFields )\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation && related !== relation.related )\n {\n this.clearModel( relation, false, true );\n this.setModel( relation, related );\n this.setProperty( relation );\n }\n }\n }\n\n});\n\nfunction HasOne()\n{\n}\n\nRekord.Relations.hasOne = HasOne;\n\nHasOne.Defaults =\n{\n model: null,\n lazy: false,\n query: false,\n store: Store.None,\n save: Save.None,\n saveCascade: Cascade.All,\n saveOptions: null,\n auto: true,\n autoCascade: Cascade.All,\n autoOptions: null,\n property: true,\n preserve: true,\n clearKey: true,\n dynamic: false,\n local: null,\n cascade: Cascade.All,\n cascadeRemoveOptions: null,\n discriminator: 'discriminator',\n discriminators: {},\n discriminatorToModel: {}\n};\n\nClass.extend( RelationSingle, HasOne,\n{\n\n type: 'hasOne',\n\n debugInit: Rekord.Debugs.HASONE_INIT,\n debugClearModel: Rekord.Debugs.HASONE_CLEAR_MODEL,\n debugSetModel: Rekord.Debugs.HASONE_SET_MODEL,\n debugLoaded: Rekord.Debugs.HASONE_LOADED,\n debugClearKey: Rekord.Debugs.HASONE_CLEAR_KEY,\n debugUpdateKey: Rekord.Debugs.HASONE_UPDATE_KEY,\n debugQuery: Rekord.Debugs.HASONE_QUERY,\n debugQueryResults: Rekord.Debugs.HASONE_QUERY_RESULTS,\n\n getDefaults: function(database, field, options)\n {\n return HasOne.Defaults;\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n isRelated: this.isRelatedFactory( model ),\n related: null,\n loaded: false,\n dirty: false,\n saving: false,\n child: equals( this.local, model.$db.key ),\n\n onRemoved: function()\n {\n Rekord.debug( Rekord.Debugs.HASONE_NINJA_REMOVE, this, model, relation );\n\n this.clearRelated( relation, false, true );\n }\n };\n\n model.$on( Model.Events.PreSave, this.preSave, this );\n model.$on( Model.Events.PostRemove, this.postRemove, this );\n\n if ( isEmpty( initialValue ) )\n {\n initialValue = this.grabInitial( model, this.local );\n\n if ( initialValue )\n {\n Rekord.debug( Rekord.Debugs.HASONE_INITIAL_PULLED, this, model, initialValue );\n }\n }\n\n if ( !isEmpty( initialValue ) )\n {\n Rekord.debug( Rekord.Debugs.HASONE_INITIAL, this, model, initialValue );\n\n this.populateInitial( initialValue, relation, model );\n this.grabModel( initialValue, this.handleModel( relation, remoteData ), remoteData );\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( model );\n }\n }),\n\n populateInitial: function(initialValue, relation, model)\n {\n if ( isObject( initialValue ) && relation.child )\n {\n var src = toArray( this.local );\n var dst = toArray( this.model.Database.key );\n\n for (var k = 0; k < src.length; k++)\n {\n initialValue[ dst[ k ] ] = model[ src[ k ] ];\n }\n }\n },\n\n sync: function(model, removeUnrelated)\n {\n var relation = model.$relations[ this.name ];\n var relatedValue = this.grabInitial( model, this.local );\n var remoteData = true;\n var ignoreLoaded = true;\n var dontClear = true;\n\n if ( relation )\n {\n if ( !isEmpty( relatedValue ) )\n {\n this.populateInitial( relatedValue, relation, model );\n this.grabModel( relatedValue, this.handleModel( relation, remoteData, ignoreLoaded ), remoteData );\n }\n else if ( removeUnrelated )\n {\n this.clearRelated( relation, remoteData, dontClear );\n }\n }\n },\n\n isDependent: function(relation, related)\n {\n return !relation.child;\n },\n\n preClone: function(model, clone, properties)\n {\n var related = this.get( model );\n\n if ( related )\n {\n var relatedClone = related.$clone( properties );\n\n updateFieldsReturnChanges( clone, this.local, relatedClone, relatedClone.$db.key );\n\n clone[ this.name ] = relatedClone;\n }\n },\n\n preSave: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation && relation.related )\n {\n var related = relation.related;\n\n if ( relation.dirty || related.$hasChanges() )\n {\n Rekord.debug( Rekord.Debugs.HASONE_PRESAVE, this, model, relation );\n\n relation.saving = true;\n\n related.$save( this.saveCascade, this.saveOptions );\n\n relation.saving = false;\n relation.dirty = false;\n }\n }\n },\n\n postRemove: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation )\n {\n if ( this.cascade )\n {\n Rekord.debug( Rekord.Debugs.HASONE_POSTREMOVE, this, model, relation );\n\n this.clearModel( relation );\n }\n }\n },\n\n clearModel: function(relation, remoteData)\n {\n var related = relation.related;\n\n if ( related )\n {\n Rekord.debug( this.debugClearModel, this, relation );\n\n related.$off( Model.Events.Removed, relation.onRemoved );\n\n if ( this.cascade && !related.$isDeleted() )\n {\n related.$remove( this.cascade, this.cascadeRemoveOptions );\n }\n\n relation.related = null;\n relation.dirty = true;\n relation.loaded = true;\n\n relation.parent.$dependents.remove( related );\n\n if ( this.clearKey )\n {\n this.clearForeignKey( relation.parent, remoteData );\n }\n }\n }\n\n});\n\nfunction HasMany()\n{\n}\n\nRekord.Relations.hasMany = HasMany;\n\nHasMany.Defaults =\n{\n model: null,\n lazy: false,\n query: false,\n store: Store.None,\n save: Save.None,\n auto: true,\n autoCascade: Cascade.All,\n autoOptions: null,\n property: true,\n preserve: true,\n clearKey: true,\n dynamic: false,\n foreign: null,\n comparator: null,\n comparatorNullsFirst: false,\n listenForRelated: true,\n loadRelated: true,\n where: false,\n saveParentCascade: Cascade.All,\n saveParentOptions: null,\n cascadeRemove: Cascade.Local,\n cascadeRemoveOptions: null,\n cascadeSave: Cascade.None,\n cascadeSaveOptions: null,\n discriminator: 'discriminator',\n discriminators: {},\n discriminatorToModel: {}\n};\n\nClass.extend( RelationMultiple, HasMany,\n{\n\n type: 'hasMany',\n\n debugAutoSave: Rekord.Debugs.HASMANY_AUTO_SAVE,\n debugInitialGrabbed: Rekord.Debugs.HASMANY_INITIAL_GRABBED,\n debugSort: Rekord.Debugs.HASMANY_SORT,\n debugQuery: Rekord.Debugs.HASMANY_QUERY,\n debugQueryResults: Rekord.Debugs.HASMANY_QUERY_RESULTS,\n debugUpdateKey: Rekord.Debugs.HASMANY_UPDATE_KEY,\n\n getDefaults: function(database, field, options)\n {\n return HasMany.Defaults;\n },\n\n onInitialized: function(database, field, options)\n {\n this.foreign = this.foreign || ( database.name + '_' + database.key );\n this.comparator = createComparator( this.comparator, this.comparatorNullsFirst );\n\n Rekord.debug( Rekord.Debugs.HASMANY_INIT, this );\n\n this.finishInitialization();\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relator = this;\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n pending: {},\n isRelated: this.isRelatedFactory( model ),\n related: this.createRelationCollection( model ),\n saving: false,\n delaySorting: false,\n delaySaving: false,\n\n onRemoved: function() // this = model removed\n {\n Rekord.debug( Rekord.Debugs.HASMANY_NINJA_REMOVE, relator, model, this, relation );\n\n relator.removeModel( relation, this, true, true );\n },\n\n onSaved: function() // this = model saved\n {\n if ( relation.saving )\n {\n return;\n }\n\n Rekord.debug( Rekord.Debugs.HASMANY_NINJA_SAVE, relator, model, this, relation );\n\n if ( !relation.isRelated( this ) )\n {\n relator.removeModel( relation, this, false, true );\n }\n else\n {\n relator.sort( relation );\n relator.checkSave( relation );\n }\n },\n\n onChange: function()\n {\n if ( relation.saving )\n {\n return;\n }\n\n if ( relator.where && !relator.where( this ) )\n {\n relator.removeModel( relation, this, false, true );\n }\n }\n\n };\n\n model.$on( Model.Events.PostSave, this.postSave, this );\n model.$on( Model.Events.PreRemove, this.preRemove, this );\n\n // When models are added to the related database, check if it's related to this model\n if ( this.listenForRelated )\n {\n this.listenToModelAdded( this.handleModelAdded( relation ) );\n }\n\n // If the model's initial value is an array, populate the relation from it!\n if ( isArray( initialValue ) )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_INITIAL, this, model, relation, initialValue );\n\n this.grabModels( relation, initialValue, this.handleModel( relation, remoteData ), remoteData );\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( model );\n }\n else if ( this.loadRelated )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_INITIAL_PULLED, this, model, relation );\n\n this.ready( this.handleLazyLoad( relation ) );\n }\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n sync: function(model, removeUnrelated)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation )\n {\n var existing = relation.related;\n var remoteData = true;\n var dontClear = true;\n var relator = this;\n\n var onRelated = function(related)\n {\n if ( removeUnrelated )\n {\n var given = this.createCollection();\n given.reset( related );\n\n existing.each(function(existingModel)\n {\n if ( !given.has( existingModel.$key() ) )\n {\n relator.removeModel( relation, existingModel, remoteData, dontClear );\n }\n });\n }\n };\n\n this.ready( this.handleLazyLoad( relation, onRelated ) );\n }\n },\n\n postClone: function(model, clone, properties)\n {\n var related = this.get( model );\n\n if ( related )\n {\n var relatedClones = [];\n\n updateFieldsReturnChanges( properties, this.foreign, clone, model.$db.key );\n\n properties[ this.foreign ] = clone[ model.$db.key ];\n\n for (var i = 0; i < related.length; i++)\n {\n relatedClones.push( related[ i ].$clone( properties ) );\n }\n\n clone[ this.name ] = relatedClones;\n }\n },\n\n postSave: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation && this.cascadeSave )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_POSTSAVE, this, model, relation );\n\n batchExecute(function()\n {\n relation.saving = true;\n relation.delaySaving = true;\n\n var models = relation.related;\n\n for (var i = 0; i < models.length; i++)\n {\n var related = models[ i ];\n\n if ( !related.$isDeleted() && related.$hasChanges() )\n {\n related.$save( this.cascadeSave, this.cascadeSaveOptions );\n }\n }\n\n relation.saving = false;\n relation.delaySaving = false;\n\n }, this );\n }\n },\n\n preRemove: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation && this.cascadeRemove )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_PREREMOVE, this, model, relation );\n\n batchExecute(function()\n {\n this.bulk( relation, function()\n {\n var models = relation.related;\n\n for (var i = models.length - 1; i >= 0; i--)\n {\n var related = models[ i ];\n\n related.$remove( this.cascadeRemove, this.cascadeRemoveOptions );\n }\n });\n\n }, this );\n }\n },\n\n handleModelAdded: function(relation)\n {\n return function (related, remoteData)\n {\n if ( relation.isRelated( related ) )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_NINJA_ADD, this, relation, related );\n\n this.addModel( relation, related, remoteData );\n }\n };\n },\n\n handleLazyLoad: function(relation, onRelated)\n {\n return function (relatedDatabase)\n {\n var related = relatedDatabase.filter( relation.isRelated );\n\n Rekord.debug( Rekord.Debugs.HASMANY_LAZY_LOAD, this, relation, related );\n\n if ( onRelated )\n {\n onRelated.call( this, related );\n }\n\n if ( related.length )\n {\n this.bulk( relation, function()\n {\n for (var i = 0; i < related.length; i++)\n {\n this.addModel( relation, related[ i ] );\n }\n });\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( relation.parent );\n }\n };\n },\n\n addModel: function(relation, related, remoteData)\n {\n if ( related.$isDeleted() || (this.where && !this.where( related ) ) )\n {\n return;\n }\n\n var model = relation.parent;\n var target = relation.related;\n var key = related.$key();\n var adding = !target.has( key );\n\n if ( adding )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_ADD, this, relation, related );\n\n target.put( key, related );\n\n related.$on( Model.Events.Removed, relation.onRemoved );\n related.$on( Model.Events.SavedRemoteUpdate, relation.onSaved );\n\n if ( this.where )\n {\n related.$on( Model.Events.Change, relation.onChange );\n }\n\n related.$dependents.add( model, this );\n\n this.updateForeignKey( related, model, remoteData );\n\n this.sort( relation );\n this.checkSave( relation, remoteData );\n }\n\n return adding;\n },\n\n removeModel: function(relation, related, remoteData, dontClear)\n {\n if ( !this.canRemoveRelated( related, remoteData ) )\n {\n return;\n }\n\n var model = relation.parent;\n var target = relation.related;\n var pending = relation.pending;\n var key = related.$key();\n var removing = target.has( key );\n\n if ( removing )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_REMOVE, this, relation, related );\n\n target.remove( key );\n\n related.$off( Model.Events.Removed, relation.onRemoved );\n related.$off( Model.Events.SavedRemoteUpdate, relation.onSaved );\n related.$off( Model.Events.Change, relation.onChange );\n\n related.$dependents.remove( model );\n\n if ( !dontClear )\n {\n if ( this.clearKey )\n {\n this.clearForeignKey( related, remoteData );\n }\n\n if ( this.cascadeRemove )\n {\n if ( remoteData )\n {\n if ( canCascade( this.cascadeRemove, Cascade.Local ) )\n {\n related.$remove( Cascade.Local );\n }\n }\n else\n {\n related.$remove( this.cascadeRemove, this.cascadeRemoveOptions );\n }\n }\n }\n\n this.sort( relation );\n this.checkSave( relation, remoteData );\n }\n\n delete pending[ key ];\n\n return removing;\n },\n\n isRelatedFactory: function(model)\n {\n var foreign = this.foreign;\n var local = model.$db.key;\n\n return function(related)\n {\n return propsMatch( related, foreign, model, local );\n };\n },\n\n getTargetFields: function(target)\n {\n return this.foreign;\n }\n\n});\n\nfunction HasManyThrough()\n{\n}\n\nRekord.Relations.hasManyThrough = HasManyThrough;\n\nHasManyThrough.Defaults =\n{\n model: null,\n lazy: false,\n query: false,\n store: Store.None,\n save: Save.None,\n auto: true,\n autoCascade: Cascade.All,\n autoOptions: null,\n property: true,\n dynamic: false,\n through: undefined,\n local: null,\n foreign: null,\n comparator: null,\n comparatorNullsFirst: false,\n listenForRelated: true,\n loadRelated: true,\n where: false,\n cascadeRemove: Cascade.NoRest,\n cascadeSave: Cascade.All,\n cascadeSaveOptions: null,\n cascadeSaveRelated: Cascade.None,\n cascadeSaveRelatedOptions: null,\n saveParentCascade: Cascade.All,\n saveParentOptions: null,\n cascadeRemoveThroughOptions: null,\n discriminator: 'discriminator',\n discriminators: {},\n discriminatorToModel: {}\n};\n\nClass.extend( RelationMultiple, HasManyThrough,\n{\n\n type: 'hasManyThrough',\n\n debugAutoSave: Rekord.Debugs.HASMANYTHRU_AUTO_SAVE,\n debugInitialGrabbed: Rekord.Debugs.HASMANYTHRU_INITIAL_GRABBED,\n debugSort: Rekord.Debugs.HASMANYTHRU_SORT,\n debugQuery: Rekord.Debugs.HASMANYTHRU_QUERY,\n debugQueryResults: Rekord.Debugs.HASMANYTHRU_QUERY_RESULTS,\n debugUpdateKey: Rekord.Debugs.HASMANYTHRU_UPDATE_KEY,\n\n getDefaults: function(database, field, options)\n {\n return HasManyThrough.Defaults;\n },\n\n onInitialized: function(database, field, options)\n {\n if ( !this.discriminated )\n {\n var relatedDatabase = this.model.Database;\n\n this.foreign = this.foreign || ( relatedDatabase.name + '_' + relatedDatabase.key );\n }\n\n this.local = this.local || ( database.name + '_' + database.key );\n this.comparator = createComparator( this.comparator, this.comparatorNullsFirst );\n\n if ( !isRekord( options.through ) )\n {\n Rekord.get( options.through ).complete( this.setThrough, this );\n }\n else\n {\n this.setThrough( options.through );\n }\n\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_INIT, this );\n },\n\n setThrough: function(through)\n {\n this.through = through;\n\n this.finishInitialization();\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relator = this;\n var throughDatabase = this.through.Database;\n\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n isRelated: this.isRelatedFactory( model ),\n pending: {},\n related: this.createRelationCollection( model ),\n throughs: new Map(),\n saving: false,\n delaySorting: false,\n delaySaving: false,\n\n onRemoved: function() // this = model removed\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_NINJA_REMOVE, relator, model, this, relation );\n\n relator.removeModel( relation, this );\n },\n\n onSaved: function() // this = model saved\n {\n if ( relation.saving )\n {\n return;\n }\n\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_NINJA_SAVE, relator, model, this, relation );\n\n relator.sort( relation );\n relator.checkSave( relation );\n },\n\n onChange: function()\n {\n if ( relation.saving )\n {\n return;\n }\n\n if ( relator.where && !relator.where( this ) )\n {\n relator.removeModel( relation, this );\n }\n },\n\n onThroughRemoved: function() // this = through removed\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_NINJA_THRU_REMOVE, relator, model, this, relation );\n\n relator.removeModelFromThrough( relation, this );\n }\n\n };\n\n // Populate the model's key if it's missing\n model.$on( Model.Events.PostSave, this.postSave, this );\n model.$on( Model.Events.PreRemove, this.preRemove, this );\n\n // When models are added to the related database, check if it's related to this model\n if ( this.listenForRelated )\n {\n throughDatabase.on( Database.Events.ModelAdded, this.handleModelAdded( relation ), this );\n }\n\n // If the model's initial value is an array, populate the relation from it!\n if ( isArray( initialValue ) )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_INITIAL, this, model, relation, initialValue );\n\n this.grabModels( relation, initialValue, this.handleModel( relation, remoteData ), remoteData );\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( model );\n }\n else if ( this.loadRelated )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_INITIAL_PULLED, this, model, relation );\n\n throughDatabase.ready( this.handleLazyLoad( relation ), this );\n }\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n sync: function(model, removeUnrelated)\n {\n var throughDatabase = this.through.Database;\n var relation = model.$relations[ this.name ];\n\n if ( relation )\n {\n var existing = relation.throughs.values;\n var remoteData = true;\n var relator = this;\n\n var onRelated = function(throughs)\n {\n if ( removeUnrelated )\n {\n var given = this.createCollection();\n given.reset( throughs );\n\n for (var i = 0; i < existing.length; i++)\n {\n var existingThrough = existing[ i ];\n\n if ( !given.has( existingThrough.$key() ) )\n {\n relator.removeModelFromThrough( relation, existingThrough, remoteData );\n }\n }\n }\n };\n\n throughDatabase.ready( this.handleLazyLoad( relation, onRelated ), this );\n }\n },\n\n preClone: function(model, clone, properties)\n {\n var related = this.get( model );\n\n if ( related )\n {\n clone[ this.name ] = related.slice();\n }\n },\n\n postSave: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n batchExecute(function()\n {\n if ( relation && this.cascadeSave )\n {\n var throughs = relation.throughs.values;\n\n for (var i = 0; i < throughs.length; i++)\n {\n var through = throughs[ i ];\n\n if ( !through.$isDeleted() && through.$hasChanges() )\n {\n through.$save( this.cascadeSave, this.cascadeSaveOptions );\n }\n }\n }\n\n if ( relation && this.cascadeSaveRelated )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_PRESAVE, this, model, relation );\n\n relation.saving = true;\n relation.delaySaving = true;\n\n var models = relation.related;\n\n for (var i = 0; i < models.length; i++)\n {\n var related = models[ i ];\n\n if ( !related.$isDeleted() && related.$hasChanges() )\n {\n related.$save( this.cascadeSaveRelated, this.cascadeSaveRelatedOptions );\n }\n }\n\n relation.saving = false;\n relation.delaySaving = false;\n }\n\n }, this );\n },\n\n preRemove: function(model)\n {\n var relation = model.$relations[ this.name ];\n\n if ( relation && this.cascadeRemove )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_PREREMOVE, this, model, relation );\n\n batchExecute(function()\n {\n this.bulk( relation, function()\n {\n var throughs = relation.throughs.values;\n\n for (var i = 0; i < throughs.length; i++)\n {\n var through = throughs[ i ];\n\n through.$remove( this.cascadeRemove, this.cascadeRemoveThroughOptions );\n }\n });\n\n }, this );\n }\n },\n\n handleModelAdded: function(relation)\n {\n return function (through, remoteData)\n {\n if ( relation.isRelated( through ) && !relation.throughs.has( through.$key() ) )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_NINJA_ADD, this, relation, through );\n\n this.addModelFromThrough( relation, through, remoteData );\n }\n };\n },\n\n handleLazyLoad: function(relation, onRelated)\n {\n return function (throughDatabase)\n {\n var throughs = throughDatabase.filter( relation.isRelated );\n\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_LAZY_LOAD, this, relation, throughs );\n\n if ( onRelated )\n {\n onRelated.call( this, throughs );\n }\n\n if ( throughs.length )\n {\n this.bulk( relation, function()\n {\n for (var i = 0; i < throughs.length; i++)\n {\n this.addModelFromThrough( relation, throughs[ i ] );\n }\n });\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( relation.parent );\n }\n };\n },\n\n addModel: function(relation, related, remoteData)\n {\n if ( related.$isDeleted() || (this.where && !this.where( related ) ) )\n {\n return;\n }\n\n var adding = this.finishAddModel( relation, related, remoteData );\n\n if ( adding )\n {\n this.addThrough( relation, related, remoteData );\n }\n\n return adding;\n },\n\n addThrough: function(relation, related, remoteData)\n {\n var throughDatabase = this.through.Database;\n var throughKey = this.createThroughKey( relation, related );\n\n throughDatabase.grabModel( throughKey, this.onAddThrough( relation, remoteData ), this, remoteData );\n },\n\n onAddThrough: function(relation, remoteData)\n {\n return function onAddThrough(through)\n {\n this.finishAddThrough( relation, through, remoteData );\n };\n },\n\n addModelFromThrough: function(relation, through, remoteData)\n {\n if ( through.$isDeleted() )\n {\n return;\n }\n\n // TODO polymoprhic logic\n var relatedDatabase = this.model.Database;\n var relatedKey = relatedDatabase.keyHandler.buildKey( through, this.foreign );\n\n relatedDatabase.grabModel( relatedKey, this.onAddModelFromThrough( relation, through, remoteData ), this, remoteData );\n },\n\n onAddModelFromThrough: function(relation, through, remoteData)\n {\n return function onAddModelFromThrough(related)\n {\n if ( related && ( !this.where || this.where( related ) ) )\n {\n this.finishAddThrough( relation, through, remoteData );\n this.finishAddModel( relation, related, remoteData );\n }\n };\n },\n\n finishAddThrough: function(relation, through, remoteData)\n {\n var model = relation.parent;\n var throughs = relation.throughs;\n var throughKey = through.$key();\n var added = !throughs.has( throughKey );\n\n if ( added )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_THRU_ADD, this, relation, through );\n\n throughs.put( throughKey, through );\n\n through.$on( Model.Events.Removed, relation.onThroughRemoved );\n\n through.$dependents.add( model, this );\n\n if ( !remoteData && this.cascadeSave )\n {\n if ( model.$isSaved() )\n {\n through.$save( this.cascadeSave, this.cascadeSaveOptions );\n }\n else\n {\n through.$save( Cascade.None );\n }\n }\n }\n\n return added;\n },\n\n finishAddModel: function(relation, related, remoteData)\n {\n var relateds = relation.related;\n var relatedKey = related.$key();\n var adding = !relateds.has( relatedKey );\n\n if ( adding )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_ADD, this, relation, related );\n\n relateds.put( relatedKey, related );\n\n related.$on( Model.Events.Removed, relation.onRemoved );\n related.$on( Model.Events.SavedRemoteUpdate, relation.onSaved );\n\n if ( this.where )\n {\n related.$on( Model.Events.Change, relation.onChange );\n }\n\n this.sort( relation );\n\n if ( !remoteData )\n {\n this.checkSave( relation );\n }\n }\n\n return adding;\n },\n\n removeModel: function(relation, related, remoteData)\n {\n var relatedKey = related.$key();\n var relateds = relation.related;\n var actualRelated = relateds.get( relatedKey );\n\n if ( actualRelated )\n {\n if ( this.removeThrough( relation, related, remoteData ) )\n {\n this.finishRemoveRelated( relation, relatedKey, remoteData );\n }\n }\n },\n\n removeThrough: function(relation, related, remoteData)\n {\n var throughDatabase = this.through.Database;\n var keyObject = this.createThroughKey( relation, related );\n var key = throughDatabase.keyHandler.getKey( keyObject );\n var throughs = relation.throughs;\n var through = throughs.get( key );\n\n return this.finishRemoveThrough( relation, through, related, true, remoteData );\n },\n\n removeModelFromThrough: function(relation, through, remoteData)\n {\n var relatedDatabase = this.model.Database;\n var relatedKey = relatedDatabase.keyHandler.buildKey( through, this.foreign );\n\n if ( this.finishRemoveThrough( relation, through, undefined, undefined, remoteData ) )\n {\n this.finishRemoveRelated( relation, relatedKey, remoteData );\n }\n },\n\n finishRemoveThrough: function(relation, through, related, callRemove, remoteData)\n {\n var model = relation.parent;\n var removing = !!through;\n\n if ( removing )\n {\n if ( !this.canRemoveRelated( through, remoteData ) )\n {\n return false;\n }\n\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_THRU_REMOVE, this, relation, through, related );\n\n var throughs = relation.throughs;\n var throughKey = through.$key();\n\n through.$off( Model.Events.Removed, relation.onThroughRemoved );\n\n through.$dependents.remove( model );\n\n if ( callRemove )\n {\n through.$remove( remoteData ? Cascade.Local : Cascade.All, this.cascadeRemoveThroughOptions );\n }\n\n throughs.remove( throughKey );\n }\n\n return removing;\n },\n\n finishRemoveRelated: function(relation, relatedKey, remoteData)\n {\n var pending = relation.pending;\n var relateds = relation.related;\n var related = relateds.get( relatedKey );\n\n if ( related )\n {\n Rekord.debug( Rekord.Debugs.HASMANYTHRU_REMOVE, this, relation, related );\n\n relateds.remove( relatedKey );\n\n related.$off( Model.Events.Removed, relation.onRemoved );\n related.$off( Model.Events.SavedRemoteUpdate, relation.onSaved );\n related.$off( Model.Events.Change, relation.onChange );\n\n this.sort( relation );\n this.checkSave( relation, remoteData );\n }\n\n delete pending[ relatedKey ];\n\n return related;\n },\n\n isRelatedFactory: function(model)\n {\n var foreign = model.$db.key;\n var local = this.local;\n\n return function(through)\n {\n return propsMatch( through, local, model, foreign );\n };\n },\n\n createThroughKey: function(relation, related)\n {\n var model = relation.parent;\n var modelKeys = model.$db.keyHandler;\n var relatedKeys = this.model.Database.keyHandler;\n var throughDatabase = this.through.Database;\n var throughKey = throughDatabase.key;\n var key = {};\n\n for (var i = 0; i < throughKey.length; i++)\n {\n var prop = throughKey[ i ];\n\n modelKeys.setKeyField( key, prop, related, this.foreign );\n relatedKeys.setKeyField( key, prop, model, this.local );\n }\n\n return key;\n },\n\n getTargetFields: function(target)\n {\n return this.local;\n }\n\n});\n\nfunction HasRemote()\n{\n}\n\nRekord.Relations.hasRemote = HasRemote;\n\nHasRemote.Defaults =\n{\n model: undefined,\n lazy: false,\n query: false,\n store: Store.None,\n save: Save.None,\n auto: false,\n property: true,\n dynamic: false,\n comparator: null,\n comparatorNullsFirst: false,\n where: false,\n autoRefresh: false // Model.Events.RemoteGets\n};\n\nClass.extend( RelationMultiple, HasRemote,\n{\n\n type: 'hasRemote',\n\n debugSort: Rekord.Debugs.HASREMOTE_SORT,\n debugQuery: Rekord.Debugs.HASREMOTE_QUERY,\n debugQueryResults: Rekord.Debugs.HASREMOTE_QUERY_RESULTS,\n\n getDefaults: function(database, field, options)\n {\n return HasRemote.Defaults;\n },\n\n onInitialized: function(database, field, options)\n {\n this.comparator = createComparator( this.comparator, this.comparatorNullsFirst );\n\n Rekord.debug( Rekord.Debugs.HASREMOTE_INIT, this );\n\n this.finishInitialization();\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relator = this;\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n pending: {},\n related: this.createRelationCollection( model ),\n delaySorting: false,\n delaySaving: false,\n\n onRemoved: function() // this = model removed\n {\n Rekord.debug( Rekord.Debugs.HASREMOTE_NINJA_REMOVE, relator, model, this, relation );\n\n relator.removeModel( relation, this, true );\n },\n\n onSaved: function() // this = model saved\n {\n Rekord.debug( Rekord.Debugs.HASREMOTE_NINJA_SAVE, relator, model, this, relation );\n\n relator.sort( relation );\n relator.checkSave( relation );\n },\n\n onChange: function()\n {\n if ( relation.saving )\n {\n return;\n }\n\n if ( relator.where && !relator.where( this ) )\n {\n relator.removeModel( relation, this, true );\n }\n }\n\n };\n\n // Populate the model's key if it's missing\n model.$key();\n\n // If auto refresh was specified, execute the query on refresh\n if ( this.autoRefresh )\n {\n model.$on( this.autoRefresh, this.onRefresh( relation ), this );\n }\n\n // Execute query!\n relation.query = this.executeQuery( model );\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n onRefresh: function(relation)\n {\n return function handleRefresh()\n {\n relation.query = this.executeQuery( relation.parent );\n };\n },\n\n addModel: function(relation, related, remoteData)\n {\n if ( related.$isDeleted() || (this.where && !this.where( related ) ) )\n {\n return;\n }\n\n var model = relation.parent;\n var target = relation.related;\n var key = related.$key();\n var adding = !target.has( key );\n\n if ( adding )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_ADD, this, relation, related );\n\n target.put( key, related );\n\n related.$on( Model.Events.Removed, relation.onRemoved );\n related.$on( Model.Events.SavedRemoteUpdate, relation.onSaved );\n\n if ( this.where )\n {\n related.$on( Model.Events.Change, relation.onChange );\n }\n\n this.sort( relation );\n this.checkSave( relation, remoteData );\n }\n\n return adding;\n },\n\n removeModel: function(relation, related, remoteData)\n {\n if ( !this.canRemoveRelated( related, remoteData ) )\n {\n return;\n }\n\n var model = relation.parent;\n var target = relation.related;\n var pending = relation.pending;\n var key = related.$key();\n\n if ( target.has( key ) )\n {\n Rekord.debug( Rekord.Debugs.HASMANY_REMOVE, this, relation, related );\n\n target.remove( key );\n\n related.$off( Model.Events.Removed, relation.onRemoved );\n related.$off( Model.Events.SavedRemoteUpdate, relation.onSaved );\n related.$off( Model.Events.Change, relation.onChange );\n\n this.sort( relation );\n this.checkSave( relation, remoteData );\n }\n\n delete pending[ key ];\n }\n\n});\n\nfunction HasList()\n{\n}\n\nRekord.Relations.hasList = HasList;\n\nHasList.Defaults =\n{\n model: undefined,\n lazy: false,\n store: Store.Model,\n save: Save.Model,\n auto: false,\n property: true,\n dynamic: false,\n comparator: null,\n comparatorNullsFirst: false\n};\n\nClass.extend( RelationMultiple, HasList,\n{\n\n type: 'hasList',\n\n debugSort: Rekord.Debugs.HASLIST_SORT,\n\n getDefaults: function(database, field, options)\n {\n return HasList.Defaults;\n },\n\n onInitialized: function(database, field, options)\n {\n this.comparator = createComparator( this.comparator, this.comparatorNullsFirst );\n\n Rekord.debug( Rekord.Debugs.HASLIST_INIT, this );\n\n this.finishInitialization();\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relator = this;\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n pending: {},\n related: this.createRelationCollection( model ),\n delaySorting: false,\n delaySaving: false,\n\n onRemoved: function() // this = model removed\n {\n Rekord.debug( Rekord.Debugs.HASLIST_NINJA_REMOVE, relator, model, this, relation );\n\n relator.removeModel( relation, this, true );\n },\n\n onSaved: function() // this = model saved\n {\n Rekord.debug( Rekord.Debugs.HASLIST_NINJA_SAVE, relator, model, this, relation );\n\n relator.sort( relation );\n relator.checkSave( relation );\n }\n\n };\n\n // If the model's initial value is an array, populate the relation from it!\n if ( isArray( initialValue ) )\n {\n Rekord.debug( Rekord.Debugs.HASLIST_INITIAL, this, model, relation, initialValue );\n\n this.grabModels( relation, initialValue, this.handleModel( relation, remoteData ), remoteData );\n }\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n addModel: function(relation, related, remoteData)\n {\n if ( related.$isDeleted() )\n {\n return;\n }\n\n var model = relation.parent;\n var target = relation.related;\n var key = related.$key();\n var adding = !target.has( key );\n\n if ( adding )\n {\n Rekord.debug( Rekord.Debugs.HASLIST_ADD, this, relation, related );\n\n target.put( key, related );\n\n related.$on( Model.Events.Removed, relation.onRemoved );\n related.$on( Model.Events.SavedRemoteUpdate, relation.onSaved );\n\n this.sort( relation );\n\n if ( !remoteData )\n {\n this.checkSave( relation );\n }\n }\n\n return adding;\n },\n\n removeModel: function(relation, related, remoteData)\n {\n if ( !this.canRemoveRelated( related, remoteData ) )\n {\n return;\n }\n\n var model = relation.parent;\n var target = relation.related;\n var pending = relation.pending;\n var key = related.$key();\n\n if ( target.has( key ) )\n {\n Rekord.debug( Rekord.Debugs.HASLIST_REMOVE, this, relation, related );\n\n target.remove( key );\n\n related.$off( Model.Events.Removed, relation.onRemoved );\n related.$off( Model.Events.SavedRemoteUpdate, relation.onSaved );\n\n this.sort( relation );\n this.checkSave( relation );\n }\n\n delete pending[ key ];\n },\n\n postClone: function(model, clone, properties)\n {\n var related = this.get( model );\n\n if ( related )\n {\n var relatedClones = [];\n\n for (var i = 0; i < related.length; i++)\n {\n relatedClones.push( related[ i ].$clone() );\n }\n\n clone[ this.name ] = relatedClones;\n }\n }\n\n});\n\nfunction HasReference()\n{\n}\n\nRekord.Relations.hasReference = HasReference;\n\nHasReference.Defaults =\n{\n model: null,\n lazy: false,\n query: false,\n store: Store.None,\n save: Save.None,\n property: true,\n dynamic: false\n};\n\nClass.extend( RelationSingle, HasReference,\n{\n\n type: 'hasReference',\n\n debugInit: Rekord.Debugs.HASREFERENCE_INIT,\n debugClearModel: Rekord.Debugs.HASREFERENCE_CLEAR_MODEL,\n debugSetModel: Rekord.Debugs.HASREFERENCE_SET_MODEL,\n debugLoaded: Rekord.Debugs.HASREFERENCE_LOADED,\n debugQuery: Rekord.Debugs.HASREFERENCE_QUERY,\n debugQueryResults: Rekord.Debugs.HASREFERENCE_QUERY_RESULTS,\n\n getDefaults: function(database, field, options)\n {\n return HasReference.Defaults;\n },\n\n load: Gate(function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ] =\n {\n parent: model,\n related: null,\n loaded: false,\n dirty: false,\n\n onRemoved: function()\n {\n Rekord.debug( Rekord.Debugs.HASREFERENCE_NINJA_REMOVE, this, model, relation );\n\n this.clearRelated( relation, false, true );\n }\n };\n\n if ( !isEmpty( initialValue ) )\n {\n Rekord.debug( Rekord.Debugs.HASREFERENCE_INITIAL, this, model, initialValue );\n\n this.grabModel( initialValue, this.handleModel( relation ), remoteData );\n }\n else if ( this.query )\n {\n relation.query = this.executeQuery( model );\n }\n }),\n\n preClone: function(model, clone, properties)\n {\n var related = this.get( model );\n\n if ( related )\n {\n clone[ this.name ] = related.$clone( properties );\n }\n },\n\n isDependent: function(relation, related)\n {\n return false;\n },\n\n updateForeignKey: function()\n {\n // nothing\n },\n\n clearForeignKey: function()\n {\n // nothing\n },\n\n});\n\n\nvar Polymorphic =\n{\n\n setReferences: function(database, field, options)\n {\n this.isRelatedFactory = this.isRelatedDiscriminatedFactory( this.isRelatedFactory );\n\n this.loadDiscriminators(function()\n {\n this.onInitialized( database, field, options );\n });\n },\n\n isRelatedDiscriminatedFactory: function(isRelatedFactory)\n {\n return function (model)\n {\n var isRelated = isRelatedFactory.call( this, model );\n var discriminator = this.getDiscriminatorForModel( model );\n var discriminatorField = this.discriminator;\n\n return function (related)\n {\n if ( !isRelated( related ) )\n {\n return false;\n }\n\n return equals( discriminator, related[ discriminatorField ] );\n };\n };\n },\n\n loadDiscriminators: function(onLoad)\n {\n var discriminators = this.discriminators;\n var total = sizeof( discriminators );\n var loaded = 0;\n\n function handleLoaded()\n {\n if ( ++loaded === total )\n {\n onLoad.apply( this );\n }\n }\n\n for (var name in discriminators)\n {\n var discriminator = discriminators[ name ];\n\n Rekord.get( name ).complete( this.setDiscriminated( discriminator, handleLoaded ), this );\n }\n },\n\n setDiscriminated: function(discriminator, onLoad)\n {\n return function(rekord)\n {\n this.discriminators[ rekord.Database.name ] = discriminator;\n this.discriminators[ rekord.Database.className ] = discriminator;\n this.discriminatorToModel[ discriminator ] = rekord;\n\n onLoad.apply( this );\n };\n },\n\n createRelationCollection: function(model)\n {\n return DiscriminateCollection( RelationCollection.create( undefined, model, this ), this.discriminator, this.discriminatorToModel );\n },\n\n createCollection: function()\n {\n return DiscriminateCollection( ModelCollection.create(), this.discriminator, this.discriminatorToModel );\n },\n\n ready: function(callback)\n {\n var models = this.discriminatorToModel;\n\n for ( var prop in models )\n {\n var model = models[ prop ];\n\n model.Database.ready( callback, this );\n }\n },\n\n listenToModelAdded: function(callback)\n {\n var models = this.discriminatorToModel;\n\n for ( var prop in models )\n {\n var model = models[ prop ];\n\n model.Database.on( Database.Events.ModelAdded, callback, this );\n }\n },\n\n executeQuery: function(model)\n {\n var queryOption = this.query;\n var queryOptions = this.queryOptions;\n var queryData = this.queryData;\n var query = isString( queryOption ) ? format( queryOption, model ) : queryOption;\n var search = model.search( query, queryOptions );\n\n if ( isObject( queryData ) )\n {\n search.$set( queryData );\n }\n\n DiscriminateCollection( search.$results, this.discriminator, this.discriminatorToModel );\n\n var promise = search.$run();\n promise.complete( this.handleExecuteQuery( model ), this );\n\n return search;\n },\n\n parseModel: function(input, remoteData)\n {\n if ( input instanceof Model )\n {\n return input;\n }\n else if ( isObject( input ) )\n {\n var db = this.getDiscriminatorDatabase( input );\n\n if ( db )\n {\n return db.parseModel( input, remoteData );\n }\n }\n\n return false;\n },\n\n clearFields: function(target, targetFields, remoteData)\n {\n var changes = clearFieldsReturnChanges( target, targetFields );\n\n if ( target[ this.discriminator ] )\n {\n target[ this.discriminator ] = null;\n changes = true;\n }\n\n if ( changes && !remoteData && this.auto && !target.$isNew() )\n {\n target.$save( this.autoCascade, this.autoOptions );\n }\n\n return changes;\n },\n\n updateFields: function(target, targetFields, source, sourceFields, remoteData)\n {\n var changes = updateFieldsReturnChanges( target, targetFields, source, sourceFields );\n\n var targetField = this.discriminator;\n var targetValue = target[ targetField ];\n var sourceValue = this.getDiscriminatorForModel( source );\n\n if ( !equals( targetValue, sourceValue ) )\n {\n target[ targetField ] = sourceValue;\n changes = true;\n }\n\n if ( changes )\n {\n if ( this.auto && !target.$isNew() && !remoteData )\n {\n target.$save( this.autoCascade, this.autoOptions );\n }\n\n target.$trigger( Model.Events.KeyUpdate, [target, source, targetFields, sourceFields] );\n }\n\n return changes;\n },\n\n grabInitial: function( model, fields )\n {\n var discriminator = this.discriminator;\n var discriminatorValue = model[ discriminator ];\n\n if ( hasFields( model, fields, isValue ) && isValue( discriminatorValue ) )\n {\n var related = this.discriminatorToModel[ discriminatorValue ];\n\n if ( related.Database )\n {\n var db = related.Database;\n var initial = {};\n\n initial[ discriminator ] = discriminatorValue;\n\n updateFieldsReturnChanges( initial, db.key, model, fields );\n\n return initial;\n }\n }\n },\n\n grabModel: function(input, callback, remoteData)\n {\n if ( input instanceof Model )\n {\n callback.call( this, input );\n }\n // At the moment I don't think this will ever work - if we are given a plain\n // object we can't really determine the related database.\n else if ( isObject( input ) )\n {\n var db = this.getDiscriminatorDatabase( input );\n\n if ( db !== false )\n {\n db.grabModel( input, callback, this, remoteData );\n }\n }\n },\n\n grabModels: function(relation, initial, callback, remoteData)\n {\n for (var i = 0; i < initial.length; i++)\n {\n var input = initial[ i ];\n\n if ( input instanceof Model )\n {\n relation.pending[ input.$key() ] = true;\n\n callback.call( this, input );\n }\n // At the moment I don't think this will ever work - if we are given a plain\n // object we can't really determine the related database.\n else if ( isObject( input ) )\n {\n var db = this.getDiscriminatorDatabase( input );\n\n if ( db )\n {\n var key = db.keyHandler.buildKeyFromInput( input );\n\n relation.pending[ key ] = true;\n\n db.grabModel( input, callback, this, remoteData );\n }\n }\n }\n },\n\n ownsForeignKey: function()\n {\n return true;\n },\n\n isModelArray: function(input)\n {\n return isArray( input );\n },\n\n getDiscriminator: function(model)\n {\n return model[ this.discriminator ];\n },\n\n getDiscriminatorDatabase: function(model)\n {\n var discriminator = this.getDiscriminator( model );\n var model = this.discriminatorToModel[ discriminator ];\n\n return model ? model.Database : false;\n },\n\n getDiscriminatorForModel: function(model)\n {\n return this.discriminators[ model.$db.name ];\n }\n\n};\n\n\nRekord.shard = function(methods)\n{\n return function createRestSharding(database)\n {\n var shard = new Shard( database );\n\n Class.props( shard, methods );\n\n shard.initialize( database );\n\n return shard;\n };\n};\n\nfunction Shard(database)\n{\n this.database = database;\n}\n\nClass.create( Shard,\n{\n\n STATUS_FAIL_ALL: 500,\n STATUS_FAIL_GET: 500,\n STATUS_FAIL_CREATE: 500,\n STATUS_FAIL_UPDATE: 500,\n STATUS_FAIL_REMOVE: 500,\n STATUS_FAIL_QUERY: 500,\n\n ATOMIC_ALL: false,\n ATOMIC_GET: false,\n ATOMIC_CREATE: true,\n ATOMIC_UPDATE: true,\n ATOMIC_REMOVE: false,\n ATOMIC_QUERY: true,\n\n getShards: function(forRead)\n {\n throw 'getShards not implemented';\n },\n\n getShardForModel: function(model, forRead)\n {\n throw 'getShardForModel not implemented';\n },\n\n getShardsForModel: function(model, forRead)\n {\n var single = this.getShardForModel( model, forRead );\n\n return single ? [ single ] : this.getShards( forRead );\n },\n\n getShardsForQuery: function(url, query)\n {\n return this.getShards();\n },\n\n initialize: function(database)\n {\n\n },\n\n all: function(options, success, failure)\n {\n var shards = this.getShards( true );\n var all = [];\n\n function invoke(shard, onShardSuccess, onShardFailure)\n {\n shard.all( options, onShardSuccess, onShardFailure );\n }\n function onSuccess(models)\n {\n if ( isArray( models ) )\n {\n all.push.apply( all, models );\n }\n }\n function onComplete(successful, alreadyFailed, failedStatus)\n {\n if ( successful || (all.length && !this.ATOMIC_ALL) )\n {\n success( all );\n }\n else if ( !alreadyFailed )\n {\n failure( all, isDefined( failedStatus ) ? failedStatus : this.STATUS_FAIL_ALL );\n }\n }\n\n this.multiplex( shards, this.ATOMIC_ALL, invoke, onSuccess, failure, onComplete );\n },\n\n get: function(model, options, success, failure)\n {\n var shards = this.getShardsForModel( model, true );\n var gotten = null;\n\n function invoke(shard, onShardSuccess, onShardFailure)\n {\n shard.get( model, options, onShardSuccess, onShardFailure );\n }\n function onSuccess(data)\n {\n if ( gotten === null && isObject( data ) )\n {\n gotten = data;\n }\n }\n function onComplete(successful, alreadyFailed, failedStatus)\n {\n if ( gotten !== null )\n {\n success( gotten );\n }\n else\n {\n failure( gotten, isDefined( failedStatus ) ? failedStatus : this.STATUS_FAIL_GET );\n }\n }\n\n this.multiplex( shards, this.ATOMIC_GET, invoke, onSuccess, noop, onComplete );\n },\n\n create: function( model, encoded, options, success, failure )\n {\n var shards = this.getShardsForModel( model, false );\n var returned = null;\n\n function invoke(shard, onShardSuccess, onShardFailure)\n {\n shard.create( model, encoded, options, onShardSuccess, onShardFailure );\n }\n function onSuccess(data)\n {\n if ( returned === null && isObject( returned ) )\n {\n returned = data;\n }\n }\n function onComplete(successful, alreadyFailed, failedStatus)\n {\n if ( successful )\n {\n success( returned );\n }\n else\n {\n failure( returned, isDefined( failedStatus ) ? failedStatus : this.STATUS_FAIL_CREATE );\n }\n }\n\n this.multiplex( shards, this.ATOMIC_CREATE, invoke, onSuccess, noop, onComplete );\n },\n\n update: function( model, encoded, options, success, failure )\n {\n var shards = this.getShardsForModel( model, false );\n var returned = null;\n\n function invoke(shard, onShardSuccess, onShardFailure)\n {\n shard.update( model, encoded, options, onShardSuccess, onShardFailure );\n }\n function onSuccess(data)\n {\n if ( returned === null && isObject( returned ) )\n {\n returned = data;\n }\n }\n function onComplete(successful, alreadyFailed, failedStatus)\n {\n if ( successful )\n {\n success( returned );\n }\n else\n {\n failure( returned, isDefined( failedStatus ) ? failedStatus : this.STATUS_FAIL_UPDATE );\n }\n }\n\n this.multiplex( shards, this.ATOMIC_UPDATE, invoke, onSuccess, noop, onComplete );\n },\n\n remove: function( model, options, success, failure )\n {\n var shards = this.getShardsForModel( model, false );\n var returned = null;\n\n function invoke(shard, onShardSuccess, onShardFailure)\n {\n shard.remove( model, options, onShardSuccess, onShardFailure );\n }\n function onSuccess(data)\n {\n if ( returned === null && isObject( returned ) )\n {\n returned = data;\n }\n }\n function onComplete(successful, alreadyFailed, failedStatus)\n {\n if ( successful )\n {\n success( returned );\n }\n else\n {\n failure( returned, isDefined( failedStatus ) ? failedStatus : this.STATUS_FAIL_REMOVE );\n }\n }\n\n this.multiplex( shards, this.ATOMIC_REMOVE, invoke, onSuccess, noop, onComplete );\n },\n\n query: function( url, query, options, success, failure )\n {\n var shards = this.getShardsForQuery( url, query );\n var results = [];\n\n function invoke(shard, onShardSuccess, onShardFailure)\n {\n shard.query( url, query, options, onShardSuccess, onShardFailure );\n }\n function onSuccess(models)\n {\n if ( isArray( models ) )\n {\n results.push.apply( results, models );\n }\n }\n function onComplete(successful, alreadyFailed, failedStatus)\n {\n if ( successful || (results.length && !this.ATOMIC_QUERY) )\n {\n success( results );\n }\n else if ( !alreadyFailed )\n {\n failure( results, isDefined( failedStatus ) ? failedStatus : this.STATUS_FAIL_QUERY );\n }\n }\n\n this.multiplex( shards, this.ATOMIC_QUERY, invoke, onSuccess, noop, onComplete );\n },\n\n multiplex: function(shards, atomic, invoke, onSuccess, onFailure, onComplete)\n {\n var successful = true;\n var failureCalled = false;\n var failedStatus;\n var total = 0;\n\n function onShardComplete()\n {\n if ( ++total === shards.length )\n {\n onComplete.call( this, successful, failureCalled, failedStatus );\n }\n }\n function onShardSuccess(data)\n {\n if ( successful || !atomic )\n {\n onSuccess.apply( this, arguments );\n }\n\n onShardComplete();\n }\n function onShardFailure(data, status)\n {\n if ( successful )\n {\n successful = false;\n\n if ( atomic )\n {\n failureCalled = true;\n onFailure.apply( this, arguments );\n }\n }\n\n if ( isNumber( status ) && (failedStatus === undefined || status < failedStatus) )\n {\n failedStatus = status;\n }\n\n onShardComplete();\n }\n\n if ( !isArray( shards ) || shards.length === 0 )\n {\n onComplete.call( this, false, false, failedStatus );\n }\n else\n {\n for (var i = 0; i < shards.length; i++)\n {\n invoke.call( this, shards[ i ], onShardSuccess, onShardFailure );\n }\n }\n }\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Returns the reference to the collection which contains all saved models.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var t0 = Task.create({name: 't0', done: true}); // saves\n * var t1 = new Task({name: 't1'});\n * Task.all(); // [t0]\n * ```\n *\n * @method all\n * @memberof Rekord.Model\n * @return {Rekord.ModelCollection} -\n * The reference to the collection of models.\n */\n model.all = function()\n {\n return db.models;\n };\n \n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Creates a collection of models.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var t0 = Task.create({id: 34, name: 't0'});\n * var t1 = new Task({name: 't1'});\n * var t2 = {name: 't2'};\n *\n * var c = Task.collect( 34, t1, t2 ); // or Task.collect( [34, t1, t2] )\n * c; // [t0, t1, t2]\n * ```\n *\n * @method collect\n * @memberof Rekord.Model\n * @param {modelInput[]|...modelInput} models -\n * The array of models to to return as a collection.\n * @return {Rekord.ModelCollection} -\n * The collection created.\n */\n model.array = function(a)\n {\n var models = arguments.length > 1 || !isArray(a) ?\n AP.slice.call( arguments ) : a;\n\n return ModelCollection.native( db, models );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Returns the model at the given index.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var t0 = Task.create({name: 't0', done: true}); // saves\n * var t1 = new Task({name: 't1'});\n * Task.at( 0 ); // t0\n * ```\n *\n * @method at\n * @memberof Rekord.Model\n * @param {Number} index -\n * The index of the model to return.\n * @return {Rekord.Model} -\n * The reference to the model at the given index.\n */\n model.at = function(index)\n {\n return db.models[ index ];\n };\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Returns an instance of a model or model collection with remote data (from\n * the server). If the model(s) exist locally then the values passed in will\n * overwrite the current values of the models. This is typically used to\n * bootstrap data from the server in your webpage.\n *\n * ```javascript\n * var User = Rekord({\n * fields: ['name', 'email']\n * });\n * var currentUser = User.boot({\n * id: 1234,\n * name: 'Administrator',\n * email: 'rekordjs@gmail.com'\n * });\n * var friends = User.boot([\n * { id: 'c1', name: 'Cat 1', email: 'cat1@gmail.com' },\n * { id: 'c2', name: 'Cat 2', email: 'cat2@gmail.com' }\n * ]);\n * ```\n *\n * @method boot\n * @memberof Rekord.Model\n * @param {modelInput[]|Object}\n * @return {Rekord.ModelCollection|Rekord.Model} -\n * The collection or model bootstrapped.\n */\n model.boot = function( input )\n {\n if ( isArray( input ) )\n {\n return ModelCollection.create( db, input, true );\n }\n else if ( isObject( input ) )\n {\n return db.putRemoteData( input );\n }\n\n return input;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n \n model.clear = function(removeListeners)\n {\n return db.clear( removeListeners );\n };\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Creates a collection of models.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var t0 = Task.create({id: 34, name: 't0'});\n * var t1 = new Task({name: 't1'});\n * var t2 = {name: 't2'};\n *\n * var c = Task.collect( 34, t1, t2 ); // or Task.collect( [34, t1, t2] )\n * c; // [t0, t1, t2]\n * ```\n *\n * @method collect\n * @memberof Rekord.Model\n * @param {modelInput[]|...modelInput} models -\n * The array of models to to return as a collection.\n * @return {Rekord.ModelCollection} -\n * The collection created.\n */\n model.collect = function(a)\n {\n var models = arguments.length > 1 || !isArray(a) ?\n AP.slice.call( arguments ) : a;\n\n return ModelCollection.create( db, models );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Counts the number of models which pass the given where expression.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var t0 = Task.create({name: 't0', done: true}); // saves\n * var t1 = Task.create({name: 't1', done: false});\n * Task.count('done', true); // 1\n * ```\n *\n * @method count\n * @memberof Rekord.Model\n * @return {Number} -\n * The number of models which pass the given where expression.\n */\n model.count = function(properties, value, equals)\n {\n return db.models.countWhere( properties, value, equals );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Creates a model instance, saves it, and returns it.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name'],\n * defaults: {\n * name: 'New Task'\n * }\n * });\n * var t0 = Task.create({id: 34, name: 't0'});\n * var t1 = Task.create({name: 't1'}); // id generated with uuid\n * var t2 = Task.create(); // name populated with default 'New Task'\n * ```\n *\n * @method create\n * @memberof Rekord.Model\n * @param {Object} [props] -\n * The initial values for the new model - if any.\n * @param {Number} [cascade] -\n * Which operations should be performed out of: store, rest, & live.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @return {Rekord.Model} -\n * The saved model instance.\n */\n model.create = function( props, cascade, options )\n {\n var instance = isObject( props ) ?\n db.createModel( props ) :\n db.instantiate();\n\n instance.$save( cascade, options );\n\n return instance;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n var dynamics = collapse( options.dynamic, Defaults.dynamic );\n\n if ( !isEmpty( dynamics ) )\n {\n for ( var property in dynamics )\n {\n addDynamicProperty( model.prototype, property, dynamics[ property ] );\n }\n }\n});\n\nfunction addDynamicProperty(modelPrototype, property, definition)\n{\n var get = isFunction( definition ) ? definition :\n ( isObject( definition ) && isFunction( definition.get ) ? definition.get : noop );\n var set = isObject( definition ) && isFunction( definition.set ) ? definition.set : noop;\n\n if ( Object.defineProperty )\n {\n Object.defineProperty( modelPrototype, property,\n {\n configurable: false,\n enumerable: true,\n get: get,\n set: set\n });\n }\n else\n {\n var $init = modelPrototype.$init;\n\n modelPrototype.$init = function()\n {\n $init.apply( this, arguments );\n\n var lastCalculatedValue = this[ property ] = get.apply( this );\n\n var handleChange = function()\n {\n var current = this[ property ];\n\n if ( current !== lastCalculatedValue )\n {\n set.call( this, current );\n }\n else\n {\n lastCalculatedValue = this[ property ] = get.apply( this );\n }\n };\n\n this.$after( Model.Events.Changes, handleChange, this );\n };\n }\n}\n\naddPlugin(function(model, db, options)\n{\n var events = collapse( options.events, Defaults.events );\n\n if ( !isEmpty( events ) )\n {\n var modelEvents = [];\n var databaseEvents = [];\n\n for ( var eventType in events )\n {\n var callback = events[ eventType ];\n var eventName = toCamelCase( eventType );\n\n var databaseEventString = Database.Events[ eventName ];\n var modelEventString = Model.Events[ eventName ];\n\n if ( databaseEventString )\n {\n parseEventListeners( databaseEventString, callback, false, databaseEvents );\n }\n\n if ( modelEventString )\n {\n parseEventListeners( modelEventString, callback, true, modelEvents );\n }\n }\n\n applyEventListeners( db, databaseEvents );\n\n if ( modelEvents.length )\n {\n Class.replace( model, '$init', function($init)\n {\n return function()\n {\n $init.apply( this, arguments );\n\n applyEventListeners( this, modelEvents );\n };\n });\n }\n }\n\n});\n\nfunction parseEventListeners(events, callback, secret, out)\n{\n var map = {\n on: secret ? '$on' : 'on',\n once: secret ? '$once' : 'once',\n after: secret ? '$after' : 'after'\n };\n\n var listeners = out || [];\n\n if ( isFunction( callback ) )\n {\n listeners.push(\n {\n when: map.on,\n events: events,\n invoke: callback\n });\n }\n else if ( isArray( callback ) && callback.length === 2 && isFunction( callback[0] ) )\n {\n listeners.push(\n {\n when: map.on,\n events: events,\n invoke: callback[0],\n context: callback[1]\n });\n }\n else if ( isObject( callback ) )\n {\n for ( var eventType in callback )\n {\n if ( eventType in map )\n {\n var subcallback = callback[ eventType ];\n var when = map[ eventType ];\n\n if ( isFunction( subcallback ) )\n {\n listeners.push(\n {\n when: when,\n events: events,\n invoke: subcallback\n });\n }\n else if ( isArray( subcallback ) && subcallback.length === 2 && isFunction( subcallback[0] ) )\n {\n listeners.push(\n {\n when: when,\n events: events,\n invoke: subcallback[0],\n context: subcallback[1]\n });\n }\n }\n }\n }\n\n return listeners;\n}\n\nfunction applyEventListeners(target, listeners)\n{\n for (var i = 0; i < listeners.length; i++)\n {\n var l = listeners[ i ];\n\n target[ l.when ]( l.events, l.invoke, l.context );\n }\n}\n\naddPlugin(function(model, db, options)\n{\n var extend = options.extend || Defaults.extend;\n\n if ( !isRekord( extend ) )\n {\n return;\n }\n\n var defaults = Defaults;\n var edb = extend.Database;\n var eoptions = edb.options;\n\n function tryOverwrite(option)\n {\n if ( !options[ option ] )\n {\n db[ option ] = edb[ option ];\n }\n }\n\n function tryMerge(option)\n {\n var dbo = db[ option ];\n var edbo = edb[ option ];\n\n for (var prop in edbo)\n {\n if ( !(prop in dbo ) )\n {\n dbo[ prop ] = edbo[ prop ];\n }\n }\n }\n\n function tryUnshift(options, sourceOptions)\n {\n var source = edb[ sourceOptions || options ];\n var target = db[ options ];\n\n for (var i = source.length - 1; i >= 0; i--)\n {\n var k = indexOf( target, source[ i ] );\n\n if ( k !== false )\n {\n target.splice( k, 1 );\n }\n\n target.unshift( source[ i ] );\n }\n }\n\n tryOverwrite( 'keySeparator' );\n tryMerge( 'defaults' );\n tryMerge( 'ignoredFields' );\n tryOverwrite( 'loadRelations' );\n tryOverwrite( 'load' );\n tryOverwrite( 'autoRefresh' );\n tryOverwrite( 'cache' );\n tryOverwrite( 'fullSave' );\n tryOverwrite( 'fullPublish' );\n tryMerge( 'encodings' );\n tryMerge( 'decodings' );\n tryOverwrite( 'summarize' );\n tryUnshift( 'fields' );\n tryUnshift( 'saveFields', 'fields' );\n\n if ( !options.comparator )\n {\n db.setComparator( eoptions.comparator, eoptions.comparatorNullsFirst );\n }\n\n if ( !options.revision )\n {\n db.setRevision( eoptions.revision );\n }\n\n if ( !options.summarize )\n {\n db.setSummarize( eoptions.summarize );\n }\n\n for (var name in edb.relations)\n {\n if ( name in db.relations )\n {\n continue;\n }\n\n var relation = edb.relations[ name ];\n var relationCopy = new relation.constructor();\n\n relationCopy.init( db, name, relation.options );\n\n if ( relationCopy.save )\n {\n db.saveFields.push( name );\n }\n\n db.relations[ name ] = relationCopy;\n db.relationNames.push( name );\n }\n\n db.rest = Rekord.rest( db );\n db.store = Rekord.store( db );\n db.live = Rekord.live( db );\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Gets the local model matching the given input (or creates one) and loads\n * it from the remote source ({@link Rekord.rest}). If `callback` is specified\n * then it is invoked with the instance once it's loaded.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var t0 = Task.fetch( 34, function(task) {\n * task; // {id: 34 name: 'Remotely Loaded'}\n * });\n * t0; // {id: 34} until remotely loaded\n * ```\n *\n * @method fetch\n * @memberof Rekord.Model\n * @param {modelInput} input -\n * The model input used to determine the key and load the model.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @param {Function} [callback] -\n * The function to invoke passing the reference of the model once it's\n * successfully remotely loaded.\n * @param {Object} [context] -\n * The context (this) for the callback.\n * @return {Rekord.Model} -\n * The model instance.\n */\n model.fetch = function( input, options, callback, context )\n {\n var key = db.keyHandler.buildKeyFromInput( input );\n var instance = db.get( key );\n\n if ( !instance )\n {\n instance = db.keyHandler.buildObjectFromKey( key );\n\n if ( isObject( input ) )\n {\n instance.$set( input );\n }\n }\n\n if ( isFunction( callback ) )\n {\n var callbackContext = context || this;\n\n instance.$once( Model.Events.RemoteGets, function()\n {\n callback.call( callbackContext, instance );\n });\n }\n\n instance.$refresh( Cascade.Rest, options );\n\n return instance;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Returns the collection of all local models and tries to reload them (and\n * any additional models returned) from a remote source ({@link Rekord.rest}).\n * If `callback` is specified then it is invoked with the collections all\n * models once it's loaded.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var tasks0 = Task.fetchAll( function(tasks1) {\n * tasks0 // tasks1\n * });\n * ```\n *\n * @method fetchAll\n * @memberof Rekord.Model\n * @param {Function} [callback] -\n * The function to invoke passing the reference of the model collection\n * when it's successfully remotely loaded.\n * @param {Object} [context] -\n * The context (this) for the callback.\n * @return {Rekord.ModelCollection} -\n * The collection of all models of this type.\n */\n model.fetchAll = function(callback, context)\n {\n db.refresh( callback, context );\n\n return db.models;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n var files = options.files || Defaults.files;\n\n if ( !isObject( files ) )\n {\n return;\n }\n\n if ( !isFilesSupported() )\n {\n Rekord.trigger( Rekord.Events.FilesNotSupported );\n\n return;\n }\n\n for (var field in files)\n {\n var fieldOption = files[ field ];\n\n if ( isString( fieldOption ) )\n {\n fieldOption = {\n type: fieldOption\n };\n }\n\n db.decodings[ field ] = FileDecodings[ fieldOption.type ]( db, fieldOption );\n db.encodings[ field ] = FileEncoder;\n }\n});\n\n/**\nfiles: {\n field: {\n type: 'text', // base64, dataURL, resource\n processor: 'processor_name',\n capacity: 1024 * 1024, // maximum bytes\n types: ['image/png', 'image/jpg', 'image/gif'], // acceptable MIME types\n autoSave: true,\n store: true,\n save: true\n }\n}\n**/\n\nRekord.fileProcessors = {};\n\nRekord.Events.FilesNotSupported = 'files-not-supported';\nRekord.Events.FileTooLarge = 'file-too-large';\nRekord.Events.FileWrongType = 'file-wrong-type';\nRekord.Events.FileOffline = 'file-offline';\n\n// {\n// fileToValue(file, model, field, callback),\n// valueToUser(value, model, field, callback)\n// }\nRekord.addFileProcessor = function(name, methods)\n{\n Rekord.fileProcessors[ name ] = methods;\n};\n\nRekord.fileProperties =\n[\n 'lastModifiedDate', 'name', 'size', 'type'\n];\n\nfunction isFilesSupported()\n{\n return win.File && win.FileReader && win.FileList;\n}\n\nfunction toFile(input)\n{\n if ( input instanceof win.File )\n {\n return input;\n }\n else if ( input instanceof win.Blob )\n {\n return input;\n }\n else if ( input instanceof win.FileList && input.length > 0 )\n {\n return input[0];\n }\n\n return false;\n}\n\nfunction convertNone(x)\n{\n return x;\n}\n\nfunction convertBase64(x)\n{\n var i = isString( x ) ? x.indexOf(';base64,') : -1;\n\n return i === -1 ? x : x.substring( i + 8 );\n}\n\nfunction trySave(model, options)\n{\n if ( options.autoSave && model.$isSaved() )\n {\n model.$save();\n }\n}\n\nfunction putFileCache(model, property, value, file, options)\n{\n model.$files = model.$files || {};\n model.$files[ property ] = {\n value: value,\n user: value,\n file: file,\n options: options\n };\n}\n\nfunction setFilesValue(processor, value, model, property, options)\n{\n var result;\n var done = false;\n\n if ( processor && processor.valueToUser )\n {\n processor.valueToUser( value, model, property, function(user)\n {\n model.$files[ property ].user = user;\n\n if ( done )\n {\n model[ property ] = user;\n trySave( model, options );\n }\n else\n {\n result = user;\n }\n });\n }\n else\n {\n result = value;\n }\n\n done = true;\n\n return result;\n}\n\nfunction fileReader(method, converter, options)\n{\n var processor = Rekord.fileProcessors[ options.processor ];\n\n if ( !(method in win.FileReader.prototype) )\n {\n Rekord.trigger( Rekord.Events.FilesNotSupported );\n }\n\n return function(input, model, property)\n {\n var file = toFile( input );\n\n if ( file !== false )\n {\n var reader = new win.FileReader();\n var result;\n var done = false;\n\n reader.onload = function(e)\n {\n var value = converter( e.target.result );\n\n putFileCache( model, property, value, file, options );\n\n result = setFilesValue( processor, value, model, property, options );\n\n if ( done )\n {\n model[ property ] = result;\n trySave( model, options );\n }\n };\n\n reader[ method ]( file );\n\n done = true;\n\n return result;\n }\n else if ( isObject( input ) && input.FILE )\n {\n var result;\n\n var setter = function(value)\n {\n result = value;\n };\n\n Rekord.trigger( Rekord.Events.FileOffline, [input, model, property, setter] );\n\n return result;\n }\n else\n {\n putFileCache( model, property, input, null, options );\n\n return setFilesValue( processor, input, model, property, options );\n }\n };\n}\n\nvar FileDecodings =\n{\n text: function(db, options)\n {\n return fileReader( 'readAsText', convertNone, options );\n },\n dataURL: function(db, options)\n {\n return fileReader( 'readAsDataURL', convertNone, options );\n },\n base64: function(db, options)\n {\n return fileReader( 'readAsDataURL', convertBase64, options );\n },\n resource: function(db, options)\n {\n return function(input, model, property)\n {\n var file = toFile( input );\n var processor = Rekord.fileProcessors[ options.processor ];\n\n if ( !processor )\n {\n throw 'Processor required for resource files.';\n }\n\n if ( file !== false )\n {\n if ( isNumber( options.capacity ) && isNumber( file.size ) && file.size > options.capacity )\n {\n Rekord.trigger( Rekord.Events.FileTooLarge, [file, model, property] );\n\n return;\n }\n\n if ( isArray( options.types ) && isString( file.type ) && indexOf( options.types, file.type ) === false )\n {\n Rekord.trigger( Rekord.Events.FileWrongType, [file, model, property] );\n\n return;\n }\n\n var result;\n var done = false;\n\n processor.fileToValue( file, model, property, function(value)\n {\n putFileCache( model, property, value, file, options );\n\n result = setFilesValue( processor, value, model, property, options );\n\n if ( done )\n {\n model[ property ] = result;\n trySave( model, options );\n }\n });\n\n done = true;\n\n return result;\n }\n else if ( isObject( input ) && input.FILE )\n {\n Rekord.trigger( Rekord.Events.FileOffline, [input, model, property] );\n }\n else\n {\n putFileCache( model, property, input, null, options );\n\n return setFilesValue( processor, input, model, property, options );\n }\n };\n }\n};\n\nfunction FileEncoder(input, model, field, forSaving)\n{\n if ( model.$files && field in model.$files )\n {\n var cached = model.$files[ field ];\n\n if ( (forSaving && cached.save === false) || (!forSaving && cached.store === false) )\n {\n return;\n }\n\n if ( !forSaving && cached.file )\n {\n var props = grab( cached.file, Rekord.fileProperties, false );\n\n props.FILE = true;\n\n return props;\n }\n\n if ( input === cached.user )\n {\n if ( forSaving && cached.file )\n {\n model.$once( Model.Events.RemoteSave, function()\n {\n delete cached.file;\n\n model.$addOperation( SaveLocal, Cascade.Local );\n });\n }\n\n return cached.value;\n }\n }\n\n return input;\n}\n\naddPlugin(function(model, db, options)\n{\n\n model.filtered = function(whereProperties, whereValue, whereEquals)\n {\n return db.models.filtered( whereProperties, whereValue, whereEquals );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n model.first = model.find = function(whereProperties, whereValue, whereEquals)\n {\n return db.models.firstWhere( whereProperties, whereValue, whereEquals );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Finds or creates a model instance based on the given values. The key for\n * the model must be derivable from the given values - or this function will\n * always create a new model instance.\n *\n * ```javascript\n * var ListItem = Rekord({\n * key: ['list_id', 'iten_id'],\n * fields: ['quantity'],\n * belongsTo: {\n * list: { model: 'list' },\n * item: { model: 'item' }\n * }\n * });\n *\n * var listItem = ListItem.findOrCreate({\n * list: someList,\n * item: someItem,\n * quantity: 23\n * });\n * // do stuff with listItem\n * ```\n *\n * @method persist\n * @memberof Rekord.Model\n * @param {Object} [input] -\n * The values to set in the model instance found or created.\n * @param {Number} [cascade] -\n * Which operations should be performed out of: store, rest, & live.\n * @param {Any} [options] -\n * The options to pass to the REST service.\n * @return {Rekord.Model} -\n * The saved model instance or undefined if the model database has not\n * finished loading.\n */\n model.findOrCreate = function( input, cascade, options, callback, context )\n {\n var callbackContext = context || this;\n var instance = db.get( input );\n var created = false;\n\n if ( !instance )\n {\n db.grabModel( input, function(grabbed)\n {\n if ( !grabbed )\n {\n instance = model.create( input, cascade, options );\n created = true;\n }\n else\n {\n instance = grabbed;\n instance.$set( input );\n\n // grab model created an instance that needs to be \"created\"\n if ( !instance.$isSaved() )\n {\n instance.$save( cascade, options );\n }\n }\n\n if ( callback )\n {\n callback.call( callbackContext, instance, created );\n }\n });\n }\n else\n {\n instance.$set( input );\n\n if ( callback )\n {\n callback.call( callbackContext, instance, created );\n }\n }\n\n return instance;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Returns the model instance identified with the given input. This includes\n * saved and unsaved models. If a `callback` is given the model will be passed\n * to the function. The `callback` method is useful for waiting for Rekord\n * to finish initializing (which includes loading models from local storage\n * followed by remote storage if configured) and returning a model instance.\n * If Rekord has finished initializing and the model doesn't exist locally\n * then it is fetched from the remoute source using {@link Rekord.rest}.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var t0 = Task.get( 34 ); // only looks at models currently loaded\n * var t1 = Task.get( 23, function(model) {\n * model; // local or remotely loaded if it didn't exist locally - could be null if it doesn't exist at all\n * })\n * ```\n *\n * @method get\n * @memberof Rekord.Model\n * @param {modelInput} input -\n * The model input used to determine the key and load the model.\n * @param {Function} [callback] -\n * The function to invoke passing the reference of the model when it's\n * successfully found.\n * @param {Object} [context] -\n * The context (this) for the callback.\n * @return {Rekord.Model} -\n * The model instance if `callback` is not given - or undefined if the\n * input doesn't resolve to a model or `callback` is given.\n */\n model.get = function( input, callback, context )\n {\n if ( isFunction( callback ) )\n {\n db.grabModel( input, callback, context );\n }\n else\n {\n return db.get( input );\n }\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Gets the model instance identified with the given input and passes it to the\n * `callback` function. If Rekord is not finished initializing this function\n * will wait until it is and check for the model. If it still doesn't exist\n * locally it is loaded from a remote source using {@link Rekord.rest}. If the\n * model doesn't exist at all a null value will be returned to the function.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var t1 = Task.grab( 23, function(model) {\n * model; // local or remotely loaded if it didn't exist locally - could be null if it doesn't exist at all\n * })\n * ```\n *\n * @method grab\n * @memberof Rekord.Model\n * @param {modelInput} input -\n * The model input used to determine the key and load the model.\n * @param {Function} callback -\n * The function to invoke passing the reference of the model when it's\n * successfully found.\n * @param {Object} [context] -\n * The context (this) for the callback.\n * @return {Rekord.Model} -\n * The model instance of it exists locally at the moment, or undefined\n * if the model hasn't been loaded yet.\n */\n model.grab = function( input, options, callback, context )\n {\n var callbackContext = context || this;\n var instance = db.get( input );\n\n if ( instance )\n {\n callback.call( callbackContext, instance );\n }\n else\n {\n db.grabModel( input, function(instance)\n {\n if ( instance )\n {\n callback.call( callbackContext, instance );\n }\n else\n {\n model.fetch( input, options, callback, context );\n }\n });\n }\n\n return instance;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Gets all model instances currently loaded, locally loaded, or remotely\n * loaded and passes it to the `callback` function.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * var tasks = Task.grabAll( function(models) {\n * models; // local or remotely loaded if it didn't exist locally.\n * })\n * ```\n *\n * @method grabAll\n * @memberof Rekord.Model\n * @param {Function} callback -\n * The function to invoke passing the reference of the model collection\n * when it's loaded.\n * @param {Object} [context] -\n * The context (this) for the callback.\n * @return {Rekord.Model} -\n * The model collection of it exists locally at the moment, or undefined\n * if models haven't been loaded yet.\n */\n model.grabAll = function( callback, context )\n {\n var callbackContext = context || this;\n var models = db.models;\n\n if ( models.length )\n {\n callback.call( callbackContext, models );\n }\n else\n {\n db.ready(function()\n {\n if ( models.length )\n {\n callback.call( callbackContext, models );\n }\n else\n {\n db.refresh(function()\n {\n callback.call( callbackContext, models );\n });\n }\n });\n }\n\n return models;\n };\n});\n\n\naddPlugin( function(model, db, options)\n{\n\n model.hasTrait = function(trait, comparator)\n {\n return db.hasTrait(trait, comparator);\n };\n\n model.hasTraits = function(traits, comparator)\n {\n return db.hasTraits(traits, comparator);\n };\n\n});\n\n\naddPlugin( function(model, db, options)\n{\n if ( options.keyChanges )\n {\n enableKeyChanges();\n }\n});\n\nvar Map_put = Map.prototype.put;\nvar Map_remove = Map.prototype.remove;\n\nfunction mapKeyChangeListener(map)\n{\n return function onKeyChange(model, oldKey, newKey)\n {\n var index = map.indices[ oldKey ];\n\n if ( isNumber( index ) )\n {\n var listener = map.listeners[ oldKey ];\n\n delete map.indices[ oldKey ];\n delete map.listeners[ oldKey ];\n\n map.keys[ index ] = newKey;\n map.indices[ newKey ] = index;\n map.listeners[ newKey ] = listener;\n }\n };\n}\n\nfunction mapKeyChangePut(key, value)\n{\n Map_put.apply( this, arguments );\n\n if ( value instanceof Model && value.$db.keyChanges )\n {\n this.listeners = this.listeners || {};\n\n this.listeners[ key ] = value.$on( Model.Events.KeyChange, mapKeyChangeListener( this ) );\n }\n\n return this;\n}\n\nfunction mapKeyChangeRemove(key)\n{\n var index = this.indices[ key ];\n\n if ( isNumber( index ) )\n {\n if ( this.listeners )\n {\n evaluate( this.listeners[ key ] );\n\n delete this.listeners[ key ];\n }\n\n this.removeAt( index );\n }\n\n return this;\n}\n\nfunction enableKeyChanges()\n{\n Class.method( Map, 'put', mapKeyChangePut );\n Class.method( Map, 'remove', mapKeyChangeRemove );\n}\n\nfunction disableKeyChanges()\n{\n Class.method( Map, 'put', Map_put );\n Class.method( Map, 'remove', Map_remove );\n}\n\naddPlugin(function(model, db, options)\n{\n var methods = collapse( options.methods, Defaults.methods );\n\n if ( !isEmpty( methods ) )\n {\n Class.methods( model, methods );\n }\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Persists model values, creating a model instance if none exists already\n * (determined by the key derived from the input).\n *\n * ```javascript\n * var ListItem = Rekord({\n * key: ['list_id', 'iten_id'],\n * fields: ['quantity'],\n * belongsTo: {\n * list: { model: 'list' },\n * item: { model: 'item' }\n * }\n * });\n *\n * var listItem = ListItem.persist({ // creates relationship if it doesn't exist already - updates existing\n * list: someList,\n * item: someItem,\n * quantity: 23\n * });\n * ```\n *\n * @method persist\n * @memberof Rekord.Model\n * @param {Object} [input] -\n * The values to persist in the model instance found or created.\n * @return {Rekord.Model} -\n * The saved model instance or undefined if the model database has not\n * finished loading.\n */\n model.persist = function( input, cascade, options, callback, context )\n {\n var callbackContext = context || this;\n\n return model.findOrCreate( input, cascade, options, function(instance, created)\n {\n if ( !created )\n {\n instance.$save( cascade, options );\n }\n\n if ( callback )\n {\n callback.call( callbackContext, instance );\n }\n });\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n model.projection = function(projectionInput)\n {\n return Projection.parse( db, projectionInput );\n };\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Invokes a function when Rekord has loaded. It's considered loaded when\n * it's loaded locally, remotely, or neither (depending on the options\n * passed to the database). The `callback` can also be invoked `persistent`ly\n * on any load event - which includes {@link Rekord.Database#refresh}.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * Task.ready( function(db) {\n * // Tasks have been loaded, lets do something about it!\n * });\n * ```\n *\n * @method ready\n * @memberof Rekord.Model\n * @param {Function} callback -\n * The function to invoke passing the reference of the database when it's\n * loaded.\n * @param {Object} [context] -\n * The context (this) for the callback.\n * @param {Boolean} [persistent=false] -\n * Whether the `callback` function should be invoked multiple times.\n * Depending on the state of initializing, the callback can be invoked when\n * models are loaded locally (if the `cache` is not equal to `None`),\n * models are loaded remotely (if `load` is Rekord.Load.All), and every time\n * {@link Rekord.Database#refresh} is called manually OR if `autoRefresh`\n * is specified as true and the application changes from offline to online.\n */\n model.ready = function( callback, context, persistent )\n {\n db.ready( callback, context, persistent );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Refreshs the model database from the remote source by calling\n * {@link Rekord.Database#refresh}. A `callback` can be passed to be invoked\n * when the model database has refreshed (or failed to refresh) where all\n * models that have been loaded will be passed as the first argument.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name']\n * });\n * Task.refresh( function(models) {\n * models; // The collection of models loaded remotely (or current models if it failed to load them remotely.\n * });\n * ```\n *\n * @method refresh\n * @memberof Rekord.Model\n * @param {Function} callback -\n * The function to invoke passing the reference model collection.\n * @param {Object} [context] -\n * The context (this) for the callback.\n */\n model.refresh = function( callback, context )\n {\n return db.refresh( callback, context );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n model.reset = function(failOnPendingChanges, removeListeners)\n {\n return db.reset( failOnPendingChanges, removeListeners );\n };\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Creates a new search for model instances and returns the results collection.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var results = Task.results('/api/task/search', {name: 'like this', done: true});\n * // results populated when search finishes running.\n * // results.$search is the Search object.\n * // results.$search.$promise is the promise of the search.\n * ```\n *\n * @method results\n * @memberof Rekord.Model\n * @param {String} url -\n * A URL to send the search data to.\n * @param {Object} [props] -\n * Initial set of properties on the search.\n * @param {searchOptions} [options] -\n * Options for the search.\n * @return {Rekord.ModelCollection} -\n * A collection containing the results of the search.\n */\n model.results = function(url, props, options)\n {\n return new Search( db, url, options, props, true ).$results;\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Creates a new search for model instances. A search is an object with\n * properties that are passed to a configurable {@link Rekord.rest} function\n * which expect an array of models to be returned from the remote call that\n * match the search parameters.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var search = Task.search('/api/task/search');\n * search.name = 'like this';\n * search.done = true;\n * search.anyProperty = [1, 3, 4];\n * var promise = search.$run();\n * promise.success( function(search) {\n * search.$results; // collection of returned results\n * });\n * ```\n *\n * @method search\n * @memberof Rekord.Model\n * @param {String} url -\n * A URL to send the search data to.\n * @param {searchOptions} [options] -\n * Options for the search.\n * @param {Object} [props] -\n * Initial set of properties on the search.\n * @param {Boolean} [run=false] -\n * Whether or not to run the search immediately.\n * @return {Rekord.Search} -\n * A new search for models.\n */\n model.search = function(url, options, props, run)\n {\n return new Search( db, url, options, props, run );\n };\n});\n\naddPlugin(function(model, db, options)\n{\n\n model.searchAt = function(index, url, paging, options, props, success, failure)\n {\n var page = {page_index: index, page_size: 1};\n\n var search = paging ?\n new SearchPaged( db, url, collapse( options, page ), props ) :\n new Search( db, url, options, props );\n\n var promise = new Promise();\n\n promise.success( success );\n promise.failure( failure );\n\n search.$run().then(\n function onSuccess(search, response, results) {\n promise.resolve( results[ paging ? 0 : index ] );\n },\n function onFailure() {\n promise.reject();\n },\n function onOffline() {\n promise.noline();\n }\n );\n\n return promise;\n };\n\n});\n\naddPlugin(function(model, db, options)\n{\n\n /**\n * Creates a new search with pagination for model instances. A paginated\n * search is an object with properties that are passed to a configurable\n * {@link Rekord.rest} function which expect an array of models to be returned\n * as well as paging information from the remote call. Special properties are\n * passed to the server (`page_index`, `page_size`) which dictate which\n * chunk of data should be returned. A special `total` property is expected to\n * be returned with `results` which tells the search how many records would've\n * been returned without the pagination.\n *\n * ```javascript\n * var Task = Rekord({\n * fields: ['name', 'done']\n * });\n * var search = Task.searchPaged('/api/task/searchPaged');\n * search.name = 'like this';\n * search.done = true;\n * search.anyProperty = [1, 3, 4];\n * var promise = search.$run();\n * promise.success( function(search) {\n * search.$results; // collection of returned results\n * search.total; // number of results that would've been returned without pagination\n * search.page_index; // the zero-based page index\n * search.page_size; // the number of results to be returned\n * });\n * search.$next(); // increase page_index, get the next page\n * ```\n *\n * @method searchPaged\n * @memberof Rekord.Model\n * @param {String} url -\n * A URL to send the search data to.\n * @param {searchPageOptions} [options] -\n * Options for the search.\n * @param {Object} [props] -\n * Initial set of properties on the search.\n * @param {Boolean} [run=false] -\n * Whether or not to run the search immediately.\n * @return {Rekord.SearchPaged} -\n * A new paginated search for models.\n */\n model.searchPaged = function(url, options, props, run)\n {\n return new SearchPaged( db, url, options, props, run );\n };\n});\n\naddPlugin(function(options)\n{\n var shard = options.shard || Defaults.shard;\n\n if ( !isObject( shard ) )\n {\n return;\n }\n\n options.createRest = Rekord.shard( shard );\n \n}, true );\n\naddPlugin(function(model, db, options)\n{\n var staticMethods = collapse( options.staticMethods, Defaults.staticMethods );\n\n if ( !isEmpty( staticMethods ) )\n {\n Class.props( model, staticMethods );\n }\n});\n\naddPlugin(function(model, db, options)\n{\n var time = options.timestamps || Defaults.timestamps;\n var timeFormat = collapseOption( options.timestampFormat, Defaults.timestampFormat );\n var timeType = collapseOption( options.timestampType, Defaults.timestampType );\n var timeUTC = collapseOption( options.timestampUTC, Defaults.timestampUTC );\n var timeCurrent = options.timestampCurrent || Defaults.timestampCurrent;\n\n if ( !time )\n {\n return;\n }\n\n function collapseOption(option, defaultValue)\n {\n if ( isObject( option ) && isObject( defaultValue ) )\n {\n return collapse( option, defaultValue );\n }\n\n return option || defaultValue;\n }\n\n function hasDefault(field)\n {\n return timeCurrent === true || indexOf( timeCurrent, field ) !== false;\n }\n\n function fieldSpecific(field, map)\n {\n return isObject( map ) ? map[ field ] : map;\n }\n\n function currentTimestamp(field)\n {\n var to = fieldSpecific( field, timeType );\n\n return function()\n {\n return convertDate( new Date(), to );\n };\n }\n\n function encode(x, model, field, forSaving)\n {\n var to = fieldSpecific( field, timeFormat );\n var encoded = convertDate( x, to );\n\n return encoded || x;\n }\n\n function decode(x, rawData, field)\n {\n var to = fieldSpecific( field, timeType );\n var utc = fieldSpecific( field, timeUTC );\n var decoded = convertDate( x, to, utc );\n\n return decoded || x;\n }\n\n function addTimestamp(field)\n {\n var i = indexOf( db.fields, field );\n\n if ( i === false )\n {\n db.fields.push( field );\n db.saveFields.push( field );\n }\n\n if ( hasDefault( field ) && !(field in db.defaults) )\n {\n db.defaults[ field ] = currentTimestamp( field );\n }\n\n if ( timeFormat && !(field in db.encodings) )\n {\n db.encodings[ field ] = encode;\n }\n\n if ( timeType && !(field in db.decodings ) )\n {\n db.decodings[ field ] = decode;\n }\n }\n\n function addCreatedAt(field)\n {\n addTimestamp( field );\n\n db.ignoredFields[ field ] = true;\n }\n\n function addUpdatedAt(field)\n {\n addTimestamp( field );\n\n db.ignoredFields[ field ] = true;\n\n Class.replace( model, '$save', function($save)\n {\n return function()\n {\n this[ field ] = evaluate( db.defaults[ field ] );\n\n return $save.apply( this, arguments );\n };\n });\n }\n\n function addTimestampField(type, field)\n {\n switch (type) {\n case 'created_at':\n return addCreatedAt( field );\n case 'updated_at':\n return addUpdatedAt( field );\n default:\n return addTimestamp( field );\n }\n }\n\n if ( isString( time ) )\n {\n addTimestampField( time, time );\n }\n else if ( isArray( time ) )\n {\n for (var i = 0; i < time.length; i++)\n {\n addTimestampField( time[ i ], time[ i ] );\n }\n }\n else if ( isObject( time ) )\n {\n for (var prop in time)\n {\n addTimestampField( prop, time[ prop ] );\n }\n }\n else\n {\n addCreatedAt( 'created_at' );\n addUpdatedAt( 'updated_at' );\n }\n\n});\n\nvar Timestamp = {\n Date: 'date',\n Millis: 'millis',\n Seconds: 'seconds'\n};\n\nDefaults.timestampFormat = Timestamp.Millis;\nDefaults.timestampType = Timestamp.Date;\nDefaults.timestampUTC = false;\nDefaults.timestampCurrent = ['created_at', 'updated_at'];\n\nfunction convertDate(x, to, utc)\n{\n var date = parseDate( x, utc );\n\n if ( date === false )\n {\n return false;\n }\n\n if ( !to )\n {\n return date;\n }\n\n switch (to)\n {\n case Timestamp.Date:\n return date;\n case Timestamp.Millis:\n return date.getTime();\n case Timestamp.Seconds:\n return Math.floor( date.getTime() / 1000 );\n default:\n return Rekord.formatDate( date, to );\n }\n}\n\nRekord.Timestamp = Timestamp;\nRekord.formatDate = noop;\nRekord.convertDate = convertDate;\n\nvar IGNORE_TRAITS = { traits: true };\n\naddPlugin(function(options)\n{\n var traits = options.traits || Defaults.traits;\n\n if ( !isEmpty( traits ) )\n {\n if ( isFunction( traits ) )\n {\n traits = traits( options );\n }\n\n if ( isArray( traits ) )\n {\n for (var i = 0; i < traits.length; i++)\n {\n var trait = traits[ i ];\n\n if ( isFunction( trait ) )\n {\n trait = trait( options );\n }\n\n if ( isObject( trait ) )\n {\n merge( options, trait, IGNORE_TRAITS );\n }\n else\n {\n throw 'traits are expected to be an object or a function which returns an object of methods';\n }\n }\n }\n else\n {\n throw 'traits are expected to be an array or a function which returns an array';\n }\n }\n\n}, true );\n\naddPlugin(function(model, db, options)\n{\n\n model.where = function(whereProperties, whereValue, whereEquals, out)\n {\n return db.models.where(whereProperties, whereValue, whereEquals, out);\n };\n});\n\n\n /* Classes */\n Rekord.Model = Model;\n Rekord.Database = Database;\n Rekord.Defaults = Defaults;\n Rekord.Relation = Relation;\n Rekord.Operation = Operation;\n Rekord.Search = Search;\n Rekord.SearchPaged = SearchPaged;\n Rekord.Promise = Promise;\n Rekord.Dependents = Dependents;\n Rekord.Shard = Shard;\n\n /* Keys */\n Rekord.KeyHandler = KeyHandler;\n Rekord.KeySimple = KeySimple;\n Rekord.KeyComposite = KeyComposite;\n Rekord.enableKeyChanges = enableKeyChanges;\n Rekord.disableKeyChanges = disableKeyChanges;\n\n /* Enums */\n Rekord.Cascade = Cascade;\n Rekord.Cache = Cache;\n Rekord.Store = Store;\n Rekord.Save = Save;\n Rekord.Load = Load;\n Rekord.RestStatus = RestStatus;\n\n /* Collections */\n Rekord.Map = Map;\n Rekord.Collection = Collection;\n Rekord.FilteredCollection = FilteredCollection;\n Rekord.ModelCollection = ModelCollection;\n Rekord.FilteredModelCollection = FilteredModelCollection;\n Rekord.RelationCollection = RelationCollection;\n Rekord.Page = Page;\n Rekord.Context = Context;\n\n /* Relationships */\n Rekord.HasOne = HasOne;\n Rekord.BelongsTo = BelongsTo;\n Rekord.HasMany = HasMany;\n Rekord.HasManyThrough = HasManyThrough;\n Rekord.HasRemote = HasRemote;\n Rekord.HasList = HasList;\n Rekord.HasReference = HasReference;\n Rekord.RelationMultiple = RelationMultiple;\n Rekord.RelationSingle = RelationSingle;\n\n /* Operations */\n Rekord.GetLocal = GetLocal;\n Rekord.GetRemote = GetRemote;\n Rekord.RemoveCache = RemoveCache;\n Rekord.RemoveLocal = RemoveLocal;\n Rekord.RemoveNow = RemoveNow;\n Rekord.RemoveRemote = RemoveRemote;\n Rekord.SaveLocal = SaveLocal;\n Rekord.SaveNow = SaveNow;\n Rekord.SaveRemote = SaveRemote;\n\n /* Projections */\n Rekord.Filters = {};\n Rekord.Projection = Projection;\n\n /* Common Functions */\n Rekord.isRekord = isRekord;\n Rekord.isDefined = isDefined;\n Rekord.isFunction = isFunction;\n Rekord.isString = isString;\n Rekord.isNumber = isNumber;\n Rekord.isBoolean = isBoolean;\n Rekord.isDate = isDate;\n Rekord.isRegExp = isRegExp;\n Rekord.isArray = isArray;\n Rekord.isObject = isObject;\n Rekord.isValue = isValue;\n Rekord.noop = noop;\n Rekord.bind = bind;\n Rekord.uuid = uuid;\n Rekord.sizeof = sizeof;\n Rekord.isEmpty = isEmpty;\n Rekord.evaluate = evaluate;\n Rekord.addPlugin = addPlugin;\n Rekord.now = now;\n Rekord.merge = merge;\n\n /* Morphing Functions */\n Rekord.Gate = Gate;\n Rekord.DiscriminateCollection = DiscriminateCollection;\n\n /* Morphing Objects */\n Rekord.Filtering = Filtering;\n Rekord.Polymorphic = Polymorphic;\n\n /* Array Functions */\n Rekord.toArray = toArray;\n Rekord.indexOf = indexOf;\n Rekord.collect = collect;\n Rekord.array = collectArray;\n Rekord.swap = swap;\n Rekord.reverse = reverse;\n Rekord.isSorted = isSorted;\n Rekord.isPrimitiveArray = isPrimitiveArray;\n\n /* Class Functions */\n Rekord.Settings = Settings;\n Rekord.Class = Class;\n Rekord.extend = Class.extend;\n Rekord.extendArray = Class.extend;\n Rekord.addMethod = Rekord.setProperty = Class.prop;\n Rekord.addMethods = Rekord.setProperties = Class.props;\n Rekord.replaceMethod = Class.replace;\n Rekord.copyConstructor = Class.copyConstructor;\n Rekord.factory = Class.factory;\n\n /* Comparator Functions */\n Rekord.Comparators = Comparators;\n Rekord.saveComparator = saveComparator;\n Rekord.addComparator = addComparator;\n Rekord.createComparator = createComparator;\n\n /* Comparison Functions */\n Rekord.equalsStrict = equalsStrict;\n Rekord.equalsWeak = equalsWeak;\n Rekord.equalsCompare = equalsCompare;\n Rekord.equals = equals;\n Rekord.compareNumbers = compareNumbers;\n Rekord.compare = compare;\n\n /* Eventful Functions */\n Rekord.addEventFunction = addEventFunction;\n Rekord.addEventful = addEventful;\n\n /* Object Functions */\n Rekord.applyOptions = applyOptions;\n Rekord.propsMatch = propsMatch;\n Rekord.hasFields = hasFields;\n Rekord.updateFieldsReturnChanges = updateFieldsReturnChanges;\n Rekord.clearFieldsReturnChanges = clearFieldsReturnChanges;\n Rekord.grab = grab;\n Rekord.pull = pull;\n Rekord.transfer = transfer;\n Rekord.collapse = collapse;\n Rekord.clean = clean;\n Rekord.cleanFunctions = cleanFunctions;\n Rekord.copy = copy;\n Rekord.diff = diff;\n\n /* Parse Functions */\n Rekord.isParseInput = isParseInput;\n Rekord.parse = parse;\n Rekord.createParser = createParser;\n Rekord.isFormatInput = isFormatInput;\n Rekord.format = format;\n Rekord.createFormatter = createFormatter;\n Rekord.parseDate = parseDate;\n\n /* Resolver Functions */\n Rekord.NumberResolvers = NumberResolvers;\n Rekord.saveNumberResolver = saveNumberResolver;\n Rekord.createNumberResolver = createNumberResolver;\n Rekord.PropertyResolvers = PropertyResolvers;\n Rekord.savePropertyResolver = savePropertyResolver;\n Rekord.createPropertyResolver = createPropertyResolver;\n\n /* String Functions */\n Rekord.toCamelCase = toCamelCase;\n Rekord.split = split;\n\n /* Where Functions */\n Rekord.Wheres = Wheres;\n Rekord.saveWhere = saveWhere;\n Rekord.createWhere = createWhere;\n Rekord.expr = expr;\n Rekord.not = not;\n Rekord.oneOf = oneOf;\n Rekord.isExpr = isExpr;\n Rekord.exprEqualsTester = exprEqualsTester;\n Rekord.exprEquals = exprEquals;\n\n /* Services */\n Rekord.Stores = Stores;\n Rekord.Lives = Lives;\n Rekord.Rests = Rests;\n\n return Rekord;\n\n}));\n"],"sourceRoot":"/source/"} \ No newline at end of file diff --git a/src/footer.js b/src/footer.js index dbb28e8..9403586 100644 --- a/src/footer.js +++ b/src/footer.js @@ -8,6 +8,8 @@ Rekord.Search = Search; Rekord.SearchPaged = SearchPaged; Rekord.Promise = Promise; + Rekord.Dependents = Dependents; + Rekord.Shard = Shard; /* Keys */ Rekord.KeyHandler = KeyHandler; @@ -22,6 +24,7 @@ Rekord.Store = Store; Rekord.Save = Save; Rekord.Load = Load; + Rekord.RestStatus = RestStatus; /* Collections */ Rekord.Map = Map; @@ -29,6 +32,7 @@ Rekord.FilteredCollection = FilteredCollection; Rekord.ModelCollection = ModelCollection; Rekord.FilteredModelCollection = FilteredModelCollection; + Rekord.RelationCollection = RelationCollection; Rekord.Page = Page; Rekord.Context = Context; @@ -39,6 +43,20 @@ Rekord.HasManyThrough = HasManyThrough; Rekord.HasRemote = HasRemote; Rekord.HasList = HasList; + Rekord.HasReference = HasReference; + Rekord.RelationMultiple = RelationMultiple; + Rekord.RelationSingle = RelationSingle; + + /* Operations */ + Rekord.GetLocal = GetLocal; + Rekord.GetRemote = GetRemote; + Rekord.RemoveCache = RemoveCache; + Rekord.RemoveLocal = RemoveLocal; + Rekord.RemoveNow = RemoveNow; + Rekord.RemoveRemote = RemoveRemote; + Rekord.SaveLocal = SaveLocal; + Rekord.SaveNow = SaveNow; + Rekord.SaveRemote = SaveRemote; /* Projections */ Rekord.Filters = {}; @@ -66,6 +84,14 @@ Rekord.now = now; Rekord.merge = merge; + /* Morphing Functions */ + Rekord.Gate = Gate; + Rekord.DiscriminateCollection = DiscriminateCollection; + + /* Morphing Objects */ + Rekord.Filtering = Filtering; + Rekord.Polymorphic = Polymorphic; + /* Array Functions */ Rekord.toArray = toArray; Rekord.indexOf = indexOf;