From 35f6addedffe65de592e9808cbf84b911e2c7077 Mon Sep 17 00:00:00 2001 From: Philip Diffenderfer Date: Tue, 14 Apr 2020 11:11:46 -0400 Subject: [PATCH] Resolves #391 --- bower.json | 2 +- build/rekord.js | 8 +- build/rekord.min.js | 12 +- build/rekord.min.js.map | 2 +- package-lock.json | 5831 +++++++++++++++++++++++++ package.json | 2 +- src/lib/relations/RelationMultiple.js | 6 +- 7 files changed, 5847 insertions(+), 16 deletions(-) create mode 100644 package-lock.json diff --git a/bower.json b/bower.json index 0d5344e..d921c0d 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "rekord", - "version": "1.5.10", + "version": "1.5.11", "homepage": "https://github.com/Rekord/rekord", "authors": [ "Philip Diffenderfer " diff --git a/build/rekord.js b/build/rekord.js index a5fad26..adfac1e 100644 --- a/build/rekord.js +++ b/build/rekord.js @@ -1,4 +1,4 @@ -/* rekord 1.5.10 - A javascript REST ORM that is offline and real-time capable http://rekord.github.io/rekord/ by Philip Diffenderfer */ +/* rekord 1.5.11 - A javascript REST ORM that is offline and real-time capable http://rekord.github.io/rekord/ by Philip Diffenderfer */ // UMD (Universal Module Definition) (function (root, factory) { @@ -14556,7 +14556,7 @@ Class.extend( Relation, RelationMultiple, this.addModel( relation, related, remoteData ); } } - }); + }, remoteData ); } else if ( isValue( input ) ) { @@ -14586,7 +14586,7 @@ Class.extend( Relation, RelationMultiple, this.removeModel( relation, related, remoteData ); } } - }); + }, remoteData ); } else if ( isValue( input ) ) { @@ -14607,7 +14607,7 @@ Class.extend( Relation, RelationMultiple, { this.removeModel( relation, all[ i ], remoteData ); } - }); + }, remoteData ); } }, diff --git a/build/rekord.min.js b/build/rekord.min.js index fdb261e..e97dd87 100644 --- a/build/rekord.min.js +++ b/build/rekord.min.js @@ -1,7 +1,7 @@ -/* rekord 1.5.10 - A javascript REST ORM that is offline and real-time capable http://rekord.github.io/rekord/ by Philip Diffenderfer */ -!function(e,t){"function"==typeof define&&define.amd?define("rekord",[],function(){return t(e)}):"object"==typeof module&&module.exports?module.exports=t(global):e.Rekord=t(e)}(this,function(e,t){function n(e,t){return e instanceof Array?e:f(e)?e.split(t):R(e)?[e]:[]}function i(e,t,n){for(var i=n||C,s=0,r=e.length;r>s;s++)if(i(e[s],t))return s;return!1}function s(e){var t=arguments.length>1||!E(e)?Array.prototype.slice.call(arguments):e;return Xe.create(t)}function r(e){var t=arguments.length>1||!E(e)?Array.prototype.slice.call(arguments):e;return Xe["native"](t)}function a(e,t,n){var i=e[t];e[t]=e[n],e[n]=i}function o(e){for(var t=e.length,n=Math.floor(t/2),i=0;n>i;i++)a(e,t-i-1,i);return e}function u(e,t){if(!e)return!0;for(var n=0,i=t.length-1;i>n;n++)if(e(t[n],t[n+1])>0)return!1;return!0}function h(e){for(var t=0;te?-1:1}function U(e,t,n){if(e==t)return 0;var i=R(e),s=R(t);return i!==s?i&&!n||s&&n?-1:1:(p(e)&&(e=e.getTime()),p(t)&&(t=t.getTime()),v(e)&&v(t)?H(e,t):E(e)&&E(t)?H(e.length,t.length):g(e)&&g(t)?e?-1:1:(e+"").localeCompare(t+""))}function x(e,t,n,i){var s=i?"$on":"on",r=i?"$off":"off",a=function(e,t){function i(){var n=e.apply(t||o,arguments);n===!1&&a()}function a(){u||(o[r](n,i),u=!0)}var o=this,u=!1;return o[s](n,i),a};e.$methods?Bt.method(e,t,a):Bt.prop(e,t,a)}function P(e,t,n,i,s){this.next=e?e:this,this.prev=e?e.prev:this,e&&(e.prev.next=this,e.prev=this),this.callback=t,this.context=n,this.type=i,this.group=s||0}function w(e,t){function i(e,t,i,s,r){if(!c(i))return $;var a=s||e,o=n(t," "),u=e.$$on;u||Bt.prop(e,"$$on",u={});for(var h=[],l=0;lr;){var o=s[r],u=o.length-n.length;if(o.substring(u)===n){var h=s[r+1],l=s[r+2],c=o.substring(0,u)+h+l;s.splice(r,3,c),a-=2}else r+=1,s.splice(r,1),a-=1}return s}function ge(e,t,n,i){var s=pe(t,n,i);return nn[e]=s,s}function pe(e,t,n){var i=n||C;if(c(e))return e;if(E(e)){for(var s=[],r=0;r0?e[0]:!1}function Ft(e){return e}function kt(e){var t=f(e)?e.indexOf(";base64,"):-1;return-1===t?e:e.substring(t+8)}function Ht(e,t){t.autoSave&&e.$isSaved()&&e.$save()}function Ut(e,t,n,i,s){e.$files=e.$files||{},e.$files[t]={value:n,user:n,file:i,options:s}}function xt(e,t,n,i,s){var r,a=!1;return e&&e.valueToUser?e.valueToUser(t,n,i,function(e){n.$files[i].user=e,a?(n[i]=e,Ht(n,s)):r=e}):r=t,a=!0,r}function Pt(e,t,n){var i=Se.fileProcessors[n.processor];return e in Qt.FileReader.prototype||Se.trigger(Se.Events.FilesNotSupported),function(s,r,a){var o=It(s);if(o!==!1){var u,h=new Qt.FileReader,l=!1;return h.onload=function(e){var s=t(e.target.result);Ut(r,a,s,o,n),u=xt(i,s,r,a,n),l&&(r[a]=u,Ht(r,n))},h[e](o),l=!0,u}if(y(s)&&s.FILE){var u,c=function(e){u=e};return Se.trigger(Se.Events.FileOffline,[s,r,a,c]),u}return Ut(r,a,s,null,n),xt(i,s,r,a,n)}}function wt(e,t,n,i){if(t.$files&&n in t.$files){var s=t.$files[n];if(i&&s.save===!1||!i&&s.store===!1)return;if(!i&&s.file){var r=Q(s.file,Se.fileProperties,!1);return r.FILE=!0,r}if(e===s.user)return i&&s.file&&t.$once(Ye.Events.RemoteSave,function(){delete s.file,t.$addOperation(gt,rn.Local)}),s.value}return e}function Kt(e){return function(t,n,i){var s=e.indices[n];if(v(s)){var r=e.listeners[n];delete e.indices[n],delete e.listeners[n],e.keys[s]=i,e.indices[i]=s,e.listeners[i]=r}}}function Vt(e,t){return $n.apply(this,arguments),t instanceof Ye&&t.$db.keyChanges&&(this.listeners=this.listeners||{},this.listeners[e]=t.$on(Ye.Events.KeyChange,Kt(this))),this}function Yt(e){var t=this.indices[e];return v(t)&&(this.listeners&&(D(this.listeners[e]),delete this.listeners[e]),this.removeAt(t)),this}function Gt(){Bt.method(ze,"put",Vt),Bt.method(ze,"remove",Yt)}function zt(){Bt.method(ze,"put",$n),Bt.method(ze,"remove",bn)}function jt(e,t,n){var i=oe(e,n);if(i===!1)return!1;if(!t)return i;switch(t){case Sn.Date:return i;case Sn.Millis:return i.getTime();case Sn.Seconds:return Math.floor(i.getTime()/1e3);default:return Se.formatDate(i,t)}}var Qt="undefined"!=typeof window?window:e,qt=Array.prototype,Bt={create:function(e,t){Bt.prop(e,"create",Bt.factory(e)),Bt.build(e,t,$)},extend:function(e,t,n){var i=J(n,e.$methods),s=Bt.copyConstructor(e);t.prototype=new s;var r=Bt.factory(t);if(Bt.isArray(e)){var a=function(){var e=[];return Bt.props(e,i),t.apply(e,arguments),e};Bt.prop(t,"native",a),Bt.prop(t,"create",en.nativeArray?a:r)}else Bt.prop(t,"create",r);Bt.build(t,i,e)},dynamic:function(e,t,n,i){var s=new Function("return function "+n+i)();return s.prototype=t,Bt.build(s,{},e),s},build:function(e,t,n){Bt.prop(e,"$methods",t),Bt.prop(e,"$prop",Bt.propThis),Bt.prop(e,"$method",Bt.methodThis),Bt.prop(e,"$replace",Bt.replaceThis),Bt.prop(e.prototype,"$super",n),Bt.prop(e.prototype,"constructor",e),Bt.props(e.prototype,t)},isArray:function(e){return Array===e||e.prototype instanceof Array},method:function(e,t,n){e.$methods&&(e.$methods[t]=n),Bt.prop(e.prototype,t,n)},methodThis:function(e,t){Bt.method(this,e,t)},methods:function(e,t){for(var n in t)Bt.method(e,n,t[n])},prop:function(){return Object.defineProperty?function(e,t,n){Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:!0,value:n})}:function(e,t,n){e[t]=n}}(),propThis:function(e,t){Bt.prop(this.prototype,e,t)},props:function(e,t){for(var n in t)Bt.prop(e,n,t[n])},replace:function(e,t,n){var i=e.prototype[t]||e[t]||$;Bt.method(e,t,n(i))},replaceThis:function(e,t){Bt.replace(this,e,t)},copyConstructor:function(e){function t(){}return t.prototype=e.prototype,t},factory:function(e){function t(t){e.apply(this,t)}return t.prototype=e.prototype,function(){return new t(arguments)}}},Jt=Date.now||function(){return(new Date).getTime()},Wt={};P.Types={Persistent:1,Once:2,After:4},Bt.create(P,{remove:function(){var e=this.next,t=this.prev;e!==this&&(t.next=e,e.prev=t,this.next=this.prev=this)},hasType:function(e){return 0!==(this.type&e)},trigger:function(e,t,n){var i=(this.type,this.hasType(P.Types.After));this.group!==e&&((n&&i||!i)&&(this.group=e,this.callback.apply(this.context,t)),this.hasType(P.Types.Once)&&this.remove())}}),ne.REGEX=/([\w$]+)/g,re.REGEX=/[\{\}]/;var Xt={},Zt={},en=e.RekordSettings||Qt.RekordSettings||{};if(Qt.document&&Qt.document.currentScript){var tn=Qt.document.currentScript;null!==tn.getAttribute("native-array")&&(en.nativeArray=!0)}fe.REGEX=/(^.|_.)/g;var nn={};Se.classes={},Se.autoload=!1,Se.unloaded=[],Se.loadPromise=null,Se.load=function(e,t){function n(e,t){if(a.push(e),r.push(t),r.length===s.length){for(var n=0;n0}:this.revisionFunction=function(e,t){return!1}},setComparator:function(e,t){this.models.setComparator(e,t); -},addComparator:function(e,t){this.models.addComparator(e,t)},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 n(n,i){Se.debug(Se.Debugs.LOCAL_LOAD,s,n);for(var r=0;r=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,!1,n);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),this.setInitial(e,t,n)}),setInitial:function(e,t,n){var i=e.$relations[this.name];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,i))},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),this.setInitial(e,t,n)}),setInitial:function(e,t,n){var i=e.$relations[this.name];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,i))},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)}}),_(function(e,t,n){e.at=function(e){return t.models[e]}}),_(function(e,t,n){e.boot=function(e){return E(e)?tt.create(t,e,!0):y(e)?t.putRemoteData(e):e}}),_(function(e,t,n){e.clear=function(e){return t.clear(e)}}),_(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)}}),_(function(e,t,n){e.count=function(e,n,i){return t.models.countWhere(e,n,i)}}),_(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}}),_(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])}),_(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)}})}}),_(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)}}),_(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}}),_(function(e,t,n){e.fetchAll=function(e,n){return t.refresh(e,n),t.models}}),_(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))}}};_(function(e,t,n){e.filtered=function(e,n,i){return t.models.filtered(e,n,i)}}),_(function(e,t,n){e.first=e.find=function(e,n,i){return t.models.firstWhere(e,n,i)}}),_(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}}),_(function(e,t,n){e.get=function(e,n,i){return c(n)?void t.grabModel(e,n,i):t.get(e)}}),_(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}}),_(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}}),_(function(e,t,n){e.hasTrait=function(e,n){return t.hasTrait(e,n)},e.hasTraits=function(e,n){return t.hasTraits(e,n)}}),_(function(e,t,n){n.keyChanges&&Gt()});var $n=ze.prototype.put,bn=ze.prototype.remove;_(function(e,t,n){var i=J(n.methods,mn.methods);M(i)||Bt.methods(e,i)}),_(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)})}}),_(function(e,t,n){e.projection=function(e){return Qe.parse(t,e)}}),_(function(e,t,n){e.ready=function(e,n,i){t.ready(e,n,i)}}),_(function(e,t,n){e.refresh=function(e,n){return t.refresh(e,n)}}),_(function(e,t,n){e.reset=function(e,n){return t.reset(e,n)}}),_(function(e,t,n){e.results=function(e,n,i){return new rt(t,e,i,n,!0).$results}}),_(function(e,t,n){e.search=function(e,n,i,s){return new rt(t,e,n,i,s)}}),_(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}}),_(function(e,t,n){e.searchPaged=function(e,n,i,s){return new at(t,e,n,i,s)}}),_(function(e){var t=e.shard||mn.shard;y(t)&&(e.createRest=Se.shard(t))},!0),_(function(e,t,n){var i=J(n.staticMethods,mn.staticMethods);M(i)||Bt.props(e,i)}),_(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]=D(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)?Array.prototype.slice.call(arguments):e;return Xe.create(t)}function r(e){var t=arguments.length>1||!E(e)?Array.prototype.slice.call(arguments):e;return Xe.native(t)}function a(e,t,n){var i=e[t];e[t]=e[n],e[n]=i}function o(e){for(var t=e.length,n=Math.floor(t/2),i=0;i0)return!1;return!0}function h(e){for(var t=0;t0&&e[0]}function Ft(e){return e}function kt(e){var t=f(e)?e.indexOf(";base64,"):-1;return-1===t?e:e.substring(t+8)}function Ht(e,t){t.autoSave&&e.$isSaved()&&e.$save()}function Ut(e,t,n,i,s){e.$files=e.$files||{},e.$files[t]={value:n,user:n,file:i,options:s}}function xt(e,t,n,i,s){var r,a=!1;return e&&e.valueToUser?e.valueToUser(t,n,i,function(e){n.$files[i].user=e,a?(n[i]=e,Ht(n,s)):r=e}):r=t,a=!0,r}function Pt(e,t,n){var i=Se.fileProcessors[n.processor];return e in Qt.FileReader.prototype||Se.trigger(Se.Events.FilesNotSupported),function(s,r,a){var o=It(s);if(!1!==o){var u,h=new Qt.FileReader,l=!1;return h.onload=function(e){var s=t(e.target.result);Ut(r,a,s,o,n),u=xt(i,s,r,a,n),l&&(r[a]=u,Ht(r,n))},h[e](o),l=!0,u}if(y(s)&&s.FILE){var u,c=function(e){u=e};return Se.trigger(Se.Events.FileOffline,[s,r,a,c]),u}return Ut(r,a,s,null,n),xt(i,s,r,a,n)}}function wt(e,t,n,i){if(t.$files&&n in t.$files){var s=t.$files[n];if(i&&!1===s.save||!i&&!1===s.store)return;if(!i&&s.file){var r=Q(s.file,Se.fileProperties,!1);return r.FILE=!0,r}if(e===s.user)return i&&s.file&&t.$once(Ye.Events.RemoteSave,function(){delete s.file,t.$addOperation(gt,sn.Local)}),s.value}return e}function Kt(e){return function(t,n,i){var s=e.indices[n];if(v(s)){var r=e.listeners[n];delete e.indices[n],delete e.listeners[n],e.keys[s]=i,e.indices[i]=s,e.listeners[i]=r}}}function Vt(e,t){return Rn.apply(this,arguments),t instanceof Ye&&t.$db.keyChanges&&(this.listeners=this.listeners||{},this.listeners[e]=t.$on(Ye.Events.KeyChange,Kt(this))),this}function Yt(e){var t=this.indices[e];return v(t)&&(this.listeners&&(D(this.listeners[e]),delete this.listeners[e]),this.removeAt(t)),this}function Gt(){Bt.method(ze,"put",Vt),Bt.method(ze,"remove",Yt)}function zt(){Bt.method(ze,"put",Rn),Bt.method(ze,"remove",$n)}function jt(e,t,n){var i=oe(e,n);if(!1===i)return!1;if(!t)return i;switch(t){case bn.Date:return i;case bn.Millis:return i.getTime();case bn.Seconds:return Math.floor(i.getTime()/1e3);default:return Se.formatDate(i,t)}}var Qt="undefined"!=typeof window?window:e,qt=Array.prototype,Bt={create:function(e,t){Bt.prop(e,"create",Bt.factory(e)),Bt.build(e,t,$)},extend:function(e,t,n){var i=J(n,e.$methods),s=Bt.copyConstructor(e);t.prototype=new s;var r=Bt.factory(t);if(Bt.isArray(e)){var a=function(){var e=[];return Bt.props(e,i),t.apply(e,arguments),e};Bt.prop(t,"native",a),Bt.prop(t,"create",en.nativeArray?a:r)}else Bt.prop(t,"create",r);Bt.build(t,i,e)},dynamic:function(e,t,n,i){var s=new Function("return function "+n+i)();return s.prototype=t,Bt.build(s,{},e),s},build:function(e,t,n){Bt.prop(e,"$methods",t),Bt.prop(e,"$prop",Bt.propThis),Bt.prop(e,"$method",Bt.methodThis),Bt.prop(e,"$replace",Bt.replaceThis),Bt.prop(e.prototype,"$super",n),Bt.prop(e.prototype,"constructor",e),Bt.props(e.prototype,t)},isArray:function(e){return Array===e||e.prototype instanceof Array},method:function(e,t,n){e.$methods&&(e.$methods[t]=n),Bt.prop(e.prototype,t,n)},methodThis:function(e,t){Bt.method(this,e,t)},methods:function(e,t){for(var n in t)Bt.method(e,n,t[n])},prop:function(){return Object.defineProperty?function(e,t,n){Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:!0,value:n})}:function(e,t,n){e[t]=n}}(),propThis:function(e,t){Bt.prop(this.prototype,e,t)},props:function(e,t){for(var n in t)Bt.prop(e,n,t[n])},replace:function(e,t,n){var i=e.prototype[t]||e[t]||$;Bt.method(e,t,n(i))},replaceThis:function(e,t){Bt.replace(this,e,t)},copyConstructor:function(e){function t(){}return t.prototype=e.prototype,t},factory:function(e){function t(t){e.apply(this,t)}return t.prototype=e.prototype,function(){return new t(arguments)}}},Jt=Date.now||function(){return(new Date).getTime()},Wt={};P.Types={Persistent:1,Once:2,After:4},Bt.create(P,{remove:function(){var e=this.next,t=this.prev;e!==this&&(t.next=e,e.prev=t,this.next=this.prev=this)},hasType:function(e){return 0!=(this.type&e)},trigger:function(e,t,n){var i=(this.type,this.hasType(P.Types.After));this.group!==e&&((n&&i||!i)&&(this.group=e,this.callback.apply(this.context,t)),this.hasType(P.Types.Once)&&this.remove())}}),ne.REGEX=/([\w$]+)/g,re.REGEX=/[\{\}]/;var Xt={},Zt={},en=e.RekordSettings||Qt.RekordSettings||{};if(Qt.document&&Qt.document.currentScript){null!==Qt.document.currentScript.getAttribute("native-array")&&(en.nativeArray=!0)}fe.REGEX=/(^.|_.)/g;var tn={};Se.classes={},Se.autoload=!1,Se.unloaded=[],Se.loadPromise=null,Se.load=function(e,t){function n(e,t){if(a.push(e),r.push(t),r.length===s.length){for(var n=0;n0}:this.revisionFunction=function(e,t){return!1}},setComparator:function(e,t){this.models.setComparator(e,t)},addComparator:function(e,t){this.models.addComparator(e,t)},setSummarize:function(e){ +c(e)?this.summarize=e:f(e)?!1!==i(this.fields,e)?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))},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===nn.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(rn.All)&&(0===e.pendingOperations?e.refresh():e.firstRefresh=!0)},hasLoad:function(e){return 0!=(this.load&e)},loadBegin:function(e){function n(n,i){Se.debug(Se.Debugs.LOCAL_LOAD,s,n);for(var r=0;r=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){return Qe.parse(this.$db,e).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;a0;)o--;r<=o&&(a(i.values,r,o),a(i.keys,r,o),r++,o--)}return r}function n(e,i){var s=t(e,i);e0&&(n(0,s),this.rebuildIndex()),this},rebuildIndex:function(){this.indices={};for(var e=0,t=this.keys.length;e0&&(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))}},u=0;u=0;u--)f=h[u](f);this.projections[n]=f},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];if(o&&(o instanceof yt?n.unshift(o.model.Database):n.unshift(o)),!1===i(a.fields,r)&&!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--)!1===i(e,t[n])&&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.pageIndex=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)},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,!1,n);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!==un.Model&&this.save!==on.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:un.None,save:on.None,auto:!0,autoCascade:sn.All,autoOptions:null,property:!0,preserve:!0,clearKey:!0,dynamic:!1,local:null,cascade:sn.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),this.setInitial(e,t,n)}),setInitial:function(e,t,n){var i=e.$relations[this.name];M(t)&&(t=this.grabInitial(e,this.local))&&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,i))},sync:function(e,t){var n=e.$relations[this.name],i=this.grabInitial(e,this.local);n&&(M(i)?t&&this.clearRelated(n,!0,!0):this.grabModel(i,this.handleModel(n,!0,!0),!0))},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:un.None,save:on.None,saveCascade:sn.All,saveOptions:null,auto:!0,autoCascade:sn.All,autoOptions:null,property:!0,preserve:!0,clearKey:!0,dynamic:!1,local:null,cascade:sn.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),this.setInitial(e,t,n)}),setInitial:function(e,t,n){var i=e.$relations[this.name];M(t)&&(t=this.grabInitial(e,this.local))&&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,i))},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--){e[n].$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;t1||!E(e)?qt.slice.call(arguments):e;return tt.native(t,n)}}),_(function(e,t,n){e.at=function(e){return t.models[e]}}),_(function(e,t,n){e.boot=function(e){return E(e)?tt.create(t,e,!0):y(e)?t.putRemoteData(e):e}}),_(function(e,t,n){e.clear=function(e){return t.clear(e)}}),_(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)}}),_(function(e,t,n){e.count=function(e,n,i){return t.models.countWhere(e,n,i)}}),_(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}}),_(function(e,t,n){var i=J(n.dynamic,pn.dynamic);if(!M(i))for(var s in i)Tt(e.prototype,s,i[s])}),_(function(e,t,n){var i=J(n.events,pn.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)}})}}),_(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]);!1!==o&&r.splice(o,1),r.unshift(s[a])}}var o=n.extend||pn.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)}}),_(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(sn.Rest,n),a}}),_(function(e,t,n){e.fetchAll=function(e,n){return t.refresh(e,n),t.models}}),_(function(e,t,n){var i=n.files||pn.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]=yn[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 yn={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(!1!==r){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)&&!1===i(t.types,r.type))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}if(!y(e)||!e.FILE)return Ut(n,s,e,null,t),xt(a,e,n,s,t);Se.trigger(Se.Events.FileOffline,[e,n,s])}}};_(function(e,t,n){e.filtered=function(e,n,i){return t.models.filtered(e,n,i)}}),_(function(e,t,n){e.first=e.find=function(e,n,i){return t.models.firstWhere(e,n,i)}}),_(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}}),_(function(e,t,n){e.get=function(e,n,i){if(!c(n))return t.get(e);t.grabModel(e,n,i)}}),_(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}}),_(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}}),_(function(e,t,n){e.hasTrait=function(e,n){return t.hasTrait(e,n)},e.hasTraits=function(e,n){return t.hasTraits(e,n)}}),_(function(e,t,n){n.keyChanges&&Gt()});var Rn=ze.prototype.put,$n=ze.prototype.remove;_(function(e,t,n){var i=J(n.methods,pn.methods);M(i)||Bt.methods(e,i)}),_(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)})}}),_(function(e,t,n){e.projection=function(e){return Qe.parse(t,e)}}),_(function(e,t,n){e.ready=function(e,n,i){t.ready(e,n,i)}}),_(function(e,t,n){e.refresh=function(e,n){return t.refresh(e,n)}}),_(function(e,t,n){e.reset=function(e,n){return t.reset(e,n)}}),_(function(e,t,n){e.results=function(e,n,i){return new rt(t,e,i,n,!0).$results}}),_(function(e,t,n){e.search=function(e,n,i,s){return new rt(t,e,n,i,s)}}),_(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}}),_(function(e,t,n){e.searchPaged=function(e,n,i,s){return new at(t,e,n,i,s)}}),_(function(e){var t=e.shard||pn.shard;y(t)&&(e.createRest=Se.shard(t))},!0),_(function(e,t,n){var i=J(n.staticMethods,pn.staticMethods);M(i)||Bt.props(e,i)}),_(function(e,t,n){function s(e,t){return y(e)&&y(t)?J(e,t):e||t}function r(e){return!0===$||!1!==i($,e)}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){return jt(e,a(n,p))||e}function h(e,t,n){return jt(e,a(n,m),a(n,R))||e}function l(e){!1===i(t.fields,e)&&(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]=D(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||pn.timestamps,p=s(n.timestampFormat,pn.timestampFormat),m=s(n.timestampType,pn.timestampType),R=s(n.timestampUTC,pn.timestampUTC),$=n.timestampCurrent||pn.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 // Load by priority defined in Database\n loading.sort(function(a, b)\n {\n return b.priority - a.priority;\n });\n\n // Begin the loading procedure for every unloaded Database\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 priority: 0,\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 existing = db.all[ key ];\n var model = existing || db.instantiate( decoded, true );\n\n if (existing)\n {\n model.$set( decoded, undefined, true );\n }\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 this.$initRelations( remoteData );\n },\n\n $initRelations: function(remoteData)\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 hasDiscriminator: false,\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, skipInitial)\n {\n\n }),\n\n setInitial: 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, relation)\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, relation)\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 hasDiscriminator: true,\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, relation );\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, relation );\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, remoteData, relation );\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, false, relation );\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, relation );\n\n if ( related )\n {\n given.add( related );\n }\n }\n }\n else\n {\n var related = this.parseModel( input, remoteData, relation );\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, relation );\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, relation );\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 ], remoteData, relation );\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, remoteData, relation );\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 ], false, relation );\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, false, relation );\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 this.setInitial( model, initialValue, remoteData );\n }),\n\n setInitial: function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ];\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, relation );\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 this.setInitial( model, initialValue, remoteData );\n }),\n\n setInitial: function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ];\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, relation );\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, relation );\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, relation ) )\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 this.setInitial( model, initialValue, remoteData );\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n setInitial: function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ];\n\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\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, relation ) ) )\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, relation ) )\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 this.setInitial( model, initialValue, remoteData );\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n setInitial: function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ];\n var throughDatabase = this.through.Database;\n\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\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, relation ) ) )\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, relation ) ) )\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, relation ) )\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, relation ) ) )\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 this.setInitial( model, initialValue, remoteData );\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n setInitial: function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ];\n\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\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 this.setInitial( model, initialValue, remoteData );\n }),\n\n setInitial: function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ];\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, relation );\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 discriminatorField = this.discriminator;\n\n if (this.hasDiscriminator)\n {\n return function(related)\n {\n if ( !isRelated( related ) )\n {\n return false;\n }\n\n var discriminator = this.getDiscriminatorForModel( related );\n\n return equals( discriminator, model[ discriminatorField ] );\n };\n }\n else\n {\n var discriminator = this.getDiscriminatorForModel( model );\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\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, relation)\n {\n if ( input instanceof Model )\n {\n return input;\n }\n else if ( isObject( input ) )\n {\n var db = this.hasDiscriminator ?\n this.getDiscriminatorDatabase( relation.parent ) :\n 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 if ( this.hasDiscriminator && hasFields( model, fields, isValue ) )\n {\n var related = this.getDiscriminatorDatabase( model );\n\n if ( related )\n {\n var initial = {};\n\n updateFieldsReturnChanges( initial, related.key, model, fields );\n\n return initial;\n }\n }\n },\n\n grabModel: function(input, callback, remoteData, relation)\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.hasDiscriminator ?\n this.getDiscriminatorDatabase( relation.parent ) :\n 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 +{"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","native","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","compare","parsedChain","equalsWeak","equalsCompare","equals","at","bt","ar","br","test","aa","ap","bp","compareNumbers","av","bv","addEventFunction","target","functionName","events","secret","off","eventFunction","listener","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","result","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","getAttribute","loadPromise","load","onLoadFinish","loadedSuccess","loading","sort","priority","promises","export","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","buildObjectFromKey","RemoteGets","$set","$refresh","hasRemote","buildKeyFromRelations","putRemoteData","instantiate","updated","revisionFunction","addReference","getKey","decoded","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","$initRelations","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","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","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","projection","alias","aliasIndex","ALIAS_DELIMITER","word","words","tokens","types","resolvers","processWord","token","TOKEN_HANDLER","unshift","post","TOKENS","pre",".","?","|","#","(",")","[","]","{",":","}","@","=","sourceType","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","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","hasDiscriminator","getDefaults","discriminated","Polymorphic","setReferences","onInitialized","setModelReference","rekord","finishInitialization","skipInitial","setInitial","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","removeThrough","finishRemoveRelated","keyObject","finishRemoveThrough","modelKeys","relatedKeys","onRefresh","hasList","hasReference","isRelatedDiscriminatedFactory","loadDiscriminators","discriminatorField","getDiscriminatorForModel","onLoad","handleLoaded","setDiscriminated","getDiscriminatorDatabase","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":"CAEC,SAAUA,EAAMC,GAEO,kBAAXC,SAAyBA,OAAOC,IAGzCD,OAAO,YAAc,WACnB,MAAOD,GAAQD,KAGQ,gBAAXI,SAAuBA,OAAOC,QAK5CD,OAAOC,QAAUJ,EAAQK,QAKzBN,EAAKO,OAASN,EAAQD,IAExBQ,KAAM,SAASF,EAAQG,GA6BzB,QAASC,GAAQC,EAAGC,GAElB,MAAKD,aAAaE,OAETF,EAEJG,EAAUH,GAENA,EAAEI,MAAOH,GAEbI,EAASL,IAEHA,MA+Bb,QAASM,GAAQC,EAAKP,EAAGQ,GAIvB,IAAK,GAFDC,GAAMD,GAAcE,EAEfC,EAAI,EAAGC,EAAIL,EAAIM,OAAQF,EAAIC,EAAGD,IAErC,GAAKF,EAAKF,EAAII,GAAIX,GAEhB,MAAOW,EAIX,QAAO,EAqBT,QAASG,GAAQC,GAEf,GAAIC,GAASC,UAAUJ,OAAS,IAAMK,EAAQH,GAAKb,MAAMiB,UAAUC,MAAMC,KAAMJ,WAAcF,CAE7F,OAAOO,IAAWC,OAAQP,GAqB5B,QAASQ,GAAaT,GAEpB,GAAIC,GAASC,UAAUJ,OAAS,IAAMK,EAAQH,GAAKb,MAAMiB,UAAUC,MAAMC,KAAMJ,WAAcF,CAE7F,OAAOO,IAAWG,OAAQT,GAG5B,QAASU,GAAKX,EAAGJ,EAAGgB,GAElB,GAAIC,GAAIb,EAAGJ,EACXI,GAAGJ,GAAMI,EAAGY,GACZZ,EAAGY,GAAMC,EAGX,QAASC,GAAQtB,GAKf,IAAK,GAHDK,GAAIL,EAAIM,OACRiB,EAAOC,KAAKC,MAAOpB,EAAI,GAElBD,EAAI,EAAGA,EAAImB,EAAMnB,IAExBe,EAAMnB,EAAKK,EAAID,EAAI,EAAGA,EAGxB,OAAOJ,GAGT,QAAS0B,GAASzB,EAAY0B,GAE5B,IAAM1B,EAEJ,OAAO,CAGT,KAAK,GAAIG,GAAI,EAAGC,EAAIsB,EAAMrB,OAAS,EAAGF,EAAIC,EAAGD,IAE3C,GAAKH,EAAY0B,EAAOvB,GAAKuB,EAAOvB,EAAI,IAAQ,EAE9C,OAAO,CAIX,QAAO,EAGT,QAASwB,GAAiBD,GAExB,IAAK,GAAIvB,GAAI,EAAGA,EAAIuB,EAAMrB,OAAQF,IAClC,CACE,GAAIyB,GAAOF,EAAMvB,EAEjB,IAAKN,EAAS+B,GAEZ,OAAQC,EAAUD,GAItB,OAAO,EA6MT,QAASE,GAAUtC,GAEjB,MAAOA,KAAMF,EAkBf,QAASyC,GAAWvC,GAElB,SAAUA,GAAKA,EAAEwC,aAAexC,EAAEqB,MAAQrB,EAAEyC,OAsB9C,QAASC,GAAS1C,GAEhB,SAAUA,GAAKA,EAAE2C,UAAYJ,EAAYvC,IAAOA,EAAEmB,oBAAqByB,KAkBzE,QAASzC,GAASH,GAEhB,MAAoB,gBAANA,GAqBhB,QAAS6C,GAAS7C,GAEhB,MAAoB,gBAANA,KAAmB8C,MAAM9C,GAmBzC,QAAS+C,GAAU/C,GAEjB,MAAoB,iBAANA,GAoBhB,QAASgD,GAAOhD,GAEd,MAAOA,aAAaiD,MAoBtB,QAASC,GAASlD,GAEhB,MAAOA,aAAamD,QAoBtB,QAASjC,GAAQlB,GAEf,MAAOA,aAAaE,OAsBtB,QAASmC,GAASrC,GAEhB,MAAa,QAANA,GAA2B,gBAANA,GAuB9B,QAASK,GAAQL,GAEf,QAAUA,IAAMF,GAAmB,OAANE,GAQ/B,QAASoD,MA2BT,QAASC,GAAKC,EAASC,GAErB,MAAO,YAEL,MAAOA,GAAKd,MAAOa,EAASrC,YAWhC,QAASuC,KAEP,MAAQC,KAAKA,IAAK,IAAIA,IAAK,IAAIA,IAAK,IAAIA,IAAK,IAAIA,IAAKA,IAAKA,IAG7D,QAASA,KAEP,OAA2B,OAAjB,EAAE1B,KAAK2B,UAAmB,GAAGC,SAAS,IAAIC,UAAU,GAQhE,QAASC,GAAO7D,GAEd,GAAKkB,EAAQlB,IAAMG,EAASH,GAE1B,MAAOA,GAAEa,MAEN,IAAKwB,EAASrC,GACnB,CACE,GAAI8D,GAAa,CAEjB,KAAK,GAAIC,KAAQ/D,GAEf8D,GAGF,OAAOA,GAEJ,MAAKjB,GAAU7C,GAEXA,EAGF,EAGT,QAASgE,GAAQhE,GAEf,GAAU,OAANA,GAAcA,IAAMF,GAAmB,IAANE,EAEnC,OAAO,CAET,IAAIkB,EAAQlB,IAAMG,EAASH,GAEzB,MAAoB,KAAbA,EAAEa,MAEX,IAAImC,EAAOhD,GAET,MAAuB,KAAhBA,EAAEiE,WAAmBnB,MAAO9C,EAAEiE,UAEvC,IAAI5B,EAASrC,GACb,CACE,IAAK,GAAI+D,KAAQ/D,GAEf,OAAO,CAGT,QAAO,EAGT,OAAO,EAGT,QAASkE,GAASlE,EAAGmE,EAAWb,GAE9B,MAAMjD,GAASL,GAKV0C,EAAU1C,GAEN,GAAIA,GAERuC,EAAYvC,GAERsD,EAAUtD,EAAEyC,MAAOa,GAAYtD,IAGjCmE,EAAYnE,EAAIoE,EAAMpE,GAZpBA,EAeX,QAASqE,GAAWC,EAAUC,GAE5B,MAAKA,GAEI3E,GAAO4E,GAAI5E,GAAO6E,OAAOC,QAASJ,GAIlC1E,GAAO4E,GAAI5E,GAAO6E,OAAOE,QAASL,GAuB7C,QAASM,GAAeC,EAAMC,EAAiBC,GAE7C,GAAIvE,GAAawE,EAAkBF,EAAiBC,EAIpD,OAFAE,IAAaJ,GAASrE,EAEfA,EAGT,QAAS0E,GAAcC,EAAQL,EAAiBC,GAE9C,GAAIK,GAAQJ,EAAkBF,EAAiBC,EAE/C,OAAMxC,GAAY4C,GAKX,SAA0BpE,EAAGsE,GAElC,GAAIC,GAAIF,EAAOrE,EAAGsE,EAElB,OAAa,KAANC,EAAUA,EAAIH,EAAQpE,EAAGsE,IAPzBD,EAqBX,QAASJ,GAAiBxE,EAAYuE,GAEpC,GAAKxC,EAAY/B,GAEf,MAAOA,EAEJ,IAAKL,EAAUK,GACpB,CACE,GAAKA,IAAcyE,IAEjB,MAAOA,IAAazE,EAGtB,IAA8B,MAAzBA,EAAW+E,OAAO,GACvB,CACE,GAAIC,GAASR,EAAkBxE,EAAWoD,UAAW,IAAMmB,EAE3D,OAAO,UAAgChE,EAAGsE,GAExC,OAAQG,EAAQzE,EAAGsE,IAGlB,GAAKI,GAAejF,GACzB,CACE,GAAIkF,GAAYC,GAAiBnF,EAEjC,OAAO,UAA0BO,EAAGsE,GAElC,GAAIO,GAAKF,EAAW3E,GAChB8E,EAAKH,EAAWL,EAEpB,OAAOO,GAAGE,cAAeD,IAGxB,GAAKE,GAAcvF,GACxB,CACE,GAAIwF,GAASC,GAAczF,EAE3B,OAAO,UAA2BO,EAAGsE,GAKnC,MAAOa,GAHEF,EAAQjF,GACRiF,EAAQX,GAEON,IAK1B,MAAO,UAAwBhE,EAAGsE,GAKhC,MAAOa,GAHE7F,EAASU,GAAMA,EAAGP,GAAeO,EACjCV,EAASgF,GAAMA,EAAG7E,GAAe6E,EAElBN,IAIzB,GAAK7D,EAASV,GACnB,CAGE,IAAK,GAFD2F,MAEKxF,EAAI,EAAGA,EAAIH,EAAWK,OAAQF,IAErCwF,EAAaxF,GAAMqE,EAAkBxE,EAAYG,GAAKoE,EAGxD,OAAO,UAA+BhE,EAAGsE,GAIvC,IAAK,GAFDC,GAAI,EAEC3E,EAAI,EAAGA,EAAIwF,EAAYtF,QAAgB,IAANyE,EAAS3E,IAEjD2E,EAAIa,EAAaxF,GAAKI,EAAGsE,EAG3B,OAAOC,IAIX,MAAO,MA2CT,QAAS5E,GAAaK,EAAGsE,GAEvB,MAAOtE,KAAMsE,EAGf,QAASe,GAAWrF,EAAGsE,GAErB,MAAOtE,IAAKsE,EAGd,QAASgB,GAActF,EAAGsE,GAExB,MAA2B,KAApBa,EAASnF,EAAGsE,GAGrB,QAASiB,GAAOvF,EAAGsE,GAEjB,GAAItE,IAAMsE,EAER,OAAO,CAET,IAAU,OAANtE,GAAoB,OAANsE,EAEhB,OAAO,CAET,IAAItE,IAAMA,GAAKsE,IAAMA,EAEnB,OAAO,CAGT,IAAIkB,SAAYxF,GACZyF,QAAYnB,GACZoB,EAAKvD,EAASnC,GACd2F,EAAKxD,EAASmC,EAElB,IAAW,WAAPkB,GAAmBG,EAErB,MAAOrB,GAAEsB,KAAK5F,EAEhB,IAAW,WAAPyF,GAAmBC,EAErB,MAAO1F,GAAE4F,KAAKtB,EAGhB,IAAIkB,IAAOC,EAET,OAAO,CAGT,IAAII,GAAK1F,EAAQH,EAEjB,IAAI6F,IADK1F,EAAQmE,GAGf,OAAO,CAGT,IAAIuB,EACJ,CACE,GAAI7F,EAAEF,SAAWwE,EAAExE,OAEjB,OAAO,CAGT,KAAK,GAAIF,GAAI,EAAGA,EAAII,EAAEF,OAAQF,IAE5B,IAAK2F,EAAOvF,EAAEJ,GAAI0E,EAAE1E,IAElB,OAAO,CAIX,QAAO,EAGT,GAAIqC,EAAOjC,GAET,MAAOiC,GAAOqC,IAAMiB,EAAQvF,EAAEkD,UAAWoB,EAAEpB,UAE7C,IAAIwC,EAEF,MAAOC,IAAM3F,EAAE4C,aAAe0B,EAAE1B,UAGlC,IAAW,WAAP4C,EACJ,CACE,IAAK,GAAIM,KAAM9F,GAEb,KAAqB,MAAjB8F,EAAGtB,OAAO,IAAehD,EAAWxB,EAAE8F,KAElCA,IAAMxB,IAAOiB,EAAOvF,EAAE8F,GAAKxB,EAAEwB,KAEjC,OAAO,CAKb,KAAK,GAAIC,KAAMzB,GAEb,KAAqB,MAAjByB,EAAGvB,OAAO,IAAehD,EAAW8C,EAAEyB,KAElCA,IAAM/F,IAEV,OAAO,CAKb,QAAO,EAGT,OAAO,EAGT,QAASgG,GAAehG,EAAGsE,GAEzB,MAAQtE,KAAMsE,EAAI,EAAKtE,EAAIsE,GAAK,EAAI,EAGtC,QAASa,GAAQnF,EAAGsE,EAAGN,GAErB,GAAIhE,GAAKsE,EAEP,MAAO,EAGT,IAAI2B,GAAK3G,EAASU,GACdkG,EAAK5G,EAASgF,EAElB,OAAI2B,KAAOC,EAEDD,IAAOjC,GAAgBkC,GAAMlC,GAAe,EAAI,GAGtD/B,EAAOjC,KAETA,EAAIA,EAAEkD,WAEJjB,EAAOqC,KAETA,EAAIA,EAAEpB,WAEJpB,EAAS9B,IAAM8B,EAASwC,GAEnB0B,EAAehG,EAAGsE,GAEvBnE,EAAQH,IAAMG,EAAQmE,GAEjB0B,EAAehG,EAAEF,OAAQwE,EAAExE,QAEhCkC,EAAUhC,IAAMgC,EAAUsC,GAEpBtE,GAAK,EAAI,GAGXA,EAAI,IAAI+E,cAAcT,EAAI,KAIpC,QAAS6B,GAAiBC,EAAQC,EAAcC,EAAQC,GAEtD,GAAI9C,GAAK8C,EAAS,MAAQ,KACtBC,EAAMD,EAAS,OAAS,MAExBE,EAAgB,SAASlD,EAAUhB,GAKrC,QAASmE,MAIS,IAFHnD,EAAS7B,MAAOa,GAAWoE,EAASzG,YAI/C0G,IAIJ,QAASA,KAEDC,IAEJF,EAASH,GAAOF,EAAQI,GACxBG,GAAa,GAlBjB,GAAIF,GAAU7H,KACV+H,GAAa,CAuBjB,OAFAF,GAASlD,GAAM6C,EAAQI,GAEhBE,EAGLR,GAAOU,SAETC,GAAMC,OAAQZ,EAAQC,EAAcI,GAIpCM,GAAM/D,KAAMoD,EAAQC,EAAcI,GAItC,QAASQ,GAAUC,EAAQ3D,EAAUhB,EAAS4E,EAAMC,GAElDtI,KAAKuI,KAAOH,GAAkBpI,KAC9BA,KAAKwI,KAAOJ,EAASA,EAAOI,KAAOxI,KAE9BoI,IAEHA,EAAOI,KAAKD,KAAOvI,KACnBoI,EAAOI,KAAOxI,MAGhBA,KAAKyE,SAAWA,EAChBzE,KAAKyD,QAAUA,EACfzD,KAAKqI,KAAOA,EACZrI,KAAKsI,MAAQA,GAAS,EA+ExB,QAASG,GAAYnB,EAAQG,GAwB3B,QAASiB,GAAYC,EAAOC,EAAanE,EAAUhB,EAAS4E,GAE1D,IAAM3F,EAAY+B,GAEhB,MAAOlB,EAGT,IAAIsF,GAAkBpF,GAAWkF,EAC7BnB,EAAStH,EAAS0I,EAAa,KAC/BE,EAAYH,EAAMI,IAEhBD,IAEJb,GAAM/D,KAAMyE,EAAO,OAAQG,KAK7B,KAAK,GAFDE,MAEKlI,EAAI,EAAGA,EAAI0G,EAAOxG,OAAQF,IACnC,CACE,GAAImI,GAAYzB,EAAQ1G,GACpBoI,EAAiBJ,EAAWG,EAE1BC,KAEJA,EAAiBJ,EAAWG,GAAc,GAAId,IAGhDa,EAAMG,KAAM,GAAIhB,GAAWe,EAAgBzE,EAAUoE,EAAiBR,EAAMe,IAG9E,MAAO,YAEL,IAAK,GAAItI,GAAI,EAAGA,EAAIkI,EAAMhI,OAAQF,IAEhCkI,EAAOlI,GAAIuI,QAGbL,GAAMhI,OAAS,GAsCnB,QAAS2D,GAAG6C,EAAQ/C,EAAUhB,GAE5B,MAAOiF,GAAa1I,KAAMwH,EAAQ/C,EAAUhB,EAAS0E,EAAUmB,MAAMC,YAqCvE,QAASC,GAAKhC,EAAQ/C,EAAUhB,GAE9B,MAAOiF,GAAa1I,KAAMwH,EAAQ/C,EAAUhB,EAAS0E,EAAUmB,MAAMG,MAGvE,QAASC,GAAMlC,EAAQ/C,EAAUhB,GAE/B,MAAOiF,GAAa1I,KAAMwH,EAAQ/C,EAAUhB,EAAS0E,EAAUmB,MAAMK,OAIvE,QAASC,GAAad,EAAWe,EAAOpF,GAEtC,GAAIqE,GAAae,IAASf,GAKxB,IAHA,GACIP,GADAW,EAAiBJ,EAAWe,GACtBC,EAAOZ,EAAeX,KAEzBuB,IAASZ,GAEdX,EAAOuB,EAAKvB,KAERuB,EAAKrF,WAAaA,GAEpBqF,EAAKT,SAGPS,EAAOvB,EAMb,QAASwB,GAAeC,EAAK9F,GAEtB8F,GAAO9F,IAAQ8F,UAEXA,GAAK9F,GAoBhB,QAASwD,GAAIkB,EAAanE,GAGxB,GAAMhC,EAAWmG,GAKjB,CACE,GAAIpB,GAAStH,EAAS0I,EAAa,IAGnC,IAAMlG,EAAY+B,GAUhB,IAAK,GAAI3D,GAAI,EAAGA,EAAI0G,EAAOxG,OAAQF,IAEjC8I,EAAc5J,KAAK+I,KAAMvB,EAAO1G,GAAI2D,OAVtC,KAAK,GAAI3D,GAAI,EAAGA,EAAI0G,EAAOxG,OAAQF,IAEjCiJ,EAAgB/J,KAAK+I,KAAMvB,EAAO1G,QAXtCiJ,GAAgB/J,KAAM,OAwBxB,OAAOA,MAIT,QAASiK,GAAiBnB,EAAWe,EAAOK,GAE1C,GAAIpB,GAAae,IAASf,GAC1B,CAKE,IAJA,GAEIP,GAFAW,EAAiBJ,EAAWe,GAC5BM,IAAiBf,EACXU,EAAOZ,EAAeX,KAEzBuB,IAASZ,GAEdX,EAAOuB,EAAKvB,KACZuB,EAAKM,QAASD,EAAcD,GAAM,GAClCJ,EAAOvB,CAKT,KAFAuB,EAAOZ,EAAeX,KAEfuB,IAASZ,GAEdX,EAAOuB,EAAKvB,KACZuB,EAAKM,QAASD,EAAcD,GAAM,GAClCJ,EAAOvB,GAcb,QAAS6B,GAAQxB,EAAasB,GAE5B,IAIE,IAAK,GAFD1C,GAAStH,EAAS0I,EAAa,KAE1B9H,EAAI,EAAGA,EAAI0G,EAAOxG,OAAQF,IAEjCmJ,EAAkBjK,KAAK+I,KAAMvB,EAAQ1G,GAAKoJ,GAG9C,MAAOG,GAELtK,GAAOqK,QAASrK,GAAO6E,OAAO0F,OAAQD,IAGxC,MAAOrK,MArRT,GAAIoJ,GAAY,EAwRZmB,EAAU,IAIZA,GAFG9C,GAGD+C,IAAK7F,EACL8F,MAAOjB,EACPkB,OAAQhB,EACRiB,KAAMjD,EACNkD,SAAUR,IAMVzF,GAAIA,EACJ6E,KAAMA,EACNE,MAAOA,EACPhC,IAAKA,EACL0C,QAASA,GAIR9C,EAAOU,SAEVC,GAAMsC,QAASjD,EAAQiD,GAIvBtC,GAAM4C,MAAOvD,EAAQiD,GAYzB,QAASO,GAAMC,EAAKC,EAAKC,GAEvB,GAAIzI,EAAUuI,IAASvI,EAAUwI,GAE/B,IAAK,GAAI9G,KAAQ8G,GAEf,IAAKC,IAAcA,EAAW/G,GAC9B,CACE,GAAIgH,GAASF,EAAK9G,EAElB,IAAIA,IAAQ6G,GACZ,CACE,GAAII,GAAWJ,EAAK7G,EAEhB7C,GAAS8J,GAEP9J,EAAS6J,GAEXC,EAAShC,KAAKvG,MAAOuI,EAAUD,GAI/BC,EAAShC,KAAM+B,GAGV1I,EAAU2I,GAEjBL,EAAOK,EAAUD,EAAQD,GAIzBF,EAAK7G,GAASK,EAAM2G,GAAQ,OAK9BH,GAAK7G,GAASK,EAAM2G,GAAQ,GAMpC,MAAOH,GAKT,QAASK,GAAc9D,EAAQ+D,EAASC,EAAU7D,GAEhD4D,EAAUA,KAEV,KAAK,GAAIE,KAAmBD,GAC5B,CACE,GAAIE,GAAeF,EAAUC,GACzBE,EAASJ,EAASE,GAClBG,EAASlL,EAASiL,EAEtB,KAAMC,GAAUF,IAAiBvL,EAE/B,KAAMsL,GAAkB,uBAIxBjE,GAAQiE,GAFAG,EAEoBD,EAIAlH,EAAMiH,GAItC,IAAK,GAAIG,KAAkBN,GAElBM,IAAkBL,KAEvBhE,EAAQqE,GAAmBN,EAASM,GAInClE,GAEHH,EAAOsE,SAAWP,EAIlB/D,EAAO+D,QAAUA,EAwBrB,QAASQ,GAAW/E,EAAMgF,EAAYC,EAAUC,EAAgBvF,GAE9D,GAAIwF,GAAWxF,GAAU1G,GAAO0G,MAEhC,IAAKnG,EAAUwL,GAEb,MAAOG,GAAUnF,EAAMgF,GAAcC,EAAUC,GAI/C,KAAK,GAAIlL,GAAI,EAAGA,EAAIgL,EAAW9K,OAAQF,IACvC,CACE,GAAIoL,GAAWJ,EAAYhL,GACvBqL,EAAeH,EAAgBlL,EAEnC,KAAMmL,EAAUnF,EAAMoF,GAAYH,EAAUI,IAE1C,OAAO,EAIX,OAAO,EAOX,QAASC,GAAUC,EAAOC,EAAQC,GAEhC,GAAKlL,EAASiL,GACd,CACE,IAAK,GAAIxL,GAAI,EAAGA,EAAIwL,EAAOtL,OAAQF,IAEjC,IAAMyL,EAAQF,EAAOC,EAAQxL,KAE3B,OAAO,CAIX,QAAO,EAIP,MAAOyL,GAAQF,EAAOC,IAI1B,QAASE,GAAyBlF,EAAQmF,GAExC,GAAIC,IAAU,CAEd,IAAKrL,EAASoL,GAEZ,IAAK,GAAI3L,GAAI,EAAGA,EAAI2L,EAAazL,OAAQF,IACzC,CACE,GAAI6L,GAAcF,EAAc3L,EAE3BwG,GAAQqF,KAEXrF,EAAQqF,GAAgB,KACxBD,GAAU,OAMTpF,GAAQmF,KAEXnF,EAAQmF,GAAiB,KACzBC,GAAU,EAId,OAAOA,GAGT,QAASE,GAA0BtF,EAAQmF,EAAcI,EAAQC,GAE/D,GAAIJ,IAAU,CAEd,IAAKrL,EAASoL,GAEZ,IAAK,GAAI3L,GAAI,EAAGA,EAAI2L,EAAazL,OAAQF,IACzC,CACE,GAAI6L,GAAcF,EAAc3L,GAC5BiM,EAAczF,EAAQqF,GACtBK,EAAcF,EAAchM,GAC5BmM,EAAcJ,EAAQG,EAEpBvG,GAAQsG,EAAaE,KAEzB3F,EAAQqF,GAAgBpI,EAAM0I,GAC9BP,GAAU,OAKhB,CACE,GAAIK,GAAczF,EAAQmF,GACtBQ,EAAcJ,EAAQC,EAEpBrG,GAAQsG,EAAaE,KAEzB3F,EAAQmF,GAAiBlI,EAAM0I,GAC/BP,GAAU,GAId,MAAOA,GAIT,QAASQ,GAAKlD,EAAKa,EAAOsC,GAIxB,IAAK,GAFDC,MAEKtM,EAAI,EAAGA,EAAI+J,EAAM7J,OAAQF,IAClC,CACE,GAAIuM,GAAIxC,EAAO/J,EAEVuM,KAAKrD,KAERoD,EAASC,GAAMF,EAAa5I,EAAMyF,EAAKqD,IAAQrD,EAAKqD,IAIxD,MAAOD,GAGT,QAASE,GAAKtD,EAAKa,EAAOsC,GAExB,GAAK7M,EAAUuK,GACf,CACE,GAAI0C,GAAcvD,EAAKa,EAEvB,OAAOsC,GAAa5I,EAAMgJ,GAAgBA,EAM1C,IAAK,GAFDC,MAEK1M,EAAI,EAAGA,EAAI+J,EAAM7J,OAAQF,IAClC,CACE,GAAIuM,GAAIxC,EAAO/J,GACXyM,EAAcvD,EAAKqD,EAEvBG,GAAOrE,KAAMgE,EAAa5I,EAAMgJ,GAAgBA,GAGlD,MAAOC,GAIX,QAASC,GAASC,EAAMC,GAEtB,IAAK,GAAIzJ,KAAQwJ,GAEfC,EAAIzJ,GAASwJ,EAAMxJ,EAGrB,OAAOyJ,GAGT,QAASC,KAIP,IAAK,GAFDtG,MAEKxG,EAAI,EAAGA,EAAIM,UAAUJ,OAAQF,IACtC,CACE,GAAII,GAAIE,UAAWN,EAEnB,IAAK0B,EAAUtB,GAEb,IAAK,GAAIgD,KAAQhD,GAERgD,IAAQoD,KAEbA,EAAQpD,GAAShD,EAAGgD,IAM5B,MAAOoD,GAGT,QAASuG,GAAM1N,GAEb,IAAK,GAAI+D,KAAQ/D,GAES,MAAnB+D,EAAKwB,OAAO,UAERvF,GAAG+D,EAId,OAAO/D,GAGT,QAAS2N,GAAe3N,GAEtB,IAAK,GAAI+D,KAAQ/D,GAEVuC,EAAYvC,EAAE+D,WAEV/D,GAAG+D,EAId,OAAO/D,GAGT,QAASoE,GAAKpE,EAAG4N,GAEf,GAAU,OAAN5N,GAAcA,IAAMF,GAA0B,gBAANE,IAAkBuC,EAAWvC,IAAMkD,EAASlD,GAEtF,MAAOA,EAGT,IAAIkB,EAAQlB,GACZ,CAGE,IAAK,GAFD6N,MAEKlN,EAAI,EAAGA,EAAIX,EAAEa,OAAQF,IAE5BkN,EAAE7E,KAAM5E,EAAKpE,EAAEW,GAAIiN,GAGrB,OAAOC,GAGT,GAAI7K,EAAOhD,GAET,MAAO,IAAIiD,MAAMjD,EAAEiE,UAGrB,IAAI4J,KAEJ,KAAK,GAAI9J,KAAQ/D,IAEX4N,GAAiC,MAAnB7J,EAAKwB,OAAO,MAE5BsI,EAAG9J,GAASK,EAAMpE,EAAE+D,GAAO6J,GAI/B,OAAOC,GAGT,QAASC,IAAKC,EAAMC,EAAKtD,EAAOlK,GAI9B,IAAK,GAFD8E,MAEK3E,EAAI,EAAGA,EAAI+J,EAAM7J,OAAQF,IAClC,CACE,GAAIuM,GAAIxC,EAAO/J,EAEVH,GAAYuN,EAAMb,GAAKc,EAAKd,MAE/B5H,EAAG4H,GAAM9I,EAAM2J,EAAMb,KAIzB,MAAO5H,GAIT,QAASS,IAAa/F,GAEpB,OAA2B,IAApBA,EAAEM,QAAQ,OAAmC,IAApBN,EAAEM,QAAQ,OAAmC,IAApBN,EAAEM,QAAQ,KAGrE,QAAS2N,IAAMC,EAAMC,GAEnB,MAAOlI,IAAciI,GAAQC,GAK/B,QAASlI,IAAaiI,GAMpB,IAJA,GAAIE,GAAQH,GAAMI,MACdxF,KACAyF,EAAQ,KAE4B,QAAhCA,EAAQF,EAAMG,KAAML,KAE1BrF,EAAMG,KAAMsF,EAAO,GAGrB,OAAO,UAASH,GAEd,IAAK,GAAIxN,GAAI,EAAGA,EAAIkI,EAAMhI,QAAUsN,IAASrO,EAAWa,IACxD,CACE,GAAIC,GAAIiI,EAAOlI,EAEV0B,GAAU8L,KAEbA,EAAOjK,EAAUiK,EAAMvN,IAAK,EAAMuN,IAItC,MAAOA,IAIX,QAAS1I,IAAczF,GAErB,OAA2B,IAApBA,EAAEM,QAAQ,KAGnB,QAASkO,IAAOC,EAAUN,GAExB,MAAOxI,IAAiB8I,GAAYN,GAKtC,QAASxI,IAAgB8I,GAKvB,IAAK,GAFDC,GAAQD,EAASrO,MAAOoO,GAAOH,OAE1B1N,EAAI,EAAGA,EAAI+N,EAAM7N,OAAQF,GAAK,EAErC+N,EAAO/N,GAAMsF,GAAcyI,EAAO/N,GAGpC,OAAO,UAAmBwN,GAIxB,IAAK,GAFDQ,GAAY,GAEPhO,EAAI,EAAGA,EAAI+N,EAAM7N,OAAQF,IAEhC,GAAiB,IAAP,EAAJA,GAEJgO,GAAaD,EAAO/N,OAGtB,CACE,GAAI6E,GAASkJ,EAAO/N,GAAKwN,EAEzBQ,IAAatO,EAASmF,GAAWA,EAAS,GAI9C,MAAOmJ,IAIX,QAASC,IAAU5O,EAAG6O,GAkBpB,MAhBK1O,GAAUH,KAERiD,KAAKgL,QAERjO,EAAIiD,KAAKgL,MAAOjO,IAGZ6C,EAAU7C,KAEdA,EAAI,GAAIiD,MAAMjD,KAGb6C,EAAU7C,KAEbA,EAAI,GAAIiD,MAAMjD,OAEXgD,EAAQhD,KAAO6C,EAAU7C,EAAEiE,cAEzB4K,IAEH7O,EAAI,GAAIiD,MAAMjD,EAAE8O,iBAAkB9O,EAAE+O,cAAe/O,EAAEgP,aAAchP,EAAEiP,cAAejP,EAAEkP,gBAAiBlP,EAAEmP,kBAGpGnP,GAuCX,QAASoP,IAAmBvK,EAAMwK,EAASC,GAEzC,GAAIC,GAAWC,GAAsBH,EAASC,EAI9C,OAFAG,IAAiB5K,GAAS0K,EAEnBA,EAGT,QAASC,IAAqBH,EAASC,GAErC,GAAIC,GAAWG,GAAwBL,EAEvC,OAAKlP,GAAUkP,IAAaA,IAAWI,IAE9BA,GAAiBJ,GAGnB,SAAuBnD,GAE5B,GAAI1G,GAASmK,WAAYJ,EAAUrD,GAEnC,OAAOpJ,OAAO0C,GAAW8J,EAAe9J,GAM5C,QAASoK,IAAqB/K,EAAMf,GAElC,GAAIyL,GAAWG,GAAwB5L,EAIvC,OAFA+L,IAAmBhL,GAAS0K,EAErBA,EAuBT,QAASG,IAAuB5L,GAE9B,GAAKvB,EAAYuB,GAEf,MAAOA,EAEJ,IAAK3D,EAAU2D,GAElB,MAAKA,KAAc+L,IAEVA,GAAmB/L,GAGvB2B,GAAe3B,GAEX6B,GAAiB7B,GAEhBiC,GAAcjC,GAEfmC,GAAcnC,GAId,SAAyBoI,GAE9B,MAAOA,GAAQA,EAAOpI,GAAehE,EAItC,IAAKoB,EAAS4C,GAEjB,MAAO,UAA2BoI,GAEhC,MAAOiB,GAAMjB,EAAOpI,GAGnB,IAAKzB,EAAUyB,GACpB,CACE,GAAIgM,MACAC,IAEJ,KAAK,GAAIhM,KAAQD,GAEfgM,EAAW9G,KAAMjF,GACjBgM,EAAc/G,KAAM0G,GAAwB5L,EAAYC,IAG1D,OAAO,UAA+BmI,GAIpC,IAAK,GAFD8D,MAEKrP,EAAI,EAAGA,EAAImP,EAAWjP,OAAQF,IACvC,CACE,GAAIoD,GAAO+L,EAAYnP,EAEvBqP,GAAUjM,GAASgM,EAAepP,GAAKuL,EAAOnI,IAGhD,MAAOiM,IAKT,MAAO,UAAqB9D,GAE1B,MAAOA,IAmBb,QAAS+D,IAAkB3B,GAEzB,MAAwB,KAAjBA,EAAMzN,OAAeyN,EAAM4B,cAAgB5B,EAAM/I,OAAO,GAAG2K,cAGpE,QAASC,IAAYtL,GAEnB,MAAOA,GAAKuL,QAASD,GAAY9B,MAAO4B,IAK1C,QAAS7P,IAAMJ,EAAGC,EAAWoQ,GAO3B,IALA,GAAIC,GAAiBpN,EAAUjD,GAAcA,EAAY,GAAIkD,QAAQ,IAAMlD,EAAY,KACnFsQ,EAASvQ,EAAEI,MAAOkQ,GAClB3P,EAAI,EACJC,EAAI2P,EAAO1P,OAAS,EAEjBF,EAAIC,GACX,CACE,GAAIG,GAAIwP,EAAQ5P,GACZ6P,EAAKzP,EAAEF,OAASwP,EAAOxP,MAE3B,IAAKE,EAAE6C,UAAW4M,KAASH,EAC3B,CACE,GAAIhL,GAAIkL,EAAQ5P,EAAI,GAChBkN,EAAI0C,EAAQ5P,EAAI,GAChB8P,EAAS1P,EAAE6C,UAAW,EAAG4M,GAAOnL,EAAIwI,CAExC0C,GAAOG,OAAQ/P,EAAG,EAAG8P,GACrB7P,GAAK,MAILD,IAAK,EACL4P,EAAOG,OAAQ/P,EAAG,GAClBC,GAAK,EAIT,MAAO2P,GAwET,QAASI,IAAU9L,EAAMf,EAAY9C,EAAQsF,GAE3C,GAAIsK,GAAQC,GAAa/M,EAAY9C,EAAQsF,EAI7C,OAFAwK,IAAQjM,GAAS+L,EAEVA,EA+BT,QAASC,IAAY/M,EAAYiN,EAAOzK,GAEtC,GAAIwF,GAAWxF,GAAU5F,CAEzB,IAAK6B,EAAYuB,GAEf,MAAOA,EAEJ,IAAK5C,EAAS4C,GACnB,CAGE,IAAK,GAFD0B,MAEK7E,EAAI,EAAGA,EAAImD,EAAWjD,OAAQF,IACvC,CACE,GAAIiQ,GAAQ9M,EAAYnD,EAExB6E,GAAOwD,KAAM9H,EAAS0P,GAAUC,GAAYpO,MAAO5C,KAAM+Q,GAAUC,GAAaD,IAGlF,MAAO,UAAuB1E,GAE5B,IAAK,GAAIvL,GAAI,EAAGA,EAAI6E,EAAO3E,OAAQF,IAEjC,IAAM6E,EAAQ7E,GAAKuL,GAEjB,OAAO,CAIX,QAAO,GAGN,GAAK7J,EAAUyB,GACpB,CACE,GAAI4G,KAEJ,KAAK,GAAI3G,KAAQD,GAEf4G,EAAM1B,MACJgI,OAAUC,GAAkBnN,EAAYC,GAAQ+H,GAChDyD,SAAUG,GAAwB3L,IAItC,OAAO,UAA2BmI,GAEhC,IAAK,GAAIvL,GAAI,EAAGA,EAAI+J,EAAM7J,OAAQF,IAClC,CACE,GAAIoD,GAAO2G,EAAO/J,EAElB,KAAMoD,EAAKiN,OAAQjN,EAAKwL,SAAUrD,IAEhC,OAAO,EAIX,OAAO,GAGN,GAAK/L,EAAU2D,GACpB,CACE,GAAKA,IAAcgN,IAEjB,MAAOA,IAAQhN,EAGjB,IAAIyL,GAAWG,GAAwB5L,EAEvC,IAAKzD,EAAS0Q,GACd,CACE,GAAIC,GAASC,GAAkBF,EAAOjF,EAEtC,OAAO,UAA0BI,GAE/B,MAAO8E,GAAQzB,EAAUrD,KAK3B,MAAO,UAAuBA,GAE5B,MAAO7L,GAASkP,EAAUrD,KAM9B,MAAO,UAAkBA,GAEvB,OAAO,GAKb,QAASgC,IAAK3K,GAIZ,MAFAA,GAAK2N,YAAa,EAEX3N,EAGT,QAAS4N,IAAWJ,EAAOpK,EAAML,GAE/B,MAAO8K,IAAQL,GAAUA,EAAOpK,EAAML,GAAWA,EAAQyK,EAAOpK,GAGlE,QAASsK,IAAiBF,EAAOzK,GAE/B,MAAK8K,IAAQL,GAEJ,SAAgBpK,GAErB,MAAOoK,GAAOpK,EAAML,IAIjB,SAAgBK,GAErB,MAAOL,GAAQyK,EAAOpK,IAI1B,QAASyK,IAAOpR,GAEd,MAAOuC,GAAYvC,IAAOA,EAAEkR,WAG9B,QAASG,IAAIrR,GAEX,MAAKoR,IAAQpR,GAEJkO,GAAK,SAAiB6C,EAAOzK,GAElC,OAAQtG,EAAG+Q,EAAOzK,KAIjB/D,EAAYvC,GAER,SAAkB+Q,GAEvB,OAAQ/Q,EAAG+Q,IAIR7C,GAAK,SAAkB6C,EAAOzK,GAEnC,OAAQA,EAAQyK,EAAO/Q,KAI3B,QAASsR,IAAMC,GAEb,GAAIvQ,GAASE,EAASqQ,GAAUA,EAAQC,GAAGpQ,MAAMC,KAAMJ,UAEvD,OAAOiN,IAAK,SAAoB6C,EAAOzK,GAErC,IAAK,GAAI3F,GAAI,EAAGA,EAAIK,EAAOH,OAAQF,IAEjC,GAAIwQ,GAAYnQ,EAAQL,GAAKoQ,EAAOzK,GAElC,OAAO,CAIX,QAAO,IAaX,QAAS1G,IAAOsL,GAEd,GAAIuG,GAAU7R,GAAO8R,IAAKxG,EAAQrG,KAElC,IAAK4M,EAAQE,aAEX,MAAOF,GAAQG,QAAQ,EAGzBhS,IAAOqK,QAASrK,GAAO6E,OAAOC,SAAUwG,GAExC,IAAI2G,GAAW,GAAIlP,IAAUuI,GAEzBgB,EAAQpE,GAAMgK,QAChBlP,GACA,GAAIA,IAAOiP,GACXA,EAASE,UACT,0DA8BF,OA3BAF,GAASjP,MAAQsJ,EACjBA,EAAMvJ,SAAWkP,EAEjBjS,GAAOoS,QAASH,EAAShN,MAASqH,EAElCtM,GAAOqK,QAASrK,GAAO6E,OAAOE,SAAUuH,EAAO2F,EAAU3G,IAEpDtL,GAAOqS,SAEVJ,EAASK,UAAU,SAAsBC,GAElCA,GAEHN,EAASO,eAMbxS,GAAOyS,SAASrJ,KAAM6I,GAGxBjS,GAAO8R,IAAKG,EAAShN,MAAOyN,QAASpG,GACrCtM,GAAO8R,IAAKG,EAASE,WAAYO,QAASpG,GAE1CtM,GAAO2S,MAAO3S,GAAO4S,OAAOC,SAAUZ,EAAU3G,GAEzCgB,EA6NT,QAASwG,IAAWC,EAASzK,GAE3B,OAAQrF,EAAU8P,KAAcA,EAAUzK,KAAUA,EA0CtD,QAAS0K,IAAMC,EAAYC,EAAiBC,GAO1C,IAAK,GALDC,GAAQjT,EAAS8S,EAAY,UAC7BI,EAAalT,EAAS+S,EAAiB,UACvCI,EAAUC,GAAcnK,KAAM+J,GAAY,EAC1CH,EAAQQ,GAASF,GAAY,GAAI5R,IAE5BX,EAAI,EAAGA,EAAIqS,EAAMnS,OAAQF,IAClC,CACE,GAAI0S,GAAYL,EAAOrS,GACnB2S,EAAeC,GAAoBN,EAAYL,EAEnD,IAAKzS,EAAUkT,GAERA,IAAazT,IAAOoS,QAEvBsB,EAAc1T,GAAOoS,QAASqB,IAI9BG,GAAmBH,EAAWC,OAG7B,IAAK5Q,EAAU2Q,GAElBC,EAAcD,OAEX,CAAA,IAAmB,IAAdA,EAWR,KAAMA,GAAY,oCATlB,KAAK,GAAII,KAAgB7T,IAAOoS,QAE9BsB,EAAc1T,GAAOoS,QAASyB,GAGhC7T,IAAO4E,GAAI5E,GAAO6E,OAAOE,QAAS2O,KASxC,QAASE,IAAkB3O,EAAMyO,GAE/B,GAAI/L,GAAM3H,GAAO4E,GAAI5E,GAAO6E,OAAOE,QAAS,SAASuH,EAAO2F,GAErDA,EAAShN,OAASA,IAErByO,EAAcpH,GAEd3E,OAKN,QAASgM,IAAmBN,EAAYL,GAEtC,MAAO,UAASc,GAKd,IAAK,GAHDC,GAAKD,EAAW/Q,SAChBiR,EAAOD,EAAGC,KAELjT,EAAI,EAAGA,EAAIsS,EAAWpS,OAAQF,IACvC,CACE,GAAIkT,GAAKZ,EAAYtS,EAIrB,QAFAmT,GAAgB9K,KAAM4K,EAAMC,EAAID,EAAMC,IAE9BA,GAEN,IAAK,MACHD,EAAKG,IAAM,SAAS7I,EAASiH,EAAS6B,GAEpCpB,EAAM5J,MACJ6I,SAAU8B,EACVM,MAAOP,EACPQ,UAAW,MACXhJ,QAASA,EACTiH,QAASA,EACT6B,QAASA,IAGb,MACF,KAAK,MACHJ,EAAKlC,IAAM,SAASxF,EAAOhB,EAASiH,EAAS6B,GAE3CpB,EAAM5J,MACJ6I,SAAU8B,EACVM,MAAOP,EACPQ,UAAW,MACXhJ,QAASA,EACTiH,QAASA,EACT6B,QAASA,EACT9H,MAAOA,IAGX,MACF,KAAK,SACH0H,EAAKrS,OAAS,SAAS2K,EAAOiI,EAASjJ,EAASiH,EAAS6B,GAEvDpB,EAAM5J,MACJ6I,SAAU8B,EACVM,MAAOP,EACPQ,UAAW,SACXhJ,QAASA,EACTiH,QAASA,EACT6B,QAASA,EACT9H,MAAOA,EACPiI,QAASA,IAGb,MACF,KAAK,SACHP,EAAKQ,OAAS,SAASlI,EAAOiI,EAASjJ,EAASiH,EAAS6B,GAEvDpB,EAAM5J,MACJ6I,SAAU8B,EACVM,MAAOP,EACPQ,UAAW,SACXhJ,QAASA,EACTiH,QAASA,EACT6B,QAASA,EACT9H,MAAOA,EACPiI,QAASA,IAGb,MACF,KAAK,SACHP,EAAK1K,OAAS,SAASgD,EAAOhB,EAASiH,EAAS6B,GAE9CpB,EAAM5J,MACJ6I,SAAU8B,EACVM,MAAOP,EACPQ,UAAW,SACXhJ,QAASA,EACTiH,QAASA,EACT6B,QAASA,EACT9H,MAAOA,IAGX,MACF,KAAK,QACH0H,EAAKS,MAAQ,SAASC,EAAKD,EAAOnJ,EAASiH,EAAS6B,GAElDpB,EAAM5J,MACJ6I,SAAU8B,EACVM,MAAOP,EACPQ,UAAW,QACXhJ,QAASA,EACTiH,QAASA,EACT6B,QAASA,EACTM,IAAKA,EACLH,QAASE,IAGb,MACF,SACE,KAAMR,GAAK,6CAMrB,QAASU,MAEP,IAAK,GAAI5T,GAAI,EAAGA,EAAIyS,GAAQvS,OAAQF,IACpC,CACE,GAAIiS,GAAQQ,GAASzS,GACjBoS,EAAUI,GAAexS,EAExBiS,GAAM/R,SAETkS,EAASH,GAETA,EAAM4B,UAKZ,QAASC,MAEPC,KAGF,QAASC,MAIa,MAFpBD,IAIEH,KAIJ,QAASK,MAEP,IAAK,GAAIjU,GAAI,EAAGA,EAAImT,GAAgBjT,OAAQF,GAAK,EACjD,CACE,GAAIiT,GAAOE,GAAiBnT,EAAI,GAC5BoD,EAAO+P,GAAiBnT,EAAI,GAC5B4C,EAAOuQ,GAAiBnT,EAAI,EAEhCiT,GAAM7P,GAASR,EAGjB6P,GAAQvS,OAAS,EACjBsS,GAActS,OAAS,EACvBiT,GAAgBjT,OAAS,EAG3B,QAASgU,IAAatR,EAAMD,GAE1B,IAEEmR,KAEAlR,EAAKd,MAAOa,GAEd,MAAO4G,GAIL,KAFAtK,IAAOqK,QAASrK,GAAO6E,OAAO0F,OAAQD,IAEhCA,EAER,QAEEyK,MAqhBJ,QAASG,IAAKxQ,GAEZ,GAAIyQ,IAAS,EACTC,KAEAC,EAAO,WAEJF,EAEHzQ,EAAS7B,MAAO5C,KAAMoB,WAItB+T,EAAQhM,KAAMnJ,KAAM2R,GAAGpQ,MAAMqB,MAAOxB,YAqBxC,OAjBAgU,GAAKC,KAAO,WAEV,IAAMH,EACN,CACE,IAAK,GAAIpU,GAAI,EAAGA,EAAIqU,EAAQnU,OAAQF,GAAK,EACzC,CACE,GAAI2C,GAAU0R,EAASrU,GACnBoJ,EAAOiL,EAASrU,EAAI,EAExB2D,GAAS7B,MAAOa,EAASyG,GAG3BiL,EAAQnU,OAAS,EACjBkU,GAAS,IAINE,EAWT,QAAStS,IAASuI,GAGhBD,EAAcpL,KAAMqL,EAASiK,IAG7BtV,KAAKuV,WAAalU,EAASrB,KAAKwV,KAC9B,GAAIC,IAAczV,MAAS,GAAI0V,IAAW1V,MAG5CA,KAAKuV,WAAWI,YAAa3V,KAAKsM,QAGlCtM,KAAK4V,aAAe5V,KAAK6V,OAASC,GAAgBpU,OAAQ1B,MAC1DA,KAAK+V,UAAY/V,KAAKkU,OACtBlU,KAAKgW,UACLhW,KAAKkS,UAAYlS,KAAKkS,WAAa5B,GAAatQ,KAAKgF,MACrDhF,KAAKiW,aAAc,EACnBjW,KAAKkW,gBAAiB,EACtBlW,KAAKmW,aAAc,EACnBnW,KAAKoW,cAAe,EACpBpW,KAAKqW,cAAe,EACpBrW,KAAKsW,kBAAoB,EACzBtW,KAAKuW,aAAc,EACnBvW,KAAKwW,WAAajS,EAAMvE,KAAKsM,QAC7BtM,KAAKyW,aAAe,GAAIC,IAAS,MAAM,GACvC1W,KAAKyD,QAAU,KACfzD,KAAK2W,cAAgB,EAGrB3W,KAAK4W,QAAS5W,KAAMqL,GAGpBrL,KAAK+T,KAAS/T,KAAK6W,WAAY7W,MAC/BA,KAAK8W,MAAS9W,KAAK+W,YAAa/W,MAChCA,KAAKgX,KAAShX,KAAKiX,WAAYjX,MAG/BA,KAAKkX,cAAelX,KAAKW,WAAYX,KAAKmX,sBAC1CnX,KAAKoX,YAAapX,KAAKqX,UACvBrX,KAAKsX,aAActX,KAAKuX,WAGxBvX,KAAKwX,aACLxX,KAAKyX,gBAEL,KAAK,GAAIC,KAAgBrM,GAEvB,GAAOqM,IAAgB3X,IAAO4X,UAA9B,CAKA,GAAIC,GAAgB7X,GAAO4X,UAAWD,EAEtC,IAAOE,EAActW,oBAAqBuW,IAA1C,CAKA,GAAIC,GAAczM,EAASqM,EAE3B,KAAM,GAAI1S,KAAQ8S,GAClB,CACE,GAAIC,GAAkBD,EAAa9S,GAC/BgT,EAAW,GAAIJ,EAEdtX,GAAUyX,GAEbA,GACE1L,MAAO0L,GAGAvV,EAAUuV,KAEnBA,MAGIA,EAAgB1L,OAAU0L,EAAgBE,gBAE9CF,EAAgB1L,MAAQrH,GAG1BgT,EAASE,KAAMlY,KAAMgF,EAAM+S,GAEtBC,EAASG,MAEZnY,KAAKwW,WAAWrN,KAAMnE,GAGxBhF,KAAKwX,UAAWxS,GAASgT,EACzBhY,KAAKyX,cAActO,KAAMnE,KAK7B,IAAK,GAAIoT,KAAkBpY,MAAKqY,YAE9BrY,KAAKqY,YAAaD,GAAmBE,GAAWlK,MAAOpO,KAAMoY,GAIjE,QAASG,IAAclM,EAAOmM,EAAMC,GAElC,GAAIC,GAAY1Y,KAAK0Y,SAErB,KAAK,GAAIxU,KAAQsU,GAEVtU,IAAQwU,KAEXF,EAAMtU,GAASwU,EAAWxU,GAAQsU,EAAMtU,GAAQmI,EAAOnI,EAAMuU,GAIjE,OAAOD,GAGT,QAASG,IAAcC,EAASJ,GAE9B,GAAIK,GAAY7Y,KAAK6Y,UACjBvR,EAASkR,GAAQI,CAErB,KAAK,GAAI1U,KAAQ0U,GAIbtR,EAAQpD,GAFLA,IAAQ2U,GAEMA,EAAW3U,GAAQ0U,EAAS1U,GAAQ0U,EAAS1U,GAI7C0U,EAAS1U,EAI9B,OAAOoD,GAGT,QAASwR,IAAiBzM,GAExB,MAAOA,GAAM0M,OAGf,QAASC,IAAkBhH,GAEzB,OAAyB,IAAlBA,EAAS+B,KAAiBhU,GAAOkZ,YAAajH,GAAajS,GAAOgU,KAAM/B,GAGjF,QAASkH,IAAmBlH,GAE1B,OAA0B,IAAnBA,EAAS8E,MAAkB/W,GAAOoZ,aAAcnH,GAAajS,GAAO+W,MAAO9E,GAGpF,QAASoH,IAAmBpH,GAE1B,OAAyB,IAAlBA,EAASgF,KAAiBjX,GAAOsZ,YAAarH,GAAajS,GAAOiX,KAAMhF,GAGjF,QAASsH,IAAqBC,GAE5B,MAAOA,GAGT,QAASC,IAAsBD,GAE7B,MAAOA,GA2tCT,QAASxW,IAAM+Q,GAEb7L,GAAM/D,KAAMlE,KAAM,MAAO8T,GAm3B3B,QAAS2F,IAAmBpN,EAAOyG,EAAS4G,EAAaC,EAAaC,EAAaC,EAAcC,GAE/F,GAAIlI,GAAU,GAAI8E,IAAS,MAAM,EAEjC,IAAK7D,GAAYC,EAASiH,GAAQC,MAEhC,GAAIC,GAAO5N,EAAM5B,MAAOiP,EAAa,SAASlB,GAC5C0B,IACAC,IACAvI,EAAQa,QAASpG,EAAOmM,KAEtB0B,EAAO7N,EAAM5B,MAAOkP,EAAa,SAASnB,EAAM4B,GAClDH,IACAE,IACAvI,EAAQyI,OAAQhO,EAAO+N,EAAQ5B,KAE7B2B,EAAO9N,EAAM5B,MAAOmP,EAAa,WACnCK,IACAC,IACAtI,EAAQ0I,OAAQjO,SAGf,IAAKwG,GAAYC,EAASiH,GAAQQ,OAErC,GAAIN,GAAO5N,EAAM5B,MAAOoP,EAAc,SAASrB,GAE7C0B,IACAtI,EAAQa,QAASpG,EAAOmM,KAEtB0B,EAAO7N,EAAM5B,MAAOqP,EAAc,SAAStB,EAAM4B,GAEnDH,IACArI,EAAQyI,OAAQhO,EAAOmM,SAKzB5G,GAAQa,QAASpG,EAGnB,OAAOuF,GAaT,QAAS4I,MAMPxa,KAAKmB,UAMLnB,KAAKya,QAMLza,KAAK0a,WAyTP,QAASC,IAAW9S,GAElB7H,KAAK4a,OACL5a,KAAK8I,aAEL9I,KAAK6H,QAAUA,EA6FjB,QAASyQ,IAAWtG,EAAUN,GAE5B1R,KAAKgS,SAAWA,EAChBhS,KAAK0R,MAAQA,EACb1R,KAAKqY,cAEL,KAAK,GAAIvX,GAAI,EAAGA,EAAI4Q,EAAM1Q,OAAQF,IAEhCd,KAAK6a,cAAenJ,EAAO5Q,IAob/B,QAASga,IAAQjF,GAMf,GAJA7V,KAAK+a,aACL/a,KAAKgb,QACLhb,KAAK6V,UAEA1R,EAAS0R,GAEZ,IAAK,GAAI7Q,KAAQjF,IAAOoS,QAEtBnS,KAAKib,IAAKjW,OAGT,IAAK3D,EAASwU,GAEjB,IAAK,GAAI/U,GAAI,EAAGA,EAAI+U,EAAO7U,OAAQF,IAEjCd,KAAKib,IAAKpF,EAAQ/U,IAwHxB,QAASoa,OAqET,QAASxF,IAAU1D,GAEjBhS,KAAKkY,KAAMlG,GA+Gb,QAASyD,IAAazD,GAEpBhS,KAAKkY,KAAMlG,GA+Jb,QAASvQ,IAAWN,GAElBnB,KAAKmb,OAAQha,GAAQ,GAgvEvB,QAASia,IAAKC,EAAYC,EAAUC,GAElCvb,KAAKwb,UAAYhY,EAAMxD,KAAMA,KAAKyb,eAClCzb,KAAKsb,SAAWA,EAChBtb,KAAKub,UAAYA,GAAa,EAC9Bvb,KAAK0b,UAAY,EACjB1b,KAAK2b,cAAeN,GAsNtB,QAASO,IAAmBtN,EAAMuN,GAEhC7b,KAAKwD,OACLxD,KAAKkY,KAAM5J,EAAMuN,GA8InB,QAAS/F,IAAgB9D,EAAU6D,EAAQiG,GAEzC9b,KAAKkY,KAAMlG,EAAU6D,EAAQiG,GA8zC/B,QAASC,IAAwBzN,EAAMuN,GAErC7b,KAAKwD,OACLxD,KAAKkY,KAAM5J,EAAMuN,GAsLnB,QAASG,IAAmBhK,EAAU3F,EAAO4P,EAASpG,EAAQiG,GAE5D7T,GAAM4C,MAAM7K,MACVqM,MAAUA,EACV4P,QAAUA,IAGZjc,KAAKkY,KAAMlG,EAAU6D,EAAQiG,GA0L/B,QAASI,IAAuBb,EAAYpD,EAAekE,GAEzDlU,GAAM4C,MAAOwQ,GAEXpD,cAAeA,EACfkE,sBAAuBA,GAIzB,IAEIC,IAFoBf,EAAWgB,kBAClBhB,EAAWiB,WAChBjB,EAAWe,OACnBG,EAAalB,EAAWkB,UAoF5B,OAlFAtU,IAAM4C,MAAOwQ,GAcXgB,kBAAmB,SAAS3K,GAE1B,GAAKlP,EAAUkP,GACf,CACE,GAAI8K,GAAqB9K,EAAO1R,KAAKiY,eACjC5L,EAAQrM,KAAKmc,sBAAuBK,EAExC,IAAKnQ,EAEH,MAAOA,GAAMvJ,SAASyS,WAAW8G,kBAAmB3K,GAIxD,MAAOA,IAcT4K,WAAY,SAAS5K,EAAOoK,GAE1B,GAAKpK,YAAiB3O,IAEpB,MAAO2O,EAGT,IAAI8K,GAAqBhc,EAASkR,GAAUA,EAAO1R,KAAKiY,eAAkB,KACtE5L,EAAQrM,KAAKmc,sBAAuBK,EAExC,OAAOnQ,GAAQA,EAAMvJ,SAASwZ,WAAY5K,EAAOoK,GAAe,MAWlEM,MAAO,WAEL,MAAOF,IAAwBE,EAAMxZ,MAAO5C,MAAQiY,EAAekE,IAWrEI,WAAY,WAEV,MAAOL,IAAwBK,EAAW3Z,MAAO5C,MAAQiY,EAAekE,MAKrEd,EAsBT,QAASoB,IAAOzK,EAAUyC,EAAKpJ,EAASR,EAAO6R,GAE7C1c,KAAK2c,MAAO3K,EAAUyC,EAAKpJ,EAASR,EAAO6R,GAsM7C,QAASE,IAAY5K,EAAUyC,EAAKpJ,EAASR,EAAO6R,GAElD1c,KAAK2c,MAAO3K,EAAUyC,EAAKpJ,EAASR,EAAO6R,GAuM7C,QAAShG,IAAQmG,EAAUC,GAEzB9c,KAAKoa,OAAS1D,GAAQqG,OAAOC,QAC7Bhd,KAAK8c,YAA4B,IAAfA,EAClB9c,KAAKid,SAELhV,GAAM/D,KAAMlE,KAAM,UAAW,MAExB0C,EAAYma,IAEfA,EACErZ,EAAKxD,KAAMA,KAAKyS,SAChBjP,EAAKxD,KAAMA,KAAKqa,QAChB7W,EAAKxD,KAAMA,KAAKsa,QAChB9W,EAAKxD,KAAMA,KAAKkd,SAyYtB,QAASC,OAoLT,QAASC,IAAS/Q,EAAOyG,EAASzH,GAEhCrL,KAAKqd,MAAOhR,EAAOyG,EAASzH,GAsE9B,QAASiS,IAAUjR,EAAOyG,EAASzH,GAEjCrL,KAAKqd,MAAOhR,EAAOyG,EAASzH,GA+E9B,QAASkS,IAAYlR,EAAOyG,GAE1B9S,KAAKqd,MAAOhR,EAAOyG,GA0BrB,QAAS0K,IAAYnR,EAAOyG,GAE1B9S,KAAKqd,MAAOhR,EAAOyG,GAqErB,QAAS2K,IAAUpR,EAAOyG,GAExB9S,KAAKqd,MAAOhR,EAAOyG,GAuDrB,QAAS4K,IAAarR,EAAOyG,EAASzH,GAEpCrL,KAAKqd,MAAOhR,EAAOyG,EAASzH,GAyH9B,QAASsS,IAAUtR,EAAOyG,EAASzH,GAEjCrL,KAAKqd,MAAOhR,EAAOyG,EAASzH,GAyJ9B,QAASuS,IAAQvR,EAAOyG,GAEtB9S,KAAKqd,MAAOhR,EAAOyG,GA6BrB,QAAS+K,IAAWxR,EAAOyG,EAASzH,GAElCrL,KAAKqd,MAAOhR,EAAOyG,EAASzH,GAyN9B,QAASwM,OAgfT,QAASiG,OAyOT,QAASC,OA6PT,QAASC,OA8JT,QAASC,OAgOT,QAASC,OAuaT,QAASC,OAwlBT,QAASC,OA8KT,QAASC,OAsKT,QAASC,OAiaT,QAASC,IAAMvM,GAEbhS,KAAKgS,SAAWA,EAuiBlB,QAASwM,IAAmBC,EAAgBC,EAAUC,GAEpD,GAAI9M,GAAMnP,EAAYic,GAAeA,EAC3Bnc,EAAUmc,IAAgBjc,EAAYic,EAAW9M,KAAQ8M,EAAW9M,IAAMtO,EAChFqb,EAAMpc,EAAUmc,IAAgBjc,EAAYic,EAAWC,KAAQD,EAAWC,IAAMrb,CAEpF,IAAKsb,OAAOC,eAEVD,OAAOC,eAAgBL,EAAgBC,GAErCK,cAAc,EACdC,YAAY,EACZnN,IAAKA,EACL+M,IAAKA,QAIT,CACE,GAAIjC,GAAQ8B,EAAe9B,KAE3B8B,GAAe9B,MAAQ,WAErBA,EAAM/Z,MAAO5C,KAAMoB,UAEnB,IAAI6d,GAAsBjf,KAAM0e,GAAa7M,EAAIjP,MAAO5C,MAEpDkf,EAAe,WAEjB,GAAIC,GAAUnf,KAAM0e,EAEfS,KAAYF,EAEfL,EAAIpd,KAAMxB,KAAMmf,GAIhBF,EAAsBjf,KAAM0e,GAAa7M,EAAIjP,MAAO5C,MAIxDA,MAAK0K,OAAQ3H,GAAM6B,OAAOwa,QAASF,EAAclf,QAmDvD,QAASqf,IAAoB7X,EAAQ/C,EAAUgD,EAAQ6X,GAErD,GAAI1E,IACFjW,GAAQ8C,EAAS,MAAQ,KACzB+B,KAAQ/B,EAAS,QAAU,OAC3BiC,MAAQjC,EAAS,SAAW,SAG1BqB,EAAYwW,KAEhB,IAAK5c,EAAY+B,GAEfqE,EAAUK,MAERoW,KAAM3E,EAAIjW,GACV6C,OAAQA,EACRgY,OAAQ/a,QAGP,IAAKpD,EAASoD,IAAkC,IAApBA,EAASzD,QAAgB0B,EAAY+B,EAAS,IAE7EqE,EAAUK,MAERoW,KAAM3E,EAAIjW,GACV6C,OAAQA,EACRgY,OAAQ/a,EAAS,GACjBhB,QAASgB,EAAS,SAGjB,IAAKjC,EAAUiC,GAElB,IAAM,GAAIgb,KAAahb,GAErB,GAAKgb,IAAa7E,GAClB,CACE,GAAI8E,GAAcjb,EAAUgb,GACxBF,EAAO3E,EAAK6E,EAEX/c,GAAYgd,GAEf5W,EAAUK,MAERoW,KAAMA,EACN/X,OAAQA,EACRgY,OAAQE,IAGFre,EAASqe,IAAwC,IAAvBA,EAAY1e,QAAgB0B,EAAYgd,EAAY,KAEtF5W,EAAUK,MAERoW,KAAMA,EACN/X,OAAQA,EACRgY,OAAQE,EAAY,GACpBjc,QAASic,EAAY,KAO/B,MAAO5W,GAGT,QAAS6W,IAAoBrY,EAAQwB,GAEnC,IAAK,GAAIhI,GAAI,EAAGA,EAAIgI,EAAU9H,OAAQF,IACtC,CACE,GAAI8e,GAAI9W,EAAWhI,EAEnBwG,GAAQsY,EAAEL,MAAQK,EAAEpY,OAAQoY,EAAEJ,OAAQI,EAAEnc,UAwR5C,QAASoc,MAEP,MAAOC,IAAIC,MAAQD,GAAIE,YAAcF,GAAIG,SAG3C,QAASC,IAAOxO,GAEd,MAAKA,aAAiBoO,IAAIC,KAEjBrO,EAECA,YAAiBoO,IAAIK,KAEtBzO,EAECA,YAAiBoO,IAAIG,UAAYvO,EAAM1Q,OAAS,GAEjD0Q,EAAM,GAMjB,QAAS0O,IAAYjgB,GAEnB,MAAOA,GAGT,QAASkgB,IAAclgB,GAErB,GAAIW,GAAIR,EAAUH,GAAMA,EAAEM,QAAQ,aAAe,CAEjD,QAAc,IAAPK,EAAWX,EAAIA,EAAE4D,UAAWjD,EAAI,GAGzC,QAASwf,IAAQjU,EAAOhB,GAEjBA,EAAQkV,UAAYlU,EAAMmU,YAE7BnU,EAAMoU,QAIV,QAASC,IAAarU,EAAOqS,EAAUxN,EAAOyP,EAAMtV,GAElDgB,EAAMuU,OAASvU,EAAMuU,WACrBvU,EAAMuU,OAAQlC,IACZxN,MAAOA,EACP2P,KAAM3P,EACNyP,KAAMA,EACNtV,QAASA,GAIb,QAASyV,IAAcC,EAAW7P,EAAO7E,EAAOqS,EAAUrT,GAExD,GAAI2V,GACAC,GAAO,CA0BX,OAxBKF,IAAaA,EAAUG,YAE1BH,EAAUG,YAAahQ,EAAO7E,EAAOqS,EAAU,SAASmC,GAEtDxU,EAAMuU,OAAQlC,GAAWmC,KAAOA,EAE3BI,GAEH5U,EAAOqS,GAAamC,EACpBP,GAASjU,EAAOhB,IAIhB2V,EAASH,IAMbG,EAAS9P,EAGX+P,GAAO,EAEAD,EAGT,QAASG,IAAWjZ,EAAQkZ,EAAW/V,GAErC,GAAI0V,GAAYhhB,GAAOshB,eAAgBhW,EAAQ0V,UAO/C,OALO7Y,KAAU4X,IAAIE,WAAW1e,WAE9BvB,GAAOqK,QAASrK,GAAO6E,OAAO0c,mBAGzB,SAAS5P,EAAOrF,EAAOqS,GAE5B,GAAIiC,GAAOT,GAAQxO,EAEnB,KAAc,IAATiP,EACL,CACE,GACIK,GADAO,EAAS,GAAIzB,IAAIE,WAEjBiB,GAAO,CAqBX,OAnBAM,GAAOC,OAAS,SAASC,GAEvB,GAAIvQ,GAAQkQ,EAAWK,EAAEna,OAAO0Z,OAEhCN,IAAcrU,EAAOqS,EAAUxN,EAAOyP,EAAMtV,GAE5C2V,EAASF,GAAeC,EAAW7P,EAAO7E,EAAOqS,EAAUrT,GAEtD4V,IAEH5U,EAAOqS,GAAasC,EACpBV,GAASjU,EAAOhB,KAIpBkW,EAAQrZ,GAAUyY,GAElBM,GAAO,EAEAD,EAEJ,GAAKxe,EAAUkP,IAAWA,EAAMgQ,KACrC,CACE,GAAIV,GAEAW,EAAS,SAASzQ,GAElB8P,EAAS9P,EAKb,OAFAnR,IAAOqK,QAASrK,GAAO6E,OAAOgd,aAAclQ,EAAOrF,EAAOqS,EAAUiD,IAE7DX,EAMP,MAFAN,IAAcrU,EAAOqS,EAAUhN,EAAO,KAAMrG,GAErCyV,GAAeC,EAAWrP,EAAOrF,EAAOqS,EAAUrT,IAiF/D,QAASwW,IAAYnQ,EAAOrF,EAAOyV,EAAOrJ,GAExC,GAAKpM,EAAMuU,QAAUkB,IAASzV,GAAMuU,OACpC,CACE,GAAImB,GAAS1V,EAAMuU,OAAQkB,EAE3B,IAAMrJ,IAA6B,IAAhBsJ,EAAO5J,OAAqBM,IAA8B,IAAjBsJ,EAAOjL,MAEjE,MAGF,KAAM2B,GAAasJ,EAAOpB,KAC1B,CACE,GAAI9V,GAAQqC,EAAM6U,EAAOpB,KAAM5gB,GAAOiiB,gBAAgB,EAItD,OAFAnX,GAAM6W,MAAO,EAEN7W,EAGT,GAAK6G,IAAUqQ,EAAOlB,KAYpB,MAVKpI,IAAasJ,EAAOpB,MAEvBtU,EAAM5B,MAAO1H,GAAM6B,OAAOqd,WAAY,iBAE7BF,GAAOpB,KAEdtU,EAAM6V,cAAevE,GAAW5D,GAAQQ,SAIrCwH,EAAO7Q,MAIlB,MAAOQ,GA2ST,QAASyQ,IAAqBvH,GAE5B,MAAO,UAAqBvO,EAAO+V,EAAQC,GAEzC,GAAIC,GAAQ1H,EAAIF,QAAS0H,EAEzB,IAAKpf,EAAUsf,GACf,CACE,GAAI1a,GAAWgT,EAAI9R,UAAWsZ,SAEvBxH,GAAIF,QAAS0H,SACbxH,GAAI9R,UAAWsZ,GAEtBxH,EAAIH,KAAM6H,GAAUD,EACpBzH,EAAIF,QAAS2H,GAAWC,EACxB1H,EAAI9R,UAAWuZ,GAAWza,IAKhC,QAAS2a,IAAgB/M,EAAKtE,GAW5B,MATAsR,IAAQ5f,MAAO5C,KAAMoB,WAEhB8P,YAAiBnO,KAASmO,EAAMuR,IAAIC,aAEvC1iB,KAAK8I,UAAY9I,KAAK8I,cAEtB9I,KAAK8I,UAAW0M,GAAQtE,EAAM1G,IAAKzH,GAAM6B,OAAO+d,UAAWR,GAAsBniB,QAG5EA,KAGT,QAAS4iB,IAAmBpN,GAE1B,GAAI8M,GAAQtiB,KAAK0a,QAASlF,EAc1B,OAZKxS,GAAUsf,KAERtiB,KAAK8I,YAERzE,EAAUrE,KAAK8I,UAAW0M,UAEnBxV,MAAK8I,UAAW0M,IAGzBxV,KAAK6iB,SAAUP,IAGVtiB,KAGT,QAAS8iB,MAEP7a,GAAMC,OAAQsS,GAAK,MAAO+H,IAC1Bta,GAAMC,OAAQsS,GAAK,SAAUoI,IAG/B,QAASG,MAEP9a,GAAMC,OAAQsS,GAAK,MAAOgI,IAC1Bva,GAAMC,OAAQsS,GAAK,SAAUwI,IA8e/B,QAASC,IAAY9iB,EAAGwN,EAAIqB,GAE1B,GAAIkU,GAAOnU,GAAW5O,EAAG6O,EAEzB,KAAc,IAATkU,EAEH,OAAO,CAGT,KAAMvV,EAEJ,MAAOuV,EAGT,QAAQvV,GAEN,IAAKwV,IAAU/f,KACb,MAAO8f,EACT,KAAKC,IAAUC,OACb,MAAOF,GAAK9e,SACd,KAAK+e,IAAUE,QACb,MAAOnhB,MAAKC,MAAO+gB,EAAK9e,UAAY,IACtC,SACE,MAAOrE,IAAOujB,WAAYJ,EAAMvV,IAlhlBpC,GAAImS,IAAwB,mBAAXyD,QAAyBA,OAASzjB,EAGjD6R,GAAKtR,MAAMiB,UA0MX2G,IAGFvG,OAAQ,SAAU8hB,EAAWjZ,GAE3BtC,GAAM/D,KAAMsf,EAAW,SAAUvb,GAAMxI,QAAS+jB,IAChDvb,GAAMwb,MAAOD,EAAWjZ,EAAShH,IAGnCmgB,OAAQ,SAAUC,EAAQH,EAAWI,GAEnC,GAAIrZ,GAAUqD,EAAUgW,EAAUD,EAAO3b,UACrC6b,EAAa5b,GAAM6b,gBAAiBH,EAExCH,GAAUliB,UAAY,GAAIuiB,EAE1B,IAAIE,GAAkB9b,GAAMxI,QAAS+jB,EAErC,IAAKvb,GAAM5G,QAASsiB,GACpB,CACE,GAAIK,GAAc,WAEhB,GAAItjB,KAGJ,OAFAuH,IAAM4C,MAAOnK,EAAK6J,GAClBiZ,EAAU5gB,MAAOlC,EAAKU,WACfV,EAGTuH,IAAM/D,KAAMsf,EAAW,SAAUQ,GACjC/b,GAAM/D,KAAMsf,EAAW,SAAUS,GAASD,YAAcA,EAAcD,OAItE9b,IAAM/D,KAAMsf,EAAW,SAAUO,EAGnC9b,IAAMwb,MAAOD,EAAWjZ,EAASoZ,IAGnC1R,QAAS,SAAS0R,EAAQO,EAAgBhS,EAAWiS,GAEnD,GAAIC,GAAe,GAAIC,UAAS,mBAAqBnS,EAAYiS,IAMjE,OAJAC,GAAa9iB,UAAY4iB,EAEzBjc,GAAMwb,MAAOW,KAAkBT,GAExBS,GAGTX,MAAO,SAASD,EAAWjZ,EAASoZ,GAElC1b,GAAM/D,KAAMsf,EAAW,WAAYjZ,GACnCtC,GAAM/D,KAAMsf,EAAW,QAASvb,GAAMqc,UACtCrc,GAAM/D,KAAMsf,EAAW,UAAWvb,GAAMsc,YACxCtc,GAAM/D,KAAMsf,EAAW,WAAYvb,GAAMuc,aACzCvc,GAAM/D,KAAMsf,EAAUliB,UAAW,SAAUqiB,GAC3C1b,GAAM/D,KAAMsf,EAAUliB,UAAW,cAAekiB,GAChDvb,GAAM4C,MAAO2Y,EAAUliB,UAAWiJ,IAGpClJ,QAAS,SAAUmiB,GAEjB,MAAOnjB,SAAUmjB,GAAaA,EAAUliB,oBAAqBjB,QAG/D6H,OAAQ,SAAUsb,EAAWiB,EAAYvc,GAEnCsb,EAAUxb,WAEZwb,EAAUxb,SAAUyc,GAAevc,GAGrCD,GAAM/D,KAAMsf,EAAUliB,UAAWmjB,EAAYvc,IAG/Cqc,WAAY,SAAUE,EAAYvc,GAEhCD,GAAMC,OAAQlI,KAAMykB,EAAYvc,IAGlCqC,QAAS,SAAUiZ,EAAWjZ,GAE5B,IAAK,GAAIka,KAAcla,GAErBtC,GAAMC,OAAQsb,EAAWiB,EAAYla,EAASka,KAIlDvgB,KAAM,WAEJ,MAAI2a,QAAOC,eAEF,SAAUxX,EAAQoX,EAAUxN,GAEjC2N,OAAOC,eAAgBxX,EAAQoX,GAC7BK,cAAc,EACdC,YAAY,EACZ0F,UAAU,EACVxT,MAAOA,KAMJ,SAAU5J,EAAQoX,EAAUxN,GAEjC5J,EAAQoX,GAAaxN,MAK3BoT,SAAU,SAAU5F,EAAUxN,GAE5BjJ,GAAM/D,KAAMlE,KAAKsB,UAAWod,EAAUxN,IAGxCrG,MAAO,SAAUvD,EAAQrD,GAEvB,IAAK,GAAI0gB,KAAgB1gB,GAEvBgE,GAAM/D,KAAMoD,EAAQqd,EAAc1gB,EAAY0gB,KAIlDpU,QAAS,SAAUjJ,EAAQmd,EAAYG,GAErC,GAAIC,GAAiBvd,EAAOhG,UAAWmjB,IAAgBnd,EAAQmd,IAAgBlhB,CAE/E0E,IAAMC,OAAQZ,EAAQmd,EAAYG,EAAeC,KAGnDL,YAAa,SAAUC,EAAYG,GAEjC3c,GAAMsI,QAASvQ,KAAMykB,EAAYG,IAGnCd,gBAAiB,SAASN,GAExB,QAASsB,MAOT,MAFAA,GAAExjB,UAAYkiB,EAAUliB,UAEjBwjB,GAGTrlB,QAAS,SAAS+jB,GAEhB,QAASsB,GAAE5a,GAETsZ,EAAU5gB,MAAO5C,KAAMkK,GAKzB,MAFA4a,GAAExjB,UAAYkiB,EAAUliB,UAEjB,WAEL,MAAO,IAAIwjB,GAAG1jB,cAqThB2jB,GAAM3hB,KAAK2hB,KAAO,WAEpB,OAAO,GAAI3hB,OAAOgB,WAuGhBgB,KA2XJ+C,GAAUmB,OAERC,WAAY,EACZE,KAAM,EACNE,MAAO,GAGT1B,GAAMvG,OAAQyG,GAEZkB,OAAQ,WAEN,GAAId,GAAOvI,KAAKuI,KACZC,EAAOxI,KAAKwI,IAEXD,KAASvI,OAEZwI,EAAKD,KAAOA,EACZA,EAAKC,KAAOA,EACZxI,KAAKuI,KAAOvI,KAAKwI,KAAOxI,OAI5BglB,QAAS,SAAS3c,GAEhB,MAA8B,KAAtBrI,KAAKqI,KAAOA,IAGtB+B,QAAS,SAAS9B,EAAO4B,EAAMR,GAE7B,GACIub,IADOjlB,KAAKqI,KACFrI,KAAKglB,QAAS7c,EAAUmB,MAAMK,OAEvC3J,MAAKsI,QAAUA,KAEZoB,GAASub,IAAaA,KAE1BjlB,KAAKsI,MAAQA,EACbtI,KAAKyE,SAAS7B,MAAO5C,KAAKyD,QAASyG,IAGhClK,KAAKglB,QAAS7c,EAAUmB,MAAMG,OAEjCzJ,KAAKqJ,aAyuBb+E,GAAMI,MAAQ,YAuCdG,GAAOH,MAAQ,QAgGf,IAAIoB,OA4BAI,MAsGAiU,GAAWnkB,EAAOolB,gBAAkBpF,GAAIoF,kBAE5C,IAAKpF,GAAIqF,UAAYrF,GAAIqF,SAASC,cAClC,CAG8C,OAF/BtF,GAAIqF,SAASC,cAEfC,aAAa,kBAEtBpB,GAASD,aAAc,GAe3B1T,GAAY9B,MAAQ,UA2EpB,IAAIyC,MAqSJlR,IAAOoS,WAEPpS,GAAOqS,UAAW,EAElBrS,GAAOyS,YAEPzS,GAAOulB,YAAc,KAErBvlB,GAAOwlB,KAAO,SAAS9gB,EAAUhB,GAW/B,QAAS+hB,GAAalT,EAASwB,GAK7B,GAHA2R,EAActc,KAAMmJ,GACpB0D,EAAO7M,KAAM2K,GAERkC,EAAOhV,SAAW0kB,EAAQ1kB,OAC/B,CACE,IAAK,GAAIc,GAAI,EAAGA,EAAIkU,EAAOhV,OAAQc,IACnC,CACE,GAAIgS,GAAKkC,EAAQlU,GACbwQ,EAAUmT,EAAe3jB,EAExBwQ,IAEHwB,EAAGvB,aAIPX,EAAQyL,QAAQ5K,WA3BpB,GAAIb,GAAU7R,GAAOulB,YAAcvlB,GAAOulB,aAAe,GAAI5O,IAAS,MAAM,GACxEgP,EAAU3lB,GAAOyS,SAASjR,QAC1ByU,KACAyP,IAEJ7T,GAAQU,QAAS7N,EAAUhB,GAAWzD,MAEtCD,GAAOyS,SAASxR,OAAS,EAyBzB0kB,EAAQC,KAAK,SAASzkB,EAAGsE,GAEvB,MAAOA,GAAEogB,SAAW1kB,EAAE0kB,UAIxB,KAAK,GAAI9kB,GAAI,EAAGA,EAAI4kB,EAAQ1kB,OAAQF,IAElC4kB,EAAS5kB,GAAIuR,UAAWmT,EAG1B,OAAO5T,IAGT7R,GAAO8lB,YAEP9lB,GAAO8R,IAAM,SAAS7M,GAEpB,GAAImG,GAAWpL,GAAO8lB,SAAU7gB,EAOhC,OALMmG,KAEJA,EAAWpL,GAAO8lB,SAAU7gB,GAAS,GAAI0R,IAAS,MAAM,IAGnDvL,GAGTpL,GAAO+lB,OAAS,WAEd,GAAI3T,GAAUpS,GAAOoS,OAErB,KAAK,GAAID,KAAaC,GAEpB2N,GAAK5N,GAAcC,EAASD,IAIhCnS,GAAO4U,MAAQ,SAASoR,GAEtB,GAAI5T,GAAUpS,GAAOoS,OAErB,KAAK,GAAID,KAAaC,GAEpBA,EAASD,GAAYyC,MAAOoR,IAIhChmB,GAAOsd,MAAQ,SAAS2I,EAAsBD,GAE5C,GAAI5T,GAAUpS,GAAOoS,OAErB,IAAK6T,EAEH,IAAK,GAAI9T,KAAaC,GACtB,CACE,GAAI2B,GAAK3B,EAASD,GAAYpP,QAE9B,IAAKgR,EAAGmS,aAEN,MAAOvP,IAAQ2D,OAAQvG,GAK7B,MAAO4C,IAAQwP,YAAYlmB,KAAM,WAE/B,IAAK,GAAIkS,KAAaC,GACtB,CACWA,EAASD,GAAYpP,SAE3Bua,OAAO,EAAO0I,OAKvBhmB,GAAOomB,OAAS,SAAShT,EAAOkK,EAAO2I,EAAsBD,GAE3D,GAAI5T,GAAUpS,GAAOoS,QACjB0T,EAAW9lB,GAAO8lB,QAEtB,IAAKG,EAEH,IAAK,GAAI9T,KAAaC,GACtB,CACE,GAAI2B,GAAK3B,EAASD,GAAYpP,SAC1BsjB,GAAW/kB,EAAS8R,KAA2C,IAAhC1S,EAAS0S,EAAOjB,EAEnD,IAAKkU,GAAStS,EAAGmS,aAEf,MAAOvP,IAAQ2D,OAAQvG,GAK7B,MAAO4C,IAAQwP,YAAYlmB,KAAM,WAE/B,IAAK,GAAIkS,KAAaC,GACtB,CACE,GAAI2B,GAAK3B,EAASD,GAAYpP,WACfzB,EAAS8R,KAA2C,IAAhC1S,EAAS0S,EAAOjB,MAI5CmL,GAEHvJ,EAAGuJ,OAAO,EAAO0I,SAGZ5T,GAASD,SACT2T,GAAU/R,EAAG9O,YACb6gB,GAAU/R,EAAG5B,gBAgC5BzJ,EAAa1I,IAEbA,GAAO6E,QAELyhB,YAAc,cACdvhB,QAAc,UACdD,QAAc,UACdyhB,OAAc,SACdC,QAAc,UACdjc,MAAc,QAIhB,IAAIkc,KAEFC,KAAY,OACZzJ,QAAY,UACZ0J,IAAY,OAIV3M,IAEF0M,KAAY,EACZlM,MAAY,EACZP,KAAY,EACZ2M,OAAY,EACZC,KAAY,EACZC,OAAY,EACZC,OAAY,EACZJ,IAAY,GAQVK,IAEFN,KAAQ,EACRC,IAAQ,EACRM,KAAQ,EACRC,KAAQ,GAKNC,IAEFC,UAAaC,KAAK,GAClBC,UAAaC,KAAK,EAAMC,KAAK,GAC7BhB,SAAaiB,GAAG,IAGdC,IAEFhB,KAAQ,EACR1jB,MAAQ,EACR2kB,IAAQ,EACRC,KAAQ,GAGNC,IAEFnB,KAAQ,EACR1jB,MAAQ,EACR2kB,IAAQ,EACRC,KAAQ,GAIN9S,GAAa,EACbtB,MACAD,MACAW,KAyOJlU,IAAOgT,MAAQA,GACfhT,GAAO2U,SAAWA,GAClB3U,GAAO6U,WAAaA,GACpB7U,GAAO+U,SAAWA,GAClB/U,GAAOgV,WAAaA,GACpBhV,GAAOiV,aAAeA,GACtBjV,GAAO8U,WAAa,WAAa,MAAOA,KAGxC9U,GAAO2S,MAAQ,SAAS7I,EAAOgD,KAiB/B9M,GAAO8nB,SAAW,SAASpoB,EAASqoB,GAE5B/nB,GAAOgoB,WAAYD,IAEvB/nB,GAAO2S,MAAQjT,EACfM,GAAOgoB,UAAW,IAItBhoB,GAAO4S,QAELC,SAAU,EAEVoV,KAAM,EACNC,aAAc,GAEdC,YAAa,GAEbC,cAAe,EACfC,cAAe,EACfC,cAAe,EACfC,YAAa,EACbC,oBAAqB,EACrBC,kBAAmB,EACnBC,mBAAoB,EACpBC,mBAAoB,GAEpBC,WAAY,EACZC,oBAAqB,GACrBC,kBAAmB,GACnBC,iBAAkB,GAElBC,cAAe,GACfC,gBAAiB,GAEjBC,YAAa,GACbC,aAAc,GACdC,cAAe,GACfC,iBAAkB,GAClBC,WAAY,GACZC,aAAc,GACdC,YAAa,GACbC,YAAa,GACbC,aAAc,GAEdC,kBAAmB,GAEnBC,WAAY,GACZC,iBAAkB,GAClBC,mBAAoB,GACpBC,mBAAoB,GAEpBC,oBAAqB,GACrBC,oBAAqB,GAErBC,eAAgB,GAChBC,aAAc,GACdC,eAAgB,GAChBC,aAAc,GACdC,eAAgB,GAChBC,cAAe,GACfC,cAAe,GACfC,mBAAoB,GAEpBC,mBAAoB,GACpBC,qBAAsB,GACtBC,kBAAmB,GACnBC,qBAAsB,GAEtBC,sBAAuB,GAEvBC,kBAAmB,IACnBC,UAAW,IACXC,gBAAiB,IACjBC,WAAY,IACZC,iBAAkB,IAElBC,OAAQ,GACRC,QAAS,GAETC,eAAgB,GAEhBC,YAAa,GACbC,oBAAqB,GACrBC,sBAAuB,GACvBC,eAAgB,GAChBC,mBAAoB,GACpBC,iBAAkB,GAClBC,eAAgB,GAChBC,kBAAmB,GACnBC,iBAAkB,GAClBC,kBAAmB,GACnBC,cAAe,GACfC,aAAc,IACdC,qBAAsB,IAEtBC,eAAgB,GAChBC,uBAAwB,GACxBC,qBAAsB,GACtBC,yBAA0B,GAC1BC,kBAAmB,GACnBC,sBAAuB,GACvBC,oBAAqB,GACrBC,qBAAsB,GACtBC,oBAAqB,GACrBC,qBAAsB,GACtBC,iBAAkB,GAClBC,gBAAiB,IACjBC,wBAAyB,IAEzBC,kBAAmB,IACnBC,0BAA2B,IAC3BC,4BAA6B,IAC7BC,qBAAsB,IACtBC,yBAA0B,IAC1BC,uBAAwB,IACxBC,uBAAwB,IACxBC,wBAAyB,IACzBC,oBAAqB,IACrBC,mBAAoB,IACpBC,2BAA4B,IAE5BC,aAAc,GACdC,qBAAsB,GACtBC,mBAAoB,GACpBC,gBAAiB,GACjBC,uBAAwB,GACxBC,eAAgB,GAChBC,aAAc,GACdC,YAAa,GACbC,kBAAmB,GACnBC,wBAAyB,GACzBC,kBAAmB,GACnBC,kBAAmB,GACnBC,kBAAmB,GACnBC,iBAAkB,GAClBC,cAAe,IACfC,sBAAuB,IACvBC,mBAAoB,IAEpBC,iBAAkB,GAClBC,yBAA0B,GAC1BC,uBAAwB,GACxBC,8BAA+B,GAC/BC,oBAAqB,GACrBC,2BAA4B,GAC5BC,mBAAoB,GACpBC,iBAAkB,GAClBC,gBAAiB,GACjBC,sBAAuB,GACvBC,4BAA6B,GAC7BC,sBAAuB,GACvBC,sBAAuB,IACvBC,sBAAuB,IACvBC,qBAAsB,IACtBC,qBAAsB,IACtBC,wBAAyB,GACzBC,kBAAmB,IACnBC,0BAA2B,IAC3BC,uBAAwB,IAExBC,eAAgB,GAChBC,eAAgB,IAChBC,uBAAwB,IACxBC,qBAAsB,IACtBC,gBAAiB,IACjBC,wBAAyB,IAEzBC,aAAc,IACdC,aAAc,IACdC,qBAAsB,IACtBC,mBAAoB,IACpBC,eAAgB,IAChBC,YAAa,IACbC,gBAAiB,IAInB,IAAIC,MAcJA,IAAMC,QACN/wB,GAAOsZ,YACPtZ,GAAOiX,KAAO,SAAShF,GAErB,OAEEmG,KAAM,SAAS9L,EAAOmM,KAKtBnP,OAAQ,SAASgD,OAoBrBtM,GAAOgxB,QAAU,SAAStxB,EAASqoB,GAE3B/nB,GAAOixB,UAAWlJ,IAEtB/nB,GAAOiX,KAAOvX,EACdM,GAAOixB,SAAU,IAOrBjxB,GAAOkxB,SAAW,WAEhB,OAAQnR,GAAIoR,YAAsC,IAAzBpR,GAAIoR,UAAUC,QAGzCpxB,GAAOqxB,OAASrxB,GAAOkxB,WAEvBlxB,GAAOsxB,cAAe,EAGtBtxB,GAAOuxB,UAAY,WAEjBvxB,GAAOqxB,QAAS,EAChBrxB,GAAO2S,MAAO3S,GAAO4S,OAAOwY,QAE5BnW,GAAa,WAEXjV,GAAOqK,QAASrK,GAAO6E,OAAO0hB,WAKlCvmB,GAAOwxB,WAAa,WAElBxxB,GAAOqxB,QAAS,EAChBrxB,GAAO2S,MAAO3S,GAAO4S,OAAOyY,SAC5BrrB,GAAOqK,QAASrK,GAAO6E,OAAO2hB,UAKhCxmB,GAAOyxB,sBAAwB,WAEzB1R,GAAI2R,kBAEN3R,GAAI2R,iBAAkB1xB,GAAO6E,OAAO0hB,OAAQvmB,GAAOuxB,WAAW,GAC9DxR,GAAI2R,iBAAkB1xB,GAAO6E,OAAO2hB,QAASxmB,GAAOwxB,YAAY,KAIhEzR,GAAIqF,SAASuM,KAAKC,SAAW5xB,GAAOuxB,UACpCxR,GAAIqF,SAASuM,KAAKE,UAAY7xB,GAAOwxB,aAKzCxxB,GAAO8xB,mBAAqB,WAE1B,GAAIT,GAASrxB,GAAOkxB,UAEflxB,IAAOsxB,eAEVD,GAAS,IAGI,IAAXA,IAAqC,IAAlBrxB,GAAOqxB,OAE5BrxB,GAAOuxB,aAGW,IAAXF,IAAsC,IAAlBrxB,GAAOqxB,QAElCrxB,GAAOwxB,aAKX,IAAIO,MAIJA,IAAMhB,QACN/wB,GAAOkZ,YACPlZ,GAAOgU,KAAO,SAAS/B,GAGrB,OAIEkC,IAAK,SAAU7I,EAASiH,EAAS6B,GAE/B7B,OAKFT,IAAK,SAAUxF,EAAOhB,EAASiH,EAAS6B,GAEtCA,EAAS,MAAO,IAKlBzS,OAAQ,SAAU2K,EAAOiI,EAASjJ,EAASiH,EAAS6B,GAElD7B,OAKFiC,OAAQ,SAAUlI,EAAOiI,EAASjJ,EAASiH,EAAS6B,GAElD7B,OAKFjJ,OAAQ,SAAUgD,EAAMhB,EAAUiH,EAAS6B,GAEzC7B,OAKFkC,MAAO,SAAUC,EAAKD,EAAOnJ,EAASiH,EAAS6B,GAE7C7B,SAmBNvS,GAAOgyB,QAAU,SAAStyB,EAASqoB,GAE3B/nB,GAAOiyB,UAAWlK,IAEtB/nB,GAAOgU,KAAOtU,EACdM,GAAOiyB,SAAU,GAKrB,IAAIC,MAWJA,IAAOnB,QACP/wB,GAAOoZ,aACPpZ,GAAO+W,MAAQ,SAAS9E,GAEtB,OAkBEkgB,IAAK,SAAS1c,EAAK2c,EAAQ7f,EAAS6B,GAElC7B,EAASkD,EAAK2c,IAIhBtgB,IAAK,SAAS2D,EAAKlD,EAAS6B,GAE1BA,EAASqB,EAAKvV,IAiBhBoJ,OAAQ,SAASmM,EAAKlD,EAAS6B,GAE7B7B,EAASkD,IAYXtB,IAAK,SAAS5B,EAAS6B,GAErB7B,UAiBF+K,MAAO,SAAS5C,EAAM2X,EAAS9f,EAAS6B,GAEtC7B,EAASmI,EAAM2X,MAmBrBryB,GAAOsyB,SAAW,SAAS5yB,EAASqoB,GAE5B/nB,GAAOuyB,WAAYxK,IAEvB/nB,GAAO+W,MAAQrX,EACfM,GAAOuyB,UAAW,IAyNtBxvB,GAAS8B,QAEP2tB,OAAoB,UACpBC,WAAoB,cACpBC,UAAoB,aACpBC,QAAoB,UACpBC,WAAoB,cACpBC,aAAoB,gBACpBC,aAAoB,gBACpBC,kBAAoB,qBACpBC,mBAAoB,sBACpBC,MAAoB,iCACpB5T,QAAoB,UAGtB,IAAI9J,IAAWxS,GAASwS,UAEtBtQ,KAAsB/E,EACtBiS,UAAsB,KACtBsD,IAAsB,KACtByd,aAAsB,IACtB3mB,UACA4mB,iBACA5nB,YACA6nB,iBACAC,cACAxN,SAAsB,EACtBjlB,WAAsB,KACtBwW,qBAAsB,KACtBE,SAAsB,KACtBgc,UACAvgB,QAAsBiH,GAAQ2M,IAC9BnB,KAAsBwB,GAAKN,KAC3B6M,aAAsB,EACtBC,eAAsB,EACtBC,aAAsB,EACtBC,MAAsBjN,GAAME,IAC5BgN,UAAsB,EACtBC,aAAsB,EACtBC,cAAsB,EACtBlb,aACAG,aACAR,eACAwb,WAAsB,KACtBC,aAAsB,KACtBC,WAAsB,KACtBC,cAAsB,KACtBC,cAAsB,KACtBC,YAAsB,KACtBC,cAAsB,KACtBC,aAAsB,KACtBC,OAAuBC,QAAQ,EAAOC,IAAK,EAAGC,UAAW,EAAGC,aAAa,GACzE7d,QAAsBrT,EACtBmxB,OAAsBnc,GACtBoc,OAAsBhc,GACtBic,aAAsBtb,GACtBub,cAAsBrb,GACtBjC,UAAsBuB,GACtBjC,WAAsBmC,GACtBjC,YAAsBmC,GACtBjC,WAAsBmC,GAGxBnR,IAAMvG,OAAQoB,IAGZgyB,gBAAiB,SAASC,GAEnBA,EAEE/0B,KAAKg1B,gBAERh1B,KAAK8W,MAAQ9W,KAAKg1B,cAClBh1B,KAAKg1B,eAAgB,GAGdh1B,KAAKg1B,gBAEdh1B,KAAKg1B,cAAgBh1B,KAAK8W,MAC1B9W,KAAK8W,MAAQ/W,GAAOoZ,aAAcnZ,QAItCi1B,eAAgB,SAASF,GAElBA,EAEE/0B,KAAKk1B,eAERl1B,KAAK+T,KAAO/T,KAAKk1B,aACjBl1B,KAAKk1B,cAAe,GAGbl1B,KAAKk1B,eAEdl1B,KAAKk1B,aAAel1B,KAAK+T,KACzB/T,KAAK+T,KAAOhU,GAAOkZ,YAAajZ,QAIpCm1B,eAAgB,SAASJ,GAElBA,EAEE/0B,KAAKo1B,eAERp1B,KAAKgX,KAAOhX,KAAKo1B,aACjBp1B,KAAKo1B,cAAe,GAGbp1B,KAAKo1B,eAEdp1B,KAAKo1B,aAAep1B,KAAKgX,KACzBhX,KAAKgX,KAAOjX,GAAOsZ,YAAarZ,QAKpCq1B,MAAO,SAAS5wB,EAAUhB,EAAS6xB,GAEjC,MAAOt1B,MAAKyW,aAAanE,QAAS7N,EAAUhB,EAAS6xB,IAGvDC,SAAU,WAER,GAAIzhB,GAAK9T,IAEL8T,GAAGrQ,QAELqQ,EAAGrQ,QAAQkR,MAAO3U,MAIlB8T,EAAGiC,UAAYjC,EAAGI,QAItBS,MAAO,SAASoR,GAEd,GAAIjS,GAAK9T,IAUT,OARA8T,GAAGyhB,WACHzhB,EAAG+B,OAAOlB,QAELoR,GAEHjS,EAAGpM,MAGEoM,GAGTmS,WAAY,WAEV,MAAOjmB,MAAK6V,OAAO2f,SAAS,SAASnpB,GAEnC,MAAOA,GAAMopB,gBAIjBpY,MAAO,SAAS2I,EAAsBD,GAEpC,GAAIjS,GAAK9T,KACL4R,EAAU,GAAI8E,GAsBlB,OApBKsP,IAAwBlS,EAAGmS,aAE9BrU,EAAQyI,OAAQvG,IAIhBA,EAAGa,MAAOoR,GAEVjS,EAAGgD,MAAMuG,YACP,WAEEzL,EAAQa,QAASqB,IAEnB,WAEElC,EAAQyI,OAAQvG,MAKflC,GAIT8jB,QAAS,SAASC,GAEhB,IAAMnzB,EAAUmzB,GAEd,OAAO,CAGT,KAAK,GAAIzxB,KAAQyxB,GAEf,IAAM31B,KAAKkzB,cAAehvB,GAExB,OAAO,CAIX,QAAO,GAIT0xB,UAAW,SAASlkB,EAAOjN,EAAUhB,EAASqY,GAO5C,QAAS+Z,KAEP,GAAI7U,GAASlN,EAAGwI,WAAY5K,EAAOoK,EAEnC,KAAgB,IAAXkF,IAAqBpP,EAAQE,cAAgBgC,EAAGmC,YACrD,CACE,GAAIG,GAAetC,EAAGsC,eAAiBtC,EAAGgiB,QAAS/O,GAAKL,KACpDqP,EAA2B,OAAX/U,IAAoBA,EAAOR,UAChC1M,GAAGgiB,QAAS/O,GAAKC,OAEf5Q,GAAgB2f,GAEzB/U,IAEJA,EAASlN,EAAGyB,WAAWygB,mBAAoBliB,EAAGyB,WAAW8G,kBAAmB3K,KAG9EsP,EAAOvW,MAAO1H,GAAM6B,OAAOqxB,WAAY,WAE/BrkB,EAAQE,eAEPtP,EAAUkP,IAEbsP,EAAOkV,KAAMxkB,GAGfE,EAAQa,QAASuO,EAAOR,WAAaQ,EAAS,SAIlDA,EAAOmV,SAAUpc,GAAQ2M,IAAK5S,EAAGggB,eAIjCliB,EAAQa,QAASuO,GAIrB,OAAOpP,EAAQE,aA3CjB,GAAIgC,GAAK9T,KACL4R,EAAU,GAAI8E,GAkDlB,OAhDA9E,GAAQU,QAAS7N,EAAUhB,GAAWqQ,GA2CjC+hB,KAEH/hB,EAAGuhB,MAAOQ,EAAY/hB,GAAI,GAGrBlC,GAgBT0K,WAAY,SAAS5K,EAAOoK,GAE1B,GAAIhI,GAAK9T,KACLuV,EAAazB,EAAGyB,WAChB6gB,EAAYtiB,EAAGsC,eAAiBtC,EAAGgiB,QAAS/O,GAAKL,IAErD,KAAMlmB,EAASkR,GAEb,QAAO0kB,GAAY,IAGhBvzB,GAAU6O,KAEbA,EAAQ,GAAIA,IAEThP,EAAYgP,KAEfA,EAAQA,IAGV,IAAI8D,GAAMD,EAAW8G,kBAAmB3K,EAExC,IAAKA,YAAiBoC,GAAG/Q,MAEvB,MAAO2O,EAEJ,IAAK8D,IAAO1B,GAAGI,IACpB,CACE,GAAI7H,GAAQyH,EAAGI,IAAKsB,EAgBpB,OAdKhT,GAAUkP,KAEb6D,EAAW8gB,sBAAuB3kB,GAE7BoK,EAEHhI,EAAGwiB,cAAe5kB,EAAO8D,EAAKnJ,GAI9BA,EAAM6pB,KAAMxkB,IAITrF,EAEJ,MAAK7J,GAAUkP,IAElB6D,EAAW8gB,sBAAuB3kB,GAE7BoK,EAEIhI,EAAGwiB,cAAe5kB,GAIlBoC,EAAGyiB,YAAaziB,EAAG6gB,OAAQjjB,OAG5B0kB,GAED,MAOXI,QAAS,WAEPx2B,KAAK2lB,OACL3lB,KAAKoK,QAAStH,GAAS8B,OAAO8tB,UAMhCtb,YAAa,SAASC,GAEf3U,EAAY2U,GAEfrX,KAAKy2B,iBAAmBpf,EAEhB/W,EAAU+W,GAElBrX,KAAKy2B,iBAAmB,SAASv1B,EAAGsE,GAElC,GAAIoB,GAAKpE,EAAUtB,IAAOmW,IAAYnW,GAAIA,EAAGmW,GAAapX,EACtD4G,EAAKrE,EAAUgD,IAAO6R,IAAY7R,GAAIA,EAAG6R,GAAapX,CAE1D,OAAO2G,KAAO3G,GAAa4G,IAAO5G,GAAoBoG,EAASO,EAAIC,GAAO,GAK5E7G,KAAKy2B,iBAAmB,SAASv1B,EAAGsE,GAElC,OAAO,IAOb0R,cAAe,SAASvW,EAAYuE,GAElClF,KAAK6V,OAAOqB,cAAevW,EAAYuE,IAGzCG,cAAe,SAAS1E,EAAYuE,GAElClF,KAAK6V,OAAOxQ,cAAe1E,EAAYuE,IAGzCoS,aAAc,SAASC,GAEhB7U,EAAY6U,GAEfvX,KAAKuX,UAAYA,EAETjX,EAAUiX,IAEyB,IAAtC9W,EAAST,KAAKsM,OAAQiL,GAEzBvX,KAAKuX,UAAY,SAASlL;4EAExB,MAAO7L,GAAS6L,GAAUA,EAAOkL,GAAclL,GAKjDrM,KAAKuX,UAAYzR,GAAiByR,GAKpCvX,KAAKuX,UAAY,SAASlL,GAExB,MAAOA,GAAM0M,SAMnB4M,KAAM,WAEJ3lB,KAAK6V,OAAO8P,QAIdvjB,SAAU,WAER,MAAOpC,MAAK6V,OAAOzT,YAGrByL,MAAO,WAEL,GAAIiG,GAAK9T,KACLya,EAAO3G,EAAG+B,OAAO4E,KACjB5E,EAAS/B,EAAG+B,MAEhB/B,GAAGyhB,UAEH,KAAK,GAAIz0B,GAAI,EAAGA,EAAI2Z,EAAKzZ,OAAQF,IAE/BgT,EAAG4iB,aAAc7gB,EAAQ/U,GAAK2Z,EAAM3Z,KAMxCw1B,cAAe,SAAShiB,EAASkB,EAAKnJ,EAAOyb,GAE3C,IAAMtlB,EAAU8R,GAEd,MAAOjI,EAGT,IAAIyH,GAAK9T,KACLwV,EAAMA,GAAO1B,EAAGyB,WAAWohB,OAAQriB,GAAS,EAGhD,KAAM9T,EAASgV,GAIb,WAFAzV,IAAO2S,MAAO3S,GAAO4S,OAAOuV,YAAapU,EAAIQ,EAK/C,IAAIjI,GAAQA,GAASyH,EAAGI,IAAKsB,GACzBohB,EAAU9iB,EAAG6gB,OAAQpwB,EAAM+P,GAG/B,IAAKjI,EACL,CAGE,GAFuBrM,KAAKy2B,iBAAkBpqB,EAAOiI,GAMnD,MAFAvU,IAAO2S,MAAO3S,GAAO4S,OAAO+W,kBAAmB5V,EAAIzH,EAAOiI,GAEnDjI,EAKX,GAAKA,EACL,CACOyH,EAAGyB,WAAWshB,aAAcxqB,EAAOuqB,KAEtCphB,EAAMnJ,EAAMyqB,QAAShjB,EAAGyB,WAAWohB,OAAQC,GAAS,KAGtD9iB,EAAG4iB,aAAcrqB,EAAOmJ,GAElBnJ,EAAM0qB,SAEV1qB,EAAM0qB,UAGR,IAAI5X,GAAU9S,EACV2qB,KACAC,GAAa,EACbT,KACAU,KACAC,KACAC,EAAiBjzB,EAASkI,EAAM0qB,QAChCvf,EAAY1D,EAAG0D,UACf6f,EAAYvjB,EAAG6gB,OAAQtoB,EAAM0qB,OAEjC,KAAK,GAAI7yB,KAAQoQ,GAEf,GAAwB,MAAnBpQ,EAAKwB,OAAO,GAKjB,GAAKxB,IAAQsT,GAEXnL,EAAM6pB,KAAMhyB,EAAMoQ,EAASpQ,IAAQ,OAFrC,CAOA,GAAIozB,GAAenY,EAASjb,GACxBqzB,EAAaF,EAAWnzB,EAE5BgzB,GAAUhzB,GAASmI,EAAOnI,GAC1BizB,EAAOjzB,GAASqzB,EAEXH,GAAkBtP,GAAarhB,EAAQ6wB,EAAcC,IAExDlrB,EAAOnI,GAAS0yB,EAAS1yB,GACzBsyB,EAAStyB,GAASoQ,EAASpQ,GAEtBmI,EAAMmrB,SAETnrB,EAAMmrB,OAAQtzB,GAASoQ,EAASpQ,MAKlC8yB,EAAW9yB,GAASoQ,EAASpQ,GAC7B+yB,GAAa,GAGf5qB,EAAM0qB,OAAQ7yB,GAASK,EAAM+P,EAASpQ,IAGnC+yB,EAEH5qB,EAAMzB,SAAU7H,GAAM6B,OAAO6yB,eAAgBnjB,EAASkiB,EAASU,EAAUC,EAAOH,IAIhF3qB,EAAMzB,SAAU7H,GAAM6B,OAAO8yB,YAAapjB,EAASkiB,EAASU,EAAUC,EAAOH,IAG/E3qB,EAAMzB,SAAU7H,GAAM6B,OAAO+yB,cAAerjB,EAASkiB,EAASU,EAAUC,EAAOH,IAE/E3qB,EAAM6V,cAAetE,IAEf9J,EAAG+B,OAAO+hB,IAAKpiB,KAEnB1B,EAAG+jB,cAAexrB,EAAOmJ,GACzB1B,EAAG1J,QAAStH,GAAS8B,OAAO+tB,YAAatmB,GAAO,UAMlDA,EAAQyH,EAAGgkB,YAAalB,GAAS,MAI1B9iB,EAAG2f,QAAUjN,GAAME,KAEtBra,EAAMmrB,OAASnrB,EAAM0rB,SAAS,GAC9B1rB,EAAMmrB,OAAOQ,QAAU3rB,EAAM2rB,QAC7B3rB,EAAM0qB,OAAS1qB,EAAMmrB,OAAOT,OAAS1qB,EAAM0rB,SAAS,GAEpD1rB,EAAM6V,cAAetE,KAIrBvR,EAAM0qB,OAAS1qB,EAAM0rB,SAAS,GAKpC,OAAO1rB,IAGTyrB,YAAa,SAASlB,EAAS9a,GAE7B,GAAIhI,GAAK9T,KACLqM,EAAQyH,EAAGyiB,YAAaK,EAAS9a,EAErC,KAAwB,IAAnBzP,EAAM4rB,SAIT,WAFAl4B,IAAO2S,MAAO3S,GAAO4S,OAAOuV,YAAapU,EAAI8iB,EAK/C,IAAIphB,GAAMnJ,EAAM0M,MAQhB,OANMjF,GAAG+B,OAAO+hB,IAAKpiB,KAEnB1B,EAAG+jB,cAAexrB,EAAOmJ,GACzB1B,EAAG1J,QAAStH,GAAS8B,OAAO+tB,YAAatmB,EAAOyP,KAG3CzP,GAGT6rB,aAAc,SAAS7rB,EAAO8rB,GAE5Bn4B,KAAKo4B,WAAY/rB,EAAO8rB,GAExB9rB,EAAMzB,SAAU7H,GAAM6B,OAAOyzB,iBAE7Bt4B,GAAO2S,MAAO3S,GAAO4S,OAAO0V,cAAeroB,KAAMqM,IAGnD+rB,WAAY,SAAS/rB,EAAO8rB,GAE1B,GAAIrkB,GAAK9T,KACLwV,EAAM2iB,GAAY9rB,EAAM0M,MAE5BjF,GAAGwkB,gBAAiB9iB,GACpB1B,EAAG+B,OAAOxM,OAAQmM,GAClB1B,EAAG1J,QAAStH,GAAS8B,OAAOiuB,cAAexmB,KAG7CisB,gBAAiB,SAAS9iB,SAEjBxV,MAAKkU,IAAKsB,IAGnB+iB,WAAY,WAEV,MAAOv4B,MAAKq0B,MAAME,KAAOv0B,KAAKq0B,MAAMG,WAGtCgE,YAAa,WAEX,GAAI1kB,GAAK9T,KACLq0B,EAAQvgB,EAAGugB,MACXxe,EAAS/B,EAAG+B,MAEhB,KAAIwe,EAAME,KAAOF,EAAMG,YAEjBH,EAAMC,OACV,CAoBE,IAnBA,GAAImE,GAAkB1T,KAAQsP,EAAMG,UAEhC4D,EAAa,SAAS/rB,GAEpBgoB,EAAMI,YAERpoB,EAAMqsB,QAAS3e,GAAQQ,OAIvBzG,EAAGskB,WAAY/rB,IAIfssB,EAAa,SAAStsB,GAExB,MAAOA,GAAMusB,UAAYH,GAGnBpE,EAAME,KAAO1e,EAAO7U,OAASqzB,EAAME,KAC3C,CACE,GAAIsE,GAAWhjB,EAAOijB,SAAS,WAE3BD,IAEFT,EAAYS,GAIXxE,EAAMG,WAET3e,EAAOkjB,UAAWX,EAAYO,KAMtCK,0BAA2B,SAAS3sB,EAAOmJ,GAEzC,GAAI1B,GAAK9T,IAET,SAAKqM,IAEEA,EAAM4sB,qBAEF5sB,GAAM0qB,OAEbjjB,EAAGyB,WAAW2jB,UAAW7sB,GAEzBA,EAAMzB,SAAU7H,GAAM6B,OAAOu0B,SAEtB,IAGTrlB,EAAGokB,aAAc7rB,EAAOmJ,IAEjB,KAMX4jB,wBAAyB,SAAS/sB,EAAOmJ,GAEvC,GAAI1B,GAAK9T,IAET,OAAKqM,GAGEA,EAAM4sB,qBAGF5sB,GAAM0qB,OAEbjjB,EAAGyB,WAAW2jB,UAAW7sB,GAEpBA,EAAMmrB,eAEFnrB,GAAMmrB,OAAOT,OAEpBjjB,EAAGyB,WAAW2jB,UAAW7sB,EAAMmrB,SAGjCnrB,EAAMzB,SAAU7H,GAAM6B,OAAOu0B,QAE7B9sB,EAAM6V,cAAetE,KAEd,IAGTvR,EAAM6V,cAAezE,IAErB3J,EAAGokB,aAAc7rB,EAAOmJ,IAgBnB,IAZL1B,EAAGgD,MAAMzN,OAAQmM,EAAK,SAAS6jB,GAEzBA,GAEFt5B,GAAO2S,MAAO3S,GAAO4S,OAAO0V,cAAevU,EAAIulB,MAK5C,IAOXC,kBAAmB,SAAS9jB,GAE1B,GAAI1B,GAAK9T,KACLqM,EAAQyH,EAAGI,IAAKsB,EAEpB,OAAK1B,GAAG2f,QAAUjN,GAAME,IAEf5S,EAAGslB,wBAAyB/sB,EAAOmJ,GAInC1B,EAAGklB,0BAA2B3sB,EAAOmJ,IAIhDjD,WAAY,WAEV,GAAIuB,GAAK9T,IAETgV,IAAa,WAEX,IAAK,GAAIQ,KAAO1B,GAAGkC,OACnB,CACE,GAAI3J,GAAQyH,EAAGkC,OAAQR,EAElBnJ,GAAM2rB,UAAYj1B,GAAMga,OAAOwc,eAElCx5B,GAAO2S,MAAO3S,GAAO4S,OAAOiW,oBAAqB9U,EAAIzH,GAErDA,EAAM6V,cAAexE,MAIhBrR,EAAM2rB,UAAYj1B,GAAMga,OAAOyc,aAElCz5B,GAAO2S,MAAO3S,GAAO4S,OAAOkW,kBAAmB/U,EAAIzH,GAEnDA,EAAM6V,cAAerE,KAIrB9d,GAAO2S,MAAO3S,GAAO4S,OAAOmW,iBAAkBhV,EAAIzH,GAGpDyH,EAAG+jB,cAAexrB,EAAOmJ,GAAK,OAKpC1B,EAAGkC,UACHlC,EAAG0iB,UAEE1iB,EAAGgiB,QAAS/O,GAAKL,OAEU,IAAzB5S,EAAGwC,kBAENxC,EAAG2lB,UAIH3lB,EAAGuC,cAAe,IAKxByf,QAAS,SAASvQ,GAEhB,MAA8B,KAAtBvlB,KAAKulB,KAAOA,IAGtBlT,UAAW,SAASqnB,GAIlB,QAASC,GAAYvH,EAAS3X,GAE5B1a,GAAO2S,MAAO3S,GAAO4S,OAAOgW,WAAY7U,EAAIse,EAE5C,KAAK,GAAItxB,GAAI,EAAGA,EAAIsxB,EAAQpxB,OAAQF,IACpC,CACE,GAAIwT,GAAU8d,EAAStxB,GACnB0U,EAAMiF,EAAM3Z,GACZ81B,EAAU9iB,EAAG6gB,OAAQpwB,EAAM+P,GAAS,IACpCnJ,EAAW2I,EAAGI,IAAKsB,GACnBnJ,EAAQlB,GAAY2I,EAAGyiB,YAAaK,GAAS,EAOjD,IALIzrB,GAEFkB,EAAM6pB,KAAMU,EAAS32B,GAAW,IAGV,IAAnBoM,EAAM4rB,SACX,CACEl4B,GAAO2S,MAAO3S,GAAO4S,OAAOuV,YAAapU,EAAIQ,EAE7C,OAGFjI,EAAMmrB,OAASljB,EACfjI,EAAM0qB,OAASziB,EAAQyiB,OAElB1qB,EAAM2rB,UAAYj1B,GAAMga,OAAO6c,UAElC9lB,EAAGkC,OAAQR,GAAQnJ,EACnByH,EAAG4iB,aAAcrqB,EAAOmJ,IAI5B1B,EAAGqC,aAAc,EACjBrC,EAAG+lB,YAAa/2B,GAAS8B,OAAO6tB,WAEhCiH,GAAU,EAAM5lB,GAGlB,QAASgmB,KAEPhmB,EAAGimB,WAEHL,GAAU,EAAO5lB,GA9CnB,GAAIA,GAAK9T,IAiDJ8T,GAAGgiB,QAAS/O,GAAKL,MAAS5S,EAAG0f,aAEhCzzB,GAAO2J,MAAO3J,GAAO6E,OAAO0hB,OAAQxS,EAAGkmB,SAAUlmB,GAG9CA,EAAG2f,QAAUjN,GAAMC,MAEtB3S,EAAGimB,WAEHL,GAAU,EAAO5lB,IAIjBA,EAAGgD,MAAM5C,IAAKylB,EAAaG,IAI/BD,YAAa,SAASI,EAAWC,GAE/B,GAAIpmB,GAAK9T,IAET8T,GAAGmC,aAAc,EACjBnC,EAAG1J,QAAS6vB,GAAanmB,GAAKqmB,OAAQD,QACtCpmB,EAAG2C,aAAa4G,QAAQ5K,QAASqB,IAGnCimB,SAAU,WAER,GAAIjmB,GAAK9T,IAEJ8T,GAAGgiB,QAAS/O,GAAKL,KAEpB5S,EAAG2lB,UAIH3lB,EAAG+lB,YAAa/2B,GAAS8B,OAAO2tB,SAIpCyH,SAAU,WAER,GAAIlmB,GAAK9T,IAET8T,GAAGyC,aAAc,EAEa,IAAzBzC,EAAGwC,mBAENxC,EAAGsmB,mBAIPA,gBAAiB,WAEf,GAAItmB,GAAK9T,MAEF8T,EAAG0f,aAAe1f,EAAGsC,cAAgBtC,EAAGyC,aAAiBzC,EAAGuC,gBAEjEvC,EAAGyC,aAAc,EACjBzC,EAAGuC,cAAe,EAElBtW,GAAO2S,MAAO3S,GAAO4S,OAAOsV,aAAcnU,GAE1CA,EAAG2lB,YAIPY,qBAAsB,SAASzoB,GAE7B,GAAIkC,GAAK9T,IAET,OAAO,UAA0BuZ,GAK/B,IAAK,GAHD1D,GAAS/B,EAAG+gB,cAAetb,GAC3B+gB,KAEKx5B,EAAI,EAAGA,EAAI+U,EAAO7U,OAAQF,IACnC,CACE,GAAIuL,GAAQyH,EAAGwiB,cAAezgB,EAAQ/U,GAEtC,IAAKuL,EACL,CAGEiuB,EAFUjuB,EAAM0M,QAEA1M,GAIpB,GAAKyH,EAAGwf,YAIN,IAAK,GAFD7Y,GAAO3G,EAAG+B,OAAO4E,OAAOlZ,QAEnBT,EAAI,EAAGA,EAAI2Z,EAAKzZ,OAAQF,IACjC,CACE,GAAIgB,GAAI2Y,EAAM3Z,EAEd,MAAOgB,IAAKw4B,IACZ,CACE,GAAInsB,GAAM2F,EAAG+B,OAAOhE,IAAK/P,EAEpBqM,GAAI4oB,SAEPh3B,GAAO2S,MAAO3S,GAAO4S,OAAO8V,mBAAoB3U,EAAIhS,GAEpDgS,EAAGwlB,kBAAmBx3B,KAM9BgS,EAAGsC,cAAe,EAClBtC,EAAG+lB,YAAa/2B,GAAS8B,OAAO4tB,YAEhC1e,EAAG0iB,UAEHz2B,GAAO2S,MAAO3S,GAAO4S,OAAO2V,YAAaxU,EAAI+B,GAE7CjE,EAAQa,QAASqB,EAAG+B,UAIxB0kB,qBAAsB,SAAS3oB,GAE7B,GAAIkC,GAAK9T,IAET,OAAO,UAA0BuZ,EAAUa,GAEzB,IAAXA,GAEHra,GAAO8xB,qBAED9xB,GAAOqxB,SAEXtd,EAAGoC,gBAAiB,EAEpBnW,GAAOyJ,KAAMzJ,GAAO6E,OAAO0hB,OAAQxS,EAAG0mB,gBAAiB1mB,IAGzD/T,GAAO2S,MAAO3S,GAAO4S,OAAO4V,oBAAqBzU,KAIjD/T,GAAO2S,MAAO3S,GAAO4S,OAAO6V,kBAAmB1U,EAAIsG,GAEnDtG,EAAG+lB,YAAa/2B,GAAS8B,OAAO2tB,QAAShZ,KAG3C3H,EAAQyI,OAAQvG,EAAG+B,UAIvB4kB,eAAgB,SAASnoB,EAAS6B,GAEhCnU,KAAK+T,KAAKG,IAAKlU,KAAK6zB,WAAYvhB,EAAS6B,IAI3CslB,QAAS,SAASh1B,EAAUhB,GAE1B,GAAIqQ,GAAK9T,KACL4R,EAAU,GAAI8E,IACdpE,EAAUtS,KAAKq6B,qBAAsBzoB,GACrCuC,EAAUnU,KAAKu6B,qBAAsB3oB,EASzC,OAPAA,GAAQ8oB,SAAUj2B,EAAUhB,GAAWqQ,GAEvCkB,GAAa,WAEXlB,EAAG2mB,eAAgBnoB,EAAS6B,KAGvBvC,GAGT4oB,gBAAiB,WAEf,GAAI1mB,GAAK9T,IAETD,IAAO2S,MAAO3S,GAAO4S,OAAO+V,mBAAoB5U,GAE3CA,EAAGoC,iBAENpC,EAAGoC,gBAAiB,EAEpBpC,EAAG2lB,YAKP5nB,IAAK,SAAS2D,GAEZ,MAAOxV,MAAKkU,IAAKlU,KAAKuV,WAAW8G,kBAAmB7G,KAGtDqG,OAAQ,SAAS8e,GAEf,GAAIzmB,GAAMlU,KAAKkU,IACX0mB,IAEJ,KAAK,GAAIplB,KAAOtB,GAChB,CACE,GAAI7H,GAAQ6H,EAAKsB,EAEZmlB,GAAStuB,IAEZuuB,EAASzxB,KAAMkD,GAInB,MAAOuuB,IAGTC,SAAU,SAASC,EAAOn6B,GAExB,GAAIC,GAAMD,GAAc8F,CAExB,OAAOpF,GAASrB,KAAKqzB,UAAmD,IAAvC5yB,EAAST,KAAKqzB,OAAQyH,EAAOl6B,IAGhEm6B,UAAW,SAAS1H,EAAQ1yB,GAE1B,IAAK,GAAIG,GAAI,EAAGA,EAAIuyB,EAAOryB,OAAQF,IAEjC,IAAMd,KAAK66B,SAAUxH,EAAQvyB,GAAKH,GAEhC,OAAO,CAIX,QAAO,GAGTq6B,SAAU,SAASxlB,EAAKlB,GAEtBtU,KAAKs2B,cAAehiB,EAASkB,GAC7BxV,KAAKw2B,UAELz2B,GAAO2S,MAAO3S,GAAO4S,OAAOoW,cAAe/oB,KAAMsU,EAASkB,IAG5DylB,WAAY,SAASzlB,GAEdxV,KAAKs5B,kBAAmB9jB,IAE3BxV,KAAKw2B,UAGPz2B,GAAO2S,MAAO3S,GAAO4S,OAAOqW,gBAAiBhpB,KAAMwV,IAIrD+gB,YAAa,SAAS/d,EAAMsD,GAE1B,MAAO,IAAI9b,MAAK+C,MAAOyV,EAAMsD,IAG/B4a,aAAc,SAASrqB,EAAOmJ,GAEvBxV,KAAK4zB,eAER5zB,KAAKkU,IAAKsB,GAAOnJ,EAAM0M,QAAW1M,IAItCwrB,cAAe,SAASxrB,EAAOmJ,EAAK0lB,GAE5Bl7B,KAAK4zB,cAET5zB,KAAK6V,OAAOqc,IAAK1c,GAAOnJ,EAAM0M,OAAQ1M,EAAO6uB,IAKjD/iB,KAAM,SAAS9L,EAAOyG,EAASzH,GAE7B,GAAIyI,GAAK9T,IAET,IAAKqM,EAAM8uB,aAIT,WAFAp7B,IAAO2S,MAAO3S,GAAO4S,OAAO8W,aAAc3V,EAAIzH,EAKhD,IAAImJ,GAAMnJ,EAAM0M,MACDjF,GAAG+B,OAAO+hB,IAAKpiB,IAI5B1B,EAAG1J,QAAStH,GAAS8B,OAAOguB,cAAevmB,IAE3CA,EAAMzB,SAAU7H,GAAM6B,OAAOw2B,iBAI7BtnB,EAAG+jB,cAAexrB,EAAOmJ,GACzB1B,EAAG1J,QAAStH,GAAS8B,OAAO+tB,YAAatmB,IACzCyH,EAAG0iB,UAEHnqB,EAAMzB,SAAU7H,GAAM6B,OAAOy2B,gBAG/BhvB,EAAM6V,cAAevE,GAAW7K,EAASzH,IAI3ChC,OAAQ,SAASgD,EAAOyG,EAASzH,GAE/B,GAAIyI,GAAK9T,IAGTA,MAAKs7B,iBAAkBjvB,GAGlBA,EAAM2rB,UAAYj1B,GAAMga,OAAOyc,aAElCz5B,GAAO2S,MAAO3S,GAAO4S,OAAO6X,mBAAoB1W,EAAIzH,GAGtDA,EAAM2rB,QAAUj1B,GAAMga,OAAOwc,cAE7BltB,EAAM6V,cAAe1E,GAAa1K,EAASzH,IAG7CiwB,iBAAkB,SAASjvB,GAEzB,GAAIyH,GAAK9T,KACLwV,EAAMnJ,EAAM0M,MAEXjF,GAAG+B,OAAO+hB,IAAKpiB,KAElB1B,EAAG+B,OAAOxM,OAAQmM,GAClB1B,EAAG1J,QAAStH,GAAS8B,OAAOiuB,cAAexmB,IAC3CyH,EAAG0iB,UAEHnqB,EAAMzB,SAAU7H,GAAM6B,OAAOg1B,aAMnCnxB,EAAa3F,IAEbuE,EAAkBvE,GAAU,SAAUA,GAAS8B,OAAOwa,SAwCtDrc,GAAM6B,QAEJ22B,QAAsB,UACtBC,MAAsB,QACtBC,QAAsB,WACtBC,SAAsB,YACtBC,UAAsB,aACtBC,WAAsB,cACtBnE,cAAsB,iBACtBC,WAAsB,cACtBhF,QAAsB,UACtByG,OAAsB,SACtB0C,OAAsB,SACtBR,cAAsB,gBACtBD,cAAsB,gBACtBU,UAAsB,aACtBC,eAAsB,kBACtBnC,QAAsB,UACtBjC,aAAsB,gBACtBqE,UAAsB,aACtBC,iBAAsB,qBACtBC,WAAsB,gCACtBja,WAAsB,cACtBka,kBAAsB,sBACtBC,kBAAsB,sBACtBC,YAAsB,sDACtBC,YAAsB,eACtBC,mBAAsB,uBACtBC,aAAsB,oCACtBC,aAAsB,gBACtBC,oBAAsB,wBACtBC,oBAAsB,wBACtBC,cAAsB,4DACtBC,SAAsB,YACtBC,gBAAsB,oBACtBC,UAAsB,8BACtBC,UAAsB,aACtBC,iBAAsB,qBACtBC,iBAAsB,qBACtBjH,WAAsB,mDACtBoC,gBAAsB,wBACtB8E,kBAAsB,sBACtBrK,kBAAsB,qBACtBC,mBAAsB,sBACtBpQ,UAAsB,aACtBvD,QAAsB,4EAGxBrc,GAAMga,QAEJqgB,OAAgB,EAChB5D,YAAgB,EAChBD,cAAgB,EAChBK,QAAgB,GAGlB72B,GAAMs6B,SAEJv5B,UAAU,EACVw5B,SAAS,GAGXr1B,GAAMvG,OAAQqB,IAGZ4Z,MAAO,SAAS9R,EAAOiR,GAcrB,GAZA9b,KAAKg4B,QAAUj1B,GAAMga,OAAOqgB,OAE5Bn1B,GAAM4C,MAAM7K,MACVu9B,WAAY,KACZC,cACAC,YAAa,GAAI9iB,IAAY3a,MAC7B09B,aAAa,EACb3G,QAAQ,EACRS,QAAQ,EACRoB,SAAU7T,OAGPjJ,EACL,CACE,GAAItG,GAAMxV,KAAKyiB,IAAIlN,WAAWohB,OAAQ9rB,GAAO,EAE7C,KAAMrK,EAASgV,GAIb,WAFAvN,IAAM/D,KAAMlE,KAAM,YAAY,EAKhCA,MAAKyiB,IAAIiU,aAAc12B,KAAMwV,GAC7BxV,KAAKk2B,KAAMrrB,EAAO5K,EAAW6b,OAI7B9b,MAAK29B,OAAQ9yB,EAGf7K,MAAK49B,eAAgB9hB,IAGvB8hB,eAAgB,SAAS9hB,GAEvB,GAAK9b,KAAKyiB,IAAI8Q,cACd,CACE,GAAIsK,GAAoB79B,KAAKyiB,IAAIjL,SAEjC,KAAK,GAAIxS,KAAQ64B,GACjB,CACiBA,EAAmB74B,GAEnB84B,MAEb99B,KAAK+9B,aAAc/4B,EAAM/E,EAAW6b,MAM5CkiB,MAAO,SAASxmB,GAEd,GAAKnW,EAASmW,GAEZ,IAAK,GAAI1W,GAAI,EAAGA,EAAI0W,EAAUxW,OAAQF,IAEpCd,KAAK+9B,aAAcvmB,EAAW1W,QAG7B,IAAKR,EAAUkX,GAElBxX,KAAK+9B,aAAcvmB,OAGrB,CACE,GAAIqmB,GAAoB79B,KAAKyiB,IAAIjL,SAEjC,KAAK,GAAIxS,KAAQ64B,GAEf79B,KAAK+9B,aAAc/4B,KAKzB24B,OAAQ,SAAS9yB,GAEf,GAAIozB,GAAMj+B,KAAKyiB,IAAInX,SACfgB,EAAStM,KAAKyiB,IAAInW,OAClBkL,EAAYxX,KAAKyiB,IAAIjL,UACrBjC,EAAavV,KAAKyiB,IAAIlN,WACtB2oB,EAAYl+B,KAAKyiB,IAAIjN,GAEzB,IAAMrR,EAAS85B,GAab,IAAK,GAAIn9B,GAAI,EAAGA,EAAIwL,EAAOtL,OAAQF,IACnC,CACE,GAAIoD,GAAOoI,EAAQxL,EAEnBd,MAAMkE,GAASjE,MAfjB,KAAK,GAAIa,GAAI,EAAGA,EAAIwL,EAAOtL,OAAQF,IACnC,CACE,GAAIoD,GAAOoI,EAAQxL,GACf0K,EAAeyyB,EAAK/5B,GACpBi6B,EAAiB95B,EAAUmH,EAE/BxL,MAAMkE,GAASi6B,EAanB,GAAI3oB,GAAM,IA6BV,IAzBK3K,IAEH2K,EAAMD,EAAWohB,OAAQ9rB,GAAO,IAI5BrK,EAASgV,GAOb5I,EAA2B5M,KAAMk+B,EAAWrzB,EAAOqzB,GALnD1oB,EAAMD,EAAWohB,OAAQ32B,MAUtBQ,EAASgV,KAEZxV,KAAKyiB,IAAIiU,aAAc12B,KAAMwV,GAC7BxV,KAAKo+B,MAAQ5oB,IAITrR,EAAS85B,GAEb,IAAK,GAAI/5B,KAAQsT,GAEf,GAAKtT,IAAQ+5B,GACb,CACE,GAAIzyB,GAAeyyB,EAAK/5B,GACpBi6B,EAAiB95B,EAAUmH,GAC3B6yB,IAAgBr+B,KAAKw9B,WAAYt5B,GACjC8T,EAAWhY,KAAK+9B,aAAc75B,EAAMi6B,EAEnCE,IAEHrmB,EAAS4G,IAAK5e,KAAMm+B,GAO5Bn+B,KAAKk2B,KAAMrrB,IAGbqrB,KAAM,SAASrrB,EAAOqG,EAAO4K,EAAYwiB,GAEvC,GAAK97B,EAAUqI,GAEb,IAAK,GAAI3G,KAAQ2G,GAEf7K,KAAKk2B,KAAMhyB,EAAM2G,EAAO3G,GAAQ4X,GAAY,OAG3C,IAAKxb,EAAUuK,GACpB,CACE,GAAK9H,GAAMs6B,QAASxyB,GAElB,MAGF,IAAI0B,GAASvM,KAAKu+B,aAAc1zB,GAC5BmN,EAAWhY,KAAK+9B,aAAclzB,EAAOqG,EAAO4K,EAE3C9D,GAEEzL,GAEHyL,EAAS4G,IAAK5e,KAAMkR,EAAO4K,GAK7B9b,KAAM6K,GAAUqG,GAIdotB,GAAe99B,EAASqK,IAE5B7K,KAAK4K,SAAU7H,GAAM6B,OAAOi3B,QAAShxB,EAAOqG,KAIhDstB,KAAM,SAAS3zB,EAAOsC,GAEpB,GAAK9L,EAASwJ,GAEZ,MAAOqC,GAAMlN,KAAM6K,EAAOsC,EAEvB,IAAK3K,EAAUqI,GACpB,CACE,IAAK,GAAIwC,KAAKxC,GAEZA,EAAOwC,GAAMF,EAAa5I,EAAMvE,KAAMqN,IAAQrN,KAAMqN,EAGtD,OAAOxC,GAEJ,GAAKvK,EAAUuK,GACpB,CACE,GAAK9H,GAAMs6B,QAASxyB,GAElB,MAGF,IAAImN,GAAWhY,KAAK+9B,aAAclzB,EAElC,IAAKmN,EACL,CACE,GAAI7W,GAAS6W,EAASnG,IAAK7R,KAE3B,OAAOmN,GAAa5I,EAAMpD,GAAWA,EAIrC,MAAOgM,GAAa5I,EAAMvE,KAAM6K,IAAY7K,KAAM6K,KAKxD4zB,QAAS,WAEPz+B,KAAKyiB,IAAIkS,OAAQ30B,OAGnB0+B,MAAO,SAASx6B,EAAMy6B,GAEpB,GAAI3mB,GAAWhY,KAAK+9B,aAAc75B,EAE7B8T,IAEHA,EAAS4mB,KAAM5+B,KAAM2+B,IAIzBE,QAAS,SAAS36B,EAAM46B,EAAQhjB,GAE9B,GAAI9D,GAAWhY,KAAK+9B,aAAc75B,EAE7B8T,IAEHA,EAAS8mB,OAAQ9+B,KAAM8+B,EAAQhjB,IAInCijB,UAAW,SAAS76B,EAAM86B,EAAWljB,GAEnC,GAAI9D,GAAWhY,KAAK+9B,aAAc75B,EAE7B8T,IAEHA,EAASinB,SAAUj/B,KAAMg/B,EAAWljB,IAIxCojB,WAAY,SAASh7B,EAAMi7B,GAEzB,GAAInnB,GAAWhY,KAAK+9B,aAAc75B,EAElC,OAAO8T,IAAYA,EAASonB,UAAWp/B,KAAMm/B,IAG/CZ,aAAc,SAASr6B,GAErB,MAAOA,KAAQlE,MAAKw9B,YAGtBO,aAAc,SAAS75B,EAAMm7B,EAAcvjB,GAEzC,GAAI+hB,GAAoB79B,KAAKyiB,IAAIjL,UAC7BQ,EAAW6lB,EAAmB35B,EAElC,SAAK8T,IAEI9T,IAAQlE,MAAKw9B,YAElBxlB,EAASuN,KAAMvlB,KAAMq/B,EAAcvjB,GAG9B9D,IAMXyI,MAAO,SAAS6e,EAAeC,EAAUzsB,EAASzH,GAqBhD,GAnBK7I,EAAU88B,IAEbj0B,EAAUyH,EACVA,EAAUysB,EACVA,EAAWt/B,GAEH+C,EAAUs8B,KAElBj0B,EAAUk0B,EACVzsB,EAAUwsB,EACVC,EAAWt/B,EACXq/B,EAAgBr/B,GAGZ+C,EAAU8P,KAEdA,EAAU9S,KAAKyiB,IAAI3P,SAGhB9S,KAAKm7B,aAIR,MAFAp7B,IAAO2S,MAAO3S,GAAO4S,OAAO8W,aAAczpB,KAAKyiB,IAAKziB,MAE7C0W,GAAQjE,QAASzS,KAG1B,KAAMA,KAAKw/B,UAET,KAAM,wBAGR,IAAI5tB,GAAU6H,GAAoBzZ,KAAM8S,EACtC/P,GAAM6B,OAAOqd,WACblf,GAAM6B,OAAOu3B,kBACbp5B,GAAM6B,OAAOw3B,kBACbr5B,GAAM6B,OAAOo3B,UACbj5B,GAAM6B,OAAOq3B,iBAGf,OAAOvlB,IAAQwP,YAAatU,EAAS5R,KAAM,SAASkmB,GAElDlR,GAAa,WAEXhV,KAAKy/B,SAELz/B,KAAKyiB,IAAIiU,aAAc12B,MAElBs/B,IAAkBr/B,GAErBD,KAAKk2B,KAAMoJ,EAAeC,GAG5Bv/B,KAAK4K,SAAU7H,GAAM6B,OAAO62B,SAAUz7B,OAEtCA,KAAKyiB,IAAItK,KAAMnY,KAAM8S,EAASzH,GAE9BrL,KAAKyiB,IAAI+V,cAETx4B,KAAK4K,SAAU7H,GAAM6B,OAAO82B,UAAW17B,QAEtCA,SAIP04B,QAAS,SAAS5lB,EAASzH,GAEzB,GAAIyH,GAAU9P,EAAU8P,GAAYA,EAAU9S,KAAKyiB,IAAI3P,OAEvD,KAAM9S,KAAK0/B,UAET,MAAOhpB,IAAQjE,QAASzS,KAG1B,IAAI4R,GAAU6H,GAAoBzZ,KAAM8S,EACtC/P,GAAM6B,OAAO63B,aACb15B,GAAM6B,OAAO83B,oBACb35B,GAAM6B,OAAO+3B,oBACb55B,GAAM6B,OAAO03B,YACbv5B,GAAM6B,OAAO23B,mBAGf,OAAO7lB,IAAQwP,YAAatU,EAAS5R,KAAM,SAASkmB,GAElDlR,GAAa,WAEXhV,KAAK4K,SAAU7H,GAAM6B,OAAO+2B,WAAY37B,OAExCA,KAAKyiB,IAAIpZ,OAAQrJ,KAAM8S,EAASzH,GAEhCrL,KAAK4K,SAAU7H,GAAM6B,OAAOg3B,YAAa57B,QAExCA,SAIPm2B,SAAU,SAASrjB,EAASzH,GAE1B,GAAIuG,GAAU6H,GAAoBzZ,KAAM8S,EACtC/P,GAAM6B,OAAOo4B,UACbj6B,GAAM6B,OAAOq4B,iBACbl6B,GAAM6B,OAAOs4B,iBACbn6B,GAAM6B,OAAOi4B,SACb95B,GAAM6B,OAAOk4B,gBAgBf,OAbKjqB,IAAYC,EAASiH,GAAQC,MAEhCha,KAAKkiB,cAAe5E,GAAWxK,EAASzH,GAEhCwH,GAAYC,EAASiH,GAAQQ,OAErCva,KAAKkiB,cAAe9E,GAAUtK,EAASzH,GAIvCuG,EAAQa,QAASzS,MAGZ4R,GAGT+tB,aAAc,SAAS7sB,EAASzH,GAE9B,GAAIu0B,GAAc,WAEhB5/B,KAAKm2B,SAAUrjB,EAASzH,GAK1B,OAFAtL,IAAO4E,GAAI5E,GAAO6E,OAAO0hB,OAAQsZ,EAAa5/B,MAEvCA,MAGT6/B,QAAS,SAASxiB,EAAOhS,GAElBrL,KAAK+2B,OAER/2B,KAAKygB,MAAOzgB,KAAK+2B,OAAQ/2B,KAAKyiB,IAAI3P,QAASzH,GAEnCgS,GAERrd,KAAK29B,UAITmC,OAAQ,SAAS77B,GAWf,IAAK,GAND6P,GAAK9T,KAAKyiB,IACVjN,EAAM1B,EAAG0B,IACTlJ,EAASwH,EAAGxH,OACZkL,EAAY1D,EAAG0D,UACfrW,KAEKL,EAAI,EAAGA,EAAIwL,EAAOtL,OAAQF,IACnC,CACE,GAAIi/B,GAAIzzB,EAAQxL,EAEXmD,IAAc87B,IAAK97B,GAEtB9C,EAAQ4+B,GAAM17B,EAAUJ,EAAY87B,IAE5BA,IAAK//B,QAEbmB,EAAQ4+B,GAAMx7B,EAAMvE,KAAM+/B,KAY9B,GARKz/B,EAAUkV,UAENrU,GAAQqU,GAGF1B,EAAGyB,WAAWohB,OAAQx1B,KACtBnB,KAAK+Y,OAIlB,KAAM,yDAGR,KAAK,GAAIinB,KAAgBxoB,GAElBvT,GAAc+7B,IAAgB/7B,IAEjCuT,EAAWwoB,GAAeC,SAAUjgC,KAAMmB,EAAQ8C,EAAY+7B,GAIlE,IAAI5jB,GAAQtI,EAAGyiB,YAAap1B,GACxB++B,IAEJ,KAAK,GAAIF,KAAgBxoB,GAElBvT,GAAc+7B,IAAgB/7B,IAEjCuT,EAAWwoB,GAAeG,UAAWngC,KAAMkgC,EAAgBj8B,EAAY+7B,GAM3E,OAFA5jB,GAAM8Z,KAAMgK,GAEL9jB,GAGTgkB,MAAO,SAAS9zB,GAEdtM,KAAK09B,YAAc19B,KAAKyiB,IAAIiS,OAAQ10B,KAAMkN,EAAMlN,KAAMsM,GAAUtM,KAAKyiB,IAAInW,QAAQ,IAAQ,IAG3F+zB,KAAM,SAASC,GAER99B,EAAUxC,KAAK09B,eAElB19B,KAAKk2B,KAAMl2B,KAAK09B,aAEV4C,GAEJtgC,KAAKugC,aAKXA,SAAU,WAERvgC,KAAK09B,aAAc,GAGrBgC,QAAS,WAEP,OAAQ1/B,KAAKm7B,cAAgBn7B,KAAKyiB,IAAI5M,OAAO+hB,IAAK53B,KAAK+Y,SAGzDmJ,cAAe,SAASse,EAAe1tB,EAASzH,GAE9C,GAAIgJ,GAAY,GAAImsB,GAAexgC,KAAM8S,EAASzH,EAE5CrL,MAAKu9B,WAOTv9B,KAAKu9B,WAAWkD,MAAOpsB,IALvBrU,KAAKu9B,WAAalpB,EAClBrU,KAAKu9B,WAAWmD,YAQpB3I,QAAS,SAAUtf,GAEjB,GAAInE,GAAUtU,KAAKyiB,IAAIiS,OAAQ10B,KAAMkN,EAAMlN,KAAMA,KAAKyiB,IAAInW,QAAQ,GAAQmM,GAEtEolB,EAAoB79B,KAAKyiB,IAAIjL,UAC7BA,EAAYxX,KAAKw9B,UAErB,KAAK,GAAIx4B,KAAQwS,GAEfqmB,EAAmB74B,GAAO0vB,OAAQ10B,KAAMsU,EAASmE,EAGnD,OAAOnE,IAGTqsB,SAAU,WAER3gC,KAAK4K,SAAU7H,GAAM6B,OAAOi3B,SAG9B+E,SAAU,WAER5gC,KAAK2gC,WACL3gC,KAAKyiB,IAAIrY,QAAStH,GAAS8B,OAAOguB,cAAe5yB,QAGnD+Y,KAAM,SAAS8nB,GAOb,MALM7gC,MAAKo+B,QAETp+B,KAAKo+B,MAAQp+B,KAAKyiB,IAAIlN,WAAWohB,OAAQ32B,KAAM6gC,IAG1C7gC,KAAKo+B,OAGd0C,MAAO,WAEL,MAAO9gC,MAAKyiB,IAAIlN,WAAWwrB,QAAS/gC,OAGtCghC,KAAM,WAEJ,MAAOhhC,MAAKyiB,IAAIzd,KAAO,IAAMhF,KAAK+Y,QAGpCymB,QAAS,WAEP,MAAOpzB,GAAWpM,KAAMA,KAAKyiB,IAAIjN,IAAKhV,IAGxCs2B,QAAS,SAASthB,EAAKyrB,GAErB,GAAIntB,GAAK9T,KAAKyiB,IACVJ,EAASvO,EAAGyB,WAAW8G,kBAAkB7G,GACzC4M,EAASpiB,KAAKo+B,KAElB,IAAI/b,IAAWD,EACf,CACE,IAAKtO,EAAG4O,WAEN,KAAM,oFAGR5O,GAAGwkB,gBAAiBlW,GACpBtO,EAAG4iB,aAAc12B,KAAMqiB,GAEvBriB,KAAKo+B,MAAQ/b,EAEP4e,GAEJntB,EAAGyB,WAAW2rB,SAAU7e,EAAQriB,MAGlCA,KAAK4K,SAAU7H,GAAM6B,OAAO+d,WAAY3iB,KAAMoiB,EAAQC,IAGxD,MAAOA,IAGT8e,QAAS,SAAS7sB,EAASwT,GAEzB9nB,KAAKyiB,IAAI6T,cAAehiB,EAAStU,KAAK+Y,OAAQ/Y,KAAM8nB,IAGtDsZ,UAAW,WAET,MAAOphC,MAAKg4B,UAAYj1B,GAAMga,OAAOqgB,QAGvCiE,UAAW,WAET,MAAOrhC,MAAKg4B,UAAYj1B,GAAMga,OAAOyc,aAGvC/D,WAAY,WAEV,MAAOz1B,MAAKg4B,UAAYj1B,GAAMga,OAAOyc,aAAex5B,KAAKg4B,UAAYj1B,GAAMga,OAAOwc,eAGpF4B,WAAY,WAEV,MAAOn7B,MAAKg4B,SAAWj1B,GAAMga,OAAOwc,eAGtC/Y,SAAU,WAER,QAASxgB,KAAK+2B,QAGhBuK,gBAAiB,WAEf,QAASthC,KAAKw3B,QAGhB+J,OAAQ,WAEN,QAASvhC,KAAK+2B,QAAU/2B,KAAKw3B,SAG/BiI,OAAQ,WAEDz/B,KAAKyiB,IAAI8V,eAEZv4B,KAAK44B,SAAW7T,OAIpByc,SAAU,SAASC,GAIjB,MAFiBnpB,IAAWlK,MAAOpO,KAAKyiB,IAAKgf,GAE3BC,QAAS1hC,OAG7B2hC,YAAa,SAASC,GAEpB,GAAI9tB,GAAK9T,KAAKyiB,IACV0U,EAAQrjB,EAAG6gB,OAAQ30B,KAAK+2B,WACxBziB,EAAUstB,GAAkB5hC,KAC5BsM,EAASwH,EAAG0C,UAEhB,OAAO2gB,GAAQlpB,GAAMqG,EAAS6iB,EAAO7qB,EAAQ7F,GAAW6N,GAG1D2kB,YAAa,SAAS4I,GAEpB,GAAIxK,GAAYwK,EAAQ7hC,KAAKw3B,OAASx3B,KAAK+2B,MAE3C,KAAKM,EAEH,OAAO,CAQT,KAAK,GALDvjB,GAAK9T,KAAKyiB,IACVqf,EAAShuB,EAAGof,cACZiE,EAAQrjB,EAAG6gB,OAAQ0C,MACnB/qB,EAASwH,EAAG0C,WAEP1V,EAAI,EAAGA,EAAIwL,EAAOtL,OAAQF,IACnC,CACE,GAAIoD,GAAOoI,EAAQxL,GACfw2B,EAAet3B,KAAMkE,GACrBqzB,EAAaJ,EAAOjzB,EAExB,KAAK49B,EAAQ59B,KAKPuC,EAAQ6wB,EAAcC,GAE1B,OAAO,EAIX,OAAO,GAGTwK,WAAY,SAAS79B,EAAM29B,GAEzB,GAAIxK,GAAYwK,EAAQ7hC,KAAKw3B,OAASx3B,KAAK+2B,MAE3C,KAAKM,EAEH,OAAO,CAGT,IAAIvjB,GAAK9T,KAAKyiB,IACVuf,EAAUluB,EAAG+E,UAAW3U,EAI5B,QAAQuC,EAHWzG,KAAMkE,GACR89B,EAAUA,EAAS3K,EAAWnzB,GAAQmzB,EAAWnzB,GAASmzB,EAAWnzB,KAKxF+9B,iBAAkB,SAASnvB,EAASzH,GAE7BrL,KAAKkiC,WAERliC,KAAKkiC,UAAW,EAEhBniC,GAAOyJ,KAAMzJ,GAAO6E,OAAO0hB,OAAQtmB,KAAKmiC,QAASniC,OAGnDiI,GAAM4C,MAAM7K,MAEVoiC,eAAgBtvB,EAChBuvB,eAAgBh3B,KAIpB82B,QAAS,WAEHniC,KAAKg4B,UAAYj1B,GAAMga,OAAOwc,eAEhCx5B,GAAO2S,MAAO3S,GAAO4S,OAAO2X,cAAetqB,MAE3CA,KAAKkiB,cAAexE,GAAc1d,KAAKoiC,eAAgBpiC,KAAKqiC,iBAErDriC,KAAKg4B,UAAYj1B,GAAMga,OAAOyc,cAErCz5B,GAAO2S,MAAO3S,GAAO4S,OAAO4W,YAAavpB,MAEzCA,KAAKkiB,cAAerE,GAAY7d,KAAKoiC,eAAgBpiC,KAAKqiC,iBAG5DriC,KAAKkiC,UAAW,GAGlBp+B,SAAU,WAER,MAAO9D,MAAKyiB,IAAIvQ,UAAY,IAAMowB,KAAKC,UAAWviC,KAAK+3B,cAK3DtvB,EAAa1F,IAAO,GAEpBsE,EAAkBtE,GAAO,UAAWA,GAAM6B,OAAOwa,SAAS,GA4E1DnX,GAAMvG,OAAQ8Y,IASZ6C,MAAO,WAML,MAJArd,MAAKmB,OAAOH,OAAS,EACrBhB,KAAKya,KAAKzZ,OAAS,EACnBhB,KAAK0a,WAEE1a,MAWTkyB,IAAK,SAAS1c,EAAKtE,GAajB,MAXKsE,KAAOxV,MAAK0a,QAEf1a,KAAKmB,OAAQnB,KAAK0a,QAASlF,IAAUtE,GAIrClR,KAAK0a,QAASlF,GAAQxV,KAAKmB,OAAOH,OAClC2Q,GAAGxI,KAAK3H,KAAMxB,KAAKmB,OAAQ+P,GAC3BS,GAAGxI,KAAK3H,KAAMxB,KAAKya,KAAMjF,IAGpBxV,MAST6R,IAAK,SAAS2D,GAEZ,MAAOxV,MAAKmB,OAAQnB,KAAK0a,QAASlF,KAUpCnM,OAAQ,SAASmM,GAEf,GAAI8M,GAAQtiB,KAAK0a,QAASlF,EAO1B,OALKxS,GAAUsf,IAEbtiB,KAAK6iB,SAAUP,GAGVtiB,MAUT6iB,SAAU,SAASP,GAEjB,GAAI9M,GAAMxV,KAAKya,KAAM6H,GACjBkgB,EAAY7wB,GAAG8wB,IAAI7/B,MAAO5C,KAAKmB,QAC/BuhC,EAAU/wB,GAAG8wB,IAAI7/B,MAAO5C,KAAKya,KAWjC,OATK6H,GAAQtiB,KAAKmB,OAAOH,SAEvBhB,KAAKmB,OAAQmhB,GAAUkgB,EACvBxiC,KAAKya,KAAM6H,GAAUogB,EACrB1iC,KAAK0a,QAASgoB,GAAYpgB,SAGrBtiB,MAAK0a,QAASlF,GAEdxV,MAST43B,IAAK,SAASpiB,GAEZ,MAAOA,KAAOxV,MAAK0a,SAQrBioB,KAAM,WAEJ,MAAO3iC,MAAKmB,OAAOH,QAGrB4hC,SAAU,SAAShoB,EAAKioB,GAOtB,IAAK,GALDvjB,GAAMujB,GAAQ,GAAIroB,IAClBzZ,EAAIf,KAAK2iC,OACTxhC,EAASnB,KAAKmB,OACdsZ,EAAOza,KAAKya,KAEP3Z,EAAI,EAAGA,EAAIC,EAAGD,IACvB,CACE,GAAIgiC,GAAI3hC,EAAQL,GACZgB,EAAI2Y,EAAM3Z,EAER8Z,GAAIgd,IAAK91B,IAEbwd,EAAI4S,IAAKpwB,EAAGghC,GAIhB,MAAOxjB,IAWTzD,OAAQ,SAASpX,EAAUo+B,GAOzB,IAAK,GALDvjB,GAAMujB,GAAQ,GAAIroB,IAClBzZ,EAAIf,KAAK2iC,OACTxhC,EAASnB,KAAKmB,OACdsZ,EAAOza,KAAKya,KAEP3Z,EAAI,EAAGA,EAAIC,EAAGD,IACvB,CACE,GAAIgiC,GAAI3hC,EAAQL,GACZgB,EAAI2Y,EAAM3Z,EAET2D,GAAUq+B,EAAGhhC,IAEhBwd,EAAI4S,IAAKpwB,EAAGghC,GAIhB,MAAOxjB,IASTtd,QAAS,WAOP,MALAA,GAAShC,KAAKmB,QACda,EAAShC,KAAKya,MAEdza,KAAK+iC,eAEE/iC,MAQToC,SAAU,SAASzB,GAEjB,MAAOyB,GAAUzB,EAAYX,KAAKmB,SAepCwkB,KAAM,SAAShlB,GAKb,QAASqiC,GAAUC,EAAMC,GAMvB,IAJA,GAAIC,GAAQvoB,EAAIzZ,OAAQe,KAAKC,OAAO+gC,EAAQD,GAAQ,IAChDniC,EAAImiC,EACJG,EAAIF,EAEDpiC,GAAKsiC,GACZ,CACE,KAAOziC,EAAYia,EAAIzZ,OAAOL,GAAIqiC,GAAU,GAE1CriC,GAEF,MAAOH,EAAYia,EAAIzZ,OAAOiiC,GAAID,GAAU,GAE1CC,GAGEtiC,IAAKsiC,IAEPvhC,EAAM+Y,EAAIzZ,OAAQL,EAAGsiC,GACrBvhC,EAAM+Y,EAAIH,KAAM3Z,EAAGsiC,GACnBtiC,IACAsiC,KAIJ,MAAOtiC,GAIT,QAASuiC,GAAMJ,EAAMC,GAEnB,GAAI5gB,GAAQ0gB,EAAWC,EAAMC,EAEzBD,GAAO3gB,EAAQ,GAEjB+gB,EAAOJ,EAAM3gB,EAAQ,GAGnBA,EAAQ4gB,GAEVG,EAAO/gB,EAAO4gB,GA5ClB,GAAItoB,GAAM5a,KAgDNkjC,EAAQljC,KAAK2iC,OAAS,CAU1B,OAPKO,GAAQ,IAEXG,EAAO,EAAGH,GAEVljC,KAAK+iC,gBAGA/iC,MAST+iC,aAAc,WAEZ/iC,KAAK0a,UAEL,KAAK,GAAI5Z,GAAI,EAAG8e,EAAI5f,KAAKya,KAAKzZ,OAAQF,EAAI8e,EAAG9e,IAE3Cd,KAAK0a,QAAS1a,KAAKya,KAAM3Z,IAAQA,CAGnC,OAAOd,OASTsjC,SAAU,SAAShkB,GAMjB,IAAK,GAJDhY,GAASgY,MACT7E,EAAOza,KAAKya,KACZtZ,EAASnB,KAAKmB,OAETL,EAAI,EAAGA,EAAI2Z,EAAKzZ,OAAQF,IAE/BwG,EAAQmT,EAAM3Z,IAAQK,EAAQL,EAGhC,OAAOwG,MAcXW,GAAMvG,OAAQiZ,IAGZM,IAAK,SAAS5O,EAAO4P,GAEnB,GAAIzG,GAAMnJ,EAAM20B,MAIhB,IAFAhhC,KAAK4a,IAAKpF,GAAQnJ,EAEbA,EAAMoW,IAAIC,aAAe1iB,KAAK8I,UAAW0M,GAC9C,CACE,GAAI5N,GAAW5H,KAAKujC,gBAAiBtnB,EAErCjc,MAAK8I,UAAW0M,GAAQnJ,EAAM7B,IAAKzH,GAAM6B,OAAO+d,UAAW/a,EAAU5H,QAIzEqJ,OAAQ,SAASgD,GAEf,GAAImJ,GAAMnJ,EAAM20B,MAEhB38B,GAAUrE,KAAK8I,UAAW0M,UAEnBxV,MAAK8I,UAAW0M,SAChBxV,MAAK4a,IAAKpF,IAGnB+tB,gBAAiB,SAAStnB,GAExB,MAAO,UAAS5P,EAAO+V,EAAQC,GAE7B,GAAImhB,GAASn3B,EAAMoW,IAAIzd,KAAO,GAE9Bod,GAASohB,EAASphB,EAClBC,EAASmhB,EAASnhB,EAElBriB,KAAK8I,UAAWuZ,GAAWriB,KAAK8I,UAAWsZ,GAC3CpiB,KAAK4a,IAAKyH,GAAWriB,KAAK4a,IAAKwH,SAExBpiB,MAAK8I,UAAWsZ,SAChBpiB,MAAK4a,IAAKwH,GAEjBnG,EAAQwnB,iBAAkBzjC,KAAK6H,QAASwE,GAAO,KAInDq3B,QAAS,SAASC,EAAiBC,GAEjC,GAAIC,GAAa7jC,KAAK4a,IAClBlT,EAAMnE,EAENugC,EAAkB,WAEpBH,EAAgB/gC,MAAOghC,GAAkB5jC,KAAMoB,WAE/CsG,IAGF,KAAK,GAAIq8B,KAAOF,GAChB,CACE,GAAIG,GAAYH,EAAYE,EAE5B,KAAMC,EAAUxjB,WAId,MAFA9Y,GAAMs8B,EAAUv5B,MAAO1H,GAAM6B,OAAOy3B,YAAayH,IAE1C,EAIX,OAAO,KAgCX77B,GAAMvG,OAAQ4W,IAGZuC,cAAe,SAASnJ,GAEtB,GAAIuyB,GAAajkC,KACbkkC,EAAQxyB,EACRyyB,EAAazyB,EAAMjR,QAAS6X,GAAW8rB,gBAEvCD,GAAa,IAEfD,EAAQxyB,EAAM3N,UAAW,EAAGogC,GAC5BzyB,EAAQA,EAAM3N,UAAWogC,EAAa,GAwCxC,KAAK,GArCDE,GAAO,GACPC,KACAC,GAAU,YACVC,GAASxkC,KAAKgS,UACdlR,EAAI,EACJ2jC,KAEAC,EAAc,SAASL,GAEzB,GAAKA,EAAL,CAKA,GAAIM,GAAQJ,EAAO,GACfrxB,EAAUoF,GAAWssB,cAAeD,EAExCL,GAAMO,QAASR,GAEXnxB,GAAWA,EAAQ4xB,MAErBL,EAAUt7B,KAAM+J,EAAQ4xB,KAAMR,EAAOC,EAAQC,EAAOP,MAgB/CnjC,EAAI,EAAGA,EAAI4Q,EAAM1Q,OAAQF,IAClC,CACE,GAAIkN,GAAI0D,EAAMhM,OAAQ5E,GAClB6jC,EAAQrsB,GAAWysB,OAAQ/2B,EAE3B22B,IAEFD,EAAaL,GAnBE,SAASM,GAE1B,GAAIzxB,GAAUoF,GAAWssB,cAAeD,EAExCJ,GAAOM,QAASF,GAEZzxB,GAAWA,EAAQ8xB,KAErBP,EAAUt7B,KAAM+J,EAAQ8xB,IAAKV,EAAOC,EAAQC,EAAOP,KAYrCU,GAEdN,EAAO,IAIPA,GAAQr2B,EAIZ02B,EAAaL,EAMb,KAAK,GAJD30B,GAAW,SAASwB,GACtB,MAAOA,IAGApQ,EAAI2jC,EAAUzjC,OAAS,EAAGF,GAAK,EAAGA,IACzC4O,EAAW+0B,EAAW3jC,GAAK4O,EAG7B1P,MAAKqY,YAAa6rB,GAAUx0B,GAG9BgyB,QAAS,SAASr1B,GAEhB,GAAIiT,KAEJ,KAAK,GAAI4kB,KAASlkC,MAAKqY,YAErBiH,EAAK4kB,GAAUlkC,KAAKqY,YAAa6rB,GAAS73B,EAG5C,OAAOiT,MAKXhH,GAAWysB,QAETE,IAAK,WACLC,IAAK,QACLC,IAAK,SACLC,IAAK,UACLC,IAAK,WACLC,IAAK,SACLC,IAAK,kBACLC,IAAK,gBACLC,IAAK,mBACLC,IAAK,uBACLC,IAAK,iBACLC,IAAK,iBACLC,IAAK,qBAGPvtB,GAAWssB,eAGTlmB,UAEEomB,KAAM,SAASR,EAAOC,EAAQC,EAAOP,GAEnC,GAAItf,GAAe2f,EAAM,GACrBwB,EAAatB,EAAM,EAEvB,MAAMsB,YAAsBhjC,KAE1B,KAAO,gBAAkB6hB,EAAe,iCAG1C,IAAI3M,GAAW8tB,EAAWtuB,UAAWmN,EAgBrC,IAdI3M,IAEEA,YAAoB8F,IAEtB0mB,EAAMK,QAAS7sB,EAAS3L,MAAMvJ,UAI9B0hC,EAAMK,QAAS7sB,KAMA,IAFFvX,EAASqlC,EAAWx5B,OAAQqY,KAEhB3M,EAE3B,KAAO,gBAAkB2M,EAAe,uDAAyDmhB,EAAW9gC,IAG9G,OAAO,UAAS0K,GAEd,MAAO,UAASrD,GAEd,MAAM7L,GAAS6L,GAKRqD,EAAUrD,EAAMmyB,KAAM7Z,IAHpB,SASjB9I,QAEEipB,KAAM,SAASR,EAAOC,EAAQC,EAAOP,GAEnC,GAAI8B,GAAazB,EAAM,GACnBzoB,EAAS9b,GAAOimC,QAASD,EAE7B,KAAKlqB,EAEH,KAAOkqB,GAAa,iCAGtB,OAAO,UAASr2B,GAEd,MAAO,UAASwB,GAEd,MAAM1Q,GAAS0Q,GAKRxB,EAAUmM,EAAQ3K,IAHhB,SASjBuB,SAEEqyB,KAAM,SAASR,EAAOC,EAAQC,EAAOP,GAEnC,GAAIgC,GAAc3B,EAAM,EAExB,OAAO,UAAS50B,GAEd,MAAO,UAAS7C,GAEd,IAAMrM,EAASqM,GAEb,MAAO,KAGT,IAAIqE,GAAQrE,EAAQo5B,EAOpB,OALKvjC,GAAYwO,KAEfA,EAAQA,EAAMtO,MAAOiK,IAGhB6C,EAAUwB,OAMzBH,OAEE+zB,KAAM,SAASR,EAAOC,EAAQC,EAAOP,GAEnC,GAAIiC,GAAY5B,EAAM,GAClB6B,EAAY3B,EAAM,GAClBzzB,EAAQhR,GAAOkR,OAAQi1B,EAE3B,KAAKn1B,EAEH,KAAOm1B,GAAY,kCAGrB,MAAMC,YAAqBpoB,KAEzB,KAAOmoB,GAAY,kDAGrB,OAAO,UAASx2B,GAEd,MAAO,UAASsI,GAEd,MAAMxX,GAASwX,GAKRtI,EAAUsI,EAASjH,MAAOA,IAHxB,SASjBq1B,mBAEEtB,KAAM,SAASR,EAAOC,EAAQC,EAAOP,GAEnC,GAAIvlB,GAAW4lB,EAAM,GACjB+B,EAAoB/B,EAAM,GAC1BgC,EAAgB9B,EAAM,EAE1B,IAAkB,mBAAdD,EAAO,GAET,KAAM,sDAGR,MAAM+B,YAAyBzuB,KAE7B,KAAO,4BAA8BwuB,EAAoB,SAAWC,EAAgB,gCAGtF,OAAO,UAAU52B,GAEf,MAAO,UAAUsI,GAEf,MAAMxX,GAASwX,GAKRtI,EAAUsI,EAAUquB,GAAqB3nB,IAHvC,SASjB6nB,QAEEvB,IAAK,SAASV,EAAOC,EAAQC,EAAOP,GAElC,GAAI7rB,GAAiBksB,EAAM,GACvB6B,EAAY3B,EAAM,EAEtB,IAAkB,aAAdD,EAAO,GAET,KAAM,gEAGR,MAAM4B,YAAqBtuB,KAEzB,KAAO,wBAA0BO,EAAiB,SAAWksB,EAAM,GAAK,gCAG1E,KAAK6B,EAAU95B,MAAMvJ,SAASuV,YAAaD,GAEzC,KAAO,kBAAoBA,EAAiB,sBAAwB+tB,EAAU95B,MAAMvJ,SAASkC,IAG/F,OAAImhC,aAAqBroB,IAEhB,SAASpO,GAEd,MAAO,UAAUsI,GAEf,MAAMxX,GAASwX,GAKRtI,EAAUsI,EAASwpB,SAAUppB,IAH3B,OASN,SAAS1I,GAEd,MAAO,UAAS8H,GAEd,MAAMhX,GAASgX,GAKR9H,EAAU8H,EAAUkqB,QAAStpB,IAH3B,SAUnBouB,eAEExB,IAAK,SAASV,EAAOC,EAAQC,EAAOP,GAElC,GAAIhgC,GAAaqgC,EAAM,GACnB6B,EAAY3B,EAAM,EAEtB,IAAkB,oBAAdD,EAAO,GAET,KAAM,6DAGR,MAAM4B,YAAqBpoB,KAEzB,KAAO,qBAAuB9Z,EAAa,SAAWqgC,EAAM,GAAK,gCAGnE,OAAO,UAAU50B,GAEf,MAAO,UAAU8H,GAEf,MAAMhX,GAASgX,GAKR9H,EAAU8H,EAAUivB,MAAOxiC,IAHzB,SASjByiC,gBAEE1B,IAAK,SAASV,EAAOC,EAAQC,EAAOP,GAElC,GAAIhgC,GAAaqgC,EAAM,GACnB7pB,EAAO6pB,EAAM,GACb6B,EAAY3B,EAAM,EAEtB,IAAkB,yBAAdD,EAAO,IAA+C,qBAAdA,EAAO,GAEjD,KAAM,iDAGR,MAAM4B,YAAqBpoB,KAEzB,KAAO,qBAAuB9Z,EAAa,SAAWqgC,EAAM,GAAK,gCAGnE,OAAO,UAAU50B,GAEf,MAAO,UAAU8H,GAEf,MAAMhX,GAASgX,GAKR9H,EAAU8H,EAAUivB,MAAOxiC,EAAYwW,IAHrC,UAUnBnC,GAAW8rB,gBAAkB,IAE7B9rB,GAAWlK,MAAQ,SAAS4D,EAAUN,GAEpC,GAAIi1B,GAAgBj1B,CAYpB,IAVKpR,EAAUoR,KAEbA,EAAQM,EAASqG,YAAa3G,IAG3BrQ,EAASqQ,KAEZA,EAAQ,GAAI4G,IAAYtG,EAAUN,MAG9BA,YAAiB4G,KAErB,KAAOquB,GAAgB,4BAGzB,OAAOj1B,IA0BToJ,GAAQ8rB,MAAQ,SAAS/wB,GAEvB,GAAIpS,GAAU,GAAIqX,IAASjF,EAI3B,OAFApS,GAAQb,QAEDa,GAGTwE,GAAMvG,OAAQoZ,IAGZG,IAAK,SAAS5S,GAEP/H,EAAU+H,KAEbA,EAAOtI,GAAOoS,QAAS9J,IAGpBxF,EAAUwF,KAEbA,EAAOA,EAAKvF,UAGTuF,YAAgBvF,MAEnB9C,KAAK+a,UAAU5R,KAAMd,GACrBrI,KAAKgb,KAAK7R,SACVnJ,KAAK6V,OAAO1M,KAAM,GAAI2M,IAAiBzN,MAI3Cw+B,WAAY,WAEV,GAAIC,GAAU,CAUd,OARA9mC,MAAK+mC,KAAK,SAASjzB,GAEbA,EAAGrQ,UAAYzD,MAEjB8mC,MAIGA,EAAU9mC,KAAK+a,UAAU/Z,QAGlC4B,MAAO,WAEL5C,KAAK+mC,KAAM/mC,KAAKgnC,gBAGlBA,cAAe,SAASlzB,EAAII,EAAK2B,EAAQ/U,GAEvCgT,EAAGI,IAAMA,EACTJ,EAAG+B,OAASA,EACZ/B,EAAGrQ,QAAUzD,KACb8T,EAAG6C,aAAe7V,GAGpBmmC,QAAS,WAEPjnC,KAAK+mC,KAAM/mC,KAAKknC,kBAGlBA,gBAAiB,SAASpzB,GAEpBA,EAAGrQ,UAAYzD,OAEjB8T,EAAGI,IAAMJ,EAAGiC,UACZjC,EAAG+B,OAAS/B,EAAG8B,aACf9B,EAAGrQ,QAAU,KACbqQ,EAAG6C,cAAgB,IAIvBwwB,QAAS,WAEPnnC,KAAK+mC,KAAM/mC,KAAKonC,iBAEhBpnC,KAAK+a,UAAU/Z,OAAS,EACxBhB,KAAKgb,KAAKha,OAAS,EACnBhB,KAAK6V,OAAO7U,OAAS,GAGvBomC,gBAAiB,SAAStzB,EAAIkH,EAAMnF,EAAQ/U,GAE1Cd,KAAKknC,gBAAiBpzB,GAEtB9T,KAAK+a,UAAWja,GAAM,KACtBd,KAAKgb,KAAMla,GAAM,KACjBd,KAAK6V,OAAQ/U,GAAI6T,QACjB3U,KAAK6V,OAAQ/U,GAAM,MAGrB6T,MAAO,SAASb,GAEd9T,KAAKgb,KAAMlH,EAAG6C,kBAGhBowB,KAAM,SAASM,GAMb,IAAK,GAJDC,GAAMtnC,KAAK+a,UACXC,EAAOhb,KAAKgb,KACZnF,EAAS7V,KAAK6V,OAET/U,EAAI,EAAGA,EAAIwmC,EAAItmC,OAAQF,IAE9BumC,EAAS7lC,KAAMxB,KAAMsnC,EAAKxmC,GAAKka,EAAMla,GAAK+U,EAAQ/U,GAAKA,MAY7DmH,GAAMvG,OAAQwZ,IAGZhD,KAAM,SAASlG,GAEbhS,KAAKwV,IAAMxD,EAASwD,IACpBxV,KAAKizB,aAAejhB,EAASihB,aAC7BjzB,KAAKgS,SAAWA,GAGlB2kB,OAAQ,SAAStqB,EAAOw0B,GAEtB,GAAI/e,GAAQ9hB,KAAKwV,IACb2iB,EAAWn4B,KAAKunC,SAAUl7B,EAAOyV,EAErC,IAAK1V,EAAWC,EAAOyV,EAAOthB,GAE5B,MAAO23B,EAEJ,KAAM0I,EAET,KAAM,6BAGR,OAAO,OAGTxK,sBAAuB,SAAS3kB,GAE9B,GAAKlP,EAAUkP,GACf,CACE,GAAI8F,GAAYxX,KAAKgS,SAASwF,SAE9B,KAAK,GAAIwoB,KAAgBxoB,GAElBwoB,IAAgBtuB,IAEnB8F,EAAWwoB,GAAeuH,SAAU71B,KAM5C2K,kBAAmB,SAAS3K,GAE1B,MAAKA,aAAiB1R,MAAKgS,SAASjP,MAE3B2O,EAAMqH,OAEL1X,EAASqQ,GAEVA,EAAM81B,KAAMxnC,KAAKizB,cAEhBzwB,EAAUkP,GAEX1R,KAAKunC,SAAU71B,GAGjBA,KAWXzJ,GAAMyb,OAAQxI,GAAYxF,IAExBqrB,QAAS,SAAS10B,GAEhB,MAAOrM,MAAKunC,SAAUl7B,IAGxB6sB,UAAW,SAAS7sB,SAIXA,GAFKrM,KAAKwV,MAKnB+xB,SAAU,SAAS71B,EAAO+1B,GAExBznC,KAAKq2B,sBAAuB3kB,EAE5B,IAAIoQ,GAAQ2lB,GAAeznC,KAAKwV,IAC5BA,EAAM9D,EAAOoQ,EAOjB,OALMthB,GAASgV,KAEbA,EAAM9D,EAAOoQ,GAAUne,KAGlB6R,GAGTwgB,mBAAoB,SAASxgB,GAE3B,GAAIsM,GAAQ9hB,KAAKwV,IACb3K,IAIJ,OAFAA,GAAOiX,GAAUtM,EAEVxV,KAAKgS,SAASukB,YAAa1rB,IAGpCgsB,aAAc,SAAS31B,EAAGsE,GAExB,GAAIsc,GAAQ9hB,KAAKwV,IACbkyB,EAAOxmC,EAAG4gB,GACV6lB,EAAOniC,EAAGsc,EAEd,OAAOthB,GAASknC,IAAUlnC,EAASmnC,IAAUD,IAASC,GAGxDhyB,YAAa,SAAS2J,GAEpB,GAAIwC,GAAQ9hB,KAAKwV,KAEc,IAA1B/U,EAAS6e,EAAKwC,IAEjBxC,EAAIulB,QAAS/iB,IAIjB6Y,QAAS,SAASnlB,GAEhB,MAAOhV,GAASgV,IAGlBoyB,WAAY,SAAStgC,EAAQmF,EAAcI,EAAQC,GAEjD,GAAIC,GAAczF,EAAQmF,GACtBQ,EAAcJ,EAAQC,IAEpBtM,EAASuM,IAAiBvM,EAASyM,KAEvC3F,EAAQmF,GAAiBlI,EAAM0I,KAInC46B,MAAO,SAAS/lB,GAEd,GAAKzgB,EAASygB,GACd,CACE,IAAK,GAAIhhB,GAAI,EAAGA,EAAIghB,EAAM9gB,OAAQF,IAEhC,GAAKghB,EAAOhhB,KAAQd,KAAKwV,IAEvB,OAAO,CAIX,QAAO,EAGT,MAAOsM,KAAU9hB,KAAKwV,KAGxBsyB,YAAa,SAAStyB,EAAKsM,EAAOjV,EAAQvF,GAEnCwa,IAAUxa,IAEbkO,EAAKsM,GAAUjV,EAAQ7M,KAAKwV,OAIhC0rB,SAAU,SAASxvB,EAAOpK,GAExBA,EAAQtH,KAAKwV,KAAQ9D,KAWzBzJ,GAAMyb,OAAQxI,GAAYzF,IAExBsrB,QAAS,SAASrvB,EAAO+1B,GAIvB,MAFAznC,MAAKq2B,sBAAuB3kB,GAErBpE,EAAMoE,EAAO+1B,GAAeznC,KAAKwV,MAG1C0jB,UAAW,SAAS7sB,GAIlB,IAAK,GAFDC,GAAStM,KAAKwV,IAET1U,EAAI,EAAGA,EAAIwL,EAAOtL,OAAQF,UAE1BuL,GAAOC,EAAQxL,KAI1BymC,SAAU,SAAS71B,EAAO+1B,GAExB,MAAOznC,MAAK+gC,QAASrvB,EAAO+1B,GAAcD,KAAMxnC,KAAKizB,eAGvD+C,mBAAoB,SAASxgB,GAE3B,GAAIlJ,GAAStM,KAAKwV,IACd3K,IAECvK,GAAUkV,KAEbA,EAAMA,EAAIjV,MAAOP,KAAKizB,cAGxB,KAAK,GAAInyB,GAAI,EAAGA,EAAIwL,EAAOtL,OAAQF,IAEjC+J,EAAOyB,EAAQxL,IAAQ0U,EAAK1U,EAG9B,OAAOd,MAAKgS,SAASukB,YAAa1rB,IAGpCgsB,aAAc,SAAS31B,EAAGsE,GAIxB,IAAK,GAFD8G,GAAStM,KAAKwV,IAET1U,EAAI,EAAGA,EAAIwL,EAAOtL,OAAQF,IACnC,CACE,GAAI4mC,GAAOxmC,EAAGoL,EAAQxL,IAClB6mC,EAAOniC,EAAG8G,EAAQxL,GAEtB,IAAKN,EAASknC,IAAUlnC,EAASmnC,IAAUD,IAASC,EAElD,OAAO,EAIX,OAAO,GAGThyB,YAAa,SAAS2J,GAIpB,IAAK,GAFDhT,GAAStM,KAAKwV,IAET1U,EAAIwL,EAAOtL,OAAS,EAAGF,GAAK,EAAGA,KAED,IAAhCL,EAAS6e,EAAKhT,EAAQxL,KAEzBwe,EAAIulB,QAASv4B,EAAQxL,KAK3B65B,QAAS,SAASnlB,GAEhB,MAAOhV,GAASgV,IAGlBoyB,WAAY,SAAStgC,EAAQmF,EAAcI,EAAQC,GAEjD,IAAK,GAAIhM,GAAI,EAAGA,EAAI2L,EAAazL,OAAQF,IACzC,CACE,GAAIiM,GAAczF,EAAQmF,EAAc3L,IACpCmM,EAAcJ,EAAQC,EAAchM,KAElCN,EAASuM,IAAiBvM,EAASyM,KAEvC3F,EAAQmF,EAAc3L,IAAQyD,EAAM0I,MAK1C46B,MAAO,SAAS/lB,GAEd,GAAKzgB,EAASygB,GACd,CACE,IAAK,GAAIhhB,GAAI,EAAGA,EAAIghB,EAAM9gB,OAAQF,IAEhC,IAAyC,IAApCL,EAAST,KAAKwV,IAAKsM,EAAOhhB,IAE7B,OAAO,CAIX,QAAO,EAGT,OAAsC,IAA/BL,EAAST,KAAKwV,IAAKsM,IAG5BgmB,YAAa,SAAStyB,EAAKsM,EAAOjV,EAAQvF,GAExC,GAAIgb,GAAQ7hB,EAAS6G,IAEN,IAAVgb,IAEH9M,EAAKsM,GAAUjV,EAAQ7M,KAAKwV,IAAK8M,MAIrC4e,SAAU,SAASxvB,EAAOpK,GAExB,GAAIgF,GAAStM,KAAKwV,GAEblV,GAAUoR,KAEbA,EAAQA,EAAMnR,MAAOP,KAAKizB,cAG5B,KAAK,GAAInyB,GAAI,EAAGA,EAAIwL,EAAOtL,OAAQF,IAEjCwG,EAAQgF,EAAQxL,IAAQ4Q,EAAO5Q,MAoDrCW,GAAWmD,QAiBTmjC,IAAgB,MAehBC,KAAgB,OAYhBC,KAAgB,OAgBhBC,OAAgB,SAiBhBC,QAAgB,UAahBC,QAAgB,UAchBC,MAAgB,QAUhBC,QAAgB,UAShBlpB,QAAgB,sDAIlBnX,GAAMyb,OAAQrjB,MAAOoB,IAiBnByV,cAAe,SAASvW,EAAYuE,GAKlC,MAHAlF,MAAKW,WAAawE,EAAkBxE,EAAYuE,GAChDlF,KAAK2lB,OAEE3lB,MAmBTqF,cAAe,SAAS1E,EAAYuE,GAKlC,MAHAlF,MAAKW,WAAa0E,EAAerF,KAAKW,WAAYA,EAAYuE,GAC9DlF,KAAK2lB,OAEE3lB,MAiBToC,SAAU,SAASzB,EAAYuE,GAI7B,MAAO9C,GAFGzB,EAAawE,EAAkBxE,EAAYuE,GAAelF,KAAKW,WAEnDX,OAyBxB2lB,KAAM,SAAShlB,EAAYuE,EAAYqjC,GAErC,GAAI3nC,GAAMD,EAAawE,EAAkBxE,EAAYuE,GAAelF,KAAKW,UASzE,OAPMyB,GAAUxB,EAAKZ,QAAauoC,GAAoB3nC,IAAO0B,EAAkBtC,SAE7E2R,GAAGgU,KAAKnkB,KAAMxB,KAAMY,GAEpBZ,KAAKoK,QAAS3I,GAAWmD,OAAOqjC,MAAOjoC,QAGlCA,MAcTqd,MAAO,SAASlc,GAgBd,MAdAnB,MAAKgB,OAAS,EAETK,EAASF,GAEZwQ,GAAGxI,KAAKvG,MAAO5C,KAAMmB,GAEbX,EAASW,IAEjBwQ,GAAGxI,KAAK3H,KAAMxB,KAAMmB,GAGtBnB,KAAKoK,QAAS3I,GAAWmD,OAAOyjC,OAAQroC,OACxCA,KAAK2lB,KAAM1lB,EAAWA,GAAW,GAE1BD,MAkBTwoC,KAAM,SAASltB,EAAUC,GAEvB,MAAO,IAAIH,IAAMpb,KAAMsb,EAAUC,IAuBnCqf,SAAU,SAAS6N,EAAiBC,EAAYC,GAE9C,GAAI9sB,GAAS7K,GAAay3B,EAAiBC,EAAYC,EAEvD,OAAO/sB,IAAmBla,OAAQ1B,KAAM6b,IA6B1C9K,MAAO,SAAS03B,EAAiBC,EAAYC,EAAarpB,GAKxD,IAAK,GAHDvO,GAAQC,GAAay3B,EAAiBC,EAAYC,GAClDrhC,EAASgY,GAAOtf,KAAKuc,aAEhBzb,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAII,GAAIlB,KAAMc,EAETiQ,GAAO7P,IAEVoG,EAAO6B,KAAMjI,GAIjB,MAAOoG,IA6BTs7B,SAAU,SAASvnB,EAAYiE,EAAK7Y,GAKlC,IAAK,GAHDa,GAASgY,GAAOtf,KAAKuc,aACrBtQ,EAAWxF,GAAU5F,EAEhBC,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CAIE,IAAK,GAHDI,GAAIlB,KAAMc,GACVyL,GAAS,EAEJ62B,EAAI,EAAGA,EAAI/nB,EAAWra,SAAWuL,EAAQ62B,IAEhD72B,EAASN,EAAU/K,EAAGma,EAAY+nB,GAG/B72B,IAEHjF,EAAO6B,KAAMjI,GAIjB,MAAOoG,IA4BTshC,UAAW,SAASvtB,EAAYiE,EAAK7Y,GAKnC,IAAK,GAHDa,GAASgY,GAAOtf,KAAKuc,aACrBtQ,EAAWxF,GAAU5F,EAEhBC,EAAI,EAAGA,EAAIua,EAAWra,OAAQF,IACvC,CAIE,IAAK,GAHDI,GAAIma,EAAYva,GAChByL,GAAS,EAEJ62B,EAAI,EAAGA,EAAIpjC,KAAKgB,SAAWuL,EAAQ62B,IAE1C72B,EAASN,EAAU/K,EAAGlB,KAAMojC,GAG1B72B,IAEFjF,EAAO6B,KAAMjI,GAIjB,MAAOoG,IA6BTuhC,WAAY,SAASxtB,EAAYiE,EAAK7Y,GAKpC,IAAK,GAHDa,GAASgY,GAAOtf,KAAKuc,aACrBtQ,EAAWxF,GAAU5F,EAEhBC,EAAI,EAAGA,EAAIua,EAAWra,OAAQF,IACvC,CAIE,IAAK,GAHDI,GAAIma,EAAYva,GAChByL,GAAS,EAEJ62B,EAAI,EAAGA,EAAIpjC,KAAKgB,SAAWuL,EAAQ62B,IAE1C72B,EAASN,EAAU/K,EAAGlB,KAAMojC,GAGzB72B,IAEHjF,EAAO6B,KAAMjI,GAIjB,MAAOoG,IAiBTqN,MAAO,WAKL,MAHA3U,MAAKgB,OAAS,EACdhB,KAAKoK,QAAS3I,GAAWmD,OAAO0jC,SAAUtoC,OAEnCA,MA0BTib,IAAK,SAAS/J,EAAOgqB,GAEnB,GAAIp6B,GAAId,KAAKgB,MAWb,OATA2Q,IAAGxI,KAAK3H,KAAMxB,KAAMkR,GAEpBlR,KAAKoK,QAAS3I,GAAWmD,OAAOmjC,KAAM/nC,KAAMkR,EAAOpQ,IAE7Co6B,GAEJl7B,KAAK2lB,KAAM1lB,EAAWA,GAAW,GAG5BD,MAsBTmJ,KAAM,WAEJ,GAAIhI,GAASwQ,GAAGpQ,MAAMqB,MAAMxB,WACxBN,EAAId,KAAKgB,MAQb,OANA2Q,IAAGxI,KAAKvG,MAAO5C,KAAMmB,GAErBnB,KAAKoK,QAAS3I,GAAWmD,OAAOojC,MAAOhoC,KAAMmB,EAAQL,IAErDd,KAAK2lB,KAAM1lB,EAAWA,GAAW,GAE1BD,KAAKgB,QAsBd6jC,QAAS,WAEP,GAAI1jC,GAASC,SAQb,OANAuQ,IAAGkzB,QAAQjiC,MAAO5C,KAAMmB,GAExBnB,KAAKoK,QAAS3I,GAAWmD,OAAOojC,MAAOhoC,KAAM2R,GAAGpQ,MAAMqB,MAAMzB,GAAS,IAErEnB,KAAK2lB,KAAM1lB,EAAWA,GAAW,GAE1BD,KAAKgB,QAyBdma,OAAQ,SAASha,EAAQ+5B,GAEvB,GAAK75B,EAASF,IAAYA,EAAOH,OACjC,CACE,GAAIF,GAAId,KAAKgB,MAEb2Q,IAAGxI,KAAKvG,MAAO5C,KAAMmB,GAErBnB,KAAKoK,QAAS3I,GAAWmD,OAAOojC,MAAOhoC,KAAMmB,EAAQL,IAE/Co6B,GAEJl7B,KAAK2lB,KAAM1lB,EAAWA,GAAW,GAIrC,MAAOD,OA4BT8oC,SAAU,SAAShoC,EAAGoQ,EAAOgqB,GAW3B,MATAvpB,IAAGd,OAAOrP,KAAMxB,KAAMc,EAAG,EAAGoQ,GAE5BlR,KAAKoK,QAAS3I,GAAWmD,OAAOmjC,KAAM/nC,KAAMkR,EAAOpQ,IAE7Co6B,GAEJl7B,KAAK2lB,KAAM1lB,EAAWA,GAAW,GAG5BD,MAuBTyiC,IAAK,SAASvH,GAEZ,GAAI6N,GAAUp3B,GAAG8wB,IAAI7/B,MAAO5C,MACxBc,EAAId,KAAKgB,MASb,OAPAhB,MAAKoK,QAAS3I,GAAWmD,OAAOsjC,QAASloC,KAAM+oC,EAASjoC,IAElDo6B,GAEJl7B,KAAK2lB,KAAM1lB,EAAWA,GAAW,GAG5B8oC,GAuBTC,MAAO,SAAS9N,GAEd,GAAI6N,GAAUp3B,GAAGq3B,MAAMpmC,MAAO5C,KAS9B,OAPAA,MAAKoK,QAAS3I,GAAWmD,OAAOsjC,QAASloC,KAAM+oC,EAAS,IAElD7N,GAEJl7B,KAAK2lB,KAAM1lB,EAAWA,GAAW,GAG5B8oC,GA2BTlmB,SAAU,SAAS/hB,EAAGo6B,GAEpB,GAAI+N,EAeJ,OAbInoC,IAAK,GAAKA,EAAId,KAAKgB,SAErBioC,EAAWjpC,KAAMc,GAEjB6Q,GAAGd,OAAOrP,KAAMxB,KAAMc,EAAG,GACzBd,KAAKoK,QAAS3I,GAAWmD,OAAOsjC,QAASloC,KAAMipC,EAAUnoC,IAEnDo6B,GAEJl7B,KAAK2lB,KAAM1lB,EAAWA,GAAW,IAI9BgpC,GA8BT5/B,OAAQ,SAAS6H,EAAOgqB,EAAWz0B,GAEjC,GAAI3F,GAAId,KAAKS,QAASyQ,EAAOzK,GACzByiC,EAAUlpC,KAAMc,EAOpB,QALY,IAAPA,GAEHd,KAAK6iB,SAAU/hB,EAAGo6B,GAGbgO,GA6BTC,UAAW,SAAShoC,EAAQ+5B,EAAWz0B,GAErC,GAAIsiC,MACAK,IAEJ,IAAK/nC,EAASF,IAAYA,EAAOH,OACjC,CACE,IAAK,GAAIF,GAAI,EAAGA,EAAIK,EAAOH,OAAQF,IACnC,CACE,GAAIoQ,GAAQ/P,EAAQL,GAChBgB,EAAI9B,KAAKS,QAASyQ,EAAOzK,IAEjB,IAAP3E,IAEHsnC,EAAejgC,KAAMrH,GACrBinC,EAAQ5/B,KAAM+H,IAIlBk4B,EAAezjB,MAEf,KAAK,GAAI7kB,GAAIsoC,EAAepoC,OAAS,EAAGF,GAAK,EAAGA,IAE9C6Q,GAAGd,OAAOrP,KAAMxB,KAAMopC,EAAgBtoC,GAAK,EAG7Cd,MAAKoK,QAAS3I,GAAWmD,OAAOujC,SAAUnoC,KAAM+oC,EAASK,IAEnDlO,GAEJl7B,KAAK2lB,KAAM1lB,EAAWA,GAAW,GAIrC,MAAO8oC,IAkCTM,YAAa,SAASZ,EAAiBC,EAAYC,EAAarpB,EAAK4b,GAMnE,IAAK,GAJDnqB,GAAQC,GAAay3B,EAAiBC,EAAYC,GAClDI,EAAUzpB,GAAOtf,KAAKuc,aACtB6sB,KAEKtoC,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIoQ,GAAQlR,KAAMc,EAEbiQ,GAAOG,KAEVk4B,EAAejgC,KAAMrI,GACrBioC,EAAQ5/B,KAAM+H,IAIlB,IAAK,GAAIpQ,GAAIsoC,EAAepoC,OAAS,EAAGF,GAAK,EAAGA,IAE9C6Q,GAAGd,OAAOrP,KAAMxB,KAAMopC,EAAgBtoC,GAAK,EAU7C,OAPAd,MAAKoK,QAAS3I,GAAWmD,OAAOujC,SAAUnoC,KAAM+oC,EAASK,IAEnDlO,GAEJl7B,KAAK2lB,KAAM1lB,EAAWA,GAAW,GAG5B8oC,GA6BTl4B,OAAQ,SAAS+1B,EAAO0C,GAEtB,GAAIp+B,GAASyG,GAAGpQ,MAAMC,KAAMJ,UAAW,GACnC2nC,EAAUp3B,GAAGd,OAAOjO,MAAO5C,KAAMoB,UAcrC,OAZKkoC,IAEHtpC,KAAKoK,QAAS3I,GAAWmD,OAAOujC,SAAUnoC,KAAM+oC,EAASnC,EAAO0C,IAG7Dp+B,EAAOlK,QAEVhB,KAAKoK,QAAS3I,GAAWmD,OAAOojC,MAAOhoC,KAAMkL,EAAQ07B,IAGvD5mC,KAAK2lB,KAAM1lB,EAAWA,GAAW,GAE1B8oC,GAiBT/mC,QAAS,WAaP,MAXK2P,IAAG3P,QAEN2P,GAAG3P,QAAQY,MAAO5C,MAIlBgC,EAAShC,MAGXA,KAAKoK,QAAS3I,GAAWmD,OAAOwjC,SAAUpoC,OAEnCA,MA0BTS,QAAS,SAASyQ,EAAOzK,GAIvB,IAAK,GAFDwF,GAAWxF,GAAU5F,EAEhBC,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IAE/B,GAAKmL,EAAUiF,EAAOlR,KAAMc,IAE1B,MAAOA,EAIX,QAAQ,GAwBVg4B,SAAU,SAASn4B,EAAY4oC,GAK7B,IAAK,GAHD3oC,GAAMuE,EAAkBxE,GAAcX,KAAKW,YAAY,GACvD6oC,EAAMD,EAEDzoC,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IAE1BF,EAAK4oC,EAAKxpC,KAAKc,IAAO,IAEzB0oC,EAAMxpC,KAAKc,GAIf,OAAO0oC,IAwBTC,SAAU,SAAS9oC,EAAY4oC,GAK7B,IAAK,GAHD3oC,GAAMuE,EAAkBxE,GAAcX,KAAKW,YAAY,GACvD4zB,EAAMgV,EAEDzoC,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IAE1BF,EAAK2zB,EAAKv0B,KAAKc,IAAO,IAEzByzB,EAAMv0B,KAAKc,GAIf,OAAOyzB,IA4BTiV,IAAK,SAASvlC,EAAYslC,EAAeG,GAMvC,IAAK,GAJD/oC,GAAa+oC,GAAmBrjC,EAChCqJ,EAAWG,GAAwB5L,GACnCulC,EAAMD,EAEDzoC,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIqP,GAAWT,EAAU1P,KAAMc,GAE1BH,GAAY6oC,EAAKr5B,GAAU,GAAU,IAExCq5B,EAAMr5B,GAIV,MAAOq5B,IA4BTjV,IAAK,SAAStwB,EAAYslC,EAAeG,GAMvC,IAAK,GAJD/oC,GAAa+oC,GAAmBrjC,EAChCqJ,EAAWG,GAAwB5L,GACnCswB,EAAMgV,EAEDzoC,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIqP,GAAWT,EAAU1P,KAAMc,GAE1BH,GAAY4zB,EAAKpkB,GAAU,GAAS,IAEvCokB,EAAMpkB,GAIV,MAAOokB,IA2BToV,WAAY,SAASlB,EAAiBC,EAAYC,GAIhD,IAAK,GAFD53B,GAAQC,GAAay3B,EAAiBC,EAAYC,GAE7C7nC,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIuL,GAAQrM,KAAMc,EAElB,IAAKiQ,EAAO1E,GAEV,MAAOA,GAIX,MAAO,OAsBT9G,MAAO,SAAStB,GAId,IAAK,GAFDyL,GAAWG,GAAwB5L,GAE9BnD,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIqP,GAAWT,EAAU1P,KAAMc,GAE/B,IAAKN,EAAS2P,GAEZ,MAAOA,KA6Bby5B,UAAW,SAAS3lC,EAAYiN,EAAOzK,GAIrC,IAAK,GAFDsK,GAAQC,GAAa/M,EAAYiN,EAAOzK,GAEnC3F,EAAId,KAAKgB,OAAS,EAAGF,GAAK,EAAGA,IACtC,CACE,GAAIuL,GAAQrM,KAAMc,EAElB,IAAKiQ,EAAO1E,GAEV,MAAOA,GAIX,MAAO,OAsBTw9B,KAAM,SAAS5lC,GAIb,IAAK,GAFDyL,GAAWG,GAAwB5L,GAE9BnD,EAAId,KAAKgB,OAAS,EAAGF,GAAK,EAAGA,IACtC,CACE,GAAIqP,GAAWT,EAAU1P,KAAMc,GAE/B,IAAKN,EAAS2P,GAEZ,MAAOA,KA4Bb25B,UAAW,SAASp6B,EAAUq6B,EAAWC,EAASC,GAEhD,IAAK,GAAInpC,GAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIqP,GAAWT,EAAU1P,KAAMc,GAE1BipC,GAAW55B,IAEd65B,EAAS75B,GAIb,MAAO85B,MAsBTC,IAAK,SAAS16B,GAKZ,QAASw6B,GAAQ7pC,GAEf6gB,GAAU7gB,EAGZ,QAAS8pC,KAEP,MAAOjpB,GAVT,GAAItR,GAAWC,GAAsBH,GACjCwR,EAAS,CAYb,OAAOhhB,MAAK8pC,UAAWp6B,EAAU1M,EAAUgnC,EAASC,IAsBtDE,IAAK,SAAS36B,GAMZ,QAASw6B,GAAQ7pC,GAEf6gB,GAAU7gB,EACViqC,IAGF,QAASH,KAEP,MAAiB,KAAVG,EAAc,EAAIppB,EAASopB,EAZpC,GAAI16B,GAAWC,GAAsBH,GACjCwR,EAAS,EACTopB,EAAQ,CAaZ,OAAOpqC,MAAK8pC,UAAWp6B,EAAU1M,EAAUgnC,EAASC,IA6BtDI,WAAY,SAASpmC,EAAYiN,EAAOzK,GAKtC,IAAK,GAHDsK,GAAQC,GAAa/M,EAAYiN,EAAOzK,GACxC6jC,EAAM,EAEDxpC,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CAGOiQ,EAFO/Q,KAAMc,KAIhBwpC,IAIJ,MAAOA,IAuBTC,MAAO,SAAStmC,GAEd,IAAMzD,EAASyD,GAEb,MAAOjE,MAAKgB,MAMd,KAAK,GAHD0O,GAAWG,GAAwB5L,GACnC+c,EAAS,EAEJlgB,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CAGON,EAFUkP,EAAU1P,KAAMc,MAI7BkgB,IAIJ,MAAOA,IA4BTylB,MAAO,SAAStlC,EAAQsZ,GAEtB,GAAI+vB,GAAiB36B,GAAwB1O,EAE7C,IAAKsZ,EACL,CAIE,IAAK,GAHDgwB,GAAe56B,GAAwB4K,GACvCuG,KAEKlgB,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIuL,GAAQrM,KAAMc,GACdoQ,EAAQs5B,EAAgBn+B,EAG5B2U,GAFUypB,EAAcp+B,IAER6E,EAGlB,MAAO8P,GAMP,IAAK,GAFDA,MAEKlgB,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIuL,GAAQrM,KAAMc,GACdoQ,EAAQs5B,EAAgBn+B,EAE5B2U,GAAO7X,KAAM+H,GAGf,MAAO8P,IAkBX+lB,KAAM,SAAStiC,EAAUhB,GAIvB,IAAK,GAFDoF,GAAkBpF,GAAWzD,KAExBc,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIyB,GAAOvC,KAAMc,EAEjB2D,GAASjD,KAAMqH,EAAiBtG,EAAMzB,GAEjCd,KAAMc,KAAQyB,GAEjBzB,IAIJ,MAAOd,OAsBT+4B,UAAW,SAASt0B,EAAUR,EAAY9C,EAAQsF,GAIhD,IAAK,GAFDsK,GAAQC,GAAa/M,EAAY9C,EAAQsF,GAEpC3F,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIyB,GAAOvC,KAAMc,EAEZiQ,GAAOxO,KAEVkC,EAASjD,KAAMxB,KAAMuC,EAAMzB,GAEtBd,KAAMc,KAAQyB,GAEjBzB,KAKN,MAAOd,OA0BT0qC,OAAQ,SAASC,EAAStL,GAExB,IAAK,GAAIv+B,GAAI,EAAGA,EAAId,KAAKgB,OAAQF,IAE/Bu+B,EAAesL,EAAStL,EAAcr/B,KAAMc,GAG9C,OAAOu+B,IAWTx7B,OAAQ,WAIN,MAAO7D,MAFCkC,KAAKC,MAAOD,KAAK2B,SAAW7D,KAAKgB,UAuB3C4pC,MAAO,SAASC,EAAWvrB,GAOzB,IAAK,GALDwrB,GAAQxrB,MACRyrB,EAAa,EACbC,EAAQF,EAAOC,GAAeD,EAAOC,OACrCE,EAAa,EAERnqC,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IAE/BkqC,EAAOC,GAAejrC,KAAMc,KAErBmqC,GAAcJ,IAEnBI,EAAa,EACbF,IACAC,EAAMhqC,OAAS6pC,EACfG,EAAQF,EAAOC,GAAeD,EAAOC,OAYzC,OARoB,KAAfE,GAEHF,IAGFC,EAAMhqC,OAASiqC,EACfH,EAAM9pC,OAAS+pC,EAERD,GA8BTtV,SAAU,SAASvxB,EAAYiN,EAAOzK,GAIpC,IAAK,GAFDsK,GAAQC,GAAa/M,EAAYiN,EAAOzK,GAEnC3F,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CAGE,GAAKiQ,EAFO/Q,KAAMc,IAIhB,OAAO,EAIX,OAAO,GAuDTwH,MAAO,SAAS4iC,GAEd,GAAIC,GAAKt7B,GAAwBq7B,EAASC,IACtCC,EAASp6B,GAAak6B,EAASE,OAAQF,EAASG,YAAaH,EAASI,cACtEC,EAASL,EAASK,WAClB3wB,IAEJ,IAAKta,EAAU4qC,EAASC,IAEfD,EAASC,KAAMI,KAEpBA,EAAQL,EAASC,IAAO,aAGvB,IAAK9pC,EAAS6pC,EAASC,IAE1B,IAAK,GAAIjnC,KAAQgnC,GAASC,GAEjBjnC,IAAQqnC,KAEbA,EAAQrnC,GAAS,QAKvB,KAAK,GAAIpD,GAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIuL,GAAQrM,KAAMc,GACd0U,EAAM21B,EAAI9+B,GACV/D,EAAQsS,EAAKpF,EAEXlN,KAEJA,EAAQsS,EAAKpF,GAAQxV,KAAKuc,cAG5BjU,EAAM2S,IAAK5O,GAAO,GAGpB,GAAIm/B,GAAYxrC,KAAKuc,YAErBivB,GAAUt0B,cAAeg0B,EAASvqC,WAAYuqC,EAAS/zB,qBAEvD,KAAK,GAAI3B,KAAOoF,GAChB;iBACE,GAAI6wB,MACAC,EAAa9wB,EAAKpF,EAEtB,KAAK,GAAIm2B,KAAYJ,GACrB,CACE,GAAIK,GAAaL,EAAQI,EAEpBrrC,GAAUsrC,GAEbH,EAASE,GAAaD,EAAYE,GAAcD,GAExCjpC,EAAYkpC,KAEpBH,EAASE,GAAaC,EAAYF,EAAYC,KAI1B,IAAnBT,EAASW,QAEZJ,EAAQK,OAASJ,IAGK,IAAnBR,EAASX,QAEZkB,EAAQM,OAASL,EAAW1qC,QAGzBoqC,EAAQK,EAASC,IAEpBF,EAAUriC,KAAMsiC,GAMpB,MAFAD,GAAU7lB,OAEH6lB,GAWTtrC,QAAS,WAEP,MAAOF,MAAKuB,SAWd6a,MAAO,WAEL,MAAOpc,MAAK2C,YAAYjB,OAAQ1B,OAWlCuc,WAAY,WAEV,MAAOvc,MAAK2C,YAAYjB,YAK5B+G,EAAahH,IAeb4F,EAAkB5F,GAAY,SAAUA,GAAWmD,OAAOwa,QAI1D,IAAI4sB,KAEFxoC,KAAM,WAEJyE,GAAM4C,MAAM7K,MACVisC,MAAYzoC,EAAMxD,KAAMgsC,GAAUE,WAClCC,OAAY3oC,EAAMxD,KAAMgsC,GAAUI,YAClCC,SAAY7oC,EAAMxD,KAAMgsC,GAAUM,cAClCC,UAAY/oC,EAAMxD,KAAMgsC,GAAUQ,eAClCC,QAAYjpC,EAAMxD,KAAMgsC,GAAUU,aAClCC,UAAYnpC,EAAMxD,KAAMgsC,GAAUY,eAClCC,UAAYrpC,EAAMxD,KAAMgsC,GAAUc,kBAItC50B,KAAM,SAAS5J,EAAMuN,GAkBnB,MAhBK7b,MAAKsO,OAASA,IAEZtO,KAAKsO,MAERtO,KAAK+sC,aAGP9kC,GAAM/D,KAAMlE,KAAM,OAAQsO,GAE1BtO,KAAKgtC,WAGP/kC,GAAM/D,KAAMlE,KAAM,SAAU6b,GAE5B7b,KAAK4+B,OAEE5+B,MAGTitC,UAAW,SAASxE,EAAiBC,EAAYC,GAK/C,MAHA3oC,MAAK6b,OAAS7K,GAAay3B,EAAiBC,EAAYC,GACxD3oC,KAAK4+B,OAEE5+B,MAGTgtC,QAAS,WAUP,MARAhtC,MAAKsO,KAAK3J,GAAIlD,GAAWmD,OAAOmjC,IAAK/nC,KAAKisC,OAC1CjsC,KAAKsO,KAAK3J,GAAIlD,GAAWmD,OAAOojC,KAAMhoC,KAAKmsC,QAC3CnsC,KAAKsO,KAAK3J,GAAIlD,GAAWmD,OAAOsjC,OAAQloC,KAAKqsC,UAC7CrsC,KAAKsO,KAAK3J,GAAIlD,GAAWmD,OAAOujC,QAASnoC,KAAKusC,WAC9CvsC,KAAKsO,KAAK3J,GAAIlD,GAAWmD,OAAOyjC,MAAOroC,KAAKysC,SAC5CzsC,KAAKsO,KAAK3J,GAAIlD,GAAWmD,OAAOwjC,QAASpoC,KAAK2sC,WAC9C3sC,KAAKsO,KAAK3J,GAAIlD,GAAWmD,OAAO0jC,QAAStoC,KAAK6sC,WAEvC7sC,MAGT+sC,WAAY,WAUV,MARA/sC,MAAKsO,KAAK5G,IAAKjG,GAAWmD,OAAOmjC,IAAK/nC,KAAKisC,OAC3CjsC,KAAKsO,KAAK5G,IAAKjG,GAAWmD,OAAOojC,KAAMhoC,KAAKmsC,QAC5CnsC,KAAKsO,KAAK5G,IAAKjG,GAAWmD,OAAOsjC,OAAQloC,KAAKqsC,UAC9CrsC,KAAKsO,KAAK5G,IAAKjG,GAAWmD,OAAOujC,QAASnoC,KAAKusC,WAC/CvsC,KAAKsO,KAAK5G,IAAKjG,GAAWmD,OAAOyjC,MAAOroC,KAAKysC,SAC7CzsC,KAAKsO,KAAK5G,IAAKjG,GAAWmD,OAAOwjC,QAASpoC,KAAK2sC,WAC/C3sC,KAAKsO,KAAK5G,IAAKjG,GAAWmD,OAAO0jC,QAAStoC,KAAK6sC,WAExC7sC,MAGT4+B,KAAM,WAMJ,IAAK,GAJDtwB,GAAOtO,KAAKsO,KACZuN,EAAS7b,KAAK6b,OACdqxB,KAEKpsC,EAAI,EAAGA,EAAIwN,EAAKtN,OAAQF,IACjC,CACE,GAAIoQ,GAAQ5C,EAAMxN,EAEb+a,GAAQ3K,IAEXg8B,EAAQ/jC,KAAM+H,GAIlB,MAAOlR,MAAKqd,MAAO6vB,IAGrBhB,UAAW,SAAS7wB,EAAYnK,IAIzB2K,EAFQ7b,KAAK6b,QAEL3K,IAEXlR,KAAKib,IAAK/J,IAIdk7B,WAAY,SAAS/wB,EAAYla,GAK/B,IAAK,GAHD0a,GAAS7b,KAAK6b,OACd+e,KAEK95B,EAAI,EAAGA,EAAIK,EAAOH,OAAQF,IACnC,CACE,GAAIoQ,GAAQ/P,EAAQL,EAEf+a,GAAQ3K,IAEX0pB,EAASzxB,KAAM+H,GAInBlR,KAAKmb,OAAQyf,IAGf0R,aAAc,SAASjxB,EAAYnK,GAEjClR,KAAKqJ,OAAQ6H,IAGfs7B,cAAe,SAASnxB,EAAYla,GAElCnB,KAAKmpC,UAAWhoC,IAGlBurC,YAAa,SAASrxB,GAEpBrb,KAAK4+B,QAGPgO,cAAe,SAASvxB,EAAY8xB,GAIlC,IAAK,GAFDtxB,GAAS7b,KAAK6b,OAET/a,EAAI,EAAGA,EAAIqsC,EAAQnsC,OAAQF,IACpC,CACE,GAAIoQ,GAAQi8B,EAASrsC,EAEhB+a,GAAQ3K,GAEXlR,KAAKib,IAAK/J,GAAO,GAIjBlR,KAAKqJ,OAAQ6H,GAAO,GAIxBlR,KAAK2lB,QAGPmnB,cAAe,SAASzxB,GAEtBrb,KAAK2U,SAGPyH,MAAO,WAEL,MAAOpc,MAAK2C,YAAYjB,OAAQ1B,KAAKsO,KAAMtO,KAAK6b,SAGlDU,WAAY,WAEV,MAAOvc,MAAK2C,YAAYjB,OAAQ1B,KAAKsO,KAAMtO,KAAK6b,SAqBpDT,IAAKxW,QAEHi3B,OAAc,SACdzc,QAAc,UAGhBnX,GAAMyb,OAAQrjB,MAAO+a,IAGnBgyB,YAAa,SAAS9xB,GAEpBtb,KAAKsb,SAAWA,EAChBtb,KAAKyb,iBAGP4xB,aAAc,SAAS9xB,GAErBvb,KAAKstC,KAAM/xB,IAGbI,cAAe,SAASN,GAEjBA,IAAerb,KAAKqb,aAElBrb,KAAKqb,YAERrb,KAAK+sC,aAGP/sC,KAAKqb,WAAaA,EAClBrb,KAAKgtC,UACLhtC,KAAKyb,eAAe,KAIxBuxB,QAAS,WAEPhtC,KAAKqb,WAAW1W,GAAIlD,GAAWmD,OAAOwa,QAASpf,KAAKwb,YAGtDuxB,WAAY,WAEV/sC,KAAKqb,WAAW3T,IAAKjG,GAAWmD,OAAOwa,QAASpf,KAAKwb,YAGvD8xB,KAAM,SAAS/xB,GAEb,GAAIgyB,GAAcvtC,KAAKwoC,KAAMjtB,EAExBgyB,KAAgBvtC,KAAKub,YAExBvb,KAAKub,UAAYgyB,EACjBvtC,KAAKuU,SACLvU,KAAKoK,QAASgR,GAAKxW,OAAOi3B,QAAU77B,SAIxCuI,KAAM,WAEJvI,KAAKstC,KAAMttC,KAAKub,UAAY,IAG9B/S,KAAM,WAEJxI,KAAKstC,KAAMttC,KAAKub,UAAY,IAG9BiyB,KAAM,SAAS7/B,GAEb3N,KAAKstC,KAAM3/B,IAGbpI,MAAO,WAELvF,KAAKstC,KAAM,IAGbzD,KAAM,WAEJ7pC,KAAKstC,KAAMttC,KAAK0b,UAAY,IAG9B0uB,MAAO,WAEL,MAAOpqC,MAAKqb,WAAWra,QAGzBysC,MAAO,WAEL,MAAOvrC,MAAKwrC,KAAM1tC,KAAKoqC,QAAUpqC,KAAKsb,WAGxCktB,KAAM,SAASlmB,GAEb,MAAOpgB,MAAKqyB,IAAK,EAAGryB,KAAKsnC,IAAKlnB,EAAOtiB,KAAKytC,QAAU,KAGtDE,IAAK,SAASrrB,GAEZ,MAAOtiB,MAAKoqC,SAAW9nB,GAAS,GAAKA,EAAQtiB,KAAK0b,WAGpDkyB,SAAU,WAER,MAAO5tC,MAAK6tC,WAGdC,QAAS,WAEP,MAAO9tC,MAAK+tC,WAGdF,QAAS,WAEP,MAAO7tC,MAAKoqC,SAAWpqC,KAAKub,UAAY,GAG1CwyB,QAAS,WAEP,MAAO/tC,MAAKoqC,SAAWpqC,KAAKub,UAAYvb,KAAK0b,UAAY,GAG3DD,cAAe,SAASuyB,GAEtB,GAAItyB,GAAY1b,KAAKytC,QACjBlyB,EAAYvb,KAAKwoC,KAAMxoC,KAAKub,WAC5B3Y,EAAQorC,GAAchuC,KAAKub,YAAcA,GAAavb,KAAKgB,SAAWhB,KAAKsb,SAC3E5O,EAAU9J,GAAS5C,KAAK0b,YAAcA,CAE1C1b,MAAKub,UAAYA,EACjBvb,KAAK0b,UAAYA,EAEZ9Y,GAEH5C,KAAKuU,SAEF7H,GAEH1M,KAAKoK,QAASgR,GAAKxW,OAAOi3B,QAAU77B,QAIxCuU,OAAQ,WAEN,GAAI1H,GAAS7M,KAAKqb,WACdta,EAAI8L,EAAO7L,OACX4lC,EAAQ5mC,KAAKub,UAAYvb,KAAKsb,SAC9B2yB,EAAM/rC,KAAKsnC,IAAK5C,EAAQ5mC,KAAKsb,SAAUva,GACvCC,EAASitC,EAAMrH,CAEnB5mC,MAAKgB,OAAS,CAEd,KAAK,GAAIF,GAAI,EAAGA,EAAIE,EAAQF,IAE1Bd,KAAKmJ,KAAM0D,EAAQ+5B,OAIvBsH,KAAM,SAAST,GAWb,IATA,GAAI5gC,GAAS7M,KAAKqb,WACd8yB,EAAQthC,EAAO7L,OACf0a,EAAY+xB,GAAS,EACrBW,EAASpuC,KAAKub,UAAYvb,KAAKsb,SAC/BsrB,EAAQwH,EAASpuC,KAAKgB,OACtBkK,EAASlL,KAAKsb,SAAWI,EACzB2yB,EAAazH,EAAQ17B,EACrBojC,EAAYpsC,KAAKsnC,IAAK2E,EAAOE,GAE1BzH,EAAQ0H,GAEbtuC,KAAKmJ,KAAM0D,EAAQ+5B,OAIvB1mC,QAAS,WAEP,MAAOF,MAAKuB,WAKhBkH,EAAa2S,IAEb/T,EAAkB+T,GAAM,SAAUA,GAAKxW,OAAOwa,SAgD9CnX,GAAMyb,OAAQjiB,GAAYma,IAWxBpY,KAAMwoC,GAAUxoC,KAiBhB0U,KAAM8zB,GAAU9zB,KAmBhB+0B,UAAWjB,GAAUiB,UAUrBD,QAAShB,GAAUgB,QAUnBD,WAAYf,GAAUe,WActBnO,KAAMoN,GAAUpN,KAUhBxiB,MAAO4vB,GAAU5vB,MAUjBG,WAAYyvB,GAAUzvB,aA0CxBtU,GAAMyb,OAAQjiB,GAAYqU,IAqBxBoC,KAAM,SAASlG,EAAU6D,EAAQiG,GAU/B,MARA7T,IAAM4C,MAAM7K,MACVgS,SAAUA,EACV4I,IAAK,GAAIJ,MAGXxa,KAAK4a,IAAIzZ,OAASnB,KAClBA,KAAKqd,MAAOxH,EAAQiG,GAEb9b,MAMT2lB,KAAM,SAAShlB,EAAYwW,GAEzB,GAAIvW,GAAMD,EAAawE,EAAkBxE,EAAYwW,GAAyBnX,KAAKW,UASnF,OAPMyB,GAAUxB,EAAKZ,QAEnBA,KAAK4a,IAAI+K,KAAM/kB,GAEfZ,KAAKoK,QAAS3I,GAAWmD,OAAOqjC,MAAOjoC,QAGlCA,MAcTqc,kBAAmB,SAAS3K,GAE1B,MAAO1R,MAAKgS,SAASuD,WAAW8G,kBAAmB3K,IAmBrD4K,WAAY,SAAS5K,EAAOoK,GAE1B,MAAO9b,MAAKgS,SAASsK,WAAY5K,EAAOoK,IAuB1C8e,SAAU,SAAS6N,EAAiBC,EAAYC,GAE9C,GAAI9sB,GAAS7K,GAAay3B,EAAiBC,EAAYC,EAEvD,OAAO5sB,IAAwBra,OAAQ1B,KAAM6b,IAQ/C+mB,SAAU,SAAS/sB,EAAQyJ,GAIzB,IAAK,GAFDhY,GAASgY,GAAOtf,KAAKuc,aAEhBzb,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAII,GAAIlB,KAAMc,GACV0U,EAAMtU,EAAE6X,OACRxM,GAAS,CAEb,IAAKsJ,YAAkBC,IAErBvJ,EAASsJ,EAAO+hB,IAAKpiB,OAIrB,KAAK,GAAI1T,GAAI,EAAGA,EAAI+T,EAAO7U,SAAWuL,EAAQzK,IAC9C,CACE,GAAIq2B,GAAWn4B,KAAKqc,kBAAmBxG,EAAQ/T,GAE/CyK,GAAUiJ,IAAQ2iB,EAIjB5rB,GAEHjF,EAAO6B,KAAMjI,GAIjB,MAAOoG,IAMTshC,UAAW,SAAS/yB,EAAQyJ,GAI1B,IAAK,GAFDhY,GAASgY,GAAOtf,KAAKuc,aAEhBzb,EAAI,EAAGA,EAAI+U,EAAO7U,OAAQF,IACnC,CACE,GAAII,GAAI2U,EAAQ/U,GACZ0U,EAAMxV,KAAKqc,kBAAmBnb,EAE7BlB,MAAK43B,IAAKpiB,IAEblO,EAAO6B,KAAMjI,GAIjB,MAAOoG,IAMTuhC,WAAY,SAAShzB,EAAQyJ,GAI3B,IAAK,GAFDhY,GAASgY,GAAOtf,KAAKuc,aAEhBzb,EAAI,EAAGA,EAAI+U,EAAO7U,OAAQF,IACnC,CACE,GAAII,GAAI2U,EAAQ/U,GACZ0U,EAAMxV,KAAKqc,kBAAmBnb,EAE5BlB,MAAK43B,IAAKpiB,IAEdlO,EAAO6B,KAAMjI,GAIjB,MAAOoG,IAMTqN,MAAO,WAEL,GAAI45B,GAAUvuC,KAAK4a,IAAIyC,OAIvB,OAFArd,MAAKoK,QAAS3I,GAAWmD,OAAO0jC,SAAUtoC,OAEnCuuC,GAmBTlxB,MAAO,SAASxH,EAAQiG,GAEtB,GAAIlB,GAAM5a,KAAK4a,GAIf,IAFAA,EAAIyC,QAEChc,EAASwU,GAEZ,IAAK,GAAI/U,GAAI,EAAGA,EAAI+U,EAAO7U,OAAQF,IACnC,CACE,GAAIuL,GAAQwJ,EAAQ/U,GAChB6E,EAAS3F,KAAKsc,WAAYjQ,EAAOyP,EAEhCnW,IAEHiV,EAAIsX,IAAKvsB,EAAOoT,OAAQpT,OAIzB,IAAKnD,EAAUqT,GACpB,CACE,GAAIlQ,GAAS3F,KAAKsc,WAAYzG,EAAQiG,EAEjCnW,IAEHiV,EAAIsX,IAAKvsB,EAAOoT,OAAQpT,GAO5B,MAHA3F,MAAKoK,QAAS3I,GAAWmD,OAAOyjC,OAAQroC,OACxCA,KAAK2lB,OAEE3lB,MAcT43B,IAAK,SAASpiB,GAEZ,MAAOxV,MAAK4a,IAAIgd,IAAKpiB,IAcvB3D,IAAK,SAAS2D,GAEZ,MAAOxV,MAAK4a,IAAI/I,IAAK2D,IAoBvB0c,IAAK,SAAS1c,EAAKnJ,EAAO6uB,GAExBl7B,KAAK4a,IAAIsX,IAAK1c,EAAKnJ,GACnBrM,KAAKoK,QAAS3I,GAAWmD,OAAOmjC,KAAM/nC,KAAMqM,EAAOrM,KAAK4a,IAAIF,QAASlF,KAE/D0lB,GAEJl7B,KAAK2lB,QAyBT1K,IAAK,SAASvJ,EAAOwpB,EAAWpf,GAE9B,GAAIzP,GAAQrM,KAAKsc,WAAY5K,EAAOoK,GAChCtG,EAAMnJ,EAAM0M,MAUhB,OARA/Y,MAAK4a,IAAIsX,IAAK1c,EAAKnJ,GACnBrM,KAAKoK,QAAS3I,GAAWmD,OAAOmjC,KAAM/nC,KAAMqM,EAAOrM,KAAK4a,IAAIF,QAASlF,KAE/D0lB,GAEJl7B,KAAK2lB,OAGA3lB,MAgBTmJ,KAAM,WAKJ,IAAK,GAHDhI,GAASwQ,GAAGpQ,MAAMqB,MAAOxB,WACzBsZ,KAEK5Z,EAAI,EAAGA,EAAIK,EAAOH,OAAQF,IACnC,CACE,GAAIuL,GAAQrM,KAAKsc,WAAYnb,EAAQL,IACjC0U,EAAMnJ,EAAM0M,MAEhB/Y,MAAK4a,IAAIsX,IAAK1c,EAAKnJ,GACnBqO,EAAQvR,KAAMnJ,KAAK4a,IAAIF,QAASlF,IAMlC,MAHAxV,MAAKoK,QAAS3I,GAAWmD,OAAOojC,MAAOhoC,KAAMmB,EAAQuZ,IACrD1a,KAAK2lB,OAEE3lB,KAAKgB,QAcd6jC,QAAS,WAEP,MAAO7kC,MAAKmJ,KAAKvG,MAAO5C,KAAMoB,YAwBhC+Z,OAAQ,SAAStF,EAAQqlB,EAAWpf,GAElC,GAAKza,EAASwU,GACd,CAGE,IAAK,GAFD6E,MAEK5Z,EAAI,EAAGA,EAAI+U,EAAO7U,OAAQF,IACnC,CACE,GAAIuL,GAAQrM,KAAKsc,WAAYzG,EAAQ/U,GAAKgb,GACtCtG,EAAMnJ,EAAM0M,MAEhB/Y,MAAK4a,IAAIsX,IAAK1c,EAAKnJ,GACnBqO,EAAQvR,KAAMnJ,KAAK4a,IAAIF,QAASlF,IAGlCxV,KAAKoK,QAAS3I,GAAWmD,OAAOojC,MAAOhoC,KAAM6V,EAAQ6E,IAE/CwgB,GAEJl7B,KAAK2lB,SAcXmjB,SAAU,SAAShoC,EAAGoQ,EAAOgqB,GAE3B,MAAOl7B,MAAKib,IAAK/J,EAAOgqB,IAkB1BuH,IAAK,SAASvH,GAEZ,GAAIp6B,GAAId,KAAKgB,OAAS,EAClB+nC,EAAU/oC,KAAMc,EAUpB,OARAd,MAAK4a,IAAIiI,SAAU/hB,GACnBd,KAAKoK,QAAS3I,GAAWmD,OAAOsjC,QAASloC,KAAM+oC,EAASjoC,IAElDo6B,GAEJl7B,KAAK2lB,OAGAojB,GAuBTC,MAAO,SAAS9N,GAEd,GAAI6N,GAAU/oC,KAAM,EAUpB,OARAA,MAAK4a,IAAIiI,SAAU,GACnB7iB,KAAKoK,QAAS3I,GAAWmD,OAAOsjC,QAASloC,KAAM+oC,EAAS,IAElD7N,GAEJl7B,KAAK2lB,OAGAojB,GAoBTlmB,SAAU,SAAS/hB,EAAGo6B,GAEpB,GAAI+N,EAeJ,OAbInoC,IAAK,GAAKA,EAAId,KAAKgB,SAErBioC,EAAWjpC,KAAMc,GAEjBd,KAAK4a,IAAIiI,SAAU/hB,GACnBd,KAAKoK,QAAS3I,GAAWmD,OAAOsjC,QAASloC,KAAMipC,EAAUnoC,IAEnDo6B,GAEJl7B,KAAK2lB,QAIFsjB,GAuBT5/B,OAAQ,SAASqI,EAAOwpB,GAEtB,GAAI1lB,GAAMxV,KAAKqc,kBAAmB3K,GAC9Bu3B,EAAWjpC,KAAK4a,IAAI/I,IAAK2D,EAE7B,IAAKyzB,EACL,CACE,GAAInoC,GAAId,KAAK4a,IAAIF,QAASlF,EAE1BxV,MAAK4a,IAAIvR,OAAQmM,GACjBxV,KAAKoK,QAAS3I,GAAWmD,OAAOsjC,QAASloC,KAAMipC,EAAUnoC,IAEnDo6B,GAEJl7B,KAAK2lB,OAIT,MAAOsjB,IAoBTE,UAAW,SAASqF,EAAQtT,GAM1B,IAAK,GAJDtgB,GAAM5a,KAAK4a,IACXmuB,KACAK,KAEKtoC,EAAI,EAAGA,EAAI0tC,EAAOxtC,OAAQF,IACnC,CACE,GAAI0U,GAAMxV,KAAKqc,kBAAmBmyB,EAAQ1tC,IACtCmoC,EAAWruB,EAAI/I,IAAK2D,EAEnByzB,KAEHG,EAAejgC,KAAMyR,EAAIF,QAASlF,IAClCuzB,EAAQ5/B,KAAM8/B,IAIlBG,EAAezjB,MAEf,KAAK,GAAI7kB,GAAIsoC,EAAepoC,OAAS,EAAGF,GAAK,EAAGA,IAE9C8Z,EAAIiI,SAAUumB,EAAgBtoC,GAUhC,OAPAd,MAAKoK,QAAS3I,GAAWmD,OAAOujC,SAAUnoC,KAAM+oC,EAASK,IAEnDlO,GAEJl7B,KAAK2lB,OAGAojB,GAcTtoC,QAAS,SAASiR,GAEhB,GAAI8D,GAAMxV,KAAKqc,kBAAmB3K,GAC9B4Q,EAAQtiB,KAAK4a,IAAIF,QAASlF,EAE9B,OAAO8M,KAAUriB,GAAa,EAAIqiB,GAYpCmsB,QAAS,WAEPzuC,KAAK4a,IAAImoB,gBAWXtoB,KAAM,WAEJ,MAAOza,MAAK4a,IAAIH,MAYlBzY,QAAS,WAMP,MAJAhC,MAAK4a,IAAI5Y,UAEThC,KAAKoK,QAAS3I,GAAWmD,OAAOwjC,SAAUpoC,OAEnCA,MA6BT6Q,OAAQ,SAAS+1B,EAAO0C,GAKtB,IAAK,GAHDp+B,GAASyG,GAAGpQ,MAAMC,KAAMJ,UAAW,GAEnCstC,GAAc9H,EAAO0C,GAChBxoC,EAAI,EAAGA,EAAIoK,EAAOlK,OAAQF,IAEjC4tC,EAAWvlC,KAAMnJ,KAAKqc,kBAAmBnR,EAAQpK,IAGnD,IAAIioC,GAAUp3B,GAAGd,OAAOjO,MAAO5C,KAAMoB,UAgBrC,OAdAuQ,IAAGd,OAAOjO,MAAO5C,KAAK4a,IAAIH,KAAMi0B,GAE3BpF,GAEHtpC,KAAKoK,QAAS3I,GAAWmD,OAAOujC,SAAUnoC,KAAM+oC,EAASnC,EAAO0C,IAG7Dp+B,EAAOlK,QAEVhB,KAAKoK,QAAS3I,GAAWmD,OAAOojC,MAAOhoC,KAAMkL,EAAQ07B,IAGvD5mC,KAAK2lB,OAEEojB,GA4BTM,YAAa,SAASsF,EAAYlG,EAAiBC,EAAYC,EAAarpB,EAAK4b,EAAWpoB,EAASzH,GAEnG,GAAI0F,GAAQC,GAAay3B,EAAiBC,EAAYC,GAClDI,EAAUzpB,GAAOtf,KAAKuc,aACtB6sB,IAqCJ,OAnCAp0B,IAAa,WAEX,IAAK,GAAIlU,GAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIuL,GAAQrM,KAAMc,EAEbiQ,GAAO1E,KAEV+8B,EAAejgC,KAAMrI,GACrBioC,EAAQ5/B,KAAMkD,IAIlB,IAAK,GAAIvL,GAAI,EAAGA,EAAIioC,EAAQ/nC,OAAQF,IACpC,CACE,GAAIuL,GAAQ08B,EAASjoC,GACjB0U,EAAMnJ,EAAM0M,MAEhB/Y,MAAK4a,IAAIvR,OAAQmM,GAEZm5B,GAEHtiC,EAAMqsB,QAAS5lB,EAASzH,KAI3BrL,MAEHA,KAAKoK,QAAS3I,GAAWmD,OAAOujC,SAAUnoC,KAAM+oC,EAASK,IAEnDlO,GAEJl7B,KAAK2lB,OAGAojB,GA6BTx0B,OAAQ,SAAS1J,EAAOqG,EAAO4K,EAAY8yB,EAAW97B,EAASzH,GAqB7D,MAnBA2J,IAAa,WAEX,IAAK,GAAIlU,GAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIuL,GAAQrM,KAAMc,EAElBuL,GAAM6pB,KAAMrrB,EAAOqG,EAAO4K,GAEpB8yB,GAEJviC,EAAMoU,MAAO3N,EAASzH,KAIzBrL,MAEHA,KAAKoK,QAAS3I,GAAWmD,OAAOwjC,SAAUpoC,KAAMA,OAChDA,KAAK2lB,OAEE3lB,MA+BT6uC,YAAa,SAAS99B,EAAOlG,EAAOqG,EAAO4K,EAAY8yB,EAAW97B,EAASzH,GAEzE,GAAImrB,KA0BJ,OAxBAxhB,IAAa,WAEX,IAAK,GAAIlU,GAAI,EAAGA,EAAId,KAAKgB,OAAQF,IACjC,CACE,GAAIuL,GAAQrM,KAAMc,EAEbiQ,GAAO1E,KAEVA,EAAM6pB,KAAMrrB,EAAOqG,EAAO4K,GAEpB8yB,GAEJviC,EAAMoU,MAAO3N,EAASzH,GAGxBmrB,EAAQrtB,KAAMkD,MAIjBrM,MAEHA,KAAKoK,QAAS3I,GAAWmD,OAAOwjC,SAAUpoC,KAAMw2B,IAChDx2B,KAAK2lB,OAEE6Q,GAuBTsY,UAAW,SAASxiC,EAAQrI,EAAYiN,EAAOzK,GAE7C,QAASsoC,GAAO1iC,GAEdA,EAAM+zB,MAAO9zB,GAGf,MAAOtM,MAAK+4B,UAAWgW,EAAQ9qC,EAAYiN,EAAOzK,IAwBpDuoC,SAAU,SAAS1O,EAAar8B,EAAYiN,EAAOzK,GAEjD,QAASwoC,GAAM5iC,GAEbA,EAAMg0B,KAAMC,GAGd,MAAOtgC,MAAK+4B,UAAWkW,EAAOhrC,EAAYiN,EAAOzK,IAoBnDyoC,aAAc,SAASjrC,EAAYiN,EAAOzK,GAExC,QAAS0oC,GAAU9iC,GAEjBA,EAAMk0B,WAGR,MAAOvgC,MAAK+4B,UAAWoW,EAAWlrC,EAAYiN,EAAOzK,IAuBvD2oC,YAAa,SAAS/xB,EAAOpZ,EAAYiN,EAAOzK,GAE9C,QAAS4oC,GAAShjC,GAEhBA,EAAMwzB,QAASxiB,GASjB,MANArI,IAAa,WAEXhV,KAAK+4B,UAAWsW,EAAUprC,EAAYiN,EAAOzK,IAE5CzG,MAEIA,MAwBTsvC,aAAc,SAASrrC,EAAYiN,EAAOzK,EAAQqM,EAASzH,GAEzD,QAASkkC,GAAUljC,GAEjBA,EAAM8pB,SAAUrjB,EAASzH,GAS3B,MANA2J,IAAa,WAEXhV,KAAK+4B,UAAWwW,EAAWtrC,EAAYiN,EAAOzK,IAE7CzG,MAEIA,MA2BT8Q,UAAW,SAAS7M,EAAYiN,EAAOzK,EAAQoE,EAAOiI,EAASzH,GAE7D,QAASmkC,GAAOnjC,GAEdA,EAAMoU,MAAO5V,EAAOiI,EAASzH,GAS/B,MANA2J,IAAa,WAEXhV,KAAK+4B,UAAWyW,EAAQvrC,EAAYiN,EAAOzK,IAE1CzG,MAEIA,MAoBTyvC,WAAY,SAASxrC,EAAYiN,EAAOzK,GAEtC,GAAIsK,GAAQC,GAAa/M,EAAYiN,EAAOzK,GAExCgpC,EAAa,SAAUpjC,GAEzB,MAAO0E,GAAO1E,IAAWA,EAAM4sB,cAGjC,OAAOj5B,MAAKw1B,SAAUia,IAwBxBC,WAAY,SAASzrC,EAAYiN,EAAOzK,EAAQ6Y,GAE9C,GAAIvO,GAAQC,GAAa/M,EAAYiN,EAAOzK,GACxCiG,EAAU4S,GAAOA,YAAexJ,IAAkBwJ,EAAMtf,KAAKuc,YAUjE,OARAvc,MAAK+mC,KAAK,SAAS16B,GAEZ0E,EAAO1E,IAAWA,EAAM4sB,eAE3BvsB,EAAQwlB,IAAK7lB,EAAM0M,OAAQ1M,EAAMs1B,iBAI9Bj1B,GAITg1B,QAAS,SAASD,EAAiBniB,GAKjC,IAAK,GAHDhY,GAASgY,MACT2kB,EAAa3rB,GAAWlK,MAAOpO,KAAKgS,SAAUyvB,GAEzC3gC,EAAI,EAAGA,EAAId,KAAKgB,OAAQF,IAE/BwG,EAAO6B,KAAM86B,EAAWvC,QAAS1hC,KAAMc,IAGzC,OAAOwG,IAcTg8B,SAAU,SAAShkB,GAEjB,MAAOtf,MAAK4a,IAAI0oB,SAAUhkB,IAkB5BlD,MAAO,SAASuzB,EAAaC,GAE3B,GAAI/iC,GAAS7M,IAEb,IAAK2vC,EACL,CACE9iC,IAEA,KAAK,GAAI/L,GAAI,EAAGA,EAAId,KAAKgB,OAAQF,IAE/B+L,EAAQ/L,GAAMd,KAAMc,GAAIg/B,OAAQ8P,GAIpC,MAAO95B,IAAgBpU,OAAQ1B,KAAKgS,SAAUnF,GAAQ,IAWxD0P,WAAY,WAEV,MAAOzG,IAAgBpU,OAAQ1B,KAAKgS,aAmDxC/J,GAAMyb,OAAQ5N,GAAiBiG,IAW7BvY,KAAM,WAEJwoC,GAAUxoC,KAAKZ,MAAO5C,MAEtBiI,GAAM4C,MAAM7K,MACV6vC,eAAgBrsC,EAAMxD,KAAMA,KAAK8vC,sBAmBrC53B,KAAM,SAAS5J,EAAMuN,GAanB,MAXK7b,MAAKsO,MAERtO,KAAKsO,KAAK0D,SAAStK,IAAK5E,GAAS8B,OAAOguB,aAAc5yB,KAAK6vC,gBAG7D/5B,GAAgBxU,UAAU4W,KAAK1W,KAAMxB,KAAMsO,EAAK0D,UAEhDg6B,GAAU9zB,KAAK1W,KAAMxB,KAAMsO,EAAMuN,GAEjCvN,EAAK0D,SAASrN,GAAI7B,GAAS8B,OAAOguB,aAAc5yB,KAAK6vC,gBAE9C7vC,MAoBTitC,UAAWjB,GAAUiB,UAUrBD,QAAShB,GAAUgB,QAUnBD,WAAYf,GAAUe,WActBnO,KAAMoN,GAAUpN,KAKhBkR,kBAAmB,SAASzjC,GAE1B,GAAIE,GAASvM,KAAK43B,IAAKvrB,EAAM0M,QACzBm0B,EAAUltC,KAAK6b,OAAQxP,EAEtBE,KAAW2gC,GAEdltC,KAAKqJ,OAAQgD,IAETE,GAAU2gC,GAEdltC,KAAKib,IAAK5O,IAYd+P,MAAO4vB,GAAU5vB,MAUjBG,WAAYyvB,GAAUzvB,aAgDxBtU,GAAMyb,OAAQ5N,GAAiBkG,IAqB7B4C,IAAK,SAASlN,EAAOoK,GAInB,MAFA9b,MAAKic,QAAQ2C,IAAK5e,KAAKqM,MAAOqF,EAAOoK,GAE9B9b,MAiBT8+B,OAAQ,SAASptB,EAAOoK,GAItB,MAFA9b,MAAKic,QAAQ6iB,OAAQ9+B,KAAKqM,MAAOqF,EAAOoK,GAEjC9b,MAkBTi/B,SAAU,SAASvtB,EAAOoK,GAIxB,MAFA9b,MAAKic,QAAQgjB,SAAUj/B,KAAKqM,MAAOqF,EAAOoK,GAEnC9b,MAeT4+B,KAAM,SAASD,GAIb,MAFA3+B,MAAKic,QAAQ2iB,KAAM5+B,KAAKqM,MAAOsyB,GAExB3+B,MAoBT+vC,cAAe,SAAS9rC,EAAYiN,EAAOzK,GAEzC,MAAOzG,MAAKi/B,SAAUj/B,KAAK+Q,MAAO9M,EAAYiN,EAAOzK,QAavD24B,UAAW,SAAS1tB,GAElB,MAAO1R,MAAKic,QAAQmjB,UAAWp/B,KAAKqM,MAAOqF,IAW7C0K,MAAO,WAEL,MAAOJ,IAAmBta,OAAQ1B,KAAKgS,SAAUhS,KAAKqM,MAAOrM,KAAKic,QAASjc,MAAM,IAWnFuc,WAAY,WAEV,MAAOP,IAAmBta,OAAQ1B,KAAKgS,SAAUhS,KAAKqM,MAAOrM,KAAKic,YA8ItEQ,GAAOnH,YAIPrN,GAAMvG,OAAQ+a,IAGZuzB,aAAc,WAEZ,MAAOvzB,IAAOnH,UAGhBqH,MAAO,SAAS3K,EAAUyC,EAAKpJ,EAASR,EAAO6R,GAE7CtR,EAAcpL,KAAMqL,EAASrL,KAAKgwC,gBAAgB,GAElD/nC,GAAM/D,KAAMlE,KAAM,MAAOgS,GAEzBhS,KAAKiwC,SAAU,EACfjwC,KAAKkwC,KAAOz7B,EACZzU,KAAKk2B,KAAMrrB,GACX7K,KAAKmwC,SAAWr6B,GAAgBpU,OAAQsQ,GACxChS,KAAKmwC,SAASC,QAAUpwC,KACxBA,KAAKqwC,SAAW35B,GAAQjE,QAASzS,MAE5B0c,GAEH1c,KAAKswC,QAITpa,KAAM,SAASrrB,GAOb,MALKrI,GAAUqI,IAEb4C,EAAU5C,EAAO7K,MAGZA,MAGTuwC,OAAQ,WAEN,IAAK,GAAIrsC,KAAQlE,MAES,MAAnBkE,EAAKwB,OAAO,UAER1F,MAAMkE,EAIjB,OAAOlE,OAGTswC,KAAM,SAAS77B,EAAK5J,GAElB7K,KAAKkwC,KAAOz7B,GAAOzU,KAAKkwC,KACxBlwC,KAAKk2B,KAAMrrB,EAEX,IAAI+G,GAAU,GAAI8E,IACdpC,EAAUtU,KAAKwwC,UACfl+B,EAAU9O,EAAMxD,KAAMA,KAAKywC,eAAgB7+B,IAC3CuC,EAAU3Q,EAAMxD,KAAMA,KAAK0wC,eAAgB9+B,IAC3CvG,EAAUrL,KAAK4L,UAAY5L,KAAKyiB,IAAI2R,YAUxC,OARApf,IAAa,WAEXhV,KAAK6/B,UACL7/B,KAAKqwC,SAAWz+B,EAChB5R,KAAKyiB,IAAI1O,KAAKS,MAAOxU,KAAKkwC,KAAM57B,EAASjJ,EAASiH,EAAS6B,IAE1DnU,MAEIA,KAAKqwC,UAGdI,eAAgB,SAAS7+B,GAEvB,MAAO,UAAS2H,GAEd,GAAMvZ,KAAKqwC,SAASM,aAAe/+B,IAAY5R,KAAKqwC,SAApD,CAKA,GAAIx6B,GAAS7V,KAAKy+B,QAAQ77B,MAAO5C,KAAMoB,UAElCpB,MAAKiwC,QAERjwC,KAAKmwC,SAASh1B,OAAQtF,GAAQ,GAAO,GAIrC7V,KAAKmwC,SAAS9yB,MAAOxH,GAAQ,GAG/B7V,KAAKqwC,SAAS59B,QAASzS,KAAMuZ,EAAUvZ,KAAKmwC,aAIhDO,eAAgB,SAAS9+B,GAEvB,MAAO,UAAS2H,EAAUa,GAExB,GAAMpa,KAAKqwC,SAASM,aAAe/+B,IAAY5R,KAAKqwC,SAApD,CAKA,GAAIO,GAAU1pB,GAAWX,QAASnM,EAE7Bw2B,KAEH7wC,GAAO8xB,qBAEP+e,GAAW7wC,GAAOqxB,QAGfwf,EAEH5wC,KAAKqwC,SAAS/1B,OAAQta,KAAMuZ,EAAUa,GAItCpa,KAAKqwC,SAASh2B,OAAQra,KAAMuZ,EAAUa,MAK5CylB,QAAS,WAEP7/B,KAAKqwC,SAASnzB,UAGhB2zB,OAAQ,WAEN7wC,KAAKmwC,SAASx7B,SAGhB67B,QAAS,WAEP,MAAO1iC,GAAgBvJ,EAAMvE,QAG/By+B,QAAS,SAAS5oB,GAEhB,MAAOA,IAGTkD,KAAM,WAEJ,MAAO,IAGT+3B,QAAS,SAASrsC,EAAUhB,GAE1B,MAAOzD,MAAKmwC,SAASY,OAAQtsC,EAAUhB,MA4C3CmZ,GAAYtH,UAEV07B,UAAa,GACbC,WAAa,EACb7G,MAAa,GAGfniC,GAAMyb,OAAQjH,GAAQG,IAGpBozB,aAAc,WAEZ,MAAOpzB,IAAYtH,UAGrB47B,MAAO,SAAS5uB,EAAO6uB,GAErB,GAAI51B,GAAYvb,KAAKoxC,gBACjB11B,EAAY1b,KAAKqxC,gBACjBC,EAAUpvC,KAAKqyB,IAAK,EAAGryB,KAAKsnC,IAAKlnB,EAAO5G,EAAY,GAaxD,OAXKH,KAAc+1B,IAEjBtxC,KAAKuxC,cAAeD,GAEdH,IAEJnxC,KAAKiwC,SAAU,EACfjwC,KAAKswC,SAIFtwC,KAAKqwC,UAGdmB,MAAO,WAEL,GAAIjpC,GAAOvI,KAAKoxC,gBAAkB,CAUlC,OARK7oC,GAAOvI,KAAKqxC,kBAEfrxC,KAAKuxC,cAAehpC,GACpBvI,KAAKiwC,SAAU,EACfjwC,KAAKswC,OACLtwC,KAAKqwC,SAAS3V,SAAU16B,KAAKyxC,WAAYzxC,OAGpCA,KAAKqwC,UAGdoB,WAAY,WAEVzxC,KAAKiwC,SAAU,GAGjByB,OAAQ,SAASP,GAEf,MAAOnxC,MAAKkxC,MAAO,EAAGC,IAGxBQ,MAAO,SAASR,GAEd,MAAOnxC,MAAKkxC,MAAOlxC,KAAKqxC,gBAAkB,EAAGF,IAG/CS,MAAO,SAAST,GAEd,MAAOnxC,MAAKkxC,MAAOlxC,KAAKoxC,gBAAkB,EAAGD,IAG/CU,MAAO,SAASV,GAEd,MAAOnxC,MAAKkxC,MAAOlxC,KAAKoxC,gBAAkB,EAAGD,IAG/CW,OAAQ,WAEN,MAAO9xC,MAAK+xC,aAGdC,OAAQ,WAEN,MAAOhyC,MAAKqxC,iBAGdY,MAAO,SAAS3vB,GAEd,MAAOpgB,MAAKqyB,IAAK,EAAGryB,KAAKsnC,IAAKlnB,EAAOtiB,KAAKgyC,SAAW,KAGvDE,KAAM,SAAS5vB,GAEb,MAAOtiB,MAAK+xC,aAAezvB,GAAS,GAAKA,EAAQtiB,KAAKqxC,iBAGxDc,UAAW,WAET,MAAOnyC,MAAKoyC,YAGdC,SAAU,WAER,MAAOryC,MAAKsyC,YAGdF,SAAU,WAER,MAAOpyC,MAAK+xC,aAAe/xC,KAAKoxC,gBAAkB,GAGpDkB,SAAU,WAER,MAAOtyC,MAAK+xC,aAAe/xC,KAAKoxC,gBAAkBpxC,KAAKqxC,gBAAkB,GAG3E5S,QAAS,SAASllB,GAMhB,MAJAvZ,MAAKuyC,gBAAiBh5B,GACtBvZ,KAAKwyC,iBAAkBj5B,GACvBvZ,KAAKyyC,aAAcl5B,GAEZvZ,KAAK0yC,eAAgBn5B,IAG9Bm5B,eAAgB,SAASn5B,GAEvB,MAAOA,GAASxH,SAGlBwgC,gBAAiB,SAASh5B,GAEnBvW,EAAUuW,EAASy3B,aAEtBhxC,KAAKgxC,UAAYz3B,EAASy3B,YAI9B2B,aAAc,SAAS3B,GAErBhxC,KAAKgxC,UAAYA,GAGnB4B,aAAc,WAEZ,MAAO5yC,MAAKgxC,WAGdwB,iBAAkB,SAASj5B,GAEpBvW,EAAUuW,EAAS03B,cAEtBjxC,KAAKixC,WAAa13B,EAAS03B,aAI/BM,cAAe,SAASN,GAEtBjxC,KAAKixC,WAAaA,GAAc,GAGlCG,cAAe,WAEb,MAAOpxC,MAAKixC,YAGd4B,eAAgB,WAEd,MAAO7yC,MAAKixC,WAAajxC,KAAKgxC,WAGhCyB,aAAc,SAASl5B,GAEhBvW,EAAUuW,EAAS6wB,SAEtBpqC,KAAKoqC,MAAQ7wB,EAAS6wB,QAI1B0I,UAAW,SAAS1I,GAElBpqC,KAAKoqC,MAAQA,GAAS,GAGxB2H,UAAW,WAET,MAAO/xC,MAAKoqC,OAGdiH,cAAe,WAEb,MAAOnvC,MAAKwrC,KAAM1tC,KAAK+xC,YAAc/xC,KAAK4yC,mBAyB9Cl8B,GAAQqG,QAENC,QAAY,UACZ+1B,QAAY,UACZC,QAAY,UACZzsB,QAAY,UACZ0sB,SAAY,YAGdv8B,GAAQ9R,QAENmuC,QAAc,UACdC,QAAc,UACdzsB,QAAc,UACd0sB,SAAc,WACdC,aAAc,2BACdC,SAAc,oCAGhBz8B,GAAQxC,IAAM,SAASk/B,GAOrB,QAASC,KAEPthC,EAAQ5I,KAAMwI,GAAGpQ,MAAMqB,MAAOxB,cAEvBkyC,IAAcC,GAEnBr/B,EAAIzB,QAASV,GAIjB,IAAK,GAfDmC,GAAM,GAAIwC,IACV48B,EAAY,EACZC,EAAOH,EAASpyC,OAChB+Q,KAYKjR,EAAI,EAAGA,EAAIsyC,EAASpyC,OAAQF,IACrC,CACE,GAAIuM,GAAI+lC,EAAUtyC,EAEbuM,aAAaqJ,IAEhBrJ,EAAEmmC,KAAMH,EAAen/B,EAAImG,OAAQnG,EAAIoG,OAAQpG,EAAIgJ,OAAQhJ,GAI3Dq/B,IAIJ,MAAOr/B,IAGTwC,GAAQ+8B,KAAO,SAASL,GAItB,IAAK,GAFDK,GAAO,GAAI/8B,IAEN5V,EAAI,EAAGA,EAAIsyC,EAASpyC,OAAQF,IACrC,CACE,GAAIuM,GAAI+lC,EAAUtyC,EAEbuM,aAAaqJ,KAEhBrJ,EAAE7J,KAAMiwC,GAIZ,MAAOA,IAGT/8B,GAAQ2D,OAAS,SAASq5B,GAExB,GAAIrmC,GAAI,GAAIqJ,GAEZ,OADArJ,GAAEgN,OAAOzX,MAAOyK,EAAGjM,WACZiM,GAGTqJ,GAAQjE,QAAU,WAEhB,GAAIpF,GAAI,GAAIqJ,GAEZ,OADArJ,GAAEoF,QAAQ7P,MAAOyK,EAAGjM,WACbiM,GAGTqJ,GAAQ4D,OAAS,SAASo5B,GAExB,GAAIrmC,GAAI,GAAIqJ,GAEZ,OADArJ,GAAEiN,OAAO1X,MAAOyK,EAAGjM,WACZiM,GAGTqJ,GAAQwG,OAAS,WAEf,GAAI7P,GAAI,GAAIqJ,GAEZ,OADArJ,GAAE6P,OAAOta,MAAOyK,EAAGjM,WACZiM,GAGTqJ,GAAQ88B,KAAO,WAEb,GAAInmC,GAAI,GAAIqJ,GAEZ,OADArJ,GAAEoF,UACKpF,EAAEmmC,KAAK5wC,MAAOyK,EAAGjM,YAG1BsV,GAAQwP,YAAc,WAQpB,QAASmtB,OAEAM,IAAoBC,GAEzB1tB,EAAYzT,QAASohC,GAIzB,QAASC,GAAYliC,GAEnBgiC,IACAhiC,EAAQ4hC,KAAMH,EAAentB,EAAY7L,OAAQ6L,EAAY5L,OAAQ,KAAM4L,GAjB7E,GAAIA,GAAc,KACd2tB,EAAoB,KACpBE,GAAY,EACZH,EAAe,EACfD,EAAkB,CAgBtB,OAAO,UAASK,EAAkBC,EAAmBC,GAEnD,GAAItiC,GAAUoiC,EACVvwC,EAAUwwC,EACVxvC,EAAWyvC,CASf,IAPMtiC,YAAmB8E,MAEvB9E,GAAU,EACVnO,EAAUuwC,EACVvvC,EAAWwvC,GAGPF,EA8BAniC,GAEFkiC,EAAaliC,GAGfnN,EAASjD,KAAMiC,EAASyiB,OAlC1B,CACE6tB,GAAY,EACZ7tB,EAAc,GAAIxP,IAAS,MAAM,GACjCm9B,EAAoBpwC,EACpBmwC,EAAe,EACfD,EAAkB,EAEd/hC,GAEFkiC,EAAaliC,EAGf,KAEEnN,EAASjD,KAAMiC,EAASyiB,GAE1B,MAAO7b,GAIL,KAFAtK,IAAOqK,QAASrK,GAAO6E,OAAO0F,OAAQD,IAEhCA,EAER,QAEE0pC,GAAY,GAkBhB,MALqB,KAAjBH,GAEF1tB,EAAYzT,UAGPyT,MAKXje,GAAMvG,OAAQgV,IAEZjE,QAAS,WAEPzS,KAAKm0C,OAAQz9B,GAAQqG,OAAOg2B,QAASr8B,GAAQ9R,OAAOmuC,QAAS3xC,YAG/DiZ,OAAQ,WAENra,KAAKm0C,OAAQz9B,GAAQqG,OAAOi2B,QAASt8B,GAAQ9R,OAAOouC,QAAS5xC,YAG/DkZ,OAAQ,WAENta,KAAKm0C,OAAQz9B,GAAQqG,OAAOwJ,QAAS7P,GAAQ9R,OAAO2hB,QAASnlB,YAG/D8b,OAAQ,WAEDld,KAAK8c,YAER9c,KAAKm0C,OAAQz9B,GAAQqG,OAAOk2B,SAAUv8B,GAAQ9R,OAAOquC,SAAU7xC,YAInEoC,KAAM,SAASoO,GAEb5R,KAAKsS,QAASV,EAAQa,QAASb,GAC/B5R,KAAKmU,QAASvC,EAAQyI,OAAQzI,GAC9B5R,KAAK4wC,QAASh/B,EAAQ0I,OAAQ1I,GAC9B5R,KAAKo0C,SAAUxiC,EAAQsL,OAAQtL,IAGjC4hC,KAAM,SAASlhC,EAAS6B,EAASy8B,EAASwD,EAAU3wC,EAAS6xB,GAI3D,GAAI/sB,GAAO,GAAImO,GAQf,OANA1W,MAAKsS,QAASA,EAAS7O,EAAS6xB,EAAY/sB,GAC5CvI,KAAKmU,QAASA,EAAS1Q,EAAS6xB,EAAY/sB,GAC5CvI,KAAK4wC,QAASA,EAASntC,EAAS6xB,EAAY/sB,GAC5CvI,KAAKo0C,SAAUA,EAAU3wC,EAAS6xB,EAAY/sB,GAC9CvI,KAAKq0C,QAAS9rC,GAEPA,GAGT8rC,QAAS,SAAS9rC,GAEhB,GAAI0U,GAAQjd,KAAKid,KAEI,KAAjBA,EAAMjc,QAGRhB,KAAKs0C,aAAa,WAEhB,IAAK,GAAIxzC,GAAI,EAAGA,EAAImc,EAAMjc,OAAQF,IAEhCmc,EAAOnc,GAAIqzC,OAAQn0C,KAAKoa,OAAQpa,KAAKoa,OAAQhZ,aAKnD6b,EAAM9T,KAAMZ,IAGd8U,MAAO,SAASk3B,GASd,MAPAv0C,MAAKoa,OAAS1D,GAAQqG,OAAOC,QAExBu3B,GAEHv0C,KAAK0H,MAGA1H,MAGTm0C,OAAQ,SAAS/5B,EAAQ5S,EAAQuK,GAE1B/R,KAAKoa,SAAW1D,GAAQqG,OAAOC,UAElChd,KAAK+R,QAAUJ,GAAGpQ,MAAMqB,MAAOmP,GAC/B/R,KAAKoa,OAASA,EACdpa,KAAKoK,QAAS5C,EAAQuK,KAI1ByiC,UAAW,SAASC,EAAWjtC,EAAQ/C,EAAUhB,EAAS6xB,EAAY/sB,GAEpE,GAAK7F,EAAY+B,GACjB,CACE,GAAIiwC,GAAe,WAEjB,GAAI1zB,GAASvc,EAAS7B,MAAOa,GAAWzD,KAAMA,KAAK+R,QAE9CiP,aAAkBtK,KAClBnO,YAAgBmO,KAChBnO,EAAKooC,aAER3vB,EAAOxd,KAAM+E,GAIZvI,MAAKoa,SAAW1D,GAAQqG,OAAOC,QAE7BsY,EAEHt1B,KAAK2E,GAAI6C,EAAQktC,EAAc10C,MAI/BA,KAAKwJ,KAAMhC,EAAQktC,EAAc10C,MAG3By0C,GAERC,EAAa9xC,MAAO5C,MAIxB,MAAOA,OAGTsS,QAAS,SAAS7N,EAAUhB,EAAS6xB,EAAY/sB,GAE/C,MAAOvI,MAAKw0C,UAAWx0C,KAAK20C,YAAaj+B,GAAQ9R,OAAOmuC,QAAStuC,EAAUhB,EAAS6xB,EAAY/sB,IAGlG+rC,aAAc,SAAS7vC,EAAUhB,EAAS6xB,EAAY/sB,GAEpD,MAAOvI,MAAKw0C,UAAWx0C,KAAK40C,iBAAkBl+B,GAAQ9R,OAAOsuC,aAAczuC,EAAUhB,EAAS6xB,EAAY/sB,IAG5G4L,QAAS,SAAS1P,EAAUhB,EAAS6xB,EAAY/sB,GAE/C,MAAOvI,MAAKw0C,UAAWx0C,KAAK60C,YAAan+B,GAAQ9R,OAAOouC,QAASvuC,EAAUhB,EAAS6xB,EAAY/sB,IAGlGusC,MAAO,SAASrwC,EAAUhB,EAAS6xB,EAAY/sB,GAE7C,MAAOvI,MAAKw0C,UAAWx0C,KAAK60C,YAAan+B,GAAQ9R,OAAOouC,QAASvuC,EAAUhB,EAAS6xB,EAAY/sB,IAGlGqoC,QAAS,SAASnsC,EAAUhB,EAAS6xB,EAAY/sB,GAE/C,MAAOvI,MAAKw0C,UAAWx0C,KAAK+0C,YAAar+B,GAAQ9R,OAAO2hB,QAAS9hB,EAAUhB,EAAS6xB,EAAY/sB,IAGlG6rC,SAAU,SAAS3vC,EAAUhB,EAAS6xB,EAAY/sB,GAEhD,MAAOvI,MAAKw0C,UAAWx0C,KAAKg1C,aAAct+B,GAAQ9R,OAAOquC,SAAUxuC,EAAUhB,EAAS6xB,EAAY/sB,IAGpGmyB,SAAU,SAASj2B,EAAUhB,EAAS6xB,EAAY/sB,GAEhD,MAAOvI,MAAKw0C,WAAW,EAAM99B,GAAQ9R,OAAOuuC,SAAU1uC,EAAUhB,EAAS6xB,EAAY/sB,IAGvFosC,UAAW,WAET,MAAO30C,MAAKoa,SAAW1D,GAAQqG,OAAOg2B,SAGxC6B,eAAgB,WAEd,MAAO50C,MAAKoa,SAAW1D,GAAQqG,OAAOg2B,SAAW/yC,KAAKoa,SAAW1D,GAAQqG,OAAOC,SAGlF63B,UAAW,WAET,MAAO70C,MAAKoa,SAAW1D,GAAQqG,OAAOi2B,SAGxC+B,UAAW,WAET,MAAO/0C,MAAKoa,SAAW1D,GAAQqG,OAAOwJ,SAGxCyuB,WAAY,WAEV,MAAOh1C,MAAKoa,SAAW1D,GAAQqG,OAAOk2B,UAGxCtC,UAAW,WAET,MAAO3wC,MAAKoa,SAAW1D,GAAQqG,OAAOC,SAGxClL,WAAY,WAEV,MAAO9R,MAAKoa,SAAW1D,GAAQqG,OAAOC,WAK1CvU,EAAaiO,IAObzO,GAAMvG,OAAQyb,IAGZE,MAAO,SAAShR,EAAOyG,EAASzH,GAE9BrL,KAAKqM,MAAQA,EACbrM,KAAK8S,QAAU9P,EAAU8P,GAAYA,EAAUiH,GAAQ2M,IACvD1mB,KAAKqL,QAAUA,EACfrL,KAAK8T,GAAKzH,EAAMoW,IAChBziB,KAAKuI,KAAO,KACZvI,KAAKi1C,UAAW,GAGlBpiC,WAAY,SAASC,GAKnB,MAA+B,MAHhBA,GAAW9S,KAAKk1C,WAClBl1C,KAAK8S,UAKpBqiC,WAAY,SAASppC,GAInB,MAA+B,KAAvBA,EAFK/L,KAAK8S,UAKpB2tB,MAAO,SAASpsB,GAETrU,KAAKuI,OAAS8L,EAAU+gC,WAE3Bp1C,KAAKuI,KAAKk4B,MAAOpsB,IAIjBrU,KAAKuI,KAAO8L,EACZrU,KAAKqM,MAAMzB,SAAU7H,GAAM6B,OAAOkuB,qBAItCuiB,QAAS,SAAS7U,GAEhB,GAAI8U,IAAWt1C,KAAKuI,IAOpB,OALK+sC,KAEHt1C,KAAKuI,KAAO,GAAIi4B,GAAexgC,KAAKqM,MAAOrM,KAAK8S,QAAS9S,KAAKqL,UAGzDiqC,GAGTC,WAAY,SAAS/U,GAEnB,GAAIxsB,GAAK,GAAIwsB,GAAexgC,KAAKqM,MAAOrM,KAAK8S,QAAS9S,KAAKqL,QAE3D2I,GAAGzL,KAAOvI,KAAKuI,KACfvI,KAAKuI,KAAOyL,GAGd0sB,QAAS,WAE4B,IAA9B1gC,KAAK8T,GAAGwC,mBAEXtW,KAAK8T,GAAG1J,QAAStH,GAAS8B,OAAOkuB,mBAGnC9yB,KAAK8T,GAAGwC,mBAER,KAEEtW,KAAK0c,IAAK1c,KAAK8T,GAAI9T,KAAKqM,OAE1B,MAAOhC,GAML,KAJArK,MAAKm0C,SAELp0C,GAAOqK,QAASrK,GAAO6E,OAAO0F,OAAQD,IAEhCA,IAIVqS,IAAK,SAAS5I,EAAIzH,GAEhB,KAAM,iCAGR8nC,OAAQ,WA0BN,MAxBMn0C,MAAKi1C,WAETj1C,KAAKi1C,UAAW,EAChBj1C,KAAKqM,MAAMkxB,WAAav9B,KAAKuI,KAExBvI,KAAKuI,MAERvI,KAAKuI,KAAKm4B,UAGZ1gC,KAAK8T,GAAGwC,oBAEFtW,KAAKuI,MAETvI,KAAKqM,MAAMzB,SAAU7H,GAAM6B,OAAOmuB,oBAGD,IAA9B/yB,KAAK8T,GAAGwC,oBAEXtW,KAAK8T,GAAGsmB,kBACRp6B,KAAK8T,GAAG1J,QAAStH,GAAS8B,OAAOmuB,sBAI9B/yB,MAGTsS,QAAS,WAEP,MAAO9O,GAAMxD,KAAMA,KAAKqzC,gBAG1BA,cAAe,WAEb,IAEErzC,KAAKw1C,UAAU5yC,MAAO5C,KAAMoB,WAE9B,MAAOiJ,GAIL,KAFAtK,IAAOqK,QAASrK,GAAO6E,OAAO0F,OAAQD,IAEhCA,EAER,QAEErK,KAAKm0C,WAITqB,UAAW,aAKXrhC,QAAS,WAEP,MAAO3Q,GAAMxD,KAAMA,KAAKy1C,gBAG1BA,cAAe,WAEb,IAEEz1C,KAAK01C,UAAU9yC,MAAO5C,KAAMoB,WAE9B,MAAOiJ,GAIL,KAFAtK,IAAOqK,QAASrK,GAAO6E,OAAO0F,OAAQD,IAEhCA,EAER,QAEErK,KAAKm0C,WAITuB,UAAW,eAYbztC,GAAMyb,OAAQvG,GAAWC,IAGvB83B,UAAWn7B,GAAQQ,MAEnB66B,YAAY,EAEZ/sC,KAAM,WAENqU,IAAK,SAAS5I,EAAIzH,GAEXA,EAAM8uB,cAET9uB,EAAMzB,SAAU7H,GAAM6B,OAAOk4B,iBAAkBzwB,IAE/CrM,KAAKm0C,UAEGn0C,KAAK6S,cAAgBiB,EAAG2f,QAAUjN,GAAME,IAEhD5S,EAAGgD,MAAMjF,IAAKxF,EAAM0M,OAAQ/Y,KAAKsS,UAAWtS,KAAKmU,YAIjDpU,GAAO2S,MAAO3S,GAAO4S,OAAOmY,kBAAmBze,GAE/CA,EAAMzB,SAAU7H,GAAM6B,OAAOi4B,UAAWxwB,IAExCrM,KAAKu1C,WAAYj4B,IACjBtd,KAAKm0C,WAITqB,UAAW,SAAShgC,EAAKlB,GAEvB,GAAIjI,GAAQrM,KAAKqM,KAEZ7J,GAAU8R,IAEbjI,EAAM6pB,KAAM5hB,GAGdvU,GAAO2S,MAAO3S,GAAO4S,OAAOoY,UAAW1e,EAAOiI,GAE9CjI,EAAMzB,SAAU7H,GAAM6B,OAAOi4B,UAAWxwB,IAEnCrM,KAAK6S,WAAYkH,GAAQC,QAAW3N,EAAM8uB,cAE7Cn7B,KAAKu1C,WAAYj4B,KAIrBo4B,UAAW,SAASj0B,GAElB,GAAIpV,GAAQrM,KAAKqM,KAEjBtM,IAAO2S,MAAO3S,GAAO4S,OAAOoY,UAAW1e,EAAOoV,GAE9CpV,EAAMzB,SAAU7H,GAAM6B,OAAOk4B,iBAAkBzwB,IAE1CrM,KAAK6S,WAAYkH,GAAQC,QAAW3N,EAAM8uB,cAE7Cn7B,KAAKu1C,WAAYj4B,OAWvBrV,GAAMyb,OAAQvG,GAAWG,IAGvB43B,UAAWn7B,GAAQC,KAEnBo7B,YAAY,EAEZ/sC,KAAM,YAENqU,IAAK,SAAS5I,EAAIzH,GAEXA,EAAM8uB,cAET9uB,EAAMzB,SAAU7H,GAAM6B,OAAOq4B,kBAAmB5wB,IAEhDrM,KAAKm0C,UAEGn0C,KAAK6S,aAEbmC,GAAa,WAEXlB,EAAGC,KAAKlC,IAAKxF,EAAOrM,KAAKqL,SAAWyI,EAAGigB,WAAY/zB,KAAKsS,UAAWtS,KAAKmU,YAEvEnU,OAIHqM,EAAMzB,SAAU7H,GAAM6B,OAAOo4B,WAAY3wB,IAEzCrM,KAAKm0C,WAITqB,UAAW,SAASj8B,GAElB,GAAIzF,GAAK9T,KAAK8T,GACV0E,EAAO1E,EAAG8gB,aAAcrb,GACxBlN,EAAQrM,KAAKqM,KAEZ7J,GAAUgW,IAEb1E,EAAGwiB,cAAe9d,EAAMnM,EAAM0M,OAAQ1M,GAAO,GAG/CtM,GAAO2S,MAAO3S,GAAO4S,OAAOsY,WAAY5e,EAAOmM,GAE/CnM,EAAMzB,SAAU7H,GAAM6B,OAAOo4B,WAAY3wB,KAG3CqpC,UAAW,SAASn8B,EAAUa,GAE5B,GAAItG,GAAK9T,KAAK8T,GACVzH,EAAQrM,KAAKqM,KAEjBtM,IAAO2S,MAAO3S,GAAO4S,OAAOuY,iBAAkB7e,EAAOkN,EAAUa,GAE1D8M,GAAWG,SAAUjN,IAExBpa,KAAKu1C,WAAY93B,IAEjB3J,EAAGokB,aAAc7rB,GAEjBA,EAAMzB,SAAU7H,GAAM6B,OAAOq4B,kBAAmB5wB,EAAOkN,KAE/C2N,GAAWX,QAASnM,GAE5B/N,EAAMzB,SAAU7H,GAAM6B,OAAOs4B,kBAAmB7wB,EAAOkN,IAIvDlN,EAAMzB,SAAU7H,GAAM6B,OAAOq4B,kBAAmB5wB,EAAOkN,OAW7DtR,GAAMyb,OAAQvG,GAAWI,IAGvB23B,UAAWn7B,GAAQ0M,KAEnB2uB,YAAY,EAEZ/sC,KAAM,cAENqU,IAAK,SAAS5I,EAAIzH,GAEXyH,EAAG2f,QAAUjN,GAAMC,KAEtBzmB,KAAKm0C,SAILrgC,EAAGgD,MAAMzN,OAAQgD,EAAM0M,OAAQ/Y,KAAKsS,UAAWtS,KAAKmU,cAW1DlM,GAAMyb,OAAQvG,GAAWK,IAGvB03B,UAAWn7B,GAAQQ,MAEnB66B,YAAY,EAEZ/sC,KAAM,cAENqU,IAAK,SAAS5I,EAAIzH,GAEhBA,EAAM2rB,QAAUj1B,GAAMga,OAAOwc,cAExBzlB,EAAG2f,QAAUjN,GAAMC,MAASpa,EAAMmrB,QAAWx3B,KAAK6S,aAS7CxG,EAAM0qB,QAAU/2B,KAAK6S,WAAYkH,GAAQC,OAEjD3N,EAAMmrB,OAAOQ,QAAU3rB,EAAM2rB,QAE7BlkB,EAAGgD,MAAMob,IAAK7lB,EAAM0M,OAAQ1M,EAAMmrB,OAAQx3B,KAAKsS,UAAWtS,KAAKmU,aAI/DpU,GAAO2S,MAAO3S,GAAO4S,OAAOiY,qBAAsBve,GAElDyH,EAAGgD,MAAMzN,OAAQgD,EAAM0M,OAAQ/Y,KAAKsS,UAAWtS,KAAKmU,aAjBpDpU,GAAO2S,MAAO3S,GAAO4S,OAAOgY,kBAAmBte,GAE/CA,EAAMzB,SAAU7H,GAAM6B,OAAO03B,aAAcjwB,IAE3CrM,KAAKu1C,WAAY73B,IACjB1d,KAAKm0C,WAgBTqB,UAAW,SAAShgC,EAAKlB,EAASqhC,GAEhC,GAAItpC,GAAQrM,KAAKqM,KAEjBtM,IAAO2S,MAAO3S,GAAO4S,OAAOuX,aAAc7d,GAE1CA,EAAMzB,SAAU7H,GAAM6B,OAAO03B,aAAcjwB,IAEtCA,EAAM0qB,QAAU/2B,KAAK6S,WAAYkH,GAAQ+M,SAE5Cza,EAAM6V,cAAexE,GAAc1d,KAAK8S,QAAS9S,KAAKqL,UAI1DqqC,UAAW,SAASj0B,GAElB,GAAIpV,GAAQrM,KAAKqM,KAEjBtM,IAAO2S,MAAO3S,GAAO4S,OAAO8X,mBAAoBpe,EAAOoV,GAEvDpV,EAAMzB,SAAU7H,GAAM6B,OAAO23B,oBAAqBlwB,IAE7CA,EAAM0qB,QAAU/2B,KAAK6S,WAAYkH,GAAQ+M,SAE5Cza,EAAM6V,cAAexE,GAAc1d,KAAK8S,QAAS9S,KAAKqL,YAW5DpD,GAAMyb,OAAQvG,GAAWM,IAGvBy3B,UAAWn7B,GAAQQ,MAEnB66B,YAAY,EAEZ/sC,KAAM,YAENqU,IAAK,SAAS5I,EAAIzH,GAEhB,GAAImJ,GAAMnJ,EAAM0M,MAEhB1M,GAAM2rB,QAAUj1B,GAAMga,OAAOwc,cAE7BzlB,EAAGwnB,iBAAkBjvB,GAEhByH,EAAG2f,QAAUjN,GAAMC,MAASzmB,KAAK6S,aAOpCiB,EAAGgD,MAAMzN,OAAQmM,EAAKxV,KAAKsS,UAAWtS,KAAKmU,YAL3CnU,KAAK41C,eACL51C,KAAKm0C,WAQTqB,UAAW,WAETx1C,KAAK41C,gBAGPF,UAAW,WAET11C,KAAK41C,gBAGPA,aAAc,WAEZ,GAAIvpC,GAAQrM,KAAKqM,KAEjBA,GAAM2rB,QAAUj1B,GAAMga,OAAO6c,cAEtBvtB,GAAMmrB,aACNnrB,GAAMwpC,cACNxpC,GAAMypC,eACNzpC,GAAM0qB,UAUjB9uB,GAAMyb,OAAQvG,GAAWO,IAGvBw3B,UAAWn7B,GAAQ+M,OAEnBsuB,YAAY,EAEZ/sC,KAAM,eAENqU,IAAK,SAAS5I,EAAIzH,GAEXrM,KAAKm1C,WAAYp7B,GAAQC,OAE5Bha,KAAKi7B,aAEL5uB,EAAMzB,SAAU7H,GAAM6B,OAAO63B,cAAepwB,IAE5CrM,KAAKm0C,WAIL9nC,EAAM2rB,QAAUj1B,GAAMga,OAAOwc,cAE7BvkB,GAAa,WAEXlB,EAAGC,KAAK1K,OAAQgD,EAAOrM,KAAKqL,SAAWrL,KAAKm0B,cAAen0B,KAAKsS,UAAWtS,KAAKmU,YAE/EnU,QAIPw1C,UAAW,SAASh9B,GAElBxY,KAAK41C,gBAGPF,UAAW,SAASn8B,EAAUa,GAE5B,GAAI/N,GAAQrM,KAAKqM,MACbmJ,EAAMnJ,EAAM0M,MAEXmO,IAAWG,SAAUjN,IAExBra,GAAO2S,MAAO3S,GAAO4S,OAAOwX,eAAgB9d,EAAOmJ,GAEnDxV,KAAK41C,cAAc,IAEX1uB,GAAWX,QAASnM,IAG5Bra,GAAO8xB,qBAGF9xB,GAAOqxB,OAQV/kB,EAAMzB,SAAU7H,GAAM6B,OAAO83B,qBAAsBrwB,EAAOkN,KAN1DlN,EAAM41B,iBAAkBjiC,KAAK8S,SAE7BzG,EAAMzB,SAAU7H,GAAM6B,OAAO+3B,qBAAsBtwB,EAAOkN,KAO5DxZ,GAAO2S,MAAO3S,GAAO4S,OAAO0X,eAAgBhe,EAAOkN,KAInDxZ,GAAO2S,MAAO3S,GAAO4S,OAAOyX,aAAc/d,EAAO+N,EAAQ5E,EAAK+D,GAE9DlN,EAAMzB,SAAU7H,GAAM6B,OAAO83B,qBAAsBrwB,EAAOkN,MAI9Dq8B,aAAc,SAASG,GAErB,GAAIjiC,GAAK9T,KAAK8T,GACVzH,EAAQrM,KAAKqM,MACbmJ,EAAMnJ,EAAM0M,MAEhBhZ,IAAO2S,MAAO3S,GAAO4S,OAAO4X,cAAele,EAAOmJ,GAGlDnJ,EAAM2rB,QAAUj1B,GAAMga,OAAO6c,QAG7BvtB,EAAMzB,SAAU7H,GAAM6B,OAAO63B,cAAepwB,IAG5CrM,KAAKu1C,WAAY93B,IAGXs4B,GAEJ/1C,KAAKi7B,aAIPnnB,EAAGwkB,gBAAiB9iB,IAGtBylB,WAAY,WAEV,GAAKj7B,KAAK6S,WAAYkH,GAAQ6M,MAC9B,CACE,GAAI9S,GAAK9T,KAAK8T,GACVzH,EAAQrM,KAAKqM,MACbmJ,EAAMnJ,EAAM0M,MAGhBhZ,IAAO2S,MAAO3S,GAAO4S,OAAOsX,eAAgB5d,EAAOmJ,GAEnD1B,EAAGkD,KAAK3N,OAAQgD,OAWtBpE,GAAMyb,OAAQvG,GAAWQ,IAGvBu3B,UAAWn7B,GAAQQ,MAEnB66B,YAAY,EAEZ/sC,KAAM,YAENqU,IAAK,SAAS5I,EAAIzH,GAEhB,GAAKA,EAAM8uB,aAETp7B,GAAO2S,MAAO3S,GAAO4S,OAAOkX,mBAAoBxd,GAEhDA,EAAMzB,SAAU7H,GAAM6B,OAAOq3B,kBAAmB5vB,IAEhDrM,KAAKm0C,aAEF,IAAKrgC,EAAG2f,QAAUjN,GAAMC,MAASzmB,KAAK6S,aAe3C,CACE,GAAI2C,GAAMnJ,EAAM0M,OACZ8oB,EAAQx1B,EAAM0rB,SAAS,EAE3B/3B,MAAKg2C,WAAYliC,EAAIzH,GAEhBA,EAAMmrB,OAET/pB,EAAUo0B,EAAOx1B,EAAMmrB,SAIvBnrB,EAAMmrB,OAASqK,EAEVx1B,EAAM0qB,SAET1qB,EAAMmrB,OAAOT,OAAS1qB,EAAM0qB,SAIhC1qB,EAAMmrB,OAAOQ,QAAU3rB,EAAM2rB,QAC7B3rB,EAAMmrB,OAAOqe,QAAUxpC,EAAMwpC,QAC7BxpC,EAAMmrB,OAAOse,SAAWzpC,EAAMypC,SAE9BhiC,EAAGgD,MAAMob,IAAK1c,EAAKnJ,EAAMmrB,OAAQx3B,KAAKsS,UAAWtS,KAAKmU,eArCjDnU,MAAK6S,WAAYkH,GAAQ+M,SAEvB9mB,KAAKq1C,QAASx3B,KAEjB7d,KAAKg2C,WAAYliC,EAAIzH,GAIzBA,EAAMzB,SAAU7H,GAAM6B,OAAOo3B,WAAY3vB,IAEzCrM,KAAKm0C,UA+BT6B,WAAY,SAASliC,EAAIzH,GAEvB,GAAI4pC,GAAS5pC,EAAM0rB,SAAS,GACxBrrB,EAAUL,EAAMs1B,YAAasU,GAE7BtgB,EAAS7hB,EAAG4f,SAAWuiB,EAASj2C,KAAKk2C,WAAYpiC,EAAGsf,WAAY1mB,EAASupC,GACzEE,EAAUriC,EAAG6f,YAAcsiB,EAASj2C,KAAKk2C,WAAYpiC,EAAGqf,cAAezmB,EAASupC,EAEpF5pC,GAAM2rB,QAAUj1B,GAAMga,OAAOyc,YAC7BntB,EAAMwpC,QAAUlgB,EAChBtpB,EAAMypC,SAAWK,GAGnBD,WAAY,SAASE,EAAQ1pC,EAAS4H,GAEpC,GAAI+hC,GAAc,IAElB,IAAKD,EAAOp1C,OAEV,IAAK,GAAIF,GAAI,EAAGA,EAAIs1C,EAAOp1C,OAAQF,IACnC,CACE,GAAIoD,GAAOkyC,EAAQt1C,EAEZoD,KAAQwI,KAEP2pC,IAEJA,EAAc9xC,EAAMmI,IAGtB2pC,EAAanyC,GAASoQ,EAASpQ,IAKrC,MAAOmyC,IAAe3pC,GAGxB4pC,WAAY,SAASjqC,GAEnBA,EAAM2rB,QAAUj1B,GAAMga,OAAOqgB,OAE7B/wB,EAAMmrB,OAAOQ,QAAU3rB,EAAM2rB,cAEtB3rB,GAAMmrB,OAAOqe,cACbxpC,GAAMmrB,OAAOse,SAEpB91C,KAAKu1C,WAAY33B,KAGnB43B,UAAW,SAAShgC,EAAKlB,EAASqhC,GAEhC,GAAItpC,GAAQrM,KAAKqM,KAEjBtM,IAAO2S,MAAO3S,GAAO4S,OAAOgX,WAAYtd,GAEnCrM,KAAK8S,QAER9S,KAAKq1C,QAASx3B,IAId7d,KAAKs2C,WAAYjqC,GAGnBA,EAAMzB,SAAU7H,GAAM6B,OAAOo3B,WAAY3vB,KAG3CqpC,UAAW,SAASj0B,GAElB,GAAIpV,GAAQrM,KAAKqM,KAEjBtM,IAAO2S,MAAO3S,GAAO4S,OAAOiX,iBAAkBvd,EAAOoV,GAEhDzhB,KAAK8S,QAER9S,KAAKq1C,QAASx3B,IAId7d,KAAKs2C,WAAYjqC,GAGnBA,EAAMzB,SAAU7H,GAAM6B,OAAOq3B,kBAAmB5vB,OAUpDpE,GAAMyb,OAAQvG,GAAWS,IAGvBs3B,UAAWn7B,GAAQQ,MAEnB66B,YAAY,EAEZ/sC,KAAM,UAENqU,IAAK,SAAS5I,EAAIzH,GAEhB,GAAImJ,GAAMnJ,EAAM0M,OACZ8oB,EAAQx1B,EAAMmrB,MAEb1jB,GAAG2f,QAAUjN,GAAME,KAAOlR,GAAOqsB,GAAS7hC,KAAK6S,aAElDiB,EAAGgD,MAAMob,IAAK1c,EAAKqsB,EAAO7hC,KAAKsS,UAAWtS,KAAKmU,WAI/CnU,KAAKm0C,YAWXlsC,GAAMyb,OAAQvG,GAAWU,IAGvBq3B,UAAWn7B,GAAQ+M,OAEnBsuB,YAAY,EAEZ/sC,KAAM,aAENqU,IAAK,SAAS5I,EAAIzH,GAEXA,EAAM8uB,cAETp7B,GAAO2S,MAAO3S,GAAO4S,OAAOoX,oBAAqB1d,GAEjDrM,KAAKu2C,WAAYlqC,GAAO,EAAMtJ,GAAM6B,OAAOu3B,kBAAmB,MAC9Dn8B,KAAKm0C,UAEI9nC,EAAMoxB,YAAYiG,QAAS1jC,KAAKw2C,SAAUx2C,OAI1C8T,EAAG4hB,QAASrpB,EAAMwpC,UAAa71C,KAAKm1C,WAAYp7B,GAAQC,OAEjEha,KAAKg7B,WACLh7B,KAAKu2C,WAAYlqC,GAAO,EAAMtJ,GAAM6B,OAAOqd,WAAY,MACvDjiB,KAAKm0C,WAIL9nC,EAAM2rB,QAAUj1B,GAAMga,OAAOyc,YAE7BxkB,GAAa,WAEN3I,EAAM0qB,OAETjjB,EAAGC,KAAKQ,OAAQlI,EAAOA,EAAMwpC,QAAS71C,KAAKqL,SAAWyI,EAAGkgB,eAAiBlgB,EAAGogB,YAAal0B,KAAKsS,UAAWtS,KAAKmU,WAI/GL,EAAGC,KAAKrS,OAAQ2K,EAAOA,EAAMwpC,QAAS71C,KAAKqL,SAAWyI,EAAGmgB,eAAiBngB,EAAGogB,YAAal0B,KAAKsS,UAAWtS,KAAKmU,YAGhHnU,OAvBHA,KAAKm0C,UA2BTqB,UAAW,SAASj8B,GAElB,GAAIzF,GAAK9T,KAAK8T,GACV0E,EAAO1E,EAAG8gB,aAAcrb,GACxBlN,EAAQrM,KAAKqM,KAEjBtM,IAAO2S,MAAO3S,GAAO4S,OAAO6W,YAAand,GAEzCrM,KAAKy2C,WAAYj+B,IAGnBk9B,UAAW,SAASn8B,EAAUa,GAE5B,GACItG,GAAK9T,KAAK8T,GACV0E,EAAO1E,EAAG8gB,aAAcrb,GACxBlN,EAAQrM,KAAKqM,KAGZ6a,IAAWC,SAAU/M,IAExBra,GAAO2S,MAAO3S,GAAO4S,OAAOwW,cAAe9c,EAAOmM,GAElDxY,KAAKy2C,WAAYj+B,IAET0O,GAAWG,SAAUjN,IAE7Bra,GAAO2S,MAAO3S,GAAO4S,OAAOyW,iBAAkB/c,GAE9CrM,KAAKu1C,WAAY93B,IAEjB3J,EAAGokB,aAAc7rB,GAEjBA,EAAMzB,SAAU7H,GAAM6B,OAAOu3B,mBAAoB9vB,EAAOkN,KAEhD2N,GAAWX,QAASnM,IAG5Bra,GAAO8xB,qBAGF9xB,GAAOqxB,OAQVpxB,KAAKu2C,WAAYlqC,GAAO,EAAMtJ,GAAM6B,OAAOu3B,kBAAmB5iB,IAN9DlN,EAAM41B,iBAAkBjiC,KAAK8S,SAE7BzG,EAAMzB,SAAU7H,GAAM6B,OAAOw3B,mBAAoB/vB,EAAOkN,KAO1DxZ,GAAO2S,MAAO3S,GAAO4S,OAAO2W,aAAcjd,EAAOkN,KAIjDxZ,GAAO2S,MAAO3S,GAAO4S,OAAO0W,WAAYhd,EAAO+N,GAE/Cpa,KAAKu2C,WAAYlqC,GAAO,EAAMtJ,GAAM6B,OAAOu3B,kBAAmB5iB,KAIlEg9B,WAAY,SAASlqC,EAAOqqC,EAASj3B,EAAWlG,GAE9ClN,EAAM2rB,QAAUj1B,GAAMga,OAAOqgB,OAE7Bp9B,KAAK22C,aAActqC,GAEdqqC,GAEH12C,KAAKu1C,WAAY33B,IAGd6B,GAEHpT,EAAMzB,SAAU6U,GAAYpT,EAAOkN,KAIvCo9B,aAAc,SAAStqC,SAEdA,GAAMwpC,cACNxpC,GAAMypC,SAERzpC,EAAMmrB,SAETnrB,EAAMmrB,OAAOQ,QAAU3rB,EAAM2rB,cAEtB3rB,GAAMmrB,OAAOqe,cACbxpC,GAAMmrB,OAAOse,WAIxBW,WAAY,SAASj+B,GAEnB,GAAI1E,GAAK9T,KAAK8T,GACVzH,EAAQrM,KAAKqM,MACbspB,EAAStpB,EAAMwpC,OAGnB,IAAKxpC,EAAM8uB,aAIT,MAFAp7B,IAAO2S,MAAO3S,GAAO4S,OAAOoX,oBAAqB1d,EAAOmM,GAEjDxY,KAAK22C,aAActqC,EAG5BtM,IAAO2S,MAAO3S,GAAO4S,OAAOsW,YAAa5c,EAAOspB,GAI1CtpB,EAAM0qB,SAEV1qB,EAAM0qB,OAAS1qB,EAAMmrB,OAAUnrB,EAAMmrB,OAAOT,cAI9CtpB,EAAUkoB,EAAQtpB,EAAM0qB,QAGlB5yB,EAASqU,IAEb1E,EAAGwiB,cAAe9d,EAAMnM,EAAM0M,OAAQ1M,GAGxCrM,KAAKg7B,SAAUxiB,GACfxY,KAAKu2C,WAAYlqC,GAAO,EAAOtJ,GAAM6B,OAAOqd,WAAY,MAEnDnO,EAAG2f,QAAUjN,GAAMxJ,QAEtBhd,KAAKu1C,WAAYh4B,IAIjBvd,KAAKu1C,WAAY33B,KAIrBod,SAAU,SAASxiB,GAEjB,GAAI1E,GAAK9T,KAAK8T,GACVzH,EAAQrM,KAAKqM,KAEZ7J,GAASgW,IAEZ/K,EAAU+K,EAAMnM,EAAMypC,UAGnB91C,KAAK6S,WAAYkH,GAAQ6M,OAAU9S,EAAG4hB,QAASrpB,EAAMypC,YAGxD/1C,GAAO2S,MAAO3S,GAAO4S,OAAOuW,aAAc7c,EAAOA,EAAMypC,UAEvDhiC,EAAGkD,KAAKmB,KAAM9L,EAAOA,EAAMypC,YAI/BU,SAAU,WAEIx2C,KAAKqM,MAEX6V,cAAevE,GAAW3d,KAAK8S,QAAS9S,KAAKqL,YAWvDtL,GAAO4X,aAEPE,GAASvC,UAEPjJ,MAAsB,KACtByxB,MAAsB,EACtBhnB,MAAsB8Q,GAAMnB,KAC5BtO,KAAsBsP,GAAKhB,KAC3BmwB,MAAsB,EACtBC,YAAsB98B,GAAQ2M,IAC9BowB,YAAsB,KACtBp4B,UAAsB,EACtBq4B,UAAsB,EACtBC,UAAsB,EACtB/kC,SAAsB,EACtBgG,cAAsB,gBACtBg/B,kBACAC,yBAGFjvC,GAAMvG,OAAQmW,IAGZs/B,WAAY,KACZC,kBAAmB,KAEnBC,kBAAkB,EAElBC,YAAa,SAAStlC,EAAU8P,EAAOzW,GAErC,MAAOwM,IAASvC,UAUlB4C,KAAM,SAASlG,EAAU8P,EAAOzW,GAW9B,GATAD,EAAcpL,KAAMqL,EAASrL,KAAKs3C,YAAatlC,EAAU8P,EAAOzW,IAEhErL,KAAKgS,SAAWA,EAChBhS,KAAKgF,KAAO8c,EACZ9hB,KAAKqL,QAAUA,EACfrL,KAAKiW,aAAc,EACnBjW,KAAK0e,SAAW1e,KAAK0e,WAAuD,IAA1Cje,EAASuR,EAAS1F,OAAQtM,KAAKgF,MACjEhF,KAAKu3C,eAAiBpzC,EAASnE,KAAKi3C,gBAE/Bj3C,KAAKu3C,cACV,CACE,IAAMC,GAEJ,KAAM,kEAGRvvC,IAAM4C,MAAO7K,KAAMw3C,IAGrBx3C,KAAKy3C,cAAezlC,EAAU8P,EAAOzW,IAGvCosC,cAAe,SAASzlC,EAAU8P,EAAOzW,GAEjCxI,EAAU7C,KAAKqM,OAMnBrM,KAAK03C,cAAe1lC,EAAU8P,EAAOzW,GAJrCtL,GAAO8R,IAAK7R,KAAKqM,OAAQquB,SAAU16B,KAAK23C,kBAAmB3lC,EAAU8P,EAAOzW,GAAWrL,OAW3F23C,kBAAmB,SAAS3lC,EAAU8P,EAAOzW,GAE3C,MAAO,UAASusC,GAEd53C,KAAKqM,MAAQurC,EAEb53C,KAAK03C,cAAe1lC,EAAU8P,EAAOzW,KAOzCqsC,cAAe,SAAS1lC,EAAU1F,EAAQjB,KAK1CwsC,qBAAsB,WAEpB73C,KAAKiW,aAAc,EACnBjW,KAAKulB,KAAKlQ,QAYZkQ,KAAMtQ,GAAK,SAAS5I,EAAOgzB,EAAcvjB,EAAYg8B,MAKrDC,WAAY,SAAS1rC,EAAOgzB,EAAcvjB,KAK1C8C,IAAK,SAASvS,EAAOqF,EAAOoK,KAK5BgjB,OAAQ,SAASzyB,EAAOqF,EAAOoK,KAK/BmjB,SAAU,SAAS5yB,EAAOqF,EAAOoK,KAKjC8iB,KAAM,SAASvyB,EAAOsyB,KAKtBS,UAAW,SAAS/yB,EAAOqF,KAK3BuuB,SAAU,SAAS5zB,EAAO+P,EAAOnY,KAKjCk8B,UAAW,SAAS9zB,EAAO+P,EAAOnY,KAKlC4N,IAAK,SAASxF,GAEZ,MAAOA,GAAMmxB,WAAYx9B,KAAKgF,MAAOm6B,SAGvCzK,OAAQ,SAASroB,EAAOiT,EAAK7G,GAE3B,GAAIT,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,MAClCgzC,EAAOv/B,EAAYzY,KAAKmY,KAAOnY,KAAK8W,KAExC,IAAKkB,GAAYggC,EACjB,CACE,GAAI7Y,GAAUnnB,EAASmnB,OAElB99B,GAAS89B,GAEZ7f,EAAKtf,KAAKgF,MAAShF,KAAKi4C,eAAgB9Y,EAAS6Y,GAIjD14B,EAAKtf,KAAKgF,MAAShF,KAAKk4C,UAAW/Y,EAAS6Y,KAKlD3iB,MAAO,SAAS5wB,GAEdzE,KAAKqM,MAAMvJ,SAASuyB,MAAO5wB,EAAUzE,OAGvCm4C,mBAAoB,SAAS1zC,GAE3BzE,KAAKqM,MAAMvJ,SAAS6B,GAAI7B,GAAS8B,OAAO+tB,WAAYluB,EAAUzE,OAGhEo4C,aAAc,SAAS/rC,GAErB,IAAMoQ,GAEJ,KAAM,qDAGR,IAAI47B,GAAcr4C,KAAKwU,MACnB4f,EAAep0B,KAAKo0B,aACpBkkB,EAAYt4C,KAAKs4C,UACjB9jC,EAAQlU,EAAU+3C,GAAgB1pC,GAAQ0pC,EAAahsC,GAAUgsC,EACjEE,EAASv4C,KAAKqM,MAAMksC,OAAQ/jC,EAAO4f,EAAckkB,EAQrD,OANAv4C,IAAO2S,MAAO1S,KAAKm3C,WAAYn3C,KAAMqM,EAAOksC,EAAQF,EAAa7jC,EAAO8jC,GAE1DC,EAAOjI,OAEb5V,SAAU16B,KAAKw4C,mBAAoBnsC,GAASrM,MAE7Cu4C,GAGTC,mBAAoB,SAASnsC,GAE3B,MAAO,UAAwBksC,GAE7B,GAAIxmC,GAAUwmC,EAAOpI,QAErBpwC,IAAO2S,MAAO1S,KAAKo3C,kBAAmBp3C,KAAMqM,EAAOksC,EAEnD,KAAK,GAAIz3C,GAAI,EAAGA,EAAIiR,EAAQ/Q,OAAQF,IAElCd,KAAK8+B,OAAQzyB,EAAO0F,EAASjR,IAAK,KAKxC23C,yBAA0B,SAASpsC,GAEjC,MAAO2P,IAAmBta,OAAQ1B,KAAKqM,MAAMvJ,SAAUuJ,EAAOrM,OAGhE04C,iBAAkB,SAASC,GAEzB,MAAO7iC,IAAgBpU,OAAQ1B,KAAKqM,MAAMvJ,SAAU61C,IAGtDr8B,WAAY,SAAS5K,EAAOoK,EAAY9D,GAEtC,MAAOhY,MAAKqM,MAAMvJ,SAASwZ,WAAY5K,EAAOoK,IAGhD88B,YAAa,SAAUvsC,EAAOC,GAE5B,GAAKF,EAAWC,EAAOC,EAAQ9L,GAE7B,MAAO8M,GAAMjB,EAAOC,IAIxBspB,UAAW,SAASlkB,EAAOjN,EAAUqX,EAAY9D,GAE/ChY,KAAKqM,MAAMvJ,SAAS8yB,UAAWlkB,EAAOjN,EAAUzE,KAAM8b,IAGxD+8B,WAAY,SAAS7gC,EAAU2gC,EAASl0C,EAAUqX,GAIhD,IAAK,GAFDhI,GAAK9T,KAAKqM,MAAMvJ,SAEXhC,EAAI,EAAGA,EAAI63C,EAAQ33C,OAAQF,IACpC,CACE,GAAI4Q,GAAQinC,EAAS73C,GACjB0U,EAAM1B,EAAGyB,WAAW8G,kBAAmB3K,EAE3CsG,GAAS8gC,QAAStjC,IAAQ,EAErB9D,YAAiB3O,IAEpB0B,EAASjD,KAAMxB,KAAM0R,GAIrBoC,EAAG8hB,UAAWlkB,EAAOjN,EAAUzE,KAAM8b,KAK3CyrB,SAAU,SAAS71B,KAKnBqnC,YAAa,SAAS/gC,GAEpB,GAAKhY,KAAK0e,SACV,CACE,GAAIrS,GAAQ2L,EAAS2L,OACjBgB,EAAe3kB,KAAKgF,KACpB8hC,IAAY9uB,EAASghC,UAEzB,KAAMlS,GAAW9mC,KAAKiS,SAAW4M,OAAOC,eACxC,CACE,GAAI7C,GAAUjc,IAEd6e,QAAOC,eAAgBzS,EAAOsY,GAE5B3F,YAAY,EAEZJ,IAAK,SAASlN,GAEZuK,EAAQ2C,IAAKvS,EAAOqF,IAEtBG,IAAK,WAEH,MAAOmG,GAASmnB,WAIpB2H,EAAU9uB,EAASghC,YAAa,EAG5BlS,IAEJz6B,EAAOsY,GAAiB3M,EAASmnB,SAG9BnnB,EAASihC,cAAgBjhC,EAASmnB,UAErC9yB,EAAMzB,SAAU7H,GAAM6B,OAAOm3B,gBAAiB/7B,KAAMgY,IAEpDA,EAASihC,YAAcjhC,EAASmnB,WAKtC+Z,aAAc,SAASxnC,GAErB,IAAMrQ,EAASqQ,GAEb,OAAO,CAGT,IAAIynC,GAAkBn5C,KAAKqM,MAAMvJ,SAC7Bs2C,EAAaD,EAAgB3jC,GAEjC,KAAMnU,EAAS+3C,GAEb,OAAO,CAGT,IAAKA,EAAWp4C,SAAW0Q,EAAM1Q,OAE/B,OAAO,CAGT,KAAM,GAAIF,GAAI,EAAGA,EAAI4Q,EAAM1Q,OAAQF,IAEjC,IAAMkC,EAAU0O,EAAO5Q,MAAUR,EAAUoR,EAAO5Q,IAEhD,OAAO,CAIX,QAAO,GAGTu4C,YAAa,SAAS/xC,EAAQmF,EAAcqP,EAAYhJ,GAEtD,GAAIpG,GAAUF,EAA0BlF,EAAQmF,EAOhD,OALKC,KAAYoP,GAAc9b,KAAK42C,OAAStvC,EAAOi6B,UAElDj6B,EAAOmZ,MAAO3N,GAAW9S,KAAK62C,YAAa72C,KAAK82C,aAG3CpqC,GAGT4sC,aAAc,SAAShyC,EAAQmF,EAAcI,EAAQC,EAAcgP,GAEjE,GAAIpP,GAAUE,EAA2BtF,EAAQmF,EAAcI,EAAQC,EAYvE,OAVKJ,MAEE1M,KAAK42C,MAAStvC,EAAOi6B,UAAazlB,GAErCxU,EAAOmZ,MAAOzgB,KAAK62C,YAAa72C,KAAK82C,aAGvCxvC,EAAOsD,SAAU7H,GAAM6B,OAAOk3B,WAAYx0B,EAAQuF,EAAQJ,EAAcK;2CAGnEJ,GAGT+2B,iBAAkB,SAASn8B,EAAQuF,EAAQiP,GAEzC,GAAIrP,GAAezM,KAAKu5C,gBAAiBjyC,GACrCwF,EAAe9M,KAAKw5C,gBAAiB3sC,GACrC4sC,EAAYnyC,EAAOyR,OACnB2gC,EAAmBpyC,EAAOmb,IAAIlN,WAC9BmN,EAAapb,EAAOmb,IAAIC,UAM5B,IAJA3iB,GAAO2S,MAAO1S,KAAK25C,eAAgB35C,KAAMsH,EAAQmF,EAAcI,EAAQC,GAEvE9M,KAAKs5C,aAAchyC,EAAQmF,EAAcI,EAAQC,EAAcgP,GAE1D4G,GAAc5G,EACnB,CACE,GAAI89B,GAAeF,EAAiB/iB,OAAQrvB,GAAQ,EAE/CoyC,GAAiB7R,MAAOp7B,IAAkBmtC,IAAiBH,GAE9DnyC,EAAOwvB,QAAS8iB,GAAc,KAKpCC,gBAAiB,SAAS1a,EAASrjB,GAEjC,GAAItG,GAAMxV,KAAKu5C,gBAAiBpa,EAEhCp/B,IAAO2S,MAAO1S,KAAK85C,cAAe95C,KAAMm/B,EAAS3pB,GAEjDxV,KAAKq5C,YAAala,EAAS3pB,EAAKsG,IAGlCy9B,gBAAiB,SAASjyC,GAExB,MAAOA,GAAOmb,IAAIjN,KAGpBgkC,gBAAiB,SAAS3sC,GAExB,MAAOA,GAAO4V,IAAIjN,KAGpByiC,eAAgB,SAAS8B,EAAU/B,GAEjC,IAAMA,EAEJ,MAAO,KAKT,KAAK,GAFDgC,MAEKl5C,EAAI,EAAGA,EAAIi5C,EAAS/4C,OAAQF,IACrC,CACE,GAAIq+B,GAAUn/B,KAAKk4C,UAAW6B,EAAUj5C,GAAKk3C,EAE5B,QAAZ7Y,GAEH6a,EAAO7wC,KAAMg2B,GAIjB,MAAO6a,IAGT9B,UAAW,SAAS/Y,EAAS6Y,GAE3B,GAAK7Y,EAEH,OAAQ6Y,GAER,IAAKvwB,IAAK1kB,MACR,MAAOo8B,GAAQpH,SAAS,EAE1B,KAAKnQ,IAAM7kB,MACT,GAAKo8B,EAAQ3H,OAEX,MAAO2H,GAAQ3H,MAGjB,IAAIqK,GAAQ1C,EAAQpH,SAAS,EAO7B,OALKoH,GAAQpI,SAEX8K,EAAM9K,OAASoI,EAAQpI,QAGlB8K,CAET,KAAKpa,IAAKC,IACV,IAAKE,IAAMF,IACT,MAAOyX,GAAQpmB,MAEjB,KAAK0O,IAAKE,KACV,IAAKC,IAAMD,KACT,MAAOwX,GAAQ2B,QAKnB,MAAO,SASX74B,GAAMyb,OAAQ7L,GAAUiG,IAGtBm8B,UAAW,KACXC,gBAAiB,KACjBC,cAAe,KACfC,YAAa,KACbN,cAAe,KACfH,eAAgB,KAEhBtC,kBAAkB,EAElBK,cAAe,SAAS1lC,EAAU8P,EAAOzW,GAEvC,IAAMrL,KAAKu3C,cACX,CACE,GAAI4B,GAAkBn5C,KAAKqM,MAAMvJ,QAEjC9C,MAAK6hC,MAAQ7hC,KAAK6hC,OAAWsX,EAAgBn0C,KAAO,IAAMm0C,EAAgB3jC,IAG5EzV,GAAO2S,MAAO1S,KAAKi6C,UAAWj6C,MAE9BA,KAAK63C,wBAGPj5B,IAAK,SAASvS,EAAOqF,EAAOoK,GAE1B,GAAK3X,EAASuN,GAEZ1R,KAAKi/B,SAAU5yB,EAAOpM,EAAW6b,OAGnC,CACE,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,MAClCm6B,EAAUn/B,KAAKsc,WAAY5K,EAAOoK,EAAY9D,EAE7CmnB,IAAWnnB,EAASmnB,UAAYA,IAEnCn/B,KAAKq6C,WAAYriC,EAAU8D,GAC3B9b,KAAKs6C,WAAYtiC,EAAUmnB,EAASrjB,MAK1CgjB,OAAQ,SAASzyB,EAAOqF,EAAOoK,GAE7B,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,MAClCm6B,EAAUn/B,KAAKsc,WAAY5K,EAAOoK,EAAY9D,EAE7CmnB,IAAWnnB,EAASmnB,UAAYA,IAEnCn/B,KAAKq6C,WAAYriC,EAAU8D,GAC3B9b,KAAKs6C,WAAYtiC,EAAUmnB,EAASrjB,KAIxCmjB,SAAU,SAAS5yB,EAAOqF,EAAOoK,GAE/B,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,MAClCm6B,EAAUn/B,KAAKsc,WAAY5K,EAAOoK,EAAY9D,EAE5CmnB,IAAWnnB,EAASmnB,UAAYA,GAEpCn/B,KAAKu6C,aAAcviC,EAAU8D,IAIjCsjB,UAAW,SAAS/yB,EAAOqF,GAEzB,GAAIsG,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAGtC,OAFchF,MAAKsc,WAAY5K,GAAO,EAAOsG,KAE1BA,EAASmnB,SAG9Bmb,WAAY,SAAStiC,EAAUmnB,EAASrjB,GAEhCqjB,EAAQhE,eAEZn7B,KAAKw6C,SAAUxiC,EAAUmnB,GACzBn/B,KAAKyjC,iBAAkBzrB,EAAS2L,OAAQwb,EAASrjB,GACjD9b,KAAK+4C,YAAa/gC,KAItBuiC,aAAc,SAASviC,EAAU8D,EAAY2+B,GAE3C,GAAK3+B,EACL,CACE,GAAIqjB,GAAUnnB,EAASmnB,OAEvB,IAAKA,GAAWA,EAAQkC,YAEtB,OAIJrhC,KAAKq6C,WAAYriC,EAAU8D,EAAY2+B,GACvCz6C,KAAK+4C,YAAa/gC,IAGpBqiC,WAAY,SAASriC,EAAU8D,EAAY2+B,GAEzC,GAAItb,GAAUnnB,EAASmnB,OAElBA,KAEHp/B,GAAO2S,MAAO1S,KAAKk6C,gBAAiBl6C,KAAMgY,GAEtCA,EAAS0iC,SAEXvb,EAAQx0B,KAAM5H,GAAM6B,OAAO42B,MAAOxjB,EAAS0iC,SAEzC1iC,EAAS2iC,WAEXxb,EAAQx0B,KAAM5H,GAAM6B,OAAOg1B,QAAS5hB,EAAS2iC,WAG/C3iC,EAASmnB,QAAU,KACnBnnB,EAAS4iC,OAAQ,EACjB5iC,EAAShC,QAAS,EAElBgC,EAAS2L,OAAO8Z,YAAYp0B,OAAQ81B,GAE9Bsb,GAAc3+B,GAEb9b,KAAKg3C,UAERh3C,KAAK65C,gBAAiB7hC,EAAS2L,OAAQ7H,KAM/C0+B,SAAU,SAASxiC,EAAUmnB,GAEvBnnB,EAAS0iC,SAEXvb,EAAQ30B,IAAKzH,GAAM6B,OAAO42B,MAAOxjB,EAAS0iC,QAAS16C,MAGjDgY,EAAS2iC,WAEXxb,EAAQ30B,IAAKzH,GAAM6B,OAAOg1B,QAAS5hB,EAAS2iC,UAAW36C,MAGzDgY,EAASmnB,QAAUA,EACnBnnB,EAAS4iC,OAAQ,EACjB5iC,EAAShC,QAAS,EAEbhW,KAAK66C,YAAa7iC,EAAUmnB,IAE/BnnB,EAAS2L,OAAO8Z,YAAYxiB,IAAKkkB,EAASn/B,MAG5CD,GAAO2S,MAAO1S,KAAKm6C,cAAen6C,KAAMgY,IAG1C6iC,YAAa,SAAS7iC,EAAUmnB,GAE9B,OAAO,GAGT2b,YAAa,SAAS9iC,EAAU8D,EAAYi/B,GAE1C,MAAO,UAAS5b,GAEd,GAAI9yB,GAAQ2L,EAAS2L,MAErB5jB,IAAO2S,MAAO1S,KAAKo6C,YAAap6C,KAAMqM,EAAO2L,EAAUmnB,KAE9B,IAApBnnB,EAAShC,QAAoB+kC,KAE3B5b,IAAYA,EAAQhE,cAEvBn7B,KAAKw6C,SAAUxiC,EAAUmnB,EAASrjB,GAClC9b,KAAKyjC,iBAAkBp3B,EAAO8yB,EAASrjB,IAIlC9b,KAAKwU,MAERwD,EAASxD,MAAQxU,KAAKo4C,aAAc/rC,GAE3BrM,KAAK+2C,UAEd/2C,KAAK65C,gBAAiBxtC,EAAOyP,GAIjC9D,EAAShC,QAAS,EAElBhW,KAAK+4C,YAAa/gC,MAKxBgjC,iBAAkB,SAAS3uC,GAEzB,GAAIw1B,GAAQ7hC,KAAK6hC,KAEjB,OAAO,UAAuB1C,GAE5B,MAAOtzB,GAAYQ,EAAOw1B,EAAO1C,EAASA,EAAQ1c,IAAIjN,OAI1D+jC,gBAAiB,SAASjyC,GAExB,MAAOtH,MAAK6hC,OAGd0F,SAAU,SAAS71B,GAEjB,GAAIytB,GAAUztB,EAAO1R,KAAKgF,MACtBwQ,EAAMxV,KAAK6hC,KAEf,IAAKr/B,EAAU28B,IAAan/B,KAAKqM,MACjC,CACE,GAAI4uC,GAAgBj7C,KAAKqM,MAAMvJ,SAC3Bo4C,EAAUD,EAAczlC,GAE5BylC,GAAc1lC,WAAWqyB,WAAYl2B,EAAO8D,EAAK2pB,EAAS+b,OAUhEjzC,GAAMyb,OAAQ7L,GAAUkG,IAGtBo9B,cAAe,KACfC,oBAAqB,KACrBC,UAAW,KAEX7C,mBAAoB,SAASnsC,GAE3B,MAAO,UAAwBksC,GAE7B,GAAIvgC,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,MAClC+M,EAAUwmC,EAAOpI,QAErBpwC,IAAO2S,MAAO1S,KAAKo3C,kBAAmBp3C,KAAMqM,EAAOksC,GAEnDv4C,KAAKs7C,KAAMtjC,EAAU,WAEnB,IAAK,GAAIlX,GAAI,EAAGA,EAAIiR,EAAQ/Q,OAAQF,IAElCd,KAAKu7C,SAAUvjC,EAAUjG,EAASjR,IAAK,KAI3Cd,KAAK2lB,KAAM3N,GACXhY,KAAKw7C,UAAWxjC,GAAU,KAI9BsjC,KAAM,SAAStjC,EAAUvT,EAAUqX,GAEjC9D,EAASyjC,cAAe,EACxBzjC,EAAS0jC,aAAc,EAEvBj3C,EAAS7B,MAAO5C,MAEhBgY,EAASyjC,cAAe,EACxBzjC,EAAS0jC,aAAc,EAEvB17C,KAAK2lB,KAAM3N,GACXhY,KAAKw7C,UAAWxjC,EAAU8D,IAG5B8C,IAAK,SAASvS,EAAOqF,EAAOoK,GAE1B,GAAK3X,EAASuN,GAEZ1R,KAAKi/B,SAAU5yB,EAAOpM,EAAW6b,OAGnC,CACE,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,MAClCmG,EAAW6M,EAASmnB,QACpBwc,EAAQ37C,KAAK04C,kBAEjB,IAAK14C,KAAKk5C,aAAcxnC,GAEtB,IAAK,GAAI5Q,GAAI,EAAGA,EAAI4Q,EAAM1Q,OAAQF,IAClC,CACE,GAAIq+B,GAAUn/B,KAAKsc,WAAY5K,EAAO5Q,GAAKgb,EAAY9D,EAElDmnB,IAEHwc,EAAM1gC,IAAKkkB,OAKjB,CACE,GAAIA,GAAUn/B,KAAKsc,WAAY5K,EAAOoK,EAAY9D,EAE7CmnB,IAEHwc,EAAM1gC,IAAKkkB,GAIf,GAAI8J,GAAW99B,EAASy3B,SAAU+Y,GAC9BzwC,EAASywC,EAAM/Y,SAAUz3B,EAE7BnL,MAAKs7C,KAAMtjC,EAAU,WAEnB,IAAK,GAAIlX,GAAI,EAAGA,EAAIoK,EAAOlK,OAAQF,IAEjCd,KAAKu7C,SAAUvjC,EAAU9M,EAAQpK,GAAKgb,EAGxC,KAAK,GAAIhb,GAAI,EAAGA,EAAImoC,EAASjoC,OAAQF,IAEnCd,KAAK47C,YAAa5jC,EAAUixB,EAAUnoC,GAAKgb,IAG5CA,KAIPgjB,OAAQ,SAASzyB,EAAOqF,EAAOoK,GAE7B,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEtC,IAAKhF,KAAKk5C,aAAcxnC,GAEtB1R,KAAKs7C,KAAMtjC,EAAU,WAEnB,IAAK,GAAIlX,GAAI,EAAGA,EAAI4Q,EAAM1Q,OAAQF,IAClC,CACE,GAAIq+B,GAAUn/B,KAAKsc,WAAY5K,EAAO5Q,GAAKgb,EAAY9D,EAElDmnB,IAEHn/B,KAAKu7C,SAAUvjC,EAAUmnB,EAASrjB,KAGrCA,OAEA,IAAKtb,EAASkR,GACnB,CACE,GAAIytB,GAAUn/B,KAAKsc,WAAY5K,EAAOoK,EAAY9D,EAE7CmnB,IAEHn/B,KAAKu7C,SAAUvjC,EAAUmnB,EAASrjB,KAKxCmjB,SAAU,SAAS5yB,EAAOqF,EAAOoK,GAE/B,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEtC,IAAKhF,KAAKk5C,aAAcxnC,GAEtB1R,KAAKs7C,KAAMtjC,EAAU,WAEnB,IAAK,GAAIlX,GAAI,EAAGA,EAAI4Q,EAAM1Q,OAAQF,IAClC,CACE,GAAIq+B,GAAUn/B,KAAKsc,WAAY5K,EAAO5Q,GAAKgb,EAAY9D,EAElDmnB,IAEHn/B,KAAK47C,YAAa5jC,EAAUmnB,EAASrjB,KAGxCA,OAEA,IAAKtb,EAASkR,GACnB,CACE,GAAIytB,GAAUn/B,KAAKsc,WAAY5K,EAAOoK,EAAY9D,EAE7CmnB,IAEHn/B,KAAK47C,YAAa5jC,EAAUmnB,EAASrjB,OAIzC,CACE,GAAI5H,GAAM8D,EAASmnB,OAEnBn/B,MAAKs7C,KAAMtjC,EAAU,WAEnB,IAAK,GAAIlX,GAAIoT,EAAIlT,OAAS,EAAGF,GAAK,EAAGA,IAEnCd,KAAK47C,YAAa5jC,EAAU9D,EAAKpT,GAAKgb,IAEvCA,KAIPsjB,UAAW,SAAS/yB,EAAOqF,GAEzB,GAAIsG,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,MAClCmG,EAAW6M,EAASmnB,OAExB,IAAKn/B,KAAKk5C,aAAcxnC,GACxB,CACE,IAAK,GAAI5Q,GAAI,EAAGA,EAAI4Q,EAAM1Q,OAAQF,IAClC,CACE,GAAIq+B,GAAUn/B,KAAKsc,WAAY5K,EAAO5Q,IAAK,EAAOkX,EAElD,IAAKmnB,IAAYh0B,EAASysB,IAAKuH,EAAQpmB,QAErC,OAAO,EAIX,MAAOrH,GAAM1Q,OAAS,EAEnB,GAAKR,EAASkR,GACnB,CACE,GAAIytB,GAAUn/B,KAAKsc,WAAY5K,GAAO,EAAOsG,EAE7C,OAAOmnB,IAAWh0B,EAASysB,IAAKuH,EAAQpmB,QAG1C,OAAO,GAGT8iC,iBAAkB,SAAS1c,EAASrjB,GAElC,OAAQA,IAAeqjB,EAAQkC,aAGjCma,UAAW,SAASxjC,EAAU8D,GAEtB9D,EAAS0jC,aAAgB5/B,IAAc9D,EAAS2L,OAAO+b,WAEtD1/B,KAAK8W,QAAU8Q,GAAM7kB,OAAS/C,KAAKmY,OAASsP,GAAK1kB,QAEpDhD,GAAO2S,MAAO1S,KAAKm7C,cAAen7C,KAAMgY,GAExCA,EAAS2L,OAAOlD,MAAOzgB,KAAK87C,kBAAmB97C,KAAK+7C,qBAK1DjB,YAAa,SAAS9iC,EAAU8D,EAAYi/B,GAE1C,MAAO,UAAU5b,GAEf,GAAI2Z,GAAU9gC,EAAS8gC,QACnBtjC,EAAM2pB,EAAQpmB,QAEbvD,IAAOsjC,IAAWiC,KAErBh7C,GAAO2S,MAAO1S,KAAKo7C,oBAAqBp7C,KAAMgY,EAAUmnB,GAExDn/B,KAAKu7C,SAAUvjC,EAAUmnB,EAASrjB,SAE3Bg9B,GAAStjC,MAKtBmQ,KAAM,SAAS3N,GAEb,GAAImnB,GAAUnnB,EAASmnB,OAEjBnnB,GAASyjC,eAEb17C,GAAO2S,MAAO1S,KAAKq7C,UAAWr7C,KAAMgY,GAEpCmnB,EAAQxZ,KAAM3lB,KAAKW,YAEnBqX,EAAS2L,OAAO/Y,SAAU7H,GAAM6B,OAAOm3B,gBAAiB/7B,KAAMgY,QAUpEjY,GAAO4X,UAAUqkC,UAAYh+B,GAE7BA,GAAU1I,UAERjJ,MAAsB,KACtByxB,MAAsB,EACtBtpB,OAAsB,EACtBsC,MAAsB8Q,GAAMnB,KAC5BtO,KAAsBsP,GAAKhB,KAC3BmwB,MAAsB,EACtBC,YAAsB98B,GAAQ2M,IAC9BowB,YAAsB,KACtBp4B,UAAsB,EACtBq4B,UAAsB,EACtBC,UAAsB,EACtB/kC,SAAsB,EACtB4vB,MAAsB,KACtB/uB,QAAsBiH,GAAQQ,MAC9B0hC,qBAAsB,KACtBhkC,cAAsB,gBACtBg/B,kBACAC,yBAGFjvC,GAAMyb,OAAQ5F,GAAgBE,IAG5B3V,KAAM,YAEN4xC,UAAoBl6C,GAAO4S,OAAOwZ,eAClC+tB,gBAAoBn6C,GAAO4S,OAAO6Z,sBAClC2tB,cAAoBp6C,GAAO4S,OAAO8Z,oBAClC2tB,YAAoBr6C,GAAO4S,OAAOka,iBAClCitB,cAAoB/5C,GAAO4S,OAAOga,oBAClCgtB,eAAoB55C,GAAO4S,OAAOia,qBAClCuqB,WAAoBp3C,GAAO4S,OAAOma,gBAClCsqB,kBAAoBr3C,GAAO4S,OAAOoa,wBAElCuqB,YAAa,SAAStlC,EAAU8P,EAAOzW,GAErC,MAAO2S,IAAU1I,UAGnBiQ,KAAMtQ,GAAK,SAAS5I,EAAOgzB,EAAcvjB,GAEvC,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,OAEpC2e,OAAQtX,EACR+yB,UAAWp/B,KAAKg7C,iBAAkB3uC,GAClC8yB,QAAS,KACTnpB,QAAQ,EAER2kC,UAAW,WAET56C,GAAO2S,MAAO3S,GAAO4S,OAAOyZ,uBAAwBpsB,KAAMqM,EAAO2L,GAEjE3L,EAAMqsB,QAAS14B,KAAK8S,QAAS9S,KAAKi8C,sBAClCj8C,KAAKu6C,aAAcviC,GAAU,GAAO,IAGtC0iC,QAAS,WAEP36C,GAAO2S,MAAO3S,GAAO4S,OAAO0Z,qBAAsBrsB,KAAMqM,EAAO2L,GAEzDA,EAASonB,UAAWpnB,EAASmnB,UAEjCn/B,KAAKu6C,aAAcviC,GAAU,GAAO,IAK1C3L,GAAM7B,IAAKzH,GAAM6B,OAAOg3B,WAAY57B,KAAKk8C,WAAYl8C,MACrDqM,EAAM7B,IAAKzH,GAAM6B,OAAOk3B,UAAW97B,KAAKm8C,YAAan8C,MAErDA,KAAK+3C,WAAY1rC,EAAOgzB,EAAcvjB,KAGxCi8B,WAAY,SAAS1rC,EAAOgzB,EAAcvjB,GAExC,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEjCb,GAASk7B,KAEZA,EAAer/B,KAAK44C,YAAavsC,EAAOrM,KAAK6hC,SAI3C9hC,GAAO2S,MAAO3S,GAAO4S,OAAO2Z,yBAA0BtsB,KAAMqM,EAAOgzB,GAIjEl7B,EAASk7B,GAMLr/B,KAAKwU,QAEbwD,EAASxD,MAAQxU,KAAKo4C,aAAc/rC,KANpCtM,GAAO2S,MAAO3S,GAAO4S,OAAO4Z,kBAAmBvsB,KAAMqM,EAAOgzB,GAE5Dr/B,KAAK41B,UAAWyJ,EAAcr/B,KAAK86C,YAAa9iC,EAAU8D,GAAcA,EAAY9D,KAQxF4mB,KAAM,SAASvyB,EAAOsyB,GAEpB,GAAI3mB,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,MAClCo3C,EAAep8C,KAAK44C,YAAavsC,EAAOrM,KAAK6hC,MAK5C7pB,KAEG7T,EAASi4C,GAILzd,GAER3+B,KAAKu6C,aAAcviC,GAZN,GAED,GAMZhY,KAAK41B,UAAWwmB,EAAcp8C,KAAK86C,YAAa9iC,GARnC,GACE,IADF,KAiBnBkkC,WAAY,SAAS7vC,GAEnB,GAAI2L,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEjCgT,KAEHjY,GAAO2S,MAAO3S,GAAO4S,OAAO+Z,qBAAsB1sB,KAAMqM,EAAO2L,GAE/DhY,KAAKq6C,WAAYriC,GACjBhY,KAAK+4C,YAAa/gC,KAItBmkC,YAAa,SAAS9vC,EAAO8yB,EAASkd,EAAaC,GAEjD,GAAKt8C,KAAK6hC,QAAUwa,EACpB,CACE,GAAIrkC,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEjCgT,IAAYmnB,IAAYnnB,EAASmnB,UAEpCn/B,KAAKq6C,WAAYriC,GAAU,GAAO,GAClChY,KAAKw6C,SAAUxiC,EAAUmnB,GACzBn/B,KAAK+4C,YAAa/gC,QAW1BjY,GAAO4X,UAAU4kC,OAASt+B,GAE1BA,GAAO3I,UAELjJ,MAAsB,KACtByxB,MAAsB,EACtBtpB,OAAsB,EACtBsC,MAAsB8Q,GAAMnB,KAC5BtO,KAAsBsP,GAAKhB,KAC3B+1B,YAAsBziC,GAAQ2M,IAC9BwN,YAAsB,KACtB0iB,MAAsB,EACtBC,YAAsB98B,GAAQ2M,IAC9BowB,YAAsB,KACtBp4B,UAAsB,EACtBq4B,UAAsB,EACtBC,UAAsB,EACtB/kC,SAAsB,EACtB4vB,MAAsB,KACtB/uB,QAAsBiH,GAAQ2M,IAC9Bu1B,qBAAsB,KACtBhkC,cAAsB,gBACtBg/B,kBACAC,yBAGFjvC,GAAMyb,OAAQ5F,GAAgBG,IAG5B5V,KAAM,SAEN4xC,UAAoBl6C,GAAO4S,OAAO2Y,YAClC4uB,gBAAoBn6C,GAAO4S,OAAO+Y,mBAClCyuB,cAAoBp6C,GAAO4S,OAAOgZ,iBAClCyuB,YAAoBr6C,GAAO4S,OAAOqZ,cAClC8tB,cAAoB/5C,GAAO4S,OAAOmZ,iBAClC6tB,eAAoB55C,GAAO4S,OAAOoZ,kBAClCorB,WAAoBp3C,GAAO4S,OAAOsZ,aAClCmrB,kBAAoBr3C,GAAO4S,OAAOuZ,qBAElCorB,YAAa,SAAStlC,EAAU8P,EAAOzW,GAErC,MAAO4S,IAAO3I,UAGhBiQ,KAAMtQ,GAAK,SAAS5I,EAAOgzB,EAAcvjB,GAEvC,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,OAEpC2e,OAAQtX,EACR+yB,UAAWp/B,KAAKg7C,iBAAkB3uC,GAClC8yB,QAAS,KACTnpB,QAAQ,EACR4kC,OAAO,EACPjlB,QAAQ,EACR8mB,MAAOh2C,EAAQzG,KAAK6hC,MAAOx1B,EAAMoW,IAAIjN,KAErCmlC,UAAW,WAET56C,GAAO2S,MAAO3S,GAAO4S,OAAO4Y,oBAAqBvrB,KAAMqM,EAAO2L,GAE9DhY,KAAKu6C,aAAcviC,GAAU,GAAO,IAIxC3L,GAAM7B,IAAKzH,GAAM6B,OAAO62B,QAASz7B,KAAK08C,QAAS18C,MAC/CqM,EAAM7B,IAAKzH,GAAM6B,OAAOg3B,WAAY57B,KAAKk8C,WAAYl8C,MAErDA,KAAK+3C,WAAY1rC,EAAOgzB,EAAcvjB,KAGxCi8B,WAAY,SAAS1rC,EAAOgzB,EAAcvjB,GAExC,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEjCb,GAASk7B,KAEZA,EAAer/B,KAAK44C,YAAavsC,EAAOrM,KAAK6hC,SAI3C9hC,GAAO2S,MAAO3S,GAAO4S,OAAO6Y,sBAAuBxrB,KAAMqM,EAAOgzB,GAI9Dl7B,EAASk7B,GAOLr/B,KAAKwU,QAEbwD,EAASxD,MAAQxU,KAAKo4C,aAAc/rC,KAPpCtM,GAAO2S,MAAO3S,GAAO4S,OAAO8Y,eAAgBzrB,KAAMqM,EAAOgzB,GAEzDr/B,KAAK28C,gBAAiBtd,EAAcrnB,EAAU3L,GAC9CrM,KAAK41B,UAAWyJ,EAAcr/B,KAAK86C,YAAa9iC,EAAU8D,GAAcA,EAAY9D,KAQxF2kC,gBAAiB,SAAStd,EAAcrnB,EAAU3L,GAEhD,GAAK7J,EAAU68B,IAAkBrnB,EAASykC,MAKxC,IAAK,GAHDzxC,GAAM9K,EAASF,KAAK6hC,OACpB92B,EAAM7K,EAASF,KAAKqM,MAAMvJ,SAAS0S,KAE9B1T,EAAI,EAAGA,EAAIkJ,EAAIhK,OAAQc,IAE9Bu9B,EAAct0B,EAAKjJ,IAAQuK,EAAOrB,EAAKlJ,KAK7C88B,KAAM,SAASvyB,EAAOsyB,GAEpB,GAAI3mB,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,MAClCo3C,EAAep8C,KAAK44C,YAAavsC,EAAOrM,KAAK6hC,MAK5C7pB,KAEG7T,EAASi4C,GAKLzd,GAER3+B,KAAKu6C,aAAcviC,GAbN,GAED,IAMZhY,KAAK28C,gBAAiBP,EAAcpkC,EAAU3L,GAC9CrM,KAAK41B,UAAWwmB,EAAcp8C,KAAK86C,YAAa9iC,GATnC,GACE,IADF,EASqFA,MASxG6iC,YAAa,SAAS7iC,EAAUmnB,GAE9B,OAAQnnB,EAASykC,OAGnBxc,SAAU,SAAS5zB,EAAO+P,EAAOnY,GAE/B,GAAIk7B,GAAUn/B,KAAK6R,IAAKxF,EAExB,IAAK8yB,EACL,CACE,GAAIyd,GAAezd,EAAQW,OAAQ77B,EAEnC2I,GAA2BwP,EAAOpc,KAAK6hC,MAAO+a,EAAcA,EAAan6B,IAAIjN,KAE7E4G,EAAOpc,KAAKgF,MAAS43C,IAIzBF,QAAS,SAASrwC,GAEhB,GAAI2L,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEtC,IAAKgT,GAAYA,EAASmnB,QAC1B,CACE,GAAIA,GAAUnnB,EAASmnB,SAElBnnB,EAAS4iC,OAASzb,EAAQlG,iBAE7Bl5B,GAAO2S,MAAO3S,GAAO4S,OAAOiZ,eAAgB5rB,KAAMqM,EAAO2L,GAEzDA,EAAS2d,QAAS,EAElBwJ,EAAQ1e,MAAOzgB,KAAKw8C,YAAax8C,KAAKk0B,aAEtClc,EAAS2d,QAAS,EAClB3d,EAAS4iC,OAAQ,KAKvBsB,WAAY,SAAS7vC,GAEnB,GAAI2L,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEjCgT,IAEEhY,KAAK8S,UAER/S,GAAO2S,MAAO3S,GAAO4S,OAAOkZ,kBAAmB7rB,KAAMqM,EAAO2L,GAE5DhY,KAAKq6C,WAAYriC,KAKvBqiC,WAAY,SAASriC,EAAU8D,GAE7B,GAAIqjB,GAAUnnB,EAASmnB,OAElBA,KAEHp/B,GAAO2S,MAAO1S,KAAKk6C,gBAAiBl6C,KAAMgY,GAE1CmnB,EAAQx0B,KAAM5H,GAAM6B,OAAOg1B,QAAS5hB,EAAS2iC,WAExC36C,KAAK8S,UAAYqsB,EAAQhE,cAE5BgE,EAAQzG,QAAS14B,KAAK8S,QAAS9S,KAAKi8C,sBAGtCjkC,EAASmnB,QAAU,KACnBnnB,EAAS4iC,OAAQ,EACjB5iC,EAAShC,QAAS,EAElBgC,EAAS2L,OAAO8Z,YAAYp0B,OAAQ81B,GAE/Bn/B,KAAKg3C,UAERh3C,KAAK65C,gBAAiB7hC,EAAS2L,OAAQ7H,OAW/C/b,GAAO4X,UAAUklC,QAAU3+B,GAE3BA,GAAQ5I,UAENjJ,MAAsB,KACtByxB,MAAsB,EACtBtpB,OAAsB,EACtBsC,MAAsB8Q,GAAMnB,KAC5BtO,KAAsBsP,GAAKhB,KAC3BmwB,MAAsB,EACtBC,YAAsB98B,GAAQ2M,IAC9BowB,YAAsB,KACtBp4B,UAAsB,EACtBq4B,UAAsB,EACtBC,UAAsB,EACtB/kC,SAAsB,EACtBipC,QAAsB,KACtBv6C,WAAsB,KACtBwW,sBAAsB,EACtB2lC,kBAAsB,EACtBC,aAAsB,EACtBhsC,OAAsB,EACtB+qC,kBAAsB/hC,GAAQ2M,IAC9Bq1B,kBAAsB,KACtBiB,cAAsBjjC,GAAQQ,MAC9B0hC,qBAAsB,KACtBgB,YAAsBljC,GAAQ0M,KAC9By2B,mBAAsB,KACtBjlC,cAAsB,gBACtBg/B,kBACAC,yBAGFjvC,GAAMyb,OAAQ3F,GAAkBG,IAG9B7V,KAAM,UAEN8yC,cAAsBp7C,GAAO4S,OAAO2b,kBACpC8sB,oBAAsBr7C,GAAO4S,OAAOyb,wBACpCitB,UAAsBt7C,GAAO4S,OAAOsb,aACpCkpB,WAAsBp3C,GAAO4S,OAAO8b,cACpC2oB,kBAAsBr3C,GAAO4S,OAAO+b,sBACpCirB,eAAsB55C,GAAO4S,OAAOgc,mBAEpC2oB,YAAa,SAAStlC,EAAU8P,EAAOzW,GAErC,MAAO6S,IAAQ5I,UAGjBoiC,cAAe,SAAS1lC,EAAU8P,EAAOzW,GAEvCrL,KAAKk7C,QAAUl7C,KAAKk7C,SAAalpC,EAAShN,KAAO,IAAMgN,EAASwD,IAChExV,KAAKW,WAAawE,EAAkBnF,KAAKW,WAAYX,KAAKmX,sBAE1DpX,GAAO2S,MAAO3S,GAAO4S,OAAOgb,aAAc3tB,MAE1CA,KAAK63C,wBAGPtyB,KAAMtQ,GAAK,SAAS5I,EAAOgzB,EAAcvjB,GAEvC,GAAIG,GAAUjc,KACVgY,EAAW3L,EAAMmxB,WAAYx9B,KAAKgF,OAEpC2e,OAAQtX,EACRysC,WACA1Z,UAAWp/B,KAAKg7C,iBAAkB3uC,GAClC8yB,QAASn/B,KAAKy4C,yBAA0BpsC,GACxCspB,QAAQ,EACR8lB,cAAc,EACdC,aAAa,EAEbf,UAAW,WAET56C,GAAO2S,MAAO3S,GAAO4S,OAAOib,qBAAsB3R,EAAS5P,EAAOrM,KAAMgY,GAExEiE,EAAQ2/B,YAAa5jC,EAAUhY,MAAM,GAAM,IAG7C06C,QAAS,WAEF1iC,EAAS2d,SAKd51B,GAAO2S,MAAO3S,GAAO4S,OAAOkb,mBAAoB5R,EAAS5P,EAAOrM,KAAMgY,GAEhEA,EAASonB,UAAWp/B,OAMxBic,EAAQ0J,KAAM3N,GACdiE,EAAQu/B,UAAWxjC,IALnBiE,EAAQ2/B,YAAa5jC,EAAUhY,MAAM,GAAO,KAShDm9C,SAAU,WAEHnlC,EAAS2d,QAKT1Z,EAAQlL,QAAUkL,EAAQlL,MAAO/Q,KAAMgY,IAE1CiE,EAAQ2/B,YAAa5jC,EAAUhY,MAAM,GAAO,IAMlDqM,GAAM7B,IAAKzH,GAAM6B,OAAO82B,SAAU17B,KAAKo9C,SAAUp9C,MACjDqM,EAAM7B,IAAKzH,GAAM6B,OAAO+2B,UAAW37B,KAAKq9C,UAAWr9C,MAG9CA,KAAK88C,kBAER98C,KAAKm4C,mBAAoBn4C,KAAKs9C,iBAAkBtlC,IAIlDhY,KAAK+3C,WAAY1rC,EAAOgzB,EAAcvjB,GAGtC9b,KAAK+4C,YAAa/gC,KAGpB+/B,WAAY,SAAS1rC,EAAOgzB,EAAcvjB,GAExC,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEjC3D,GAASg+B,IAEZt/B,GAAO2S,MAAO3S,GAAO4S,OAAOmb,gBAAiB9tB,KAAMqM,EAAO2L,EAAUqnB,GAEpEr/B,KAAK64C,WAAY7gC,EAAUqnB,EAAcr/B,KAAK86C,YAAa9iC,EAAU8D,GAAcA,IAE3E9b,KAAKwU,MAEbwD,EAASxD,MAAQxU,KAAKo4C,aAAc/rC,GAE5BrM,KAAK+8C,cAEbh9C,GAAO2S,MAAO3S,GAAO4S,OAAOob,uBAAwB/tB,KAAMqM,EAAO2L,GAEjEhY,KAAKq1B,MAAOr1B,KAAKu9C,eAAgBvlC,MAIrC4mB,KAAM,SAASvyB,EAAOsyB,GAEpB,GAAI3mB,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEtC,IAAKgT,EACL,CACE,GAAI7M,GAAW6M,EAASmnB,QAGpBljB,EAAUjc,KAEVw9C,EAAY,SAASre,GAEvB,GAAKR,EACL,CACE,GAAIgd,GAAQ37C,KAAK04C,kBACjBiD,GAAMt+B,MAAO8hB,GAEbh0B,EAAS47B,KAAK,SAAS0W,GAEf9B,EAAM/jB,IAAK6lB,EAAc1kC,SAE7BkD,EAAQ2/B,YAAa5jC,EAAUylC,GAftB,GACD,MAoBhBz9C,MAAKq1B,MAAOr1B,KAAKu9C,eAAgBvlC,EAAUwlC,MAI/Crd,UAAW,SAAS9zB,EAAO+P,EAAOnY,GAEhC,GAAIk7B,GAAUn/B,KAAK6R,IAAKxF,EAExB,IAAK8yB,EACL,CACE,GAAIue,KAEJ9wC,GAA2B3I,EAAYjE,KAAKk7C,QAAS9+B,EAAO/P,EAAMoW,IAAIjN,KAEtEvR,EAAYjE,KAAKk7C,SAAY9+B,EAAO/P,EAAMoW,IAAIjN,IAE9C,KAAK,GAAI1U,GAAI,EAAGA,EAAIq+B,EAAQn+B,OAAQF,IAElC48C,EAAcv0C,KAAMg2B,EAASr+B,GAAIg/B,OAAQ77B,GAG3CmY,GAAOpc,KAAKgF,MAAS04C,IAIzBN,SAAU,SAAS/wC,GAEjB,GAAI2L,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEjCgT,IAAYhY,KAAKi9C,cAEpBl9C,GAAO2S,MAAO3S,GAAO4S,OAAO6b,iBAAkBxuB,KAAMqM,EAAO2L,GAE3DhD,GAAa,WAEXgD,EAAS2d,QAAS,EAClB3d,EAAS0jC,aAAc,CAIvB,KAAK,GAFD7lC,GAASmC,EAASmnB,QAEbr+B,EAAI,EAAGA,EAAI+U,EAAO7U,OAAQF,IACnC,CACE,GAAIq+B,GAAUtpB,EAAQ/U,IAEhBq+B,EAAQhE,cAAgBgE,EAAQlG,eAEpCkG,EAAQ1e,MAAOzgB,KAAKi9C,YAAaj9C,KAAKk9C,oBAI1CllC,EAAS2d,QAAS,EAClB3d,EAAS0jC,aAAc,GAEtB17C,QAIPq9C,UAAW,SAAShxC,GAElB,GAAI2L,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEjCgT,IAAYhY,KAAKg9C,gBAEpBj9C,GAAO2S,MAAO3S,GAAO4S,OAAO4b,kBAAmBvuB,KAAMqM,EAAO2L,GAE5DhD,GAAa,WAEXhV,KAAKs7C,KAAMtjC,EAAU,WAInB,IAAK,GAFDnC,GAASmC,EAASmnB,QAEbr+B,EAAI+U,EAAO7U,OAAS,EAAGF,GAAK,EAAGA,IACxC,CACgB+U,EAAQ/U,GAEd43B,QAAS14B,KAAKg9C,cAAeh9C,KAAKi8C,0BAI7Cj8C,QAIPs9C,iBAAkB,SAAStlC,GAEzB,MAAO,UAAUmnB,EAASrjB,GAEnB9D,EAASonB,UAAWD,KAEvBp/B,GAAO2S,MAAO3S,GAAO4S,OAAO0b,kBAAmBruB,KAAMgY,EAAUmnB,GAE/Dn/B,KAAKu7C,SAAUvjC,EAAUmnB,EAASrjB,MAKxCyhC,eAAgB,SAASvlC,EAAUwlC,GAEjC,MAAO,UAAUrE,GAEf,GAAIha,GAAUga,EAAgBt9B,OAAQ7D,EAASonB,UAE/Cr/B,IAAO2S,MAAO3S,GAAO4S,OAAOwb,kBAAmBnuB,KAAMgY,EAAUmnB,GAE1Dqe,GAEHA,EAAUh8C,KAAMxB,KAAMm/B,GAGnBA,EAAQn+B,OAEXhB,KAAKs7C,KAAMtjC,EAAU,WAEnB,IAAK,GAAIlX,GAAI,EAAGA,EAAIq+B,EAAQn+B,OAAQF,IAElCd,KAAKu7C,SAAUvjC,EAAUmnB,EAASr+B,MAI9Bd,KAAKwU,QAEbwD,EAASxD,MAAQxU,KAAKo4C,aAAcpgC,EAAS2L,WAKnD43B,SAAU,SAASvjC,EAAUmnB,EAASrjB,GAEpC,KAAKqjB,EAAQhE,cAAiBn7B,KAAK+Q,QAAU/Q,KAAK+Q,MAAOouB,EAASnnB,IAAlE,CAKA,GAAI3L,GAAQ2L,EAAS2L,OACjBrc,EAAS0Q,EAASmnB,QAClB3pB,EAAM2pB,EAAQpmB,OACd7N,GAAU5D,EAAOswB,IAAKpiB,EAwB1B,OAtBKtK,KAEHnL,GAAO2S,MAAO3S,GAAO4S,OAAOub,YAAaluB,KAAMgY,EAAUmnB,GAEzD73B,EAAO4qB,IAAK1c,EAAK2pB,GAEjBA,EAAQ30B,IAAKzH,GAAM6B,OAAOg1B,QAAS5hB,EAAS2iC,WAC5Cxb,EAAQ30B,IAAKzH,GAAM6B,OAAOu4B,kBAAmBnlB,EAAS0iC,SAEjD16C,KAAK+Q,OAERouB,EAAQ30B,IAAKzH,GAAM6B,OAAOi3B,OAAQ7jB,EAASmlC,UAG7Che,EAAQ1B,YAAYxiB,IAAK5O,EAAOrM,MAEhCA,KAAKyjC,iBAAkBtE,EAAS9yB,EAAOyP,GAEvC9b,KAAK2lB,KAAM3N,GACXhY,KAAKw7C,UAAWxjC,EAAU8D,IAGrB5Q,IAGT0wC,YAAa,SAAS5jC,EAAUmnB,EAASrjB,EAAY2+B,GAEnD,GAAMz6C,KAAK67C,iBAAkB1c,EAASrjB,GAAtC,CAKA,GAAIzP,GAAQ2L,EAAS2L,OACjBrc,EAAS0Q,EAASmnB,QAClB2Z,EAAU9gC,EAAS8gC,QACnBtjC,EAAM2pB,EAAQpmB,OACdkwB,EAAW3hC,EAAOswB,IAAKpiB,EA2C3B,OAzCKyzB,KAEHlpC,GAAO2S,MAAO3S,GAAO4S,OAAOqb,eAAgBhuB,KAAMgY,EAAUmnB,GAE5D73B,EAAO+B,OAAQmM,GAEf2pB,EAAQx0B,KAAM5H,GAAM6B,OAAOg1B,QAAS5hB,EAAS2iC,WAC7Cxb,EAAQx0B,KAAM5H,GAAM6B,OAAOu4B,kBAAmBnlB,EAAS0iC,SACvDvb,EAAQx0B,KAAM5H,GAAM6B,OAAOi3B,OAAQ7jB,EAASmlC,UAE5Che,EAAQ1B,YAAYp0B,OAAQgD,GAEtBouC,IAECz6C,KAAKg3C,UAERh3C,KAAK65C,gBAAiB1a,EAASrjB,GAG5B9b,KAAKg9C,gBAEHlhC,EAEEjJ,GAAY7S,KAAKg9C,cAAejjC,GAAQQ,QAE3C4kB,EAAQzG,QAAS3e,GAAQQ,OAK3B4kB,EAAQzG,QAAS14B,KAAKg9C,cAAeh9C,KAAKi8C,wBAKhDj8C,KAAK2lB,KAAM3N,GACXhY,KAAKw7C,UAAWxjC,EAAU8D,UAGrBg9B,GAAStjC,GAETyzB,IAGT+R,iBAAkB,SAAS3uC,GAEzB,GAAI6uC,GAAUl7C,KAAKk7C,QACfrZ,EAAQx1B,EAAMoW,IAAIjN,GAEtB,OAAO,UAAS2pB,GAEd,MAAOtzB,GAAYszB,EAAS+b,EAAS7uC,EAAOw1B,KAIhD0X,gBAAiB,SAASjyC,GAExB,MAAOtH,MAAKk7C,WAShBn7C,GAAO4X,UAAUgmC,eAAiBx/B,GAElCA,GAAe7I,UAEbjJ,MAAsB,KACtByxB,MAAsB,EACtBtpB,OAAsB,EACtBsC,MAAsB8Q,GAAMnB,KAC5BtO,KAAsBsP,GAAKhB,KAC3BmwB,MAAsB,EACtBC,YAAsB98B,GAAQ2M,IAC9BowB,YAAsB,KACtBp4B,UAAsB,EACtBzM,SAAsB,EACtB2rC,QAAsB39C,EACtB4hC,MAAsB,KACtBqZ,QAAsB,KACtBv6C,WAAsB,KACtBwW,sBAAsB,EACtB2lC,kBAAsB,EACtBC,aAAsB,EACtBhsC,OAAsB,EACtBisC,cAAsBjjC,GAAQ8M,OAC9Bo2B,YAAsBljC,GAAQ2M,IAC9Bw2B,mBAAsB,KACtBW,mBAAsB9jC,GAAQ0M,KAC9Bq3B,0BAA2B,KAC3BhC,kBAAsB/hC,GAAQ2M,IAC9Bq1B,kBAAsB,KACtBgC,4BAA6B,KAC7B9lC,cAAsB,gBACtBg/B,kBACAC,yBAGFjvC,GAAMyb,OAAQ3F,GAAkBI,IAG9B9V,KAAM,iBAEN8yC,cAAsBp7C,GAAO4S,OAAO6c,sBACpC4rB,oBAAsBr7C,GAAO4S,OAAO2c,4BACpC+rB,UAAsBt7C,GAAO4S,OAAOwc,iBACpCgoB,WAAsBp3C,GAAO4S,OAAOkd,kBACpCunB,kBAAsBr3C,GAAO4S,OAAOmd,0BACpC6pB,eAAsB55C,GAAO4S,OAAOod,uBAEpCunB,YAAa,SAAStlC,EAAU8P,EAAOzW,GAErC,MAAO8S,IAAe7I,UAGxBoiC,cAAe,SAAS1lC,EAAU8P,EAAOzW,GAEvC,IAAMrL,KAAKu3C,cACX,CACE,GAAI4B,GAAkBn5C,KAAKqM,MAAMvJ,QAEjC9C,MAAKk7C,QAAUl7C,KAAKk7C,SAAa/B,EAAgBn0C,KAAO,IAAMm0C,EAAgB3jC,IAGhFxV,KAAK6hC,MAAQ7hC,KAAK6hC,OAAW7vB,EAAShN,KAAO,IAAMgN,EAASwD,IAC5DxV,KAAKW,WAAawE,EAAkBnF,KAAKW,WAAYX,KAAKmX,sBAEpDtU,EAAUwI,EAAQuyC,SAMtB59C,KAAKg+C,WAAY3yC,EAAQuyC,SAJzB79C,GAAO8R,IAAKxG,EAAQuyC,SAAUljB,SAAU16B,KAAKg+C,WAAYh+C,MAO3DD,GAAO2S,MAAO3S,GAAO4S,OAAOic,iBAAkB5uB,OAGhDg+C,WAAY,SAASJ,GAEnB59C,KAAK49C,QAAUA,EAEf59C,KAAK63C,wBAGPtyB,KAAMtQ,GAAK,SAAS5I,EAAOgzB,EAAcvjB,GAEvC,GAAIG,GAAUjc,KACVi+C,EAAkBj+C,KAAK49C,QAAQ96C,SAE/BkV,EAAW3L,EAAMmxB,WAAYx9B,KAAKgF,OAEpC2e,OAAQtX,EACR+yB,UAAWp/B,KAAKg7C,iBAAkB3uC,GAClCysC,WACA3Z,QAASn/B,KAAKy4C,yBAA0BpsC,GACxC6xC,SAAU,GAAI1jC,IACdmb,QAAQ,EACR8lB,cAAc,EACdC,aAAa,EAEbf,UAAW,WAET56C,GAAO2S,MAAO3S,GAAO4S,OAAOkc,yBAA0B5S,EAAS5P,EAAOrM,KAAMgY,GAE5EiE,EAAQ2/B,YAAa5jC,EAAUhY,OAGjC06C,QAAS,WAEF1iC,EAAS2d,SAKd51B,GAAO2S,MAAO3S,GAAO4S,OAAOmc,uBAAwB7S,EAAS5P,EAAOrM,KAAMgY,GAE1EiE,EAAQ0J,KAAM3N,GACdiE,EAAQu/B,UAAWxjC,KAGrBmlC,SAAU,WAEHnlC,EAAS2d,QAKT1Z,EAAQlL,QAAUkL,EAAQlL,MAAO/Q,KAAMgY,IAE1CiE,EAAQ2/B,YAAa5jC,EAAUhY,OAInCm+C,iBAAkB,WAEhBp+C,GAAO2S,MAAO3S,GAAO4S,OAAOoc,8BAA+B9S,EAAS5P,EAAOrM,KAAMgY,GAEjFiE,EAAQmiC,uBAAwBpmC,EAAUhY,OAM9CqM,GAAM7B,IAAKzH,GAAM6B,OAAO82B,SAAU17B,KAAKo9C,SAAUp9C,MACjDqM,EAAM7B,IAAKzH,GAAM6B,OAAO+2B,UAAW37B,KAAKq9C,UAAWr9C,MAG9CA,KAAK88C,kBAERmB,EAAgBt5C,GAAI7B,GAAS8B,OAAO+tB,WAAY3yB,KAAKs9C,iBAAkBtlC,GAAYhY,MAIrFA,KAAK+3C,WAAY1rC,EAAOgzB,EAAcvjB,GAGtC9b,KAAK+4C,YAAa/gC,KAGpB+/B,WAAY,SAAS1rC,EAAOgzB,EAAcvjB,GAExC,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,MAClCi5C,EAAkBj+C,KAAK49C,QAAQ96C,QAE9BzB,GAASg+B,IAEZt/B,GAAO2S,MAAO3S,GAAO4S,OAAOqc,oBAAqBhvB,KAAMqM,EAAO2L,EAAUqnB,GAExEr/B,KAAK64C,WAAY7gC,EAAUqnB,EAAcr/B,KAAK86C,YAAa9iC,EAAU8D,GAAcA,IAE3E9b,KAAKwU,MAEbwD,EAASxD,MAAQxU,KAAKo4C,aAAc/rC,GAE5BrM,KAAK+8C,cAEbh9C,GAAO2S,MAAO3S,GAAO4S,OAAOsc,2BAA4BjvB,KAAMqM,EAAO2L,GAErEimC,EAAgB5oB,MAAOr1B,KAAKu9C,eAAgBvlC,GAAYhY,QAI5D4+B,KAAM,SAASvyB,EAAOsyB,GAEpB,GAAIsf,GAAkBj+C,KAAK49C,QAAQ96C,SAC/BkV,EAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEtC,IAAKgT,EACL,CACE,GAAI7M,GAAW6M,EAASkmC,SAAS/8C,OAE7B8a,EAAUjc,KAEVw9C,EAAY,SAASU,GAEvB,GAAKvf,EACL,CACE,GAAIgd,GAAQ37C,KAAK04C,kBACjBiD,GAAMt+B,MAAO6gC,EAEb,KAAK,GAAIp9C,GAAI,EAAGA,EAAIqK,EAASnK,OAAQF,IACrC,CACE,GAAIu9C,GAAkBlzC,EAAUrK,EAE1B66C,GAAM/jB,IAAKymB,EAAgBtlC,SAE/BkD,EAAQmiC,uBAAwBpmC,EAAUqmC,GAhBjC,KAsBjBJ,GAAgB5oB,MAAOr1B,KAAKu9C,eAAgBvlC,EAAUwlC,GAAax9C,QAIvEigC,SAAU,SAAS5zB,EAAO+P,EAAOnY,GAE/B,GAAIk7B,GAAUn/B,KAAK6R,IAAKxF,EAEnB8yB,KAEH/iB,EAAOpc,KAAKgF,MAASm6B,EAAQ59B,UAIjC67C,SAAU,SAAS/wC,GAEjB,GAAI2L,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEtCgQ,IAAa,WAEX,GAAKgD,GAAYhY,KAAKi9C,YAIpB,IAAK,GAFDiB,GAAWlmC,EAASkmC,SAAS/8C,OAExBL,EAAI,EAAGA,EAAIo9C,EAASl9C,OAAQF,IACrC,CACE,GAAI88C,GAAUM,EAAUp9C,IAElB88C,EAAQziB,cAAgByiB,EAAQ3kB,eAEpC2kB,EAAQn9B,MAAOzgB,KAAKi9C,YAAaj9C,KAAKk9C,oBAK5C,GAAKllC,GAAYhY,KAAK69C,mBACtB,CACE99C,GAAO2S,MAAO3S,GAAO4S,OAAO2rC,oBAAqBt+C,KAAMqM,EAAO2L,GAE9DA,EAAS2d,QAAS,EAClB3d,EAAS0jC,aAAc,CAIvB,KAAK,GAFD7lC,GAASmC,EAASmnB,QAEbr+B,EAAI,EAAGA,EAAI+U,EAAO7U,OAAQF,IACnC,CACE,GAAIq+B,GAAUtpB,EAAQ/U,IAEhBq+B,EAAQhE,cAAgBgE,EAAQlG,eAEpCkG,EAAQ1e,MAAOzgB,KAAK69C,mBAAoB79C,KAAK89C,2BAIjD9lC,EAAS2d,QAAS,EAClB3d,EAAS0jC,aAAc,IAGxB17C,OAGLq9C,UAAW,SAAShxC,GAElB,GAAI2L,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEjCgT,IAAYhY,KAAKg9C,gBAEpBj9C,GAAO2S,MAAO3S,GAAO4S,OAAO8c,sBAAuBzvB,KAAMqM,EAAO2L,GAEhEhD,GAAa,WAEXhV,KAAKs7C,KAAMtjC,EAAU,WAInB,IAAK,GAFDkmC,GAAWlmC,EAASkmC,SAAS/8C,OAExBL,EAAI,EAAGA,EAAIo9C,EAASl9C,OAAQF,IACrC,CACgBo9C,EAAUp9C,GAEhB43B,QAAS14B,KAAKg9C,cAAeh9C,KAAK+9C,iCAI7C/9C,QAIPs9C,iBAAkB,SAAStlC,GAEzB,MAAO,UAAU4lC,EAAS9hC,GAEnB9D,EAASonB,UAAWwe,KAAc5lC,EAASkmC,SAAStmB,IAAKgmB,EAAQ7kC,UAEpEhZ,GAAO2S,MAAO3S,GAAO4S,OAAO4c,sBAAuBvvB,KAAMgY,EAAU4lC,GAEnE59C,KAAKu+C,oBAAqBvmC,EAAU4lC,EAAS9hC,MAKnDyhC,eAAgB,SAASvlC,EAAUwlC,GAEjC,MAAO,UAAUS,GAEf,GAAIC,GAAWD,EAAgBpiC,OAAQ7D,EAASonB,UAEhDr/B,IAAO2S,MAAO3S,GAAO4S,OAAO0c,sBAAuBrvB,KAAMgY,EAAUkmC,GAE9DV,GAEHA,EAAUh8C,KAAMxB,KAAMk+C,GAGnBA,EAASl9C,OAEZhB,KAAKs7C,KAAMtjC,EAAU,WAEnB,IAAK,GAAIlX,GAAI,EAAGA,EAAIo9C,EAASl9C,OAAQF,IAEnCd,KAAKu+C,oBAAqBvmC,EAAUkmC,EAAUp9C,MAI1Cd,KAAKwU,QAEbwD,EAASxD,MAAQxU,KAAKo4C,aAAcpgC,EAAS2L,WAKnD43B,SAAU,SAASvjC,EAAUmnB,EAASrjB,GAEpC,KAAKqjB,EAAQhE,cAAiBn7B,KAAK+Q,QAAU/Q,KAAK+Q,MAAOouB,EAASnnB,IAAlE,CAKA,GAAI9M,GAASlL,KAAKw+C,eAAgBxmC,EAAUmnB,EAASrjB,EAOrD,OALK5Q,IAEHlL,KAAKy+C,WAAYzmC,EAAUmnB,EAASrjB,GAG/B5Q,IAGTuzC,WAAY,SAASzmC,EAAUmnB,EAASrjB,GAEtC,GAAImiC,GAAkBj+C,KAAK49C,QAAQ96C,SAC/B47C,EAAa1+C,KAAK2+C,iBAAkB3mC,EAAUmnB,EAElD8e,GAAgBroB,UAAW8oB,EAAY1+C,KAAK4+C,aAAc5mC,EAAU8D,GAAc9b,KAAM8b,IAG1F8iC,aAAc,SAAS5mC,EAAU8D,GAE/B,MAAO,UAAsB8hC,GAE3B59C,KAAK6+C,iBAAkB7mC,EAAU4lC,EAAS9hC,KAI9CyiC,oBAAqB,SAASvmC,EAAU4lC,EAAS9hC,GAE/C,IAAK8hC,EAAQziB,aAAb,CAMA,GAAIge,GAAkBn5C,KAAKqM,MAAMvJ,SAC7Bs2C,EAAaD,EAAgB5jC,WAAWgyB,SAAUqW,EAAS59C,KAAKk7C,QAEpE/B,GAAgBvjB,UAAWwjB,EAAYp5C,KAAK8+C,sBAAuB9mC,EAAU4lC,EAAS9hC,GAAc9b,KAAM8b,KAG5GgjC,sBAAuB,SAAS9mC,EAAU4lC,EAAS9hC,GAEjD,MAAO,UAA+BqjB,IAE/BA,GAAcn/B,KAAK+Q,QAAS/Q,KAAK+Q,MAAOouB,EAASnnB,KAEpDhY,KAAK6+C,iBAAkB7mC,EAAU4lC,EAAS9hC,GAC1C9b,KAAKw+C,eAAgBxmC,EAAUmnB,EAASrjB,MAK9C+iC,iBAAkB,SAAS7mC,EAAU4lC,EAAS9hC,GAE5C,GAAIzP,GAAQ2L,EAAS2L,OACjBu6B,EAAWlmC,EAASkmC,SACpBQ,EAAad,EAAQ7kC,OACrBgmC,GAASb,EAAStmB,IAAK8mB,EAyB3B,OAvBKK,KAEHh/C,GAAO2S,MAAO3S,GAAO4S,OAAOgd,qBAAsB3vB,KAAMgY,EAAU4lC,GAElEM,EAAShsB,IAAKwsB,EAAYd,GAE1BA,EAAQpzC,IAAKzH,GAAM6B,OAAOg1B,QAAS5hB,EAASmmC,kBAE5CP,EAAQngB,YAAYxiB,IAAK5O,EAAOrM,OAE1B8b,GAAc9b,KAAKi9C,cAElB5wC,EAAMmU,WAETo9B,EAAQn9B,MAAOzgB,KAAKi9C,YAAaj9C,KAAKk9C,oBAItCU,EAAQn9B,MAAO1G,GAAQ0M,QAKtBs4B,GAGTP,eAAgB,SAASxmC,EAAUmnB,EAASrjB,GAE1C,GAAIi+B,GAAW/hC,EAASmnB,QACpBia,EAAaja,EAAQpmB,OACrB7N,GAAU6uC,EAASniB,IAAKwhB,EAwB5B,OAtBKluC,KAEHnL,GAAO2S,MAAO3S,GAAO4S,OAAOyc,gBAAiBpvB,KAAMgY,EAAUmnB,GAE7D4a,EAAS7nB,IAAKknB,EAAYja,GAE1BA,EAAQ30B,IAAKzH,GAAM6B,OAAOg1B,QAAS5hB,EAAS2iC,WAC5Cxb,EAAQ30B,IAAKzH,GAAM6B,OAAOu4B,kBAAmBnlB,EAAS0iC,SAEjD16C,KAAK+Q,OAERouB,EAAQ30B,IAAKzH,GAAM6B,OAAOi3B,OAAQ7jB,EAASmlC,UAG7Cn9C,KAAK2lB,KAAM3N,GAEL8D,GAEJ9b,KAAKw7C,UAAWxjC,IAIb9M,GAGT0wC,YAAa,SAAS5jC,EAAUmnB,EAASrjB,GAEvC,GAAIs9B,GAAaja,EAAQpmB,MACVf,GAASmnB,QACKttB,IAAKunC,IAI3Bp5C,KAAKg/C,cAAehnC,EAAUmnB,EAASrjB,IAE1C9b,KAAKi/C,oBAAqBjnC,EAAUohC,EAAYt9B,IAKtDkjC,cAAe,SAAShnC,EAAUmnB,EAASrjB,GAEzC,GAAImiC,GAAkBj+C,KAAK49C,QAAQ96C,SAC/Bo8C,EAAYl/C,KAAK2+C,iBAAkB3mC,EAAUmnB,GAC7C3pB,EAAMyoC,EAAgB1oC,WAAWohB,OAAQuoB,GACzChB,EAAWlmC,EAASkmC,SACpBN,EAAUM,EAASrsC,IAAK2D,EAE5B,OAAOxV,MAAKm/C,oBAAqBnnC,EAAU4lC,EAASze,GAAS,EAAMrjB,IAGrEsiC,uBAAwB,SAASpmC,EAAU4lC,EAAS9hC,GAElD,GAAIq9B,GAAkBn5C,KAAKqM,MAAMvJ,SAC7Bs2C,EAAaD,EAAgB5jC,WAAWgyB,SAAUqW,EAAS59C,KAAKk7C,QAE/Dl7C,MAAKm/C,oBAAqBnnC,EAAU4lC,EAAS39C,EAAWA,EAAW6b,IAEtE9b,KAAKi/C,oBAAqBjnC,EAAUohC,EAAYt9B,IAIpDqjC,oBAAqB,SAASnnC,EAAU4lC,EAASze,EAASwP,EAAY7yB,GAEpE,GAAIzP,GAAQ2L,EAAS2L,OACjBslB,IAAa2U,CAEjB,IAAK3U,EACL,CACE,IAAMjpC,KAAK67C,iBAAkB+B,EAAS9hC,GAEpC,OAAO,CAGT/b,IAAO2S,MAAO3S,GAAO4S,OAAOid,wBAAyB5vB,KAAMgY,EAAU4lC,EAASze,EAE9E,IAAI+e,GAAWlmC,EAASkmC,SACpBQ,EAAad,EAAQ7kC,MAEzB6kC,GAAQjzC,KAAM5H,GAAM6B,OAAOg1B,QAAS5hB,EAASmmC,kBAE7CP,EAAQngB,YAAYp0B,OAAQgD,GAEvBsiC,GAEHiP,EAAQllB,QAAS5c,EAAa/B,GAAQQ,MAAQR,GAAQ2M,IAAK1mB,KAAK+9C,6BAGlEG,EAAS70C,OAAQq1C,GAGnB,MAAOzV,IAGTgW,oBAAqB,SAASjnC,EAAUohC,EAAYt9B,GAElD,GAAIg9B,GAAU9gC,EAAS8gC,QACnBiB,EAAW/hC,EAASmnB,QACpBA,EAAU4a,EAASloC,IAAKunC,EAkB5B,OAhBKja,KAEHp/B,GAAO2S,MAAO3S,GAAO4S,OAAOuc,mBAAoBlvB,KAAMgY,EAAUmnB,GAEhE4a,EAAS1wC,OAAQ+vC,GAEjBja,EAAQx0B,KAAM5H,GAAM6B,OAAOg1B,QAAS5hB,EAAS2iC,WAC7Cxb,EAAQx0B,KAAM5H,GAAM6B,OAAOu4B,kBAAmBnlB,EAAS0iC,SACvDvb,EAAQx0B,KAAM5H,GAAM6B,OAAOi3B,OAAQ7jB,EAASmlC,UAE5Cn9C,KAAK2lB,KAAM3N,GACXhY,KAAKw7C,UAAWxjC,EAAU8D,UAGrBg9B,GAASM,GAETja,GAGT6b,iBAAkB,SAAS3uC,GAEzB,GAAI6uC,GAAU7uC,EAAMoW,IAAIjN,IACpBqsB,EAAQ7hC,KAAK6hC,KAEjB,OAAO,UAAS+b,GAEd,MAAO/xC,GAAY+xC,EAAS/b,EAAOx1B,EAAO6uC,KAI9CyD,iBAAkB,SAAS3mC,EAAUmnB,GASnC,IAAK,GAPD9yB,GAAQ2L,EAAS2L,OACjBy7B,EAAY/yC,EAAMoW,IAAIlN,WACtB8pC,EAAcr/C,KAAKqM,MAAMvJ,SAASyS,WAClC0oC,EAAkBj+C,KAAK49C,QAAQ96C,SAC/B47C,EAAaT,EAAgBzoC,IAC7BA,KAEK1U,EAAI,EAAGA,EAAI49C,EAAW19C,OAAQF,IACvC,CACE,GAAIoD,GAAOw6C,EAAY59C,EAEvBs+C,GAAUtX,YAAatyB,EAAKtR,EAAMi7B,EAASn/B,KAAKk7C,SAChDmE,EAAYvX,YAAatyB,EAAKtR,EAAMmI,EAAOrM,KAAK6hC,OAGlD,MAAOrsB,IAGT+jC,gBAAiB,SAASjyC,GAExB,MAAOtH,MAAK6hC,SAShB9hC,GAAO4X,UAAUye,UAAYhY,GAE7BA,GAAU9I,UAERjJ,MAAsBpM,EACtB69B,MAAsB,EACtBtpB,OAAsB,EACtBsC,MAAsB8Q,GAAMnB,KAC5BtO,KAAsBsP,GAAKhB,KAC3BmwB,MAAsB,EACtBl4B,UAAsB,EACtBzM,SAAsB,EACtBtR,WAAsB,KACtBwW,sBAAsB,EACtBpG,OAAsB,EACtByiB,aAAsB,GAGxBvrB,GAAMyb,OAAQ3F,GAAkBK,IAG9B/V,KAAM,YAENgzC,UAAsBt7C,GAAO4S,OAAOsd,eACpCknB,WAAsBp3C,GAAO4S,OAAOyd,gBACpCgnB,kBAAsBr3C,GAAO4S,OAAO0d,wBAEpCinB,YAAa,SAAStlC,EAAU8P,EAAOzW,GAErC,MAAO+S,IAAU9I,UAGnBoiC,cAAe,SAAS1lC,EAAU8P,EAAOzW,GAEvCrL,KAAKW,WAAawE,EAAkBnF,KAAKW,WAAYX,KAAKmX,sBAE1DpX,GAAO2S,MAAO3S,GAAO4S,OAAOqd,eAAgBhwB,MAE5CA,KAAK63C,wBAGPtyB,KAAMtQ,GAAK,SAAS5I,EAAOgzB,EAAcvjB,GAEvC,GAAIG,GAAUjc,KACVgY,EAAW3L,EAAMmxB,WAAYx9B,KAAKgF,OAEpC2e,OAAQtX,EACRysC,WACA3Z,QAASn/B,KAAKy4C,yBAA0BpsC,GACxCovC,cAAc,EACdC,aAAa,EAEbf,UAAW,WAET56C,GAAO2S,MAAO3S,GAAO4S,OAAOud,uBAAwBjU,EAAS5P,EAAOrM,KAAMgY,GAE1EiE,EAAQ2/B,YAAa5jC,EAAUhY,MAAM,IAGvC06C,QAAS,WAEP36C,GAAO2S,MAAO3S,GAAO4S,OAAOwd,qBAAsBlU,EAAS5P,EAAOrM,KAAMgY,GAExEiE,EAAQ0J,KAAM3N,GACdiE,EAAQu/B,UAAWxjC,IAGrBmlC,SAAU,WAEHnlC,EAAS2d,QAKT1Z,EAAQlL,QAAUkL,EAAQlL,MAAO/Q,KAAMgY,IAE1CiE,EAAQ2/B,YAAa5jC,EAAUhY,MAAM,IAO3CqM,GAAM0M,OAGD/Y,KAAKwzB,aAERnnB,EAAM7B,IAAKxK,KAAKwzB,YAAaxzB,KAAKs/C,UAAWtnC,GAAYhY,MAI3DgY,EAASxD,MAAQxU,KAAKo4C,aAAc/rC,GAGpCrM,KAAK+4C,YAAa/gC,KAGpBsnC,UAAW,SAAStnC,GAElB,MAAO,YAELA,EAASxD,MAAQxU,KAAKo4C,aAAcpgC,EAAS2L,UAIjD43B,SAAU,SAASvjC,EAAUmnB,EAASrjB,GAEpC,KAAKqjB,EAAQhE,cAAiBn7B,KAAK+Q,QAAU/Q,KAAK+Q,MAAOouB,EAASnnB,IAAlE,CAKA,GACI1Q,IADQ0Q,EAAS2L,OACR3L,EAASmnB,SAClB3pB,EAAM2pB,EAAQpmB,OACd7N,GAAU5D,EAAOswB,IAAKpiB,EAoB1B,OAlBKtK,KAEHnL,GAAO2S,MAAO3S,GAAO4S,OAAOub,YAAaluB,KAAMgY,EAAUmnB,GAEzD73B,EAAO4qB,IAAK1c,EAAK2pB,GAEjBA,EAAQ30B,IAAKzH,GAAM6B,OAAOg1B,QAAS5hB,EAAS2iC,WAC5Cxb,EAAQ30B,IAAKzH,GAAM6B,OAAOu4B,kBAAmBnlB,EAAS0iC,SAEjD16C,KAAK+Q,OAERouB,EAAQ30B,IAAKzH,GAAM6B,OAAOi3B,OAAQ7jB,EAASmlC,UAG7Cn9C,KAAK2lB,KAAM3N,GACXhY,KAAKw7C,UAAWxjC,EAAU8D,IAGrB5Q,IAGT0wC,YAAa,SAAS5jC,EAAUmnB,EAASrjB,GAEvC,GAAM9b,KAAK67C,iBAAkB1c,EAASrjB,GAAtC,CAKA,GACIxU,IADQ0Q,EAAS2L,OACR3L,EAASmnB,SAClB2Z,EAAU9gC,EAAS8gC,QACnBtjC,EAAM2pB,EAAQpmB,MAEbzR,GAAOswB,IAAKpiB,KAEfzV,GAAO2S,MAAO3S,GAAO4S,OAAOqb,eAAgBhuB,KAAMgY,EAAUmnB,GAE5D73B,EAAO+B,OAAQmM,GAEf2pB,EAAQx0B,KAAM5H,GAAM6B,OAAOg1B,QAAS5hB,EAAS2iC,WAC7Cxb,EAAQx0B,KAAM5H,GAAM6B,OAAOu4B,kBAAmBnlB,EAAS0iC,SACvDvb,EAAQx0B,KAAM5H,GAAM6B,OAAOi3B,OAAQ7jB,EAASmlC,UAE5Cn9C,KAAK2lB,KAAM3N,GACXhY,KAAKw7C,UAAWxjC,EAAU8D,UAGrBg9B,GAAStjC,OASpBzV,GAAO4X,UAAU4nC,QAAUlhC,GAE3BA,GAAQ/I,UAENjJ,MAAsBpM,EACtB69B,MAAsB,EACtBhnB,MAAsB8Q,GAAM7kB,MAC5BoV,KAAsBsP,GAAK1kB,MAC3B6zC,MAAsB,EACtBl4B,UAAsB,EACtBzM,SAAsB,EACtBtR,WAAsB,KACtBwW,sBAAsB,GAGxBlP,GAAMyb,OAAQ3F,GAAkBM,IAG9BhW,KAAM,UAENgzC,UAAsBt7C,GAAO4S,OAAO4d,aAEpC+mB,YAAa,SAAStlC,EAAU8P,EAAOzW,GAErC,MAAOgT,IAAQ/I,UAGjBoiC,cAAe,SAAS1lC,EAAU8P,EAAOzW,GAEvCrL,KAAKW,WAAawE,EAAkBnF,KAAKW,WAAYX,KAAKmX,sBAE1DpX,GAAO2S,MAAO3S,GAAO4S,OAAO2d,aAActwB,MAE1CA,KAAK63C,wBAGPtyB,KAAMtQ,GAAK,SAAS5I,EAAOgzB,EAAcvjB,GAEvC,GAAIG,GAAUjc,KACVgY,EAAW3L,EAAMmxB,WAAYx9B,KAAKgF,OAEpC2e,OAAQtX,EACRysC,WACA3Z,QAASn/B,KAAKy4C,yBAA0BpsC,GACxCovC,cAAc,EACdC,aAAa,EAEbf,UAAW,WAET56C,GAAO2S,MAAO3S,GAAO4S,OAAO6d,qBAAsBvU,EAAS5P,EAAOrM,KAAMgY,GAExEiE,EAAQ2/B,YAAa5jC,EAAUhY,MAAM,IAGvC06C,QAAS,WAEP36C,GAAO2S,MAAO3S,GAAO4S,OAAO8d,mBAAoBxU,EAAS5P,EAAOrM,KAAMgY,GAEtEiE,EAAQ0J,KAAM3N,GACdiE,EAAQu/B,UAAWxjC,IAMvBhY,MAAK+3C,WAAY1rC,EAAOgzB,EAAcvjB,GAGtC9b,KAAK+4C,YAAa/gC,KAGpB+/B,WAAY,SAAS1rC,EAAOgzB,EAAcvjB,GAExC,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEjC3D,GAASg+B,KAEZt/B,GAAO2S,MAAO3S,GAAO4S,OAAOie,gBAAiB5wB,KAAMqM,EAAO2L,EAAUqnB,GAEpEr/B,KAAK64C,WAAY7gC,EAAUqnB,EAAcr/B,KAAK86C,YAAa9iC,EAAU8D,GAAcA,KAIvFy/B,SAAU,SAASvjC,EAAUmnB,EAASrjB,GAEpC,IAAKqjB,EAAQhE,aAAb,CAKA,GACI7zB,IADQ0Q,EAAS2L,OACR3L,EAASmnB,SAClB3pB,EAAM2pB,EAAQpmB,OACd7N,GAAU5D,EAAOswB,IAAKpiB,EAmB1B,OAjBKtK,KAEHnL,GAAO2S,MAAO3S,GAAO4S,OAAOge,YAAa3wB,KAAMgY,EAAUmnB,GAEzD73B,EAAO4qB,IAAK1c,EAAK2pB,GAEjBA,EAAQ30B,IAAKzH,GAAM6B,OAAOg1B,QAAS5hB,EAAS2iC,WAC5Cxb,EAAQ30B,IAAKzH,GAAM6B,OAAOu4B,kBAAmBnlB,EAAS0iC,SAEtD16C,KAAK2lB,KAAM3N,GAEL8D,GAEJ9b,KAAKw7C,UAAWxjC,IAIb9M,IAGT0wC,YAAa,SAAS5jC,EAAUmnB,EAASrjB,GAEvC,GAAM9b,KAAK67C,iBAAkB1c,EAASrjB,GAAtC,CAKA,GACIxU,IADQ0Q,EAAS2L,OACR3L,EAASmnB,SAClB2Z,EAAU9gC,EAAS8gC,QACnBtjC,EAAM2pB,EAAQpmB,MAEbzR,GAAOswB,IAAKpiB,KAEfzV,GAAO2S,MAAO3S,GAAO4S,OAAO+d,eAAgB1wB,KAAMgY,EAAUmnB,GAE5D73B,EAAO+B,OAAQmM,GAEf2pB,EAAQx0B,KAAM5H,GAAM6B,OAAOg1B,QAAS5hB,EAAS2iC,WAC7Cxb,EAAQx0B,KAAM5H,GAAM6B,OAAOu4B,kBAAmBnlB,EAAS0iC,SAEvD16C,KAAK2lB,KAAM3N,GACXhY,KAAKw7C,UAAWxjC,UAGX8gC,GAAStjC,KAGlB2qB,UAAW,SAAS9zB,EAAO+P,EAAOnY,GAEhC,GAAIk7B,GAAUn/B,KAAK6R,IAAKxF,EAExB,IAAK8yB,EACL,CAGE,IAAK,GAFDue,MAEK58C,EAAI,EAAGA,EAAIq+B,EAAQn+B,OAAQF,IAElC48C,EAAcv0C,KAAMg2B,EAASr+B,GAAIg/B,SAGnC1jB,GAAOpc,KAAKgF,MAAS04C,MAU3B39C,GAAO4X,UAAU6nC,aAAelhC,GAEhCA,GAAahJ,UAEXjJ,MAAsB,KACtByxB,MAAsB,EACtBtpB,OAAsB,EACtBsC,MAAsB8Q,GAAMnB,KAC5BtO,KAAsBsP,GAAKhB,KAC3B/H,UAAsB,EACtBzM,SAAsB,GAGxBhK,GAAMyb,OAAQ5F,GAAgBQ,IAG5BjW,KAAM,eAEN4xC,UAAoBl6C,GAAO4S,OAAOqa,kBAClCktB,gBAAoBn6C,GAAO4S,OAAOya,yBAClC+sB,cAAoBp6C,GAAO4S,OAAO0a,uBAClC+sB,YAAoBr6C,GAAO4S,OAAO6a,oBAClC2pB,WAAoBp3C,GAAO4S,OAAO8a,mBAClC2pB,kBAAoBr3C,GAAO4S,OAAO+a,2BAElC4pB,YAAa,SAAStlC,EAAU8P,EAAOzW,GAErC,MAAOiT,IAAahJ,UAGtBiQ,KAAMtQ,GAAK,SAAS5I,EAAOgzB,EAAcvjB,GAEvC,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,OAEpC2e,OAAQtX,EACR8yB,QAAS,KACTnpB,QAAQ,EACR4kC,OAAO,EAEPD,UAAW,WAET56C,GAAO2S,MAAO3S,GAAO4S,OAAOsa,0BAA2BjtB,KAAMqM,EAAO2L,GAEpEhY,KAAKu6C,aAAcviC,GAAU,GAAO,IAIxChY,MAAK+3C,WAAY1rC,EAAOgzB,EAAcvjB,KAGxCi8B,WAAY,SAAS1rC,EAAOgzB,EAAcvjB,GAExC,GAAI9D,GAAW3L,EAAMmxB,WAAYx9B,KAAKgF,KAEhCb,GAASk7B,GAMLr/B,KAAKwU,QAEbwD,EAASxD,MAAQxU,KAAKo4C,aAAc/rC,KANpCtM,GAAO2S,MAAO3S,GAAO4S,OAAOwa,qBAAsBntB,KAAMqM,EAAOgzB,GAE/Dr/B,KAAK41B,UAAWyJ,EAAcr/B,KAAK86C,YAAa9iC,GAAY8D,EAAY9D,KAQ5EioB,SAAU,SAAS5zB,EAAO+P,EAAOnY,GAE/B,GAAIk7B,GAAUn/B,KAAK6R,IAAKxF,EAEnB8yB,KAEH/iB,EAAOpc,KAAKgF,MAASm6B,EAAQW,OAAQ77B,KAIzC42C,YAAa,SAAS7iC,EAAUmnB,GAE9B,OAAO,GAGTsE,iBAAkB,aAKlBoW,gBAAiB,cAQnB,IAAIrC,KAGFC,cAAe,SAASzlC,EAAU8P,EAAOzW,GAEvCrL,KAAKg7C,iBAAmBh7C,KAAKy/C,8BAA+Bz/C,KAAKg7C,kBAEjEh7C,KAAK0/C,mBAAmB,WAEtB1/C,KAAK03C,cAAe1lC,EAAU8P,EAAOzW,MAIzCo0C,8BAA+B,SAASzE,GAEtC,MAAO,UAAU3uC,GAEf,GAAI+yB,GAAY4b,EAAiBx5C,KAAMxB,KAAMqM,GACzCszC,EAAqB3/C,KAAKiY,aAE9B,IAAIjY,KAAKq3C,iBAEP,MAAO,UAASlY,GAEd,QAAMC,EAAWD,IAOV14B,EAFazG,KAAK4/C,yBAA0BzgB,GAErB9yB,EAAOszC,IAKvC,IAAI1nC,GAAgBjY,KAAK4/C,yBAA0BvzC,EAEnD,OAAO,UAAU8yB,GAEf,QAAMC,EAAWD,IAKV14B,EAAQwR,EAAeknB,EAASwgB,OAM/CD,mBAAoB,SAASG,GAM3B,QAASC,OAEA9pC,IAAWo0B,GAEhByV,EAAOj9C,MAAO5C,MARlB,GAAIi3C,GAAiBj3C,KAAKi3C,eACtB7M,EAAQpmC,EAAQizC,GAChBjhC,EAAS,CAUb,KAAK,GAAIhR,KAAQiyC,GACjB,CACE,GAAIh/B,GAAgBg/B,EAAgBjyC,EAEpCjF,IAAO8R,IAAK7M,GAAO01B,SAAU16B,KAAK+/C,iBAAkB9nC,EAAe6nC,GAAgB9/C,QAIvF+/C,iBAAkB,SAAS9nC,EAAe4nC,GAExC,MAAO,UAASjI,GAEd53C,KAAKi3C,eAAgBW,EAAO90C,SAASkC,MAASiT,EAC9CjY,KAAKi3C,eAAgBW,EAAO90C,SAASoP,WAAc+F,EACnDjY,KAAKk3C,qBAAsBj/B,GAAkB2/B,EAE7CiI,EAAOj9C,MAAO5C,QAIlBy4C,yBAA0B,SAASpsC,GAEjC,MAAO6P,IAAwBF,GAAmBta,OAAQzB,EAAWoM,EAAOrM,MAAQA,KAAKiY,cAAejY,KAAKk3C,uBAG/GwB,iBAAkB,WAEhB,MAAOx8B,IAAwBpG,GAAgBpU,SAAU1B,KAAKiY,cAAejY,KAAKk3C,uBAGpF7hB,MAAO,SAAS5wB,GAEd,GAAIoR,GAAS7V,KAAKk3C,oBAElB,KAAM,GAAIhzC,KAAQ2R,GAClB,CACcA,EAAQ3R,GAEdpB,SAASuyB,MAAO5wB,EAAUzE,QAIpCm4C,mBAAoB,SAAS1zC,GAE3B,GAAIoR,GAAS7V,KAAKk3C,oBAElB,KAAM,GAAIhzC,KAAQ2R,GAClB,CACcA,EAAQ3R,GAEdpB,SAAS6B,GAAI7B,GAAS8B,OAAO+tB,WAAYluB,EAAUzE,QAI7Do4C,aAAc,SAAS/rC,GAErB,GAAIgsC,GAAcr4C,KAAKwU,MACnB4f,EAAep0B,KAAKo0B,aACpBkkB,EAAYt4C,KAAKs4C,UACjB9jC,EAAQlU,EAAU+3C,GAAgB1pC,GAAQ0pC,EAAahsC,GAAUgsC,EACjEE,EAASlsC,EAAMksC,OAAQ/jC,EAAO4f,EAYlC,OAVK5xB,GAAU81C,IAEbC,EAAOriB,KAAMoiB,GAGfp8B,GAAwBq8B,EAAOpI,SAAUnwC,KAAKiY,cAAejY,KAAKk3C,sBAEpDqB,EAAOjI,OACb5V,SAAU16B,KAAKw4C,mBAAoBnsC,GAASrM,MAE7Cu4C,GAGTj8B,WAAY,SAAS5K,EAAOoK,EAAY9D,GAEtC,GAAKtG,YAAiB3O,IAEpB,MAAO2O,EAEJ,IAAKlP,EAAUkP,GACpB,CACE,GAAIoC,GAAK9T,KAAKq3C,iBACZr3C,KAAKggD,yBAA0BhoC,EAAS2L,QACxC3jB,KAAKggD,yBAA0BtuC,EAEjC,IAAKoC,EAEH,MAAOA,GAAGwI,WAAY5K,EAAOoK,GAIjC,OAAO,GAGTu9B,YAAa,SAAS/xC,EAAQmF,EAAcqP,GAE1C,GAAIpP,GAAUF,EAA0BlF,EAAQmF,EAahD,OAXKnF,GAAQtH,KAAKiY,iBAEhB3Q,EAAQtH,KAAKiY,eAAkB,KAC/BvL,GAAU,GAGPA,IAAYoP,GAAc9b,KAAK42C,OAAStvC,EAAOi6B,UAElDj6B,EAAOmZ,MAAOzgB,KAAK62C,YAAa72C,KAAK82C,aAGhCpqC,GAGT4sC,aAAc,SAAShyC,EAAQmF,EAAcI,EAAQC,EAAcgP,GAEjE,GAAIpP,GAAUE,EAA2BtF,EAAQmF,EAAcI,EAAQC,GAEnEH,EAAc3M,KAAKiY,cACnBlL,EAAczF,EAAQqF,GACtBM,EAAcjN,KAAK4/C,yBAA0B/yC,EAkBjD,OAhBMpG,GAAQsG,EAAaE,KAEzB3F,EAAQqF,GAAgBM,EACxBP,GAAU,GAGPA,KAEE1M,KAAK42C,MAAStvC,EAAOi6B,UAAazlB,GAErCxU,EAAOmZ,MAAOzgB,KAAK62C,YAAa72C,KAAK82C,aAGvCxvC,EAAOsD,SAAU7H,GAAM6B,OAAOk3B,WAAYx0B,EAAQuF,EAAQJ,EAAcK,KAGnEJ,GAGTksC,YAAa,SAAUvsC,EAAOC,GAE5B,GAAKtM,KAAKq3C,kBAAoBjrC,EAAWC,EAAOC,EAAQ9L,GACxD,CACE,GAAI2+B,GAAUn/B,KAAKggD,yBAA0B3zC,EAE7C,IAAK8yB,EACL,CACE,GAAIwZ,KAIJ,OAFA/rC,GAA2B+rC,EAASxZ,EAAQ3pB,IAAKnJ,EAAOC,GAEjDqsC,KAKb/iB,UAAW,SAASlkB,EAAOjN,EAAUqX,EAAY9D,GAE/C,GAAKtG,YAAiB3O,IAEpB0B,EAASjD,KAAMxB,KAAM0R,OAIlB,IAAKlP,EAAUkP,GACpB,CACE,GAAIoC,GAAK9T,KAAKq3C,iBACZr3C,KAAKggD,yBAA0BhoC,EAAS2L,QACxC3jB,KAAKggD,yBAA0BtuC,IAErB,IAAPoC,GAEHA,EAAG8hB,UAAWlkB,EAAOjN,EAAUzE,KAAM8b,KAK3C+8B,WAAY,SAAS7gC,EAAU2gC,EAASl0C,EAAUqX,GAEhD,IAAK,GAAIhb,GAAI,EAAGA,EAAI63C,EAAQ33C,OAAQF,IACpC,CACE,GAAI4Q,GAAQinC,EAAS73C,EAErB,IAAK4Q,YAAiB3O,IAEpBiV,EAAS8gC,QAASpnC,EAAMqH,SAAW,EAEnCtU,EAASjD,KAAMxB,KAAM0R,OAIlB,IAAKlP,EAAUkP,GACpB,CACE,GAAIoC,GAAK9T,KAAKggD,yBAA0BtuC,EAExC,IAAKoC,EACL,CACE,GAAI0B,GAAM1B,EAAGyB,WAAW8G,kBAAmB3K,EAE3CsG,GAAS8gC,QAAStjC,IAAQ,EAE1B1B,EAAG8hB,UAAWlkB,EAAOjN,EAAUzE,KAAM8b,OAM7CmkC,eAAgB,WAEd,OAAO,GAGT/G,aAAc,SAASxnC,GAErB,MAAOrQ,GAASqQ,IAGlBwuC,iBAAkB,SAAS7zC,GAEzB,MAAOA,GAAOrM,KAAKiY,gBAGrB+nC,yBAA0B,SAAS3zC,GAEjC,GAAI4L,GAAgBjY,KAAKkgD,iBAAkB7zC,GACvCA,EAAQrM,KAAKk3C,qBAAsBj/B,EAEvC,SAAO5L,GAAQA,EAAMvJ,UAGvB88C,yBAA0B,SAASvzC,GAEjC,MAAOrM,MAAKi3C,eAAgB5qC,EAAMoW,IAAIzd,OAM1CjF,IAAOogD,MAAQ,SAAS51C,GAEtB,MAAO,UAA4ByH,GAEjC,GAAImuC,GAAQ,GAAI5hC,IAAOvM,EAMvB,OAJA/J,IAAM4C,MAAOs1C,EAAO51C,GAEpB41C,EAAMC,WAAYpuC,GAEXmuC,IASXl4C,GAAMvG,OAAQ6c,IAGZ8hC,gBAAiB,IACjBC,gBAAiB,IACjBC,mBAAoB,IACpBC,mBAAoB,IACpBC,mBAAoB,IACpBC,kBAAmB,IAEnBC,YAAY,EACZC,YAAY,EACZC,eAAe,EACfC,eAAe,EACfC,eAAe,EACfC,cAAc,EAEdC,UAAW,SAASC,GAElB,KAAM,6BAGRC,iBAAkB,SAAS90C,EAAO60C,GAEhC,KAAM,oCAGRE,kBAAmB,SAAS/0C,EAAO60C,GAEjC,GAAIG,GAASrhD,KAAKmhD,iBAAkB90C,EAAO60C,EAE3C,OAAOG,IAAWA,GAAWrhD,KAAKihD,UAAWC,IAG/CI,kBAAmB,SAAS7sC,EAAKD,GAE/B,MAAOxU,MAAKihD,aAGdb,WAAY,SAASpuC,KAKrBkC,IAAK,SAAS7I,EAASiH,EAAS6B,GAK9B,QAASqL,GAAO2gC,EAAOoB,EAAgBC,GAErCrB,EAAMjsC,IAAK7I,EAASk2C,EAAgBC,GAEtC,QAAShM,GAAU3/B,GAEZxU,EAASwU,IAEZ3B,EAAI/K,KAAKvG,MAAOsR,EAAK2B,GAGzB,QAAS4rC,GAAWC,EAAYC,EAAeC,GAExCF,GAAextC,EAAIlT,SAAWhB,KAAK2gD,WAEtCruC,EAAS4B,GAEAytC,GAETxtC,EAASD,EAAKzR,EAAWm/C,GAAiBA,EAAe5hD,KAAKqgD,iBAtBlE,GAAIwB,GAAS7hD,KAAKihD,WAAW,GACzB/sC,IAyBJlU,MAAK8hD,UAAWD,EAAQ7hD,KAAK2gD,WAAYnhC,EAAQg2B,EAAWrhC,EAASstC,IAGvE5vC,IAAK,SAASxF,EAAOhB,EAASiH,EAAS6B,GAKrC,QAASqL,GAAO2gC,EAAOoB,EAAgBC,GAErCrB,EAAMtuC,IAAKxF,EAAOhB,EAASk2C,EAAgBC,GAE7C,QAAShM,GAAUh9B,GAED,OAAXupC,GAAmBv/C,EAAUgW,KAEhCupC,EAASvpC,GAGb,QAASipC,GAAWC,EAAYC,EAAeC,GAE7B,OAAXG,EAEHzvC,EAASyvC,GAIT5tC,EAAS4tC,EAAQt/C,EAAWm/C,GAAiBA,EAAe5hD,KAAKsgD,iBAtBrE,GAAIuB,GAAS7hD,KAAKohD,kBAAmB/0C,GAAO,GACxC01C,EAAS,IAyBb/hD,MAAK8hD,UAAWD,EAAQ7hD,KAAK4gD,WAAYphC,EAAQg2B,EAAWjyC,EAAMk+C,IAGpE//C,OAAQ,SAAU2K,EAAOiI,EAASjJ,EAASiH,EAAS6B,GAKlD,QAASqL,GAAO2gC,EAAOoB,EAAgBC,GAErCrB,EAAMz+C,OAAQ2K,EAAOiI,EAASjJ,EAASk2C,EAAgBC,GAEzD,QAAShM,GAAUh9B,GAEC,OAAbwpC,GAAqBx/C,EAAUw/C,KAElCA,EAAWxpC,GAGf,QAASipC,GAAWC,EAAYC,EAAeC,GAExCF,EAEHpvC,EAAS0vC,GAIT7tC,EAAS6tC,EAAUv/C,EAAWm/C,GAAiBA,EAAe5hD,KAAKugD,oBAtBvE,GAAIsB,GAAS7hD,KAAKohD,kBAAmB/0C,GAAO,GACxC21C,EAAW,IAyBfhiD,MAAK8hD,UAAWD,EAAQ7hD,KAAK6gD,cAAerhC,EAAQg2B,EAAWjyC,EAAMk+C,IAGvEltC,OAAQ,SAAUlI,EAAOiI,EAASjJ,EAASiH,EAAS6B,GAKlD,QAASqL,GAAO2gC,EAAOoB,EAAgBC,GAErCrB,EAAM5rC,OAAQlI,EAAOiI,EAASjJ,EAASk2C,EAAgBC,GAEzD,QAAShM,GAAUh9B,GAEC,OAAbwpC,GAAqBx/C,EAAUw/C,KAElCA,EAAWxpC,GAGf,QAASipC,GAAWC,EAAYC,EAAeC,GAExCF,EAEHpvC,EAAS0vC,GAIT7tC,EAAS6tC,EAAUv/C,EAAWm/C,GAAiBA,EAAe5hD,KAAKwgD,oBAtBvE,GAAIqB,GAAS7hD,KAAKohD,kBAAmB/0C,GAAO,GACxC21C,EAAW,IAyBfhiD,MAAK8hD,UAAWD,EAAQ7hD,KAAK8gD,cAAethC,EAAQg2B,EAAWjyC,EAAMk+C,IAGvEp4C,OAAQ,SAAUgD,EAAOhB,EAASiH,EAAS6B,GAKzC,QAASqL,GAAO2gC,EAAOoB,EAAgBC,GAErCrB,EAAM92C,OAAQgD,EAAOhB,EAASk2C,EAAgBC,GAEhD,QAAShM,GAAUh9B,GAEC,OAAbwpC,GAAqBx/C,EAAUw/C,KAElCA,EAAWxpC,GAGf,QAASipC,GAAWC,EAAYC,EAAeC,GAExCF,EAEHpvC,EAAS0vC,GAIT7tC,EAAS6tC,EAAUv/C,EAAWm/C,GAAiBA,EAAe5hD,KAAKygD,oBAtBvE,GAAIoB,GAAS7hD,KAAKohD,kBAAmB/0C,GAAO,GACxC21C,EAAW,IAyBfhiD,MAAK8hD,UAAWD,EAAQ7hD,KAAK+gD,cAAevhC,EAAQg2B,EAAWjyC,EAAMk+C,IAGvEjtC,MAAO,SAAUC,EAAKD,EAAOnJ,EAASiH,EAAS6B,GAK7C,QAASqL,GAAO2gC,EAAOoB,EAAgBC,GAErCrB,EAAM3rC,MAAOC,EAAKD,EAAOnJ,EAASk2C,EAAgBC,GAEpD,QAAShM,GAAU3/B,GAEZxU,EAASwU,IAEZ9D,EAAQ5I,KAAKvG,MAAOmP,EAAS8D,GAGjC,QAAS4rC,GAAWC,EAAYC,EAAeC,GAExCF,GAAe3vC,EAAQ/Q,SAAWhB,KAAKghD,aAE1C1uC,EAASP,GAEA4vC,GAETxtC,EAASpC,EAAStP,EAAWm/C,GAAiBA,EAAe5hD,KAAK0gD,mBAtBtE,GAAImB,GAAS7hD,KAAKshD,kBAAmB7sC,EAAKD,GACtCzC,IAyBJ/R,MAAK8hD,UAAWD,EAAQ7hD,KAAKghD,aAAcxhC,EAAQg2B,EAAWjyC,EAAMk+C;8CAGtEK,UAAW,SAASD,EAAQI,EAAQziC,EAAQg2B,EAAWE,EAAW+L,GAOhE,QAASS,OAEA9X,IAAUyX,EAAO7gD,QAEtBygD,EAAWjgD,KAAMxB,KAAM0hD,EAAYS,EAAeP,GAGtD,QAASL,GAAe/oC,IAEjBkpC,GAAeO,GAElBzM,EAAU5yC,MAAO5C,KAAMoB,WAGzB8gD,IAEF,QAASV,GAAehpC,EAAM4B,GAEvBsnC,IAEHA,GAAa,EAERO,IAEHE,GAAgB,EAChBzM,EAAU9yC,MAAO5C,KAAMoB,aAItB4B,EAAUoX,KAAawnC,IAAiB3hD,GAAama,EAASwnC,KAEjEA,EAAexnC,GAGjB8nC,IAvCF,GAEIN,GAFAF,GAAa,EACbS,GAAgB,EAEhB/X,EAAQ,CAuCZ,IAAM/oC,EAASwgD,IAA8B,IAAlBA,EAAO7gD,OAMhC,IAAK,GAAIF,GAAI,EAAGA,EAAI+gD,EAAO7gD,OAAQF,IAEjC0e,EAAOhe,KAAMxB,KAAM6hD,EAAQ/gD,GAAKygD,EAAgBC,OANlDC,GAAWjgD,KAAMxB,MAAM,GAAO,EAAO4hD,MAa3Cp9C,EAAU,SAAS6H,EAAOyH,EAAIzI,GAoB5BgB,EAAM6H,IAAM,WAEV,MAAOJ,GAAG+B,UAKdrR,EAAU,SAAS6H,EAAOyH,EAAIzI,GAyB5BgB,EAAMhK,MAAQ,SAASnB,GAErB,GAAI2U,GAASzU,UAAUJ,OAAS,IAAMK,EAAQH,GAC5CyQ,GAAGpQ,MAAMC,KAAMJ,WAAcF,CAE/B,OAAO4U,IAAgBlU,OAAQkS,EAAI+B,MAIvCrR,EAAU,SAAS6H,EAAOyH,EAAIzI,GAsB5BgB,EAAM3F,GAAK,SAAS4b,GAElB,MAAOxO,GAAG+B,OAAQyM,MAKtB9d,EAAU,SAAS6H,EAAOyH,EAAIzI,GA8B5BgB,EAAM+1C,KAAO,SAAU1wC,GAErB,MAAKrQ,GAASqQ,GAELoE,GAAgBpU,OAAQoS,EAAIpC,GAAO,GAElClP,EAAUkP,GAEXoC,EAAGwiB,cAAe5kB,GAGpBA,KAIXlN,EAAU,SAAS6H,EAAOyH,EAAIzI,GAG5BgB,EAAMsI,MAAQ,SAASoR,GAErB,MAAOjS,GAAGa,MAAOoR,MAKrBvhB,EAAU,SAAS6H,EAAOyH,EAAIzI,GAyB5BgB,EAAMpL,QAAU,SAASC,GAEvB,GAAI2U,GAASzU,UAAUJ,OAAS,IAAMK,EAAQH,GAC5CyQ,GAAGpQ,MAAMC,KAAMJ,WAAcF,CAE/B,OAAO4U,IAAgBpU,OAAQoS,EAAI+B,MAIvCrR,EAAU,SAAS6H,EAAOyH,EAAIzI,GAoB5BgB,EAAMk+B,MAAQ,SAAStmC,EAAYiN,EAAOzK,GAExC,MAAOqN,GAAG+B,OAAOw0B,WAAYpmC,EAAYiN,EAAOzK,MAIpDjC,EAAU,SAAS6H,EAAOyH,EAAIzI,GA6B5BgB,EAAM3K,OAAS,SAAUmJ,EAAOiI,EAASzH,GAEvC,GAAIg3C,GAAW7/C,EAAUqI,GACvBiJ,EAAGgkB,YAAajtB,GAChBiJ,EAAGyiB,aAIL,OAFA8rB,GAAS5hC,MAAO3N,EAASzH,GAElBg3C,KAIX79C,EAAU,SAAS6H,EAAOyH,EAAIzI,GAE5B,GAAIi3C,GAAW10C,EAAUvC,EAAQ4G,QAASqD,GAASrD,QAEnD,KAAM9N,EAASm+C,GAEb,IAAM,GAAI5jC,KAAY4jC,GAEpB9jC,GAAoBnS,EAAM/K,UAAWod,EAAU4jC,EAAU5jC,MAkD/Dla,EAAU,SAAS6H,EAAOyH,EAAIzI,GAE5B,GAAI7D,GAASoG,EAAUvC,EAAQ7D,OAAQ8N,GAAS9N,OAEhD,KAAMrD,EAASqD,GACf,CACE,GAAI+6C,MACAC,IAEJ,KAAM,GAAI/iC,KAAajY,GACvB,CACE,GAAI/C,GAAW+C,EAAQiY,GACnBxW,EAAYqH,GAAamP,GAEzBgjC,EAAsB3/C,GAAS8B,OAAQqE,GACvCy5C,EAAmB3/C,GAAM6B,OAAQqE,EAEhCw5C,IAEHpjC,GAAqBojC,EAAqBh+C,GAAU,EAAO+9C,GAGxDE,GAEHrjC,GAAqBqjC,EAAkBj+C,GAAU,EAAM89C,GAI3D5iC,GAAqB7L,EAAI0uC,GAEpBD,EAAYvhD,QAEfiH,GAAMsI,QAASlE,EAAO,QAAS,SAASsQ,GAEtC,MAAO,YAELA,EAAM/Z,MAAO5C,KAAMoB,WAEnBue,GAAqB3f,KAAMuiD,SAkFrC/9C,EAAU,SAAS6H,EAAOyH,EAAIzI,GAa5B,QAASs3C,GAAal3C,GAEdJ,EAASI,KAEbqI,EAAIrI,GAAWm3C,EAAKn3C,IAIxB,QAASo3C,GAASp3C,GAEhB,GAAIq3C,GAAMhvC,EAAIrI,GACVs3C,EAAOH,EAAKn3C,EAEhB,KAAK,GAAIvH,KAAQ6+C,GAER7+C,IAAQ4+C,KAEbA,EAAK5+C,GAAS6+C,EAAM7+C,IAK1B,QAAS8+C,GAAW33C,EAAS43C,GAK3B,IAAK,GAHDp2C,GAAS+1C,EAAKK,GAAiB53C,GAC/B/D,EAASwM,EAAIzI,GAERvK,EAAI+L,EAAO7L,OAAS,EAAGF,GAAK,EAAGA,IACxC,CACE,GAAIgB,GAAIrB,EAAS6G,EAAQuF,EAAQ/L,KAEtB,IAANgB,GAEHwF,EAAOuJ,OAAQ/O,EAAG,GAGpBwF,EAAOu9B,QAASh4B,EAAQ/L,KA/C5B,GAAI4iB,GAASrY,EAAQqY,QAAUpO,GAASoO,MAExC,IAAM7gB,EAAU6gB,GAAhB,CAKA,GACIk/B,GAAMl/B,EAAO5gB,SACbogD,EAAWN,EAAIv3C,OA0CnBs3C,GAAc,gBACdE,EAAU,YACVA,EAAU,iBACVF,EAAc,iBACdA,EAAc,QACdA,EAAc,eACdA,EAAc,SACdA,EAAc,YACdA,EAAc,eACdE,EAAU,aACVA,EAAU,aACVF,EAAc,aACdK,EAAY,UACZA,EAAY,aAAc,UAEpB33C,EAAQ1K,YAEZmT,EAAGoD,cAAegsC,EAASviD,WAAYuiD,EAAS/rC,sBAG5C9L,EAAQgM,UAEZvD,EAAGsD,YAAa8rC,EAAS7rC,UAGrBhM,EAAQkM,WAEZzD,EAAGwD,aAAc4rC,EAAS3rC,UAG5B,KAAK,GAAIvS,KAAQ49C,GAAIprC,UAEnB,KAAKxS,IAAQ8O,GAAG0D,WAAhB,CAKA,GAAIQ,GAAW4qC,EAAIprC,UAAWxS,GAC1Bm+C,EAAe,GAAInrC,GAASrV,WAEhCwgD,GAAajrC,KAAMpE,EAAI9O,EAAMgT,EAAS3M,SAEjC83C,EAAahrC,MAEhBrE,EAAG0C,WAAWrN,KAAMnE,GAGtB8O,EAAG0D,UAAWxS,GAASm+C,EACvBrvC,EAAG2D,cAActO,KAAMnE,GAGzB8O,EAAGC,KAAShU,GAAOgU,KAAMD,GACzBA,EAAGgD,MAAS/W,GAAO+W,MAAOhD,GAC1BA,EAAGkD,KAASjX,GAAOiX,KAAMlD,MAI3BtP,EAAU,SAAS6H,EAAOyH,EAAIzI,GAgC5BgB,EAAM+2C,MAAQ,SAAU1xC,EAAOrG,EAAS5G,EAAUhB,GAEhD,GAAI+R,GAAM1B,EAAGyB,WAAW8G,kBAAmB3K,GACvC2wC,EAAWvuC,EAAGjC,IAAK2D,EAYvB,IAVM6sC,IAEJA,EAAWvuC,EAAGyB,WAAWygB,mBAAoBxgB,GAExChT,EAAUkP,IAEb2wC,EAASnsB,KAAMxkB,IAIdhP,EAAY+B,GACjB,CACE,GAAIoE,GAAkBpF,GAAWzD,IAEjCqiD,GAAS53C,MAAO1H,GAAM6B,OAAOqxB,WAAY,WAEvCxxB,EAASjD,KAAMqH,EAAiBw5C,KAMpC,MAFAA,GAASlsB,SAAUpc,GAAQC,KAAM3O,GAE1Bg3C,KAIX79C,EAAU,SAAS6H,EAAOyH,EAAIzI,GA4B5BgB,EAAMg3C,SAAW,SAAS5+C,EAAUhB,GAIlC,MAFAqQ,GAAG2lB,QAASh1B,EAAUhB,GAEfqQ,EAAG+B,UAIdrR,EAAU,SAAS6H,EAAOyH,EAAIzI,GAE5B,GAAIi4C,GAAQj4C,EAAQi4C,OAAShuC,GAASguC,KAEtC,IAAM9gD,EAAU8gD,GAAhB,CAKA,IAAMzjC,KAIJ,WAFA9f,IAAOqK,QAASrK,GAAO6E,OAAO0c,kBAKhC,KAAK,GAAIQ,KAASwhC,GAClB,CACE,GAAIC,GAAcD,EAAOxhC,EAEpBxhB,GAAUijD,KAEbA,GACEl7C,KAAMk7C,IAIVzvC,EAAG+E,UAAWiJ,GAAU0hC,GAAeD,EAAYl7C,MAAQyL,EAAIyvC,GAC/DzvC,EAAG4E,UAAWoJ,GAAUD,OAkB5B9hB,GAAOshB,kBAEPthB,GAAO6E,OAAO0c,kBAAoB,sBAClCvhB,GAAO6E,OAAO6+C,aAAe,iBAC7B1jD,GAAO6E,OAAO8+C,cAAgB,kBAC9B3jD,GAAO6E,OAAOgd,YAAc,eAM5B7hB,GAAO4jD,iBAAmB,SAAS3+C,EAAMuF,GAEvCxK,GAAOshB,eAAgBrc,GAASuF,GAGlCxK,GAAOiiB,gBAEL,mBAAoB,OAAQ,OAAQ,OAuJtC,IAAIwhC,KAEFI,KAAM,SAAS9vC,EAAIzI,GAEjB,MAAO8V,IAAY,aAAcf,GAAa/U,IAEhDw4C,QAAS,SAAS/vC,EAAIzI,GAEpB,MAAO8V,IAAY,gBAAiBf,GAAa/U,IAEnDy4C,OAAQ,SAAShwC,EAAIzI,GAEnB,MAAO8V,IAAY,gBAAiBd,GAAehV,IAErD04C,SAAU,SAASjwC,EAAIzI,GAErB,MAAO,UAASqG,EAAOrF,EAAOqS,GAE5B,GAAIiC,GAAOT,GAAQxO,GACfqP,EAAYhhB,GAAOshB,eAAgBhW,EAAQ0V,UAE/C,KAAMA,EAEJ,KAAM,wCAGR,KAAc,IAATJ,EACL,CACE,GAAK3d,EAAUqI,EAAQ24C,WAAchhD,EAAU2d,EAAKgiB,OAAUhiB,EAAKgiB,KAAOt3B,EAAQ24C,SAIhF,WAFAjkD,IAAOqK,QAASrK,GAAO6E,OAAO6+C,cAAe9iC,EAAMtU,EAAOqS,GAK5D,IAAKrd,EAASgK,EAAQm5B,QAAWlkC,EAAUqgB,EAAKtY,QAAkD,IAAxC5H,EAAS4K,EAAQm5B,MAAO7jB,EAAKtY,MAIrF,WAFAtI,IAAOqK,QAASrK,GAAO6E,OAAO8+C,eAAgB/iC,EAAMtU,EAAOqS,GAK7D,IAAIsC,GACAC,GAAO,CAiBX,OAfAF,GAAUkjC,YAAatjC,EAAMtU,EAAOqS,EAAU,SAASxN,GAErDwP,GAAcrU,EAAOqS,EAAUxN,EAAOyP,EAAMtV,GAE5C2V,EAASF,GAAeC,EAAW7P,EAAO7E,EAAOqS,EAAUrT,GAEtD4V,IAEH5U,EAAOqS,GAAasC,EACpBV,GAASjU,EAAOhB,MAIpB4V,GAAO,EAEAD,EAEJ,IAAKxe,EAAUkP,KAAWA,EAAMgQ,KAQnC,MAFAhB,IAAcrU,EAAOqS,EAAUhN,EAAO,KAAMrG,GAErCyV,GAAeC,EAAWrP,EAAOrF,EAAOqS,EAAUrT,EANzDtL,IAAOqK,QAASrK,GAAO6E,OAAOgd,aAAclQ,EAAOrF,EAAOqS,MAmDlEla,GAAU,SAAS6H,EAAOyH,EAAIzI,GAG5BgB,EAAMuuB,SAAW,SAAS6N,EAAiBC,EAAYC,GAErD,MAAO70B,GAAG+B,OAAO+kB,SAAU6N,EAAiBC,EAAYC,MAI5DnkC,EAAU,SAAS6H,EAAOyH,EAAIzI,GAE5BgB,EAAM9G,MAAQ8G,EAAM63C,KAAO,SAASzb,EAAiBC,EAAYC,GAE/D,MAAO70B,GAAG+B,OAAO8zB,WAAYlB,EAAiBC,EAAYC,MAI9DnkC,EAAU,SAAS6H,EAAOyH,EAAIzI,GAsC5BgB,EAAM83C,aAAe,SAAUzyC,EAAOoB,EAASzH,EAAS5G,EAAUhB,GAEhE,GAAIoF,GAAkBpF,GAAWzD,KAC7BqiD,EAAWvuC,EAAGjC,IAAKH,GACnB0yC,GAAU,CAuCd,OArCM/B,IA6BJA,EAASnsB,KAAMxkB,GAEVjN,GAEHA,EAASjD,KAAMqH,EAAiBw5C,EAAU+B,IA/B5CtwC,EAAG8hB,UAAWlkB,EAAO,SAAStE,GAEtBA,GAOJi1C,EAAWj1C,EACXi1C,EAASnsB,KAAMxkB,GAGT2wC,EAAS7hC,YAEb6hC,EAAS5hC,MAAO3N,EAASzH,KAX3Bg3C,EAAWh2C,EAAM3K,OAAQgQ,EAAOoB,EAASzH,GACzC+4C,GAAU,GAcP3/C,GAEHA,EAASjD,KAAMqH,EAAiBw5C,EAAU+B,KAczC/B,KAIX79C,EAAU,SAAS6H,EAAOyH,EAAIzI,GAmC5BgB,EAAMwF,IAAM,SAAUH,EAAOjN,EAAUhB,GAErC,IAAKf,EAAY+B,GAMf,MAAOqP,GAAGjC,IAAKH,EAJfoC,GAAG8hB,UAAWlkB,EAAOjN,EAAUhB,MASrCe,EAAU,SAAS6H,EAAOyH,EAAIzI,GAgC5BgB,EAAMa,KAAO,SAAUwE,EAAOrG,EAAS5G,EAAUhB,GAE/C,GAAIoF,GAAkBpF,GAAWzD,KAC7BqiD,EAAWvuC,EAAGjC,IAAKH,EAqBvB,OAnBK2wC,GAEH59C,EAASjD,KAAMqH,EAAiBw5C,GAIhCvuC,EAAG8hB,UAAWlkB,EAAO,SAAS2wC,GAEvBA,EAEH59C,EAASjD,KAAMqH,EAAiBw5C,GAIhCh2C,EAAM+2C,MAAO1xC,EAAOrG,EAAS5G,EAAUhB,KAKtC4+C,KAIX79C,EAAU,SAAS6H,EAAOyH,EAAIzI,GA2B5BgB,EAAMg4C,QAAU,SAAU5/C,EAAUhB,GAElC,GAAIoF,GAAkBpF,GAAWzD,KAC7B6V,EAAS/B,EAAG+B,MAwBhB,OAtBKA,GAAO7U,OAEVyD,EAASjD,KAAMqH,EAAiBgN,GAIhC/B,EAAGuhB,MAAM,WAEFxf,EAAO7U,OAEVyD,EAASjD,KAAMqH,EAAiBgN,GAIhC/B,EAAG2lB,QAAQ,WAETh1B,EAASjD,KAAMqH,EAAiBgN,OAMjCA,KAKXrR,EAAW,SAAS6H,EAAOyH,EAAIzI,GAG7BgB,EAAMwuB,SAAW,SAASC,EAAOn6B,GAE/B,MAAOmT,GAAG+mB,SAASC,EAAOn6B,IAG5B0L,EAAM0uB,UAAY,SAAS1H,EAAQ1yB,GAEjC,MAAOmT,GAAGinB,UAAU1H,EAAQ1yB,MAMhC6D,EAAW,SAAS6H,EAAOyH,EAAIzI,GAExBA,EAAQqX,YAEXI,MAIJ,IAAIN,IAAUhI,GAAIlZ,UAAU4wB,IACxBlP,GAAaxI,GAAIlZ,UAAU+H,MAmE/B7E,GAAU,SAAS6H,EAAOyH,EAAIzI,GAE5B,GAAId,GAAUqD,EAAUvC,EAAQd,QAAS+K,GAAS/K,QAE5CpG,GAASoG,IAEbtC,GAAMsC,QAAS8B,EAAO9B,KAI1B/F,EAAU,SAAS6H,EAAOyH,EAAIzI,GAgC5BgB,EAAMi4C,QAAU,SAAU5yC,EAAOoB,EAASzH,EAAS5G,EAAUhB,GAE3D,GAAIoF,GAAkBpF,GAAWzD,IAEjC,OAAOqM,GAAM83C,aAAczyC,EAAOoB,EAASzH,EAAS,SAASg3C,EAAU+B,GAE/DA,GAEJ/B,EAAS5hC,MAAO3N,EAASzH,GAGtB5G,GAEHA,EAASjD,KAAMqH,EAAiBw5C,QAMxC79C,EAAU,SAAS6H,EAAOyH,EAAIzI,GAG5BgB,EAAM43B,WAAa,SAASxC,GAE1B,MAAOnpB,IAAWlK,MAAO0F,EAAI2tB,MAKjCj9B,EAAU,SAAS6H,EAAOyH,EAAIzI,GAiC5BgB,EAAMgpB,MAAQ,SAAU5wB,EAAUhB,EAAS6xB,GAEzCxhB,EAAGuhB,MAAO5wB,EAAUhB,EAAS6xB,MAIjC9wB,EAAU,SAAS6H,EAAOyH,EAAIzI,GAyB5BgB,EAAMotB,QAAU,SAAUh1B,EAAUhB,GAElC,MAAOqQ,GAAG2lB,QAASh1B,EAAUhB,MAIjCe,EAAU,SAAS6H,EAAOyH,EAAIzI,GAG5BgB,EAAMgR,MAAQ,SAAS2I,EAAsBD,GAE3C,MAAOjS,GAAGuJ,MAAO2I,EAAsBD,MAK3CvhB,EAAU,SAAS6H,EAAOyH,EAAIzI,GA2B5BgB,EAAM0F,QAAU,SAAS0C,EAAK5J,EAAOQ,GAEnC,MAAO,IAAIoR,IAAQ3I,EAAIW,EAAKpJ,EAASR,GAAO,GAAOslC,YAIvD3rC,EAAU,SAAS6H,EAAOyH,EAAIzI,GAoC5BgB,EAAMksC,OAAS,SAAS9jC,EAAKpJ,EAASR,EAAO6R,GAE3C,MAAO,IAAID,IAAQ3I,EAAIW,EAAKpJ,EAASR,EAAO6R,MAIhDlY,EAAU,SAAS6H,EAAOyH,EAAIzI,GAG5BgB,EAAMk4C,SAAW,SAASjiC,EAAO7N,EAAK+vC,EAAQn5C,EAASR,EAAOyH,EAAS6B,GAErE,GAAIq0B,IAAQyI,WAAY3uB,EAAO0uB,UAAW,GAEtCuH,EAASiM,EACX,GAAI5nC,IAAa9I,EAAIW,EAAK7G,EAAUvC,EAASm9B,GAAQ39B,GACrD,GAAI4R,IAAQ3I,EAAIW,EAAKpJ,EAASR,GAE5B+G,EAAU,GAAI8E,GAiBlB,OAfA9E,GAAQU,QAASA,GACjBV,EAAQuC,QAASA,GAEjBokC,EAAOjI,OAAOkD,KACZ,SAAmB+E,EAAQh/B,EAAUxH,GACnCH,EAAQa,QAASV,EAASyyC,EAAS,EAAIliC,KAEzC,WACE1Q,EAAQyI,UAEV,WACEzI,EAAQ0I,WAIL1I,KAKXpN,EAAU,SAAS6H,EAAOyH,EAAIzI,GA4C5BgB,EAAMo4C,YAAc,SAAShwC,EAAKpJ,EAASR,EAAO6R,GAEhD,MAAO,IAAIE,IAAa9I,EAAIW,EAAKpJ,EAASR,EAAO6R,MAIrDlY,EAAU,SAAS6G,GAEjB,GAAI80C,GAAQ90C,EAAQ80C,OAAS7qC,GAAS6qC,KAEhC39C,GAAU29C,KAKhB90C,EAAQwL,WAAa9W,GAAOogD,MAAOA,MAElC,GAEH37C,EAAU,SAAS6H,EAAOyH,EAAIzI,GAE5B,GAAIq5C,GAAgB92C,EAAUvC,EAAQq5C,cAAepvC,GAASovC,cAExDvgD,GAASugD,IAEbz8C,GAAM4C,MAAOwB,EAAOq4C,KAIxBlgD,EAAU,SAAS6H,EAAOyH,EAAIzI,GAa5B,QAASs5C,GAAel5C,EAAQD,GAE9B,MAAKhJ,GAAUiJ,IAAYjJ,EAAUgJ,GAE5BoC,EAAUnC,EAAQD,GAGpBC,GAAUD,EAGnB,QAASo5C,GAAW9iC,GAElB,OAAuB,IAAhB+iC,IAA0D,IAAlCpkD,EAASokD,EAAa/iC,GAGvD,QAASgjC,GAAchjC,EAAOlH,GAE5B,MAAOpY,GAAUoY,GAAQA,EAAKkH,GAAUlH,EAG1C,QAASmqC,GAAiBjjC,GAExB,GAAInU,GAAKm3C,EAAehjC,EAAOkjC,EAE/B,OAAO,YAEL,MAAO/hC,IAAa,GAAI7f,MAAQuK,IAIpC,QAAS+mB,GAAOv0B,EAAGkM,EAAOyV,EAAOrJ,GAK/B,MAFcwK,IAAa9iB,EADlB2kD,EAAehjC,EAAOmjC,KAGb9kD,EAGpB,QAASw0B,GAAOx0B,EAAGyY,EAASkJ,GAM1B,MAFcmB,IAAa9iB,EAFlB2kD,EAAehjC,EAAOkjC,GACrBF,EAAehjC,EAAOojC,KAGd/kD,EAGpB,QAASglD,GAAarjC,IAIT,IAFHrhB,EAASqT,EAAGxH,OAAQwV,KAI1BhO,EAAGxH,OAAOnD,KAAM2Y,GAChBhO,EAAG0C,WAAWrN,KAAM2Y,KAGjB8iC,EAAY9iC,IAAaA,IAAShO,GAAGxI,WAExCwI,EAAGxI,SAAUwW,GAAUijC,EAAkBjjC,KAGtCmjC,GAAgBnjC,IAAShO,GAAG4E,YAE/B5E,EAAG4E,UAAWoJ,GAAU4S,IAGrBswB,GAAcljC,IAAShO,GAAG+E,YAE7B/E,EAAG+E,UAAWiJ,GAAU6S,GAI5B,QAASywB,GAAatjC,GAEpBqjC,EAAcrjC,GAEdhO,EAAGof,cAAepR,IAAU,EAG9B,QAASujC,GAAavjC,GAEpBqjC,EAAcrjC,GAEdhO,EAAGof,cAAepR,IAAU,EAE5B7Z,GAAMsI,QAASlE,EAAO,QAAS,SAASoU,GAEtC,MAAO,YAIL,MAFAzgB,MAAM8hB,GAAUzd,EAAUyP,EAAGxI,SAAUwW,IAEhCrB,EAAM7d,MAAO5C,KAAMoB,cAKhC,QAASkkD,GAAkBj9C,EAAMyZ,GAE/B,OAAQzZ,GACN,IAAK,aACH,MAAO+8C,GAActjC,EACvB,KAAK,aACH,MAAOujC,GAAcvjC,EACvB,SACE,MAAOqjC,GAAcrjC,IApH3B,GAAIyjC,GAAOl6C,EAAQm6C,YAAclwC,GAASkwC,WACtCP,EAAaN,EAAgBt5C,EAAQo6C,gBAAiBnwC,GAASmwC,iBAC/DT,EAAWL,EAAgBt5C,EAAQq6C,cAAepwC,GAASowC,eAC3DR,EAAUP,EAAgBt5C,EAAQs6C,aAAcrwC,GAASqwC,cACzDd,EAAcx5C,EAAQu6C,kBAAoBtwC,GAASswC,gBAEvD,IAAML,EAkHN,GAAKjlD,EAAUilD,GAEbD,EAAmBC,EAAMA,OAEtB,IAAKlkD,EAASkkD,GAEjB,IAAK,GAAIzkD,GAAI,EAAGA,EAAIykD,EAAKvkD,OAAQF,IAE/BwkD,EAAmBC,EAAMzkD,GAAKykD,EAAMzkD,QAGnC,IAAK0B,EAAU+iD,GAElB,IAAK,GAAIrhD,KAAQqhD,GAEfD,EAAmBphD,EAAMqhD,EAAMrhD,QAKjCkhD,GAAc,cACdC,EAAc,eAKlB,IAAIliC,KACF/f,KAAM,OACNggB,OAAQ,SACRC,QAAS,UAGX/N,IAASmwC,gBAAkBtiC,GAAUC,OACrC9N,GAASowC,cAAgBviC,GAAU/f,KACnCkS,GAASqwC,cAAe,EACxBrwC,GAASswC,kBAAoB,aAAc,cA6B3C7lD,GAAOojB,UAAYA,GACnBpjB,GAAOujB,WAAa/f,EACpBxD,GAAOkjB,YAAcA,EAErB,IAAI4iC,KAAkBxyB,QAAQ,EA4O5B,OA1OF7uB,GAAU,SAAS6G,GAEjB,GAAIgoB,GAAShoB,EAAQgoB,QAAU/d,GAAS+d,MAExC,KAAMlvB,EAASkvB,GACf,CAME,GALK3wB,EAAY2wB,KAEfA,EAASA,EAAQhoB,KAGdhK,EAASgyB,GAuBZ,KAAM,yEArBN,KAAK,GAAIvyB,GAAI,EAAGA,EAAIuyB,EAAOryB,OAAQF,IACnC,CACE,GAAIg6B,GAAQzH,EAAQvyB,EAOpB,IALK4B,EAAYo4B,KAEfA,EAAQA,EAAOzvB,KAGZ7I,EAAUs4B,GAMb,KAAM,sFAJNhwB,GAAOO,EAASyvB,EAAO+qB,QAc9B,GAEHrhD,EAAU,SAAS6H,EAAOyH,EAAIzI,GAG5BgB,EAAM0E,MAAQ,SAAS03B,EAAiBC,EAAYC,EAAarpB,GAE/D,MAAOxL,GAAG+B,OAAO9E,MAAM03B,EAAiBC,EAAYC,EAAarpB,MAMnEvf,GAAOgD,MAAQA,GACfhD,GAAO+C,SAAWA,GAClB/C,GAAOuV,SAAWA,GAClBvV,GAAO8X,SAAWA,GAClB9X,GAAOod,UAAYA,GACnBpd,GAAO0c,OAASA,GAChB1c,GAAO6c,YAAcA,GACrB7c,GAAO2W,QAAUA,GACjB3W,GAAO4a,WAAaA,GACpB5a,GAAOwe,MAAQA,GAGfxe,GAAOmb,WAAaA,GACpBnb,GAAO2V,UAAYA,GACnB3V,GAAO0V,aAAeA,GACtB1V,GAAO+iB,iBAAmBA,GAC1B/iB,GAAOgjB,kBAAoBA,GAG3BhjB,GAAOga,QAAUA,GACjBha,GAAOymB,MAAQA,GACfzmB,GAAO6nB,MAAQA,GACf7nB,GAAO0nB,KAAOA,GACd1nB,GAAOgnB,KAAOA,GACdhnB,GAAOmnB,WAAaA,GAGpBnnB,GAAOya,IAAMA,GACbza,GAAO0B,WAAaA,GACpB1B,GAAO6b,mBAAqBA,GAC5B7b,GAAO+V,gBAAkBA,GACzB/V,GAAOgc,wBAA0BA,GACjChc,GAAOic,mBAAqBA,GAC5Bjc,GAAOqb,KAAOA,GACdrb,GAAO+a,QAAUA,GAGjB/a,GAAOke,OAASA,GAChBle,GAAOie,UAAYA,GACnBje,GAAOme,QAAUA,GACjBne,GAAOoe,eAAiBA,GACxBpe,GAAOqe,UAAYA,GACnBre,GAAOse,QAAUA,GACjBte,GAAOue,aAAeA,GACtBve,GAAOge,iBAAmBA,GAC1Bhe,GAAO+d,eAAiBA,GAGxB/d,GAAOqd,SAAWA,GAClBrd,GAAOud,UAAYA,GACnBvd,GAAOwd,YAAcA,GACrBxd,GAAOyd,YAAcA,GACrBzd,GAAO0d,UAAYA,GACnB1d,GAAO2d,aAAeA,GACtB3d,GAAO4d,UAAYA,GACnB5d,GAAO6d,QAAUA,GACjB7d,GAAO8d,WAAaA,GAGpB9d,GAAOimC,WACPjmC,GAAOuY,WAAaA,GAGpBvY,GAAO8C,SAAWA,EAClB9C,GAAO0C,UAAYA,EACnB1C,GAAO2C,WAAaA,EACpB3C,GAAOO,SAAWA,EAClBP,GAAOiD,SAAWA,EAClBjD,GAAOmD,UAAYA,EACnBnD,GAAOoD,OAASA,EAChBpD,GAAOsD,SAAWA,EAClBtD,GAAOsB,QAAUA,EACjBtB,GAAOyC,SAAWA,EAClBzC,GAAOS,QAAUA,EACjBT,GAAOwD,KAAOA,EACdxD,GAAOyD,KAAOA,EACdzD,GAAO4D,KAAOA,EACd5D,GAAOiE,OAASA,EAChBjE,GAAOoE,QAAUA,EACjBpE,GAAOsE,SAAWA,EAClBtE,GAAOyE,UAAYA,EACnBzE,GAAOglB,IAAMA,GACbhlB,GAAO+K,MAAQA,EAGf/K,GAAOkV,KAAOA,GACdlV,GAAOmc,uBAAyBA,GAGhCnc,GAAOisC,UAAYA,GACnBjsC,GAAOy3C,YAAcA,GAGrBz3C,GAAOG,QAAUA,EACjBH,GAAOU,QAAUA,EACjBV,GAAOkB,QAAUA,EACjBlB,GAAOsC,MAAQV,EACf5B,GAAO8B,KAAOA,EACd9B,GAAOiC,QAAUA,EACjBjC,GAAOqC,SAAWA,EAClBrC,GAAOuC,iBAAmBA,EAG1BvC,GAAOkkB,SAAWA,GAClBlkB,GAAOkI,MAAQA,GACflI,GAAO2jB,OAASzb,GAAMyb,OACtB3jB,GAAO+lD,YAAc79C,GAAMyb,OAC3B3jB,GAAOgmD,UAAYhmD,GAAOg5C,YAAc9wC,GAAM/D,KAC9CnE,GAAOimD,WAAajmD,GAAOu/B,cAAgBr3B,GAAM4C,MACjD9K,GAAOkmD,cAAgBh+C,GAAMsI,QAC7BxQ,GAAO+jB,gBAAkB7b,GAAM6b,gBAC/B/jB,GAAON,QAAUwI,GAAMxI,QAGvBM,GAAOqF,YAAcA,GACrBrF,GAAOgF,eAAiBA,EACxBhF,GAAOsF,cAAgBA,EACvBtF,GAAOoF,iBAAmBA,EAG1BpF,GAAOc,aAAeA,EACtBd,GAAOwG,WAAaA,EACpBxG,GAAOyG,cAAgBA,EACvBzG,GAAO0G,OAASA,EAChB1G,GAAOmH,eAAiBA,EACxBnH,GAAOsG,QAAUA,EAGjBtG,GAAOsH,iBAAmBA,EAC1BtH,GAAO0I,YAAcA,EAGrB1I,GAAOqL,aAAeA,EACtBrL,GAAO8L,WAAaA,EACpB9L,GAAOqM,UAAYA,EACnBrM,GAAO6M,0BAA4BA,EACnC7M,GAAOyM,yBAA2BA,EAClCzM,GAAOmN,KAAOA,EACdnN,GAAOuN,KAAOA,EACdvN,GAAO0N,SAAWA,EAClB1N,GAAO6N,SAAWA,EAClB7N,GAAO8N,MAAQA,EACf9N,GAAO+N,eAAiBA,EACxB/N,GAAOwE,KAAOA,EACdxE,GAAOkO,KAAOA,GAGdlO,GAAOmG,aAAeA,GACtBnG,GAAOqO,MAAQA,GACfrO,GAAOqG,aAAeA,GACtBrG,GAAO6F,cAAgBA,GACvB7F,GAAO4O,OAASA,GAChB5O,GAAO+F,gBAAkBA,GACzB/F,GAAOgP,UAAYA,GAGnBhP,GAAO6P,gBAAkBA,GACzB7P,GAAOwP,mBAAqBA,GAC5BxP,GAAO4P,qBAAuBA,GAC9B5P,GAAOiQ,kBAAoBA,GAC3BjQ,GAAOgQ,qBAAuBA,GAC9BhQ,GAAO8P,uBAAyBA,GAGhC9P,GAAOuQ,YAAcA,GACrBvQ,GAAOQ,MAAQA,GAGfR,GAAOkR,OAASA,GAChBlR,GAAO+Q,UAAYA,GACnB/Q,GAAOiR,YAAcA,GACrBjR,GAAOsO,KAAOA,GACdtO,GAAOyR,IAAMA,GACbzR,GAAO0R,MAAQA,GACf1R,GAAOwR,OAASA,GAChBxR,GAAOqR,iBAAmBA,GAC1BrR,GAAOuR,WAAaA,GAGpBvR,GAAOkyB,OAASA,GAChBlyB,GAAO8wB,MAAQA,GACf9wB,GAAO+xB,MAAQA,GAER/xB","file":"rekord.min.js","sourcesContent":["/* rekord 1.5.11 - 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 // Load by priority defined in Database\n loading.sort(function(a, b)\n {\n return b.priority - a.priority;\n });\n\n // Begin the loading procedure for every unloaded Database\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 priority: 0,\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 existing = db.all[ key ];\n var model = existing || db.instantiate( decoded, true );\n\n if (existing)\n {\n model.$set( decoded, undefined, true );\n }\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 this.$initRelations( remoteData );\n },\n\n $initRelations: function(remoteData)\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 hasDiscriminator: false,\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, skipInitial)\n {\n\n }),\n\n setInitial: 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, relation)\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, relation)\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 hasDiscriminator: true,\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, relation );\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, relation );\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, remoteData, relation );\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, false, relation );\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, relation );\n\n if ( related )\n {\n given.add( related );\n }\n }\n }\n else\n {\n var related = this.parseModel( input, remoteData, relation );\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, relation );\n\n if ( related )\n {\n this.addModel( relation, related, remoteData );\n }\n }\n }, remoteData );\n }\n else if ( isValue( input ) )\n {\n var related = this.parseModel( input, remoteData, relation );\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 ], remoteData, relation );\n\n if ( related )\n {\n this.removeModel( relation, related, remoteData );\n }\n }\n }, remoteData );\n }\n else if ( isValue( input ) )\n {\n var related = this.parseModel( input, remoteData, relation );\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 }, remoteData );\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 ], false, relation );\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, false, relation );\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 this.setInitial( model, initialValue, remoteData );\n }),\n\n setInitial: function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ];\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, relation );\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 this.setInitial( model, initialValue, remoteData );\n }),\n\n setInitial: function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ];\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, relation );\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, relation );\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, relation ) )\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 this.setInitial( model, initialValue, remoteData );\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n setInitial: function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ];\n\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\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, relation ) ) )\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, relation ) )\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 this.setInitial( model, initialValue, remoteData );\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n setInitial: function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ];\n var throughDatabase = this.through.Database;\n\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\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, relation ) ) )\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, relation ) ) )\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, relation ) )\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, relation ) ) )\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 this.setInitial( model, initialValue, remoteData );\n\n // We only need to set the property once since the underlying array won't change.\n this.setProperty( relation );\n }),\n\n setInitial: function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ];\n\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\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 this.setInitial( model, initialValue, remoteData );\n }),\n\n setInitial: function(model, initialValue, remoteData)\n {\n var relation = model.$relations[ this.name ];\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, relation );\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 discriminatorField = this.discriminator;\n\n if (this.hasDiscriminator)\n {\n return function(related)\n {\n if ( !isRelated( related ) )\n {\n return false;\n }\n\n var discriminator = this.getDiscriminatorForModel( related );\n\n return equals( discriminator, model[ discriminatorField ] );\n };\n }\n else\n {\n var discriminator = this.getDiscriminatorForModel( model );\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\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, relation)\n {\n if ( input instanceof Model )\n {\n return input;\n }\n else if ( isObject( input ) )\n {\n var db = this.hasDiscriminator ?\n this.getDiscriminatorDatabase( relation.parent ) :\n 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 if ( this.hasDiscriminator && hasFields( model, fields, isValue ) )\n {\n var related = this.getDiscriminatorDatabase( model );\n\n if ( related )\n {\n var initial = {};\n\n updateFieldsReturnChanges( initial, related.key, model, fields );\n\n return initial;\n }\n }\n },\n\n grabModel: function(input, callback, remoteData, relation)\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.hasDiscriminator ?\n this.getDiscriminatorDatabase( relation.parent ) :\n 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"]} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..1b272ef --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5831 @@ +{ + "name": "rekord", + "version": "1.5.11", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/parser": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", + "dev": true + }, + "@gulp-sourcemaps/map-sources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", + "integrity": "sha1-iQrnxdjId/bThIYCFazp1+yUW9o=", + "dev": true, + "requires": { + "normalize-path": "2.1.1", + "through2": "2.0.5" + } + }, + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + }, + "ajv": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "dev": true, + "requires": { + "fast-deep-equal": "3.1.1", + "fast-json-stable-stringify": "2.1.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" + } + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", + "dev": true + }, + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "requires": { + "micromatch": "2.3.11", + "normalize-path": "2.1.1" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.3" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "0.1.1" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" + } + } + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "1.0.3" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.3.0", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.2", + "pascalcase": "0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.3" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.3" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.3" + } + } + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "dev": true + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.3", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "1.0.0", + "component-emitter": "1.3.0", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.1", + "to-object-path": "0.3.0", + "union-value": "1.0.1", + "unset-value": "1.0.0" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "2.1.1", + "map-obj": "1.0.1" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "catharsis": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", + "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", + "dev": true, + "requires": { + "lodash": "4.17.15" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "0.1.4", + "lazy-cache": "1.0.4" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true, + "requires": { + "anymatch": "1.3.2", + "async-each": "1.0.3", + "fsevents": "1.2.12", + "glob-parent": "2.0.0", + "inherits": "2.0.4", + "is-binary-path": "1.0.1", + "is-glob": "2.0.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.2.1" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "2.0.1" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + } + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + } + } + }, + "cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "7.1.6" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.4", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + } + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "requires": { + "inherits": "2.0.4", + "process-nextick-args": "2.0.1", + "readable-stream": "2.3.7" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + } + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "1.0.0", + "object-visit": "1.0.1" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "1.1.1", + "inherits": "2.0.4", + "readable-stream": "2.3.7", + "typedarray": "0.0.6" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + } + } + }, + "concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "dev": true, + "requires": { + "source-map": "0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "0.1.4" + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "2.0.4", + "source-map": "0.6.1", + "source-map-resolve": "0.5.3", + "urix": "0.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "1.0.2" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "debug-fabulous": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-0.0.4.tgz", + "integrity": "sha1-+gccXYdIRoVCSAdCHKSxawsaB2M=", + "dev": true, + "requires": { + "debug": "2.6.9", + "lazy-debug-legacy": "0.0.1", + "object-assign": "4.1.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", + "dev": true + } + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "1.0.4" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "1.0.2", + "isobject": "3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.3" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.3" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.3" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "deprecated": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", + "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=", + "dev": true + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "2.0.1", + "entities": "2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "dev": true, + "requires": { + "domelementtype": "1.3.1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0.2.2", + "domelementtype": "1.3.1" + } + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "dev": true, + "requires": { + "readable-stream": "1.1.14" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" + } + }, + "end-of-stream": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", + "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", + "dev": true, + "requires": { + "once": "1.3.3" + } + }, + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "0.2.1" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "2.2.4" + }, + "dependencies": { + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "requires": { + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "3.1.1", + "repeat-element": "1.1.3", + "repeat-string": "1.6.1" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "1.0.3" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.3" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.3" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.3" + } + } + } + }, + "extract-zip": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", + "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", + "dev": true, + "requires": { + "concat-stream": "1.6.2", + "debug": "2.6.9", + "mkdirp": "0.5.5", + "yauzl": "2.10.0" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "requires": { + "ansi-gray": "0.1.1", + "color-support": "1.1.3", + "parse-node-version": "1.0.1", + "time-stamp": "1.1.0" + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "1.2.0" + } + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "find-index": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", + "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=", + "dev": true + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "1.0.0", + "is-glob": "3.1.0", + "micromatch": "3.1.10", + "resolve-dir": "1.0.1" + } + }, + "fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "dev": true, + "requires": { + "expand-tilde": "2.0.2", + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0", + "object.pick": "1.3.0", + "parse-filepath": "1.0.2" + } + }, + "first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=", + "dev": true + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.8", + "mime-types": "2.1.26" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "0.2.2" + } + }, + "fs-extra": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", + "dev": true, + "requires": { + "graceful-fs": "4.2.3", + "jsonfile": "2.4.0", + "klaw": "1.3.1" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz", + "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==", + "dev": true, + "optional": true, + "requires": { + "bindings": "1.5.0", + "nan": "2.14.0", + "node-pre-gyp": "0.14.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.7" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "3.2.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.9.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.3" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.4", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": "2.1.2" + } + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "1.2.5", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.9.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.1.2", + "yallist": "3.1.1" + } + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "2.9.0" + } + }, + "mkdirp": { + "version": "0.5.3", + "bundled": true, + "dev": true, + "requires": { + "minimist": "1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "3.2.6", + "iconv-lite": "0.4.24", + "sax": "1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.14.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "1.0.3", + "mkdirp": "0.5.3", + "needle": "2.3.3", + "nopt": "4.0.3", + "npm-packlist": "1.4.8", + "npmlog": "4.1.2", + "rc": "1.2.8", + "rimraf": "2.7.1", + "semver": "5.7.1", + "tar": "4.4.13" + } + }, + "nopt": { + "version": "4.0.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.5" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "npm-packlist": { + "version": "1.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "3.0.3", + "npm-bundled": "1.1.1", + "npm-normalize-package-bin": "1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "1.1.5", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "0.6.0", + "ini": "1.3.5", + "minimist": "1.2.5", + "strip-json-comments": "2.0.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "7.1.6" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "1.1.4", + "fs-minipass": "1.2.7", + "minipass": "2.9.0", + "minizlib": "1.3.3", + "mkdirp": "0.5.3", + "safe-buffer": "5.1.2", + "yallist": "3.1.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.1.1", + "bundled": true, + "dev": true + } + } + }, + "gaze": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", + "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", + "dev": true, + "requires": { + "globule": "0.1.0" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "glob": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", + "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.4", + "minimatch": "2.0.10", + "once": "1.3.3" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "2.0.0", + "is-glob": "2.0.1" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "2.0.1" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + } + }, + "glob-stream": { + "version": "3.1.18", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", + "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", + "dev": true, + "requires": { + "glob": "4.5.3", + "glob2base": "0.0.12", + "minimatch": "2.0.10", + "ordered-read-streams": "0.1.0", + "through2": "0.6.5", + "unique-stream": "1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.2" + } + } + } + }, + "glob-watcher": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", + "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", + "dev": true, + "requires": { + "gaze": "0.5.2" + } + }, + "glob2base": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", + "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", + "dev": true, + "requires": { + "find-index": "0.1.1" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "1.0.2", + "is-windows": "1.0.2", + "resolve-dir": "1.0.1" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "2.0.2", + "homedir-polyfill": "1.0.3", + "ini": "1.3.5", + "is-windows": "1.0.2", + "which": "1.3.1" + } + }, + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", + "dev": true, + "requires": { + "glob": "3.1.21", + "lodash": "1.0.2", + "minimatch": "0.2.14" + }, + "dependencies": { + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", + "dev": true, + "requires": { + "graceful-fs": "1.2.3", + "inherits": "1.0.2", + "minimatch": "0.2.14" + } + }, + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=", + "dev": true + }, + "inherits": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=", + "dev": true + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "dev": true, + "requires": { + "lru-cache": "2.7.3", + "sigmund": "1.0.1" + } + } + } + }, + "glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "dev": true, + "requires": { + "sparkles": "1.0.1" + } + }, + "graceful-fs": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.12.tgz", + "integrity": "sha512-J55gaCS4iTTJfTXIxSVw3EMQckcqkpdRv3IR7gu6sq0+tbC363Zx6KH/SEwXASK9JRbhyZmVjJEVJIOxYsB3Qg==", + "dev": true, + "requires": { + "natives": "1.1.6" + } + }, + "gulp": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", + "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", + "dev": true, + "requires": { + "archy": "1.0.0", + "chalk": "1.1.3", + "deprecated": "0.0.1", + "gulp-util": "3.0.8", + "interpret": "1.2.0", + "liftoff": "2.5.0", + "minimist": "1.2.5", + "orchestrator": "0.3.8", + "pretty-hrtime": "1.0.3", + "semver": "4.3.6", + "tildify": "1.2.0", + "v8flags": "2.1.1", + "vinyl-fs": "0.3.14" + } + }, + "gulp-check-filesize": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gulp-check-filesize/-/gulp-check-filesize-2.0.1.tgz", + "integrity": "sha1-s0BsBpZR2RzZUecApYZoc08VcOA=", + "dev": true, + "requires": { + "filesize": "3.6.1", + "gulp-util": "2.2.20", + "map-stream": "0.0.5", + "through2": "2.0.5" + }, + "dependencies": { + "ansi-regex": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-0.2.1.tgz", + "integrity": "sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk=", + "dev": true + }, + "ansi-styles": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.1.0.tgz", + "integrity": "sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94=", + "dev": true + }, + "chalk": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.5.1.tgz", + "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", + "dev": true, + "requires": { + "ansi-styles": "1.1.0", + "escape-string-regexp": "1.0.5", + "has-ansi": "0.1.0", + "strip-ansi": "0.3.0", + "supports-color": "0.2.0" + } + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } + }, + "gulp-util": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", + "integrity": "sha1-1xRuVyiRC9jwR6awseVJvCLb1kw=", + "dev": true, + "requires": { + "chalk": "0.5.1", + "dateformat": "1.0.12", + "lodash._reinterpolate": "2.4.1", + "lodash.template": "2.4.1", + "minimist": "0.2.1", + "multipipe": "0.1.2", + "through2": "0.5.1", + "vinyl": "0.2.3" + }, + "dependencies": { + "through2": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "3.0.0" + } + } + } + }, + "has-ansi": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-0.1.0.tgz", + "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", + "dev": true, + "requires": { + "ansi-regex": "0.2.1" + } + }, + "lodash._reinterpolate": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz", + "integrity": "sha1-TxInqlqHEfxjL1sHofRgequLMiI=", + "dev": true + }, + "lodash.escape": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-2.4.1.tgz", + "integrity": "sha1-LOEsXghNsKV92l5dHu659dF1o7Q=", + "dev": true, + "requires": { + "lodash._escapehtmlchar": "2.4.1", + "lodash._reunescapedhtml": "2.4.1", + "lodash.keys": "2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" + } + }, + "lodash.template": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-2.4.1.tgz", + "integrity": "sha1-nmEQB+32KRKal0qzxIuBez4c8g0=", + "dev": true, + "requires": { + "lodash._escapestringchar": "2.4.1", + "lodash._reinterpolate": "2.4.1", + "lodash.defaults": "2.4.1", + "lodash.escape": "2.4.1", + "lodash.keys": "2.4.1", + "lodash.templatesettings": "2.4.1", + "lodash.values": "2.4.1" + } + }, + "lodash.templatesettings": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz", + "integrity": "sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk=", + "dev": true, + "requires": { + "lodash._reinterpolate": "2.4.1", + "lodash.escape": "2.4.1" + } + }, + "minimist": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.1.tgz", + "integrity": "sha512-GY8fANSrTMfBVfInqJAY41QkOM+upUTytK1jZ0c8+3HdHrJxBJ3rF5i9moClXTE8uUSnUo8cAsCoxDXvSY4DHg==", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "strip-ansi": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.3.0.tgz", + "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", + "dev": true, + "requires": { + "ansi-regex": "0.2.1" + } + }, + "supports-color": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-0.2.0.tgz", + "integrity": "sha1-2S3iaU6z9nMjlz1649i1W0wiGQo=", + "dev": true + }, + "vinyl": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.2.3.tgz", + "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", + "dev": true, + "requires": { + "clone-stats": "0.0.1" + } + }, + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", + "dev": true + } + } + }, + "gulp-concat": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", + "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", + "dev": true, + "requires": { + "concat-with-sourcemaps": "1.1.0", + "through2": "2.0.5", + "vinyl": "2.2.0" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "dev": true, + "requires": { + "clone": "2.1.2", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.1.3", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" + } + } + } + }, + "gulp-insert": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/gulp-insert/-/gulp-insert-0.5.0.tgz", + "integrity": "sha1-MjE/E+SiPPWsylzl8MCAkjx3hgI=", + "dev": true, + "requires": { + "readable-stream": "1.1.14", + "streamqueue": "0.0.6" + } + }, + "gulp-jshint": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gulp-jshint/-/gulp-jshint-2.1.0.tgz", + "integrity": "sha512-sP3NK8Y/1e58O0PH9t6s7DAr/lKDSUbIY207oWSeufM6/VclB7jJrIBcPCsyhrFTCDUl9DauePbt6VqP2vPM5w==", + "dev": true, + "requires": { + "lodash": "4.17.15", + "minimatch": "3.0.4", + "plugin-error": "0.1.2", + "rcloader": "0.2.2", + "through2": "2.0.5" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + } + } + }, + "gulp-load-plugins": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/gulp-load-plugins/-/gulp-load-plugins-1.6.0.tgz", + "integrity": "sha512-HlCODki0WHJvQIgAsJYOTkyo0c7TsDCetvfhrdGz9JYPL6A4mFRMGmKfoi6JmXjA/vvzg+fkT91c9FBh7rnkyg==", + "dev": true, + "requires": { + "array-unique": "0.2.1", + "fancy-log": "1.3.3", + "findup-sync": "3.0.0", + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "micromatch": "3.1.10", + "resolve": "1.15.1" + }, + "dependencies": { + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "1.0.0", + "is-glob": "4.0.1", + "micromatch": "3.1.10", + "resolve-dir": "1.0.1" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + } + } + }, + "gulp-qunit": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/gulp-qunit/-/gulp-qunit-1.5.2.tgz", + "integrity": "sha512-Cj619t43lNMwX40dwgYn5wX+Wx2Jh3iQk0uIwlhPcs+zaQuNDHEhNVWYO6Bz2sSkQCeVjRq8aOG6z/dHfPqBSQ==", + "dev": true, + "requires": { + "chalk": "2.4.2", + "gulp-util": "3.0.8", + "phantomjs-prebuilt": "2.1.16", + "qunit-phantomjs-runner": "2.4.1", + "through2": "0.6.5" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "1.9.3" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" + } + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.2" + } + } + } + }, + "gulp-rename": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.4.0.tgz", + "integrity": "sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==", + "dev": true + }, + "gulp-shell": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/gulp-shell/-/gulp-shell-0.5.2.tgz", + "integrity": "sha1-pJWcoGUa0ce7/nCy0K27tOGuqY0=", + "dev": true, + "requires": { + "async": "1.5.2", + "gulp-util": "3.0.8", + "lodash": "4.17.15", + "through2": "2.0.5" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, + "gulp-sourcemaps": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-1.12.1.tgz", + "integrity": "sha1-tDfR89mAzyboEYSCNxjOFa5ll7Y=", + "dev": true, + "requires": { + "@gulp-sourcemaps/map-sources": "1.0.0", + "acorn": "4.0.13", + "convert-source-map": "1.7.0", + "css": "2.2.4", + "debug-fabulous": "0.0.4", + "detect-newline": "2.1.0", + "graceful-fs": "4.2.3", + "source-map": "0.6.1", + "strip-bom": "2.0.0", + "through2": "2.0.5", + "vinyl": "1.2.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "1.0.4", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "gulp-uglify": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/gulp-uglify/-/gulp-uglify-2.1.2.tgz", + "integrity": "sha1-bbhbHQ7mPRgFhZK2WGSdZcLsRUE=", + "dev": true, + "requires": { + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash": "4.17.15", + "make-error-cause": "1.2.2", + "through2": "2.0.5", + "uglify-js": "2.8.29", + "uglify-save-license": "0.4.1", + "vinyl-sourcemaps-apply": "0.2.1" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "dev": true, + "requires": { + "array-differ": "1.0.0", + "array-uniq": "1.0.3", + "beeper": "1.1.1", + "chalk": "1.1.3", + "dateformat": "2.2.0", + "fancy-log": "1.3.3", + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash._reescape": "3.0.0", + "lodash._reevaluate": "3.0.0", + "lodash._reinterpolate": "3.0.0", + "lodash.template": "3.6.2", + "minimist": "1.2.5", + "multipipe": "0.1.2", + "object-assign": "3.0.0", + "replace-ext": "0.0.1", + "through2": "2.0.5", + "vinyl": "0.5.3" + } + }, + "gulp-watch": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/gulp-watch/-/gulp-watch-4.3.11.tgz", + "integrity": "sha1-Fi/FY96fx3DpH5p845VVE6mhGMA=", + "dev": true, + "requires": { + "anymatch": "1.3.2", + "chokidar": "1.7.0", + "glob-parent": "3.1.0", + "gulp-util": "3.0.8", + "object-assign": "4.1.1", + "path-is-absolute": "1.0.1", + "readable-stream": "2.3.7", + "slash": "1.0.0", + "vinyl": "1.2.0", + "vinyl-file": "2.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "1.0.4", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "requires": { + "glogg": "1.0.2" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "6.12.0", + "har-schema": "2.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "dev": true, + "requires": { + "sparkles": "1.0.1" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "kind-of": "4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "hasha": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz", + "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", + "dev": true, + "requires": { + "is-stream": "1.1.0", + "pinkie-promise": "2.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "htmlparser2": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "dev": true, + "requires": { + "domelementtype": "1.3.1", + "domhandler": "2.3.0", + "domutils": "1.5.1", + "entities": "1.0.0", + "readable-stream": "1.1.14" + }, + "dependencies": { + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", + "dev": true + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.16.1" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "2.0.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.3.3", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "requires": { + "is-relative": "1.0.0", + "is-windows": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "1.13.1" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "3.0.1" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "requires": { + "is-unc-path": "1.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "requires": { + "unc-path-regex": "0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "jaguarjs-jsdoc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/jaguarjs-jsdoc/-/jaguarjs-jsdoc-1.0.2.tgz", + "integrity": "sha1-v9YDX2mgWiTCJvP1qfHCD+6rKbk=", + "dev": true + }, + "js2xmlparser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.1.tgz", + "integrity": "sha512-KrPTolcw6RocpYjdC7pL7v62e55q7qOMHvLX1UCLc5AAS8qeJ6nukarEJAF2KL2PZxlbGueEbINqZR2bDe/gUw==", + "dev": true, + "requires": { + "xmlcreate": "2.0.3" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdoc": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.4.tgz", + "integrity": "sha512-3G9d37VHv7MFdheviDCjUfQoIjdv4TC5zTTf5G9VODLtOnVS6La1eoYBDlbWfsRT3/Xo+j2MIqki2EV12BZfwA==", + "dev": true, + "requires": { + "@babel/parser": "7.9.4", + "bluebird": "3.7.2", + "catharsis": "0.8.11", + "escape-string-regexp": "2.0.0", + "js2xmlparser": "4.0.1", + "klaw": "3.0.0", + "markdown-it": "10.0.0", + "markdown-it-anchor": "5.2.7", + "marked": "0.8.2", + "mkdirp": "1.0.4", + "requizzle": "0.2.3", + "strip-json-comments": "3.1.0", + "taffydb": "2.6.2", + "underscore": "1.10.2" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "requires": { + "graceful-fs": "4.2.3" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + } + } + }, + "jshint": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.11.0.tgz", + "integrity": "sha512-ooaD/hrBPhu35xXW4gn+o3SOuzht73gdBuffgJzrZBJZPGgGiiTvJEgTyxFvBO2nz0+X1G6etF8SzUODTlLY6Q==", + "dev": true, + "requires": { + "cli": "1.0.1", + "console-browserify": "1.1.0", + "exit": "0.1.2", + "htmlparser2": "3.8.3", + "lodash": "4.17.15", + "minimatch": "3.0.4", + "shelljs": "0.3.0", + "strip-json-comments": "1.0.4" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true + } + } + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "requires": { + "graceful-fs": "4.2.3" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true, + "optional": true + } + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kew": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, + "requires": { + "graceful-fs": "4.2.3" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true, + "optional": true + } + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, + "lazy-debug-legacy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/lazy-debug-legacy/-/lazy-debug-legacy-0.0.1.tgz", + "integrity": "sha1-U3cWwHduTPeePtG2IfdljCkRsbE=", + "dev": true + }, + "liftoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", + "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", + "dev": true, + "requires": { + "extend": "3.0.2", + "findup-sync": "2.0.0", + "fined": "1.2.0", + "flagged-respawn": "1.0.1", + "is-plain-object": "2.0.4", + "object.map": "1.0.1", + "rechoir": "0.6.2", + "resolve": "1.15.1" + } + }, + "linkify-it": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "dev": true, + "requires": { + "uc.micro": "1.0.6" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "4.2.3", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + } + } + }, + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", + "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=", + "dev": true + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "dev": true + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "dev": true + }, + "lodash._escapehtmlchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz", + "integrity": "sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0=", + "dev": true, + "requires": { + "lodash._htmlescapes": "2.4.1" + } + }, + "lodash._escapestringchar": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz", + "integrity": "sha1-7P4iYYoq3lC/7qQ5N+Ud9m8O23I=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._htmlescapes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz", + "integrity": "sha1-MtFL8IRLbeb4tioFG09nwii2JMs=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash._isnative": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", + "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=", + "dev": true + }, + "lodash._objecttypes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", + "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=", + "dev": true + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "dev": true + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash._reunescapedhtml": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz", + "integrity": "sha1-dHxPxAED6zu4oJduVx96JlnpO6c=", + "dev": true, + "requires": { + "lodash._htmlescapes": "2.4.1", + "lodash.keys": "2.4.1" + }, + "dependencies": { + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" + } + } + } + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, + "lodash._shimkeys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", + "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", + "dev": true, + "requires": { + "lodash._objecttypes": "2.4.1" + } + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.defaults": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-2.4.1.tgz", + "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", + "dev": true, + "requires": { + "lodash._objecttypes": "2.4.1", + "lodash.keys": "2.4.1" + }, + "dependencies": { + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" + } + } + } + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "dev": true, + "requires": { + "lodash._root": "3.0.1" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "dev": true, + "requires": { + "lodash._objecttypes": "2.4.1" + } + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "dev": true + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "requires": { + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "requires": { + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" + } + }, + "lodash.values": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-2.4.1.tgz", + "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=", + "dev": true, + "requires": { + "lodash.keys": "2.4.1" + }, + "dependencies": { + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "dev": true, + "requires": { + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" + } + } + } + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.3" + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "dev": true + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "make-error-cause": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-1.2.2.tgz", + "integrity": "sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0=", + "dev": true, + "requires": { + "make-error": "1.3.6" + } + }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "requires": { + "kind-of": "6.0.3" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.5.tgz", + "integrity": "sha1-gP/g9YjaMr8Aa38S2Rl8W4cl3xw=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "1.0.1" + } + }, + "markdown-it": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", + "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", + "dev": true, + "requires": { + "argparse": "1.0.10", + "entities": "2.0.0", + "linkify-it": "2.2.0", + "mdurl": "1.0.1", + "uc.micro": "1.0.6" + } + }, + "markdown-it-anchor": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.2.7.tgz", + "integrity": "sha512-REFmIaSS6szaD1bye80DMbp7ePwsPNvLTR5HunsUcZ0SG0rWJQ+Pz24R4UlTKtjKBPhxo0v0tOBDYjZQQknW8Q==", + "dev": true + }, + "marked": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", + "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==", + "dev": true + }, + "math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.5", + "normalize-package-data": "2.5.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" + }, + "dependencies": { + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + } + } + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "2.3.7" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.3", + "nanomatch": "1.2.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + } + }, + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", + "dev": true + }, + "mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "dev": true, + "requires": { + "mime-db": "1.43.0" + } + }, + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "1.0.2", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "1.2.5" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "dev": true, + "requires": { + "duplexer2": "0.0.2" + } + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-windows": "1.0.2", + "kind-of": "6.0.3", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + } + }, + "natives": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz", + "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "2.8.8", + "resolve": "1.15.1", + "semver": "4.3.6", + "validate-npm-package-license": "3.0.4" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "1.1.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "3.0.1" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "dev": true, + "requires": { + "array-each": "1.0.1", + "array-slice": "1.1.0", + "for-own": "1.0.0", + "isobject": "3.0.1" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "dev": true, + "requires": { + "for-own": "1.0.0", + "make-iterator": "1.0.1" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "0.1.5", + "is-extendable": "0.1.1" + }, + "dependencies": { + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "1.0.2" + } + } + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "3.0.1" + } + }, + "once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "orchestrator": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", + "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", + "dev": true, + "requires": { + "end-of-stream": "0.1.5", + "sequencify": "0.0.7", + "stream-consume": "0.1.1" + } + }, + "ordered-read-streams": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", + "integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "dev": true, + "requires": { + "is-absolute": "1.0.0", + "map-cache": "0.2.2", + "path-root": "0.1.1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "1.0.0" + } + } + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "1.3.2" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "requires": { + "path-root-regex": "0.1.2" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "4.2.3", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + } + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "phantomjs-prebuilt": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz", + "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=", + "dev": true, + "requires": { + "es6-promise": "4.2.8", + "extract-zip": "1.7.0", + "fs-extra": "1.0.0", + "hasha": "2.2.0", + "kew": "0.7.0", + "progress": "1.1.8", + "request": "2.88.2", + "request-progress": "2.0.1", + "which": "1.3.1" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "0.1.1", + "ansi-red": "0.1.1", + "arr-diff": "1.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0", + "array-slice": "0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "dev": true + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "qunit-phantomjs-runner": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/qunit-phantomjs-runner/-/qunit-phantomjs-runner-2.4.1.tgz", + "integrity": "sha512-UBpfyREVhwSmGlJqG+KHDbA5mXGQnSjpluhhIap+5ElymTegjgsNPtq0qLAVaOA2KjcxeByDdYZ13LYNGfHCgg==", + "dev": true, + "requires": { + "qunit-reporter-junit": "1.1.1" + } + }, + "qunit-reporter-junit": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/qunit-reporter-junit/-/qunit-reporter-junit-1.1.1.tgz", + "integrity": "sha1-7rYiZFeJaZPnlaEZQPGK9q+lebQ=", + "dev": true + }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "requires": { + "is-number": "4.0.0", + "kind-of": "6.0.3", + "math-random": "1.0.4" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "rcfinder": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/rcfinder/-/rcfinder-0.1.9.tgz", + "integrity": "sha1-8+gPOH3fmugK4wpBADKWQuroERU=", + "dev": true, + "requires": { + "lodash.clonedeep": "4.5.0" + } + }, + "rcloader": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/rcloader/-/rcloader-0.2.2.tgz", + "integrity": "sha1-WNIpi0YtC5v9ITPSoex0+9cFxxc=", + "dev": true, + "requires": { + "lodash.assign": "4.2.0", + "lodash.isobject": "3.0.2", + "lodash.merge": "4.6.2", + "rcfinder": "0.1.9" + }, + "dependencies": { + "lodash.isobject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", + "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=", + "dev": true + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.5.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "4.2.3", + "micromatch": "3.1.10", + "readable-stream": "2.3.7" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + } + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "1.15.1" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "2.1.0", + "strip-indent": "1.0.1" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "0.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "1.1.0" + } + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "0.7.0", + "aws4": "1.9.1", + "caseless": "0.12.0", + "combined-stream": "1.0.8", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.3.3", + "har-validator": "5.1.3", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.26", + "oauth-sign": "0.9.0", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.5.0", + "tunnel-agent": "0.6.0", + "uuid": "3.4.0" + } + }, + "request-progress": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz", + "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", + "dev": true, + "requires": { + "throttleit": "1.0.0" + } + }, + "requizzle": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", + "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "dev": true, + "requires": { + "lodash": "4.17.15" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "2.0.2", + "global-modules": "1.0.0" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "requires": { + "align-text": "0.1.4" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "0.1.15" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "dev": true + }, + "sequencify": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", + "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", + "dev": true + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.3", + "use": "3.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "6.0.3" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "6.0.3" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.3" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "2.1.2", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.5" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "2.2.0", + "spdx-license-ids": "3.0.5" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "3.0.2" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "0.2.4", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.2", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "0.2.5", + "object-copy": "0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "0.1.6" + } + } + } + }, + "stream-consume": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", + "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==", + "dev": true + }, + "streamqueue": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/streamqueue/-/streamqueue-0.0.6.tgz", + "integrity": "sha1-ZvX17JTpuK8knkrsLdH3Qb/pTeM=", + "dev": true, + "requires": { + "readable-stream": "1.1.14" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", + "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", + "dev": true, + "requires": { + "first-chunk-stream": "1.0.0", + "is-utf8": "0.2.1" + } + }, + "strip-bom-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", + "integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=", + "dev": true, + "requires": { + "first-chunk-stream": "2.0.0", + "strip-bom": "2.0.0" + }, + "dependencies": { + "first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", + "dev": true, + "requires": { + "readable-stream": "2.3.7" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + } + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "4.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "dev": true + }, + "throttleit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "2.3.7", + "xtend": "4.0.2" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + } + } + }, + "tildify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", + "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", + "dev": true, + "requires": { + "os-homedir": "1.0.2" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "3.0.0", + "repeat-string": "1.6.1" + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "1.8.0", + "punycode": "2.1.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" + } + }, + "uglify-save-license": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/uglify-save-license/-/uglify-save-license-0.4.1.tgz", + "integrity": "sha1-lXJsF8xv0XHDYX479NjYKqjEzOE=", + "dev": true + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true, + "optional": true + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "dev": true + }, + "underscore": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", + "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "2.0.1" + } + }, + "unique-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", + "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "0.3.1", + "isobject": "3.0.1" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "2.1.1" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "user-home": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + }, + "v8flags": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", + "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", + "dev": true, + "requires": { + "user-home": "1.1.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "3.1.0", + "spdx-expression-parse": "3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + } + }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "dev": true, + "requires": { + "clone": "1.0.4", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + }, + "vinyl-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-2.0.0.tgz", + "integrity": "sha1-p+v1/779obfRjRQPyweyI++2dRo=", + "dev": true, + "requires": { + "graceful-fs": "4.2.3", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0", + "strip-bom-stream": "2.0.0", + "vinyl": "1.2.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "1.0.4", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + } + } + }, + "vinyl-fs": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.14.tgz", + "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", + "dev": true, + "requires": { + "defaults": "1.0.3", + "glob-stream": "3.1.18", + "glob-watcher": "0.0.6", + "graceful-fs": "3.0.12", + "mkdirp": "0.5.5", + "strip-bom": "1.0.0", + "through2": "0.6.5", + "vinyl": "0.4.6" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "dev": true, + "requires": { + "readable-stream": "1.0.34", + "xtend": "4.0.2" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "dev": true, + "requires": { + "clone": "0.2.0", + "clone-stats": "0.0.1" + } + } + } + }, + "vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", + "dev": true, + "requires": { + "source-map": "0.5.7" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xmlcreate": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz", + "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + } + } + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "0.2.13", + "fd-slicer": "1.1.0" + } + } + } +} diff --git a/package.json b/package.json index b18e131..81e2ade 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rekord", - "version": "1.5.10", + "version": "1.5.11", "description": "A javascript REST ORM that is offline and real-time capable http://rekord.github.io/rekord/", "author": "Philip Diffenderfer", "license": "MIT", diff --git a/src/lib/relations/RelationMultiple.js b/src/lib/relations/RelationMultiple.js index cdeeff6..04de27f 100644 --- a/src/lib/relations/RelationMultiple.js +++ b/src/lib/relations/RelationMultiple.js @@ -115,7 +115,7 @@ Class.extend( Relation, RelationMultiple, this.addModel( relation, related, remoteData ); } } - }); + }, remoteData ); } else if ( isValue( input ) ) { @@ -145,7 +145,7 @@ Class.extend( Relation, RelationMultiple, this.removeModel( relation, related, remoteData ); } } - }); + }, remoteData ); } else if ( isValue( input ) ) { @@ -166,7 +166,7 @@ Class.extend( Relation, RelationMultiple, { this.removeModel( relation, all[ i ], remoteData ); } - }); + }, remoteData ); } },