From 94d2a5aaa7cb485ec46a3f8de9d404dd48b22543 Mon Sep 17 00:00:00 2001 From: Firebase Operations Date: Wed, 15 Apr 2015 01:10:45 +0000 Subject: [PATCH] [firebase-release] Updated firebase-util to 0.2.4 --- bower.json | 2 +- dist/firebase-util-normalize.min.js | 11 + dist/firebase-util-paginate.min.js | 11 + dist/firebase-util.js | 4680 +++++++++++++++++++++++++++ dist/firebase-util.min.js | 12 + package.json | 2 +- 6 files changed, 4716 insertions(+), 2 deletions(-) create mode 100644 dist/firebase-util-normalize.min.js create mode 100644 dist/firebase-util-paginate.min.js create mode 100644 dist/firebase-util.js create mode 100644 dist/firebase-util.min.js diff --git a/bower.json b/bower.json index e39e32c..773fde6 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "firebase-util", "description": "A set of experimental power tools for Firebase.", - "version": "0.0.0", + "version": "0.2.4", "authors": [ "Firebase (https://www.firebase.com/)" ], diff --git a/dist/firebase-util-normalize.min.js b/dist/firebase-util-normalize.min.js new file mode 100644 index 0000000..4c1be11 --- /dev/null +++ b/dist/firebase-util-normalize.min.js @@ -0,0 +1,11 @@ +/*! + * Firebase-util: A set of experimental power tools for Firebase. + * + * Version: 0.2.4 + * URL: https://github.com/firebase/firebase-util + * Date: 2015-04-15T01:10:43.370Z + * License: MIT http://firebase.mit-license.org/ + */ +require=function t(e,n,r){function i(a,o){if(!n[a]){if(!e[a]){var h="function"==typeof require&&require;if(!o&&h)return h(a,!0);if(s)return s(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var c=n[a]={exports:{}};e[a][0].call(c.exports,function(t){var n=e[a][1][t];return i(n?n:t)},c,c.exports,t,e,n,r)}return n[a].exports}for(var s="function"==typeof require&&require,a=0;a=0}function a(t,e){if(t instanceof s)return u.pick(t,["path","id","key","alias","pathName","url"]);"string"==typeof t&&(t={key:t});var n=t.key.split("."),r=e.getPath(n[0]);return{pathName:n[0],id:n[1],key:t.key,alias:t.alias||n[1],path:r,url:r?r.url()+"/"+n[1]:null}}function o(t,e,n){if(null!==n){if(e.indexOf(".")>0){for(var r,i=e.split(".").reverse();i.length>1&&(r=i.pop());)t=t[r]=u.has(t,r)?t[r]:{};e=i.pop()}t[e]=n}}function h(t,e){var n=e;if(!u.isObject(t))return u.undef;var r=t[e];if(e.indexOf(".")>0){var i=e.split(".").reverse();for(r=t;i.length;)n=i.pop(),r=u.isObject(r)&&r.hasOwnProperty(n)?r[n]:u.undef}return r}var u=t("../../common"),c=t("./PathManager");r.prototype={add:function(t){var e=new s(a(t,this.pathMgr));if(this.fields.hasOwnProperty(e.alias))throw new Error("Duplicate field alias "+e.alias+"("+e.key+")");if(null===e.path)throw new Error("Invalid path specified for field "+e.key+"; it was not in the paths provided, which are : "+this.pathMgr.getPathNames().join(","));var n=e.path.getDependency();if(null!==n&&null===i(this.fields,n))throw new Error("Dynamic paths must reference a field declared in the map. Please add "+r.key(n.path,n.field)+" to the select() criteria before using it in a dynamic field");this.fields[e.alias]=e,this.length++},forEach:function(t,e){return u.find(this.fields,t,e)!==u.undef},getField:function(t){return this.fields[t]||null},getPath:function(t){return this.getPathManager().getPath(t)},getPathManager:function(){return this.pathMgr},pathFor:function(t){var e=this.getField(t);return e?e.path:this.pathMgr.first()},fieldsFor:function(t){return u.filter(u.toArray(this.fields),function(e){return e.pathName===t})},aliasFor:function(t){var e=u.find(this.fields,function(e){return e.url===t},this);return e?e.alias:null},extractData:function(t,e){var n={},r=this.pathMgr.getPathName(t.ref().toString());if(null===r&&null!==t.ref().parent()){var i=this.pathMgr.getPathFor(t.ref().parent().toString());i&&i.hasDependency()&&(r=i.name())}var s=e?"exportVal":"val";return u.each(this.fieldsFor(r),function(e){switch(e.id){case"$key":o(n,e.alias,t.key());break;case"$value":o(n,e.alias,t[s]());break;default:t.hasChild(e.id)&&o(n,e.alias,t.child(e.id)[s]())}}),n},snapFor:function(t,e){var n,r=this.pathFor(e);if(!r)return null;var s=r.getDependency();if(null!==s){var a=i(this.fields,s),o=this.snapFor(t,a.alias);o&&(n="$key"===s.field?r.child(o.key()).url():"$value"===s.field?r.child(o.val()).url():r.child(o.child(s.field).val()).url())}else n=r.url();return n?u.find(t,function(t){return t.ref().toString()===n})||null:null},denest:function(t){var e={};return u.each(this.getPathManager().getPaths(),function(t){e[t.name()]={path:t,data:{}}}),this.forEach(function(n){var r=h(t,n.alias);if(r!==u.undef)switch(n.id){case"$value":e[n.pathName].data=r;break;case"$key":break;default:e[n.pathName].data[n.id]=r}}),e},idFor:function(t){var e=this.getField(t);return e?e.id:t}},r.key=function(t,e){return"string"!=typeof t&&(t=t.name()),t+"."+e},r.fieldMap=function(t,e){var n,i=t.getField(e);i?(n=i.path,"$value"!==i.id&&(n=n.child(i.id))):n=t.pathFor(e).child(e);var s=new c([n]),a=new r(s);return a.add({key:r.key(n,"$value"),alias:e}),a},r.recordMap=function(t,e){var n=t.getPathManager(),i=u.map(n.getPaths(),function(t){return t.normChild(e)}),s=new r(new c(i));return t.forEach(function(t){s.add({key:t.key,alias:t.alias})}),s},e.exports=r},{"../../common":18,"./PathManager":10}],5:[function(t,e,n){"use strict";function r(){this.criteria=[],s.each(arguments,this.add,this)}function i(t){this.match=t}var s=t("../../common");r.prototype={add:function(t){this.criteria.push(new i(t))},test:function(t,e,n){return s.contains(this.criteria,function(r){return!r.test(t,e,n)})===!1}},i.prototype.test=function(t,e,n){return this.match(t,e,n)===!0},e.exports=r},{"../../common":18}],6:[function(t,e,n){"use strict";function r(t){i(arguments),this.pathMgr=new u(arguments),this.map=new l(this.pathMgr),this.filters=new c,this.finalized=!1}function i(t){function e(t){return h.isArray(t)&&(t=t[0]),!h.isFirebaseRef(t)}if(t.length<1)throw new Error("Must provide at least one path definition");if(h.contains(t,e))throw new Error("Each argument to the NormalizedCollection constructor must be a valid Firebase reference or an Array containing a Firebase ref as the first argument")}function s(t,e){if(t.finalized)throw new Error("Cannot call "+e+"() after ref() has been invoked")}function a(t){var e=[],n=[],r="";return h.each(t.pathMgr.getPaths(),function(t){var n=t.getDependency();e.push(h.printf(' "%s%s"%s',t.url(),t.id()===t.name()?"":" as "+t.name(),n?"-> "+n.path+"."+n.field:""))}),t.map.forEach(function(t){n.push(h.printf('"%s%s"',t.key,t.alias===t.id?"":" as "+t.alias)),n.length%5===0&&n.push("\n")}),t.filters.criteria.length>0&&(r=h.printf("<%s filters applied>",t.filters.criteria.length)),h.printf("NormalizedCollection(\n%s\n).select(%s)%s.ref()",e.join("\n"),n.join(", "),r)}function o(t){var e;if(e="string"==typeof t?t:h.has(t,"key")?t.key:h.undef,"string"!=typeof e||e.indexOf(".")<=0)throw new Error('Each field passed to NormalizedCollection.select() must either be a string in the format "pathAlias.fieldId", or an object in the format {key: "pathAlias.fieldId", alias: "any_name_for_field"}, but I received '+JSON.stringify(t))}var h=t("../../common"),u=t("./PathManager"),c=t("./Filter"),l=t("./FieldMap"),f=t("./NormalizedRef"),d=t("./RecordSet");r.prototype={select:function(t){s(this,"select");var e=h.args("NormalizedCollection.select",arguments,1);return h.each(e.restAsList(0,["string","object"]),function(t){o(t),this.map.add(t)},this),this},filter:function(t){s(this,"filter");var e=h.args("NormalizedCollection.filter",arguments,1,1);return this.filters.add(e.nextReq("function")),this},ref:function(){if(!this.map.length)throw new Error("Must call select() with at least one field before creating a ref");this.finalized=!0,h.log.isInfoEnabled()&&h.log.info("NormalizedRef created using %s",a(this));var t=new d(this.map,this.filters);return new f(t)}},e.exports=r},{"../../common":18,"./FieldMap":4,"./Filter":5,"./NormalizedRef":7,"./PathManager":10,"./RecordSet":14}],7:[function(t,e,n){"use strict";function r(t,e){this._super(this,t),this._parent=e||null,this._key=t.getName(),this._toString=t.getUrl()}function i(t){return function(){var e=o.toArray(arguments);o.each(this.$getPaths(),function(n){var r=n.ref();r[t].apply(r,e)})}}function s(t){return function(){var e=o.toArray(arguments),n=this.$getMaster();return n[t].apply(n,e)}}function a(t){return function(){throw new Error(t+" is not supported for NormalizedCollection references. Try calling it on the original reference used to create the NormalizedCollection instead.")}}var o=t("../../common"),h=t("./Query");o.inherits(r,h,{child:function(t){for(var e=t.split("/").reverse(),n=this,i=this;e.length;){var s=e.pop();i=new r(i.$getRecord().makeChild(s),n),n=i}return i},parent:function(){return this._parent},root:function(){for(var t=this;null!==t.parent();)t=t.parent();return t},name:function(){return console.warn("The name() function has been deprecated. Use key() instead."),this.key()},key:function(){return this._key},toString:function(){return this._toString},set:function(t,e,n){this.$getRecord().saveData(t,{callback:e,context:n,isUpdate:!1})},update:function(t,e,n){this.$getRecord().saveData(t,{callback:e,context:n,isUpdate:!0})},remove:function(t,e){this.$getRecord().saveData(null,{callback:t,context:e,isUpdate:!1})},push:function(t,e,n){var r=this.$getMaster().push().key(),i=this.child(r);return arguments.length&&i.set.apply(i,arguments),i},setWithPriority:function(t,e,n,r){this.$getRecord().saveData(t,{callback:n,context:r,isUpdate:!1,priority:e})},setPriority:function(t,e,n){this.$getMaster().setPriority(t,e,n)},auth:s("auth"),unauth:s("unauth"),authWithCustomToken:s("authWithCustomToken"),authAnonymously:s("authAnonymously"),authWithPassword:s("authWithPassword"),authWithOAuthPopup:s("authWithOAuthPopup"),authWithOAuthRedirect:s("authWithOAuthRedirect"),authWithOAuthToken:s("authWithOAuthToken"),getAuth:s("getAuth"),onAuth:s("onAuth"),offAuth:s("offAuth"),createUser:s("createUser"),changePassword:s("changePassword"),removeUser:s("removeUser"),resetPassword:s("resetPassword"),changeEmail:s("changeEmail"),goOffline:i("goOffline"),goOnline:i("goOnline"),transaction:a("transaction"),onDisconnect:a("onDisconnect")}),e.exports=r},{"../../common":18,"./Query":11}],8:[function(t,e,n){"use strict";function r(t,e){if(this._ref=t,this._rec=t.$getRecord(),!i.isArray(e))throw new Error("Must provide an array of snapshots to merge");this._pri=this._rec.getPriority(e),this._snaps=e}var i=t("../../common");r.prototype={val:function(){return this._snaps.length?this._rec.mergeData(this._snaps,!1):null},child:function(t){var e,n=t.split("/").reverse(),i=n.pop();for(e=new r(this._ref.child(i),this._rec.getChildSnaps(this._snaps,i));n.length;)e=e.child(n.pop());return e},forEach:function(t,e){return this._rec.forEachKey(this._snaps,function(n,r){return"$value"===n||"$key"===n?!1:t.call(e,this.child(r))},this)},hasChild:function(t){for(var e=t.split("/").reverse(),n=e.length>0,r=this._ref,i=this;n&&e.length;){var s=e.pop();n=r.$getRecord().hasChild(i._snaps,s),n&&e.length&&(r=r.child(s),i=i.child(s))}return n},hasChildren:function(){return this._rec.forEachKey(this._snaps,function(t){return"$key"!==t&&"$value"!==t})},name:function(){return console.warn("name() has been deprecated. Use key() instead."),this.key()},key:function(){return this._rec.getName()},numChildren:function(){var t=0;return this._rec.forEachKey(this._snaps,function(e){"$key"!==e&&"$value"!==e&&t++}),t},ref:function(){return this._ref.ref()},getPriority:function(){return this._pri},exportVal:function(){return this._rec.mergeData(this._snaps,!0)},exists:function(){return null!==this.val()}},e.exports=r},{"../../common":18}],9:[function(t,e,n){"use strict";function r(t,e){var n=i(t);this._ref=n.ref,this._alias=n.alias,this._dep=n.dep,this._parent=e||null}function i(t){var e,n,r=null;return a.isArray(t)?(e=t[0],n=t[1],r=t[2]):e=a.isFunction(t.ref)?t.ref():t,{ref:e,alias:n||e.key(),dep:s(r)}}function s(t){if(a.isObject(t))return t;if(t){var e=t.split(".");return{path:e[0],field:e[1]}}return null}var a=t("../../common");r.prototype={ref:function(){return this._ref},reff:function(){return this.ref().ref()},child:function(t){return new r(this.reff().child(t),this)},normChild:function(t){var e=this.getDependency();return null!==e?new r([this.reff(),this.name(),e.path+"."+e.field],this):new r([this.reff().child(t),this.name()],this)},hasDependency:function(){return null!==this._dep},getDependency:function(){return this._dep},url:function(){return this.reff().toString()},name:function(){return this._alias},id:function(){return this.reff().key()},parent:function(){return this._parent},clone:function(){return new r([this._ref,this._alias,this._dep],this._parent)}},e.exports=r},{"../../common":18}],10:[function(t,e,n){"use strict";function r(t){this.paths=[],this.pathsByUrl={},this.deps={},this.pathNames=[],a.each(t,this.add,this)}function i(t,e){return a.map(t,function(t){return e[t].path+"."+e[t].field}).join(" >> ")}var s=t("./Path"),a=t("../../common");r.prototype={add:function(t){var e=t instanceof s?t.clone():new s(t);if(!this.paths.length&&e.hasDependency())throw new Error("The master path (i.e. the first) may not declare a dependency. Perhaps you have put the wrong path first in the list?");if(a.has(this.pathsByUrl,e.url()))throw new Error("Duplicate path: "+e.url());if(a.contains(this.pathNames,e.name()))throw new Error("Duplicate path name. The .key() value for each path must be unique, or you can give each a path an alias by using [firebaseRef, alias] in the constructor. The aliases must also be unique.");this._map(e),this.paths.push(e),this.pathsByUrl[e.url()]=e.name(),this.pathNames.push(e.name())},count:function(){return this.paths.length},first:function(){return this.paths[0]},getPath:function(t){return a.find(this.paths,function(e){return e.name()===t})||null},getPathFor:function(t){var e=this.getPathName(t);return e?this.getPath(e):null},getPaths:function(){return this.paths.slice()},getPathName:function(t){return this.pathsByUrl[t]||null},getPathNames:function(){return this.pathNames.slice()},getUrls:function(){return a.keys(this.pathsByUrl)},getDependencyGraph:function(){return a.extend(!0,this.deps)},_map:function(t){var e=this.first(),n=t.getDependency();!n&&e&&(n={path:e.name(),field:"$key"}),n&&(this.deps[t.name()]=n,this._assertNotCircularDep(t.name()))},_assertNotCircularDep:function(t){for(var e=[t],n=this.deps[t];a.isDefined(n);){var r=n.path;if(a.contains(e,r))throw e.push(r),new Error("Circular dependencies in paths: "+i(e,this.deps));e.push(r),n=a.val(this.deps,r)}}},e.exports=r},{"../../common":18,"./Path":9}],11:[function(t,e,n){"use strict";function r(t,e){var n=this;n._ref=t,n._rec=e,e&&e.setRef(n)}var i=t("../../common"),s=t("./Transmogrifier");r.prototype={on:function(t,e,n,r){3===arguments.length&&i.isObject(n)&&(r=n,n=i.undef),this.$getRecord().watch(t,e,n,r)},once:function(t,e,n,r){function s(n){o.off(t,s),e.call(r,n)}function a(t){"function"==typeof n&&null!==t&&n.call(r,t)}var o=this;3===arguments.length&&i.isObject(n)&&(r=n,n=i.undef),this.on(t,s,a)},off:function(t,e,n){this.$getRecord().unwatch(t,e,n)},orderByChild:function(){return this.$replicate("orderByChild",i.toArray(arguments))},orderByKey:function(){return this.$replicate("orderByKey",i.toArray(arguments))},orderByPriority:function(){return this.$replicate("orderByPriority",i.toArray(arguments))},limitToFirst:function(){return this.$replicate("limitToFirst",i.toArray(arguments))},limitToLast:function(){return this.$replicate("limitToLast",i.toArray(arguments))},limit:function(){return this.$replicate("limit",i.toArray(arguments))},startAt:function(){return this.$replicate("startAt",i.toArray(arguments))},endAt:function(){return this.$replicate("endAt",i.toArray(arguments))},equalTo:function(){return this.$replicate("equalTo",i.toArray(arguments))},ref:function(){return this._ref},$getRecord:function(){return this._rec},$getMaster:function(){return this._rec.getPathManager().first().ref()},$getPaths:function(){return this._rec.getPathManager().getPaths()},$replicate:function(t,e){var n=this.$getRecord(),i=this.$getMaster();return i=i[t].apply(i,e),new r(this._ref,s.replicate(n,i))}},i.registerFirebaseWrapper(r),e.exports=r},{"../../common":18,"./Transmogrifier":16}],12:[function(t,e,n){"use strict";function r(t){var e=t.getPathManager().first().id(),n=l.mergeToString(t.getPathManager().getUrls());this._super(t,e,n),this._eventManagers={},l.log.debug("Record created",this.getName(),this.getUrl())}function i(t){this.rec=t,this.pm=t.getPathManager(),this.running=!1,this._init()}function s(t,e){this.event=t,this.rec=e,this.map=e.getFieldMap(),this.pm=e.getPathManager(),this.subs=[],this.dyno=null}function a(t,e,n,r){var i=t.getDependency(),s=e.getPath(i.path),a=s.ref();if("$key"===i.field)throw new Error("Dynamic paths do not support $key (you should probably just join on this path)");"$value"!==i.field&&(a=a.child(i.field));var o,h=a.on("value",function(e){o&&o.key()!==e.val()&&(l.log.debug("Record.Dyno: stopped monitoring %s",o.toString()),o.off(n,r),r(null)),null!==e.val()&&(o=t.ref().child(e.val()),o.on(n,r),l.log("Record.Dyno: monitoring %s",o.toString()))});this.dispose=function(){a.off("value",h),o&&o.off(n,r)}}function o(t,e,n){l.each(t.fieldsFor(e.name()),function(t){switch(t.id){case"$key":break;case"$value":l.has(n,".value")||(n[".value"]=null);break;default:l.has(n,t.id)||(n[t.id]=null)}})}var h=t("./FieldMap"),u=t("./RecordField"),c=t("./AbstractRecord"),l=t("../../common");l.inherits(r,c,{makeChild:function(t){var e=h.fieldMap(this.getFieldMap(),t);return new u(e)},hasChild:function(t,e){var n=this.getFieldMap().getField(e);if(!n)return!1;var r=this.getFieldMap().snapFor(t,e);return null!==r&&r.hasChild(e)},getChildSnaps:function(t,e){var n,r=this.getFieldMap().snapFor(t,e),i=this.getFieldMap().getField(e);if(i)switch(i.id){case"$key":throw new Error("Cannot get child snapshot from key (not a real child element)");case"$value":n=r;break;default:n=r.child(i.id)}else n=r.child(e);return[n]},forEachKey:function(t,e,n){function r(t,e){switch(e){case"$key":return!0;case"$value":return t&&null!==t.val();default:return t&&t.hasChild(e)}}var i=this.getFieldMap();return i.forEach(function(s){var a=i.snapFor(t,s.alias);return r(a,s.id)?e.call(n,s.id,s.alias)===!0:!1})},mergeData:function(t,e){var n=this.getFieldMap(),r=l.extend.apply(null,l.map(t,function(t){return n.extractData(t,e)}));return e&&t.length>0&&null!==t[0].getPriority()&&(l.isObject(r)||(r={".value":r}),r[".priority"]=t[0].getPriority()),r},getPriority:function(t){return t[0].getPriority()},getClass:function(){return r},saveData:function(t,e){var n=l.queue(),r=this.getFieldMap(),i=this.getPathManager().getPaths();if(e.isUpdate&&!l.isObject(t))throw new Error("First argument to update() command must be an object");if(null===t)l.each(i,function(t){t.hasDependency()||t.reff().remove(n.getHandler())});else if(l.isObject(t)){var s=r.denest(t);l.each(s,function(t){var a=t.path,h=t.data,u=this._writeRef(s,a);null!==u?l.isEmpty(h)&&e.isUpdate||(l.isObject(h)||(h={".value":h}),e.isUpdate||o(r,a,h),l.isDefined(e.priority)&&(h[".priority"]=e.priority),l.has(h,".value")?u.set(h,n.getHandler()):u.update(h,n.getHandler())):l.log.info("No dynamic key found for master",i[0].ref().toString(),"with dynamic path",a.ref().toString())},this)}else{if(1!==i.length)throw new Error("Cannot set multiple paths to a non-object value. Since this is a NormalizedCollection, the data will be split between the paths. But I can't split a primitive value");l.isDefined(e.priority)?i[0].ref().setWithPriority(t,e.priority,n.getHandler()):i[0].ref().set(t,n.getHandler())}n.handler(e.callback||l.noop,e.context)},getName:function(){return this._name},getUrl:function(){return this._url},_start:function(t){l.has(this._eventManagers,t)||(l.log.debug("Record._start: event=%s, url=%s",t,this.getUrl()),this._eventManagers[t]="value"===t?new i(this):new s(t,this)),this._eventManagers[t].start()},_stop:function(t){l.has(this._eventManagers,t)&&(l.log.debug("Record._stop: event=%s, url=%s",t,this.getUrl()),this._eventManagers[t].stop())},_writeRef:function(t,e){var n=e.reff(),r=e.getDependency();if(null!==r){var i=this.getPathManager().getPath(r.path),s=this._depKey(t,i,r.field);n=null===s?null:n.child(s)}return n},_depKey:function(t,e,n){var r,i=t[e.name()].data;switch(n){case"$key":r=e.id();break;case"$value":r=l.has(i,".value")?i[".value"]:l.isEmpty(i)?null:i;break;default:r=l.has(i,n)?i[n]:null}var s=typeof r;if(null!==r&&"string"!==s)throw new Error("Dynamic key values must be a string. Type was "+s+" for "+e.ref().toString()+"->"+n);return r}}),i.prototype={start:function(){this.running||(this.running=!0,l.each(this.pm.getPathNames(),this._startPath,this))},stop:function(){this.running&&(this.running=!1,l.each(this.subs,function(t){t()}),this._init())},update:function(t,e){this.snaps[t]=e,this._checkLoadState(),l.log("Record.ValueEventManager.update: url=%s, loadCompleted=%s",e.ref().toString(),this.loadCompleted),this.loadCompleted&&this.rec.handler("value")(l.toArray(this.snaps))},_startPath:function(t){var e=this,n=e.pm.getPath(t),r=l.bind(e.update,e,t);if(n.hasDependency()){var i=new a(n,this.rec.getFieldMap(),"value",r);this.subs.push(i.dispose)}else n.ref().on("value",r),e.subs.push(function(){n.ref().off("value",r)})},_checkLoadState:function(){if(!this.loadCompleted){var t=this.snaps,e=this.pm.getPathNames();this.loadCompleted=!l.contains(e,function(e){return!t.hasOwnProperty(e)})}},_init:function(){this.loadCompleted=!1,this.snaps={},this.subs=[]}},s.prototype={start:function(){l.each(this.pm.getPathNames(),function(t){var e=this.event,n=this.pm.getPath(t),r=l.bind(this.update,this);n.hasDependency()?(this.dyno=new a(n,this.map,e,r),this.subs.push(this.dyno.dispose)):(n.ref().on(e,r),this.subs.push(function(){n.ref().off(e,r)}))},this)},stop:function(){l.each(this.subs,function(t){t()}),this.subs=[]},update:function(t,e){if(null!==t&&null!==this.map.aliasFor(t.ref().toString())){var n=[t.key(),t];e!==l.undef&&n.push(e),l.log("Record.ChildEventManager.update: event=%s, key=%s/%s",this.event,t.ref().parent().key(),t.key()),this.rec.handler(this.event).apply(this.rec,n)}}},e.exports=r},{"../../common":18,"./AbstractRecord":3,"./FieldMap":4,"./RecordField":13}],13:[function(t,e,n){"use strict";function r(t){if(this.path=t.getPathManager().first(),this._super(t,this.path.name(),this.path.url()),1!==t.getPathManager().count())throw new Error("RecordField must have exactly one path, but we got "+t.getPathManager().count());if(1!==t.length)throw new Error("RecordField must have exactly one field, but we found "+t.length);h.log.debug("RecordField created",this.getName(),this.getUrl())}function i(t){return t.callback?function(){t.callback.apply(t.context,arguments)}:h.noop}var s=t("./PathManager"),a=t("./FieldMap"),o=t("./AbstractRecord"),h=t("../../common");h.inherits(r,o,{makeChild:function(t){var e=new s([this.path.child(t)]),n=new a(e);return n.add({key:a.key(e.first(),"$value"),alias:t}),new r(n)},hasChild:function(t,e){return t[0].hasChild(e)},getChildSnaps:function(t,e){return[t[0].child(e)]},mergeData:function(t,e){return e?t[0].exportVal():t[0].val()},getPriority:function(t){return t[0].getPriority()},forEachKey:function(t,e,n){var r=t[0];return r.forEach(function(t){e.call(n,t.key(),t.key())})},saveData:function(t,e){var n=this.path.ref();if(e.isUpdate){if(!h.isObject(t))throw new Error("When using update(), the data must be an object.");h.has(e,"priority")&&(t[".priority"]=e.priority),n.update(t,i(e))}else h.has(e,"priority")?n.setWithPriority(t,e.priority,i(e)):n.set(t,i(e))},getClass:function(){return r},_start:function(t){this.path.ref().on(t,this.handler(t),this._cancel,this)},_stop:function(t){this.path.ref().off(t,this.handler(t),this)}}),e.exports=r},{"../../common":18,"./AbstractRecord":3,"./FieldMap":4,"./PathManager":10}],14:[function(t,e,n){"use strict";function r(t,e){var n=a.mergeToString(t.getPathManager().getPathNames()),r=a.mergeToString(t.getPathManager().getUrls());this._super(t,n,r),this.filters=e,this.monitor=new h(this)}var i=t("./Record"),s=t("./AbstractRecord"),a=t("../../common"),o=t("./FieldMap"),h=t("./RecordSetEventManager");a.inherits(r,s,{makeChild:function(t){var e=o.recordMap(this.getFieldMap(),t);return new i(e)},hasChild:function(t,e){return a.contains(t,function(t){return t.key()===e})},getChildSnaps:function(t,e){return a.filter(t,function(t){return t.key()===e})},mergeData:function(t,e){var n=this,r={};return a.each(t,function(t){null!==t.val()&&n.filters.test(t.val(),t.key(),t.getPriority())&&(r[t.key()]=e?t.exportVal():t.val())}),r},getPriority:function(){return null},forEachKey:function(t,e,n){t.forEach(function(t){return e.call(n,t.key(),t.key())})},getClass:function(){return r},saveData:function(t,e){var n=a.queue();if(null===t)a.each(this.getPathManager().getPaths(),function(t){t.ref().remove(n.getHandler())});else{if(!a.isObject(t))throw new Error("Calls to set() or update() on a NormalizedCollection must pass either null or an object value. There is no way to split a primitive value between the paths");a.each(t,function(t,r){if(".value"===r||".priority"===r)throw new Error("Cannot use .priority or .value on the root path of a NormalizedCollection. You probably meant to sort the records anyway (i.e. one level lower).");this.child(r).saveData(t,{isUpdate:e.isUpdate,callback:n.getHandler()})},this),e.priority&&this.getPathManager().first().ref().setPriority(e.priority,n.getHandler())}n.handler(e.callback||a.noop,e.context)},_getChildKey:function(t,e,n){var r=n,i=this.getPathManager().getPathFor(t.ref().toString());if(i.hasDependency()){var s=i.getDependency(),o=this.getFieldMap().getPath(s.path);if(!o)throw new Error("Invalid dependency path. "+t.ref.toString()+" depends on "+s.path+", but that alias does not exist in the paths provided.");var h=a.find(e,function(t){return t.ref().toString()===o.url()});h?(h=h.child(n),"$value"!==s.field&&(h=h.child(s.field)),r=h.val()):r=null}return r},_start:function(){this.monitor.start()},_stop:function(t,e){0===e&&this.monitor.stop()}}),e.exports=r},{"../../common":18,"./AbstractRecord":3,"./FieldMap":4,"./Record":12,"./RecordSetEventManager":15}],15:[function(t,e,n){"use strict";function r(t){var e=t.getPathManager();this.masterRef=e.first().ref(),this.url=this.masterRef.toString(),this.recList=new i(t,this.url),this.running=!1}function i(t,e){this.obs=t,this.url=e,this._reset()}var s=t("../../common");r.prototype={start:function(){return this.running||(s.log("RecordSetEventManager: Loading normalized records from master list %s",this.url),this.running=!0,this.masterRef.on("child_added",this._add,this),this.masterRef.on("child_removed",this._remove,this),this.masterRef.on("child_moved",this._move,this),this.masterRef.once("value",this.recList.masterPathLoaded,this.recList)),this},stop:function(){return this.running&&(s.log("RecordSetEventManager: Stopped monitoring master list %s",this.url),this.running=!1,this.masterRef.off("child_added",this._add,this),this.masterRef.off("child_removed",this._remove,this),this.masterRef.off("child_moved",this._move,this),this.recList.unloaded()),this},_add:function(t,e){this.recList.add(t.key(),e)},_remove:function(t){this.recList.remove(t.key())},_move:function(t,e){this.recList.move(t.key(),e)}},i.prototype={add:function(t,e){s.log.debug("RecordList.add: key=%s, prevChild=%s",t,e);var n=this.obs.child(t),r=s.bind(this._valueUpdated,this,t);this.loading[t]={rec:n,prev:e,fn:r},this.loadComplete||this.initialKeysLeft.push(t),n.watch("value",r)},remove:function(t){s.log.debug("RecordList.remove: key=%s",t);var e=this._dropRecord(t);null!==e&&this._notify("child_removed",t,e)},move:function(t,e){if(s.has(this.recs,t)){var n=s.indexOf(this.recIds,t);this.recIds.splice(n,1),this._putAfter(t,e),this._notify("child_moved",t)}},masterPathLoaded:function(){s.log.debug("RecordList: Initial data has been loaded from master list at %s",this.url),this.masterLoaded=!0,this._checkLoadState()},unloaded:function(){this._reset()},findKey:function(t){return s.indexOf(this.recIds,t)},_reset:function(){s.each(this.recs,function(t,e){this.remove(e)},this),this.recs={},this.recIds=[],this.snaps={},this.loading={},this.loadComplete=!1,this.initialKeysLeft=[],this.masterLoaded=!1},_valueUpdated:function(t,e){if(this.snaps[t]=e,s.has(this.loading,t)){var n=this.loading[t];delete this.loading[t],this.obs.filters.test(e.val(),t,e.getPriority())?(this.recs[t]=n.rec,this._putAfter(t,n.prev),this._checkLoadState(t),this._notify("child_added",t)):(s.log("RecordList: Filtered key %s",t),n.rec.unwatch("value",n.fn))}else s.has(this.recs,t)?this._notify("child_changed",t):s.log("RecordList: Orphan key %s ignored. Probably a concurrent edit.",t)},_notify:function(t,e,n){var r=[e];switch(t){case"child_added":case"child_moved":var i=this._getPrevChild(e);r.push(this.snaps[e],i);break;case"child_changed":r.push(this.snaps[e]);break;case"child_removed":r.push(n);break;default:throw new Error("Invalid event type "+t+" for key "+e)}s.log("RecordList._notify: event=%s, key=%s, prev=%s",t,e,i),this.obs.handler(t).apply(this.obs,r),this._notifyValue()},_notifyValue:function(){s.log.debug("RecordList._notifyValue: snap_keys=%s, loadComplete=%s",s.keys(this.snaps),this.loadComplete),this.loadComplete&&this.obs.handler("value")(s.toArray(this.snaps))},_getPrevChild:function(t){if(!this.recIds.length)return null;var e=this.findKey(t);return-1===e?this.recIds[this.recIds.length-1]:0===e?null:this.recIds[e-1]},_posFor:function(t){var e,n;return null===t?e=0:(n=this.findKey(t),e=-1===n?this.recIds.length:n+1),e},_putAfter:function(t,e){var n=this._posFor(e);this.recIds.splice(n,0,t)},_dropRecord:function(t){if(s.has(this.recs,t)){var e=this.snaps[t];return this.recs[t].unwatch("value",this._valueUpdated,this),delete this.recs[t],delete this.snaps[t],delete this.loading[t],s.remove(this.recIds,t),e}return null},_checkLoadState:function(t){this.loadComplete||(t&&s.remove(this.initialKeysLeft,t),!this.initialKeysLeft.length&&this.masterLoaded&&(this.loadComplete=!0,t||this._notifyValue()))}},e.exports=r},{"../../common":18}],16:[function(t,e,n){"use strict";var r=t("../../common"),i=t("./PathManager"),s=t("./Path"),a=t("./FieldMap"),o=t("./RecordSet");e.exports={replicate:function(t,e){var n=t.getPathManager().getPaths().slice(0),h=n[0];n[0]=new s([e,h.name(),h.getDependency()]);var u=new i(r.map(n,function(t){return t.clone()})),c=new a(u);t.getFieldMap().forEach(c.add,c); + +var l,f=t.getClass();return l=f===o?new f(c,t.filters):new f(c)}}},{"../../common":18,"./FieldMap":4,"./Path":9,"./PathManager":10,"./RecordSet":14}],17:[function(t,e,n){var r=t("./index.js");n.log=r.log,n.logLevel=r.logLevel,n.escapeEmail=r.escapeEmail},{"./index.js":18}],18:[function(t,e,n){var r=t("./libs/util.js"),i=t("./libs/logger.js");r.extend(n,r,{args:t("./libs/args.js"),log:i,logLevel:i.logLevel,Observable:t("./libs/Observable.js"),Observer:t("./libs/Observer.js"),queue:t("./libs/queue.js")})},{"./libs/Observable.js":19,"./libs/Observer.js":20,"./libs/args.js":21,"./libs/logger.js":22,"./libs/queue.js":23,"./libs/util.js":24}],19:[function(t,e,n){"use strict";function r(t,e){e||(e={}),this._observableProps=o(t,e),this.resetObservers()}function i(t,e){h.each(e,function(e){var n=h.indexOf(t,e);n>=0&&t.splice(n,1)})}function s(t,e){var n=[];return h.each(e,function(e){h.has(t.observers,e)?t.observers[e].length&&(n=n.concat(t.observers[e])):c.warn("Observable.hasObservers: invalid event type %s",e)}),n}function a(t,e,n){h.has(e.oneTimeResults,t)&&n.notify.apply(n,e.oneTimeResults[t])}function o(t,e){return h.extend({onAdd:h.noop,onRemove:h.noop,onEvent:h.noop,oneTimeEvents:[]},e,{eventsMonitored:t,observers:{},oneTimeResults:{}})}var h=t("./util.js"),u=t("./args.js"),c=t("./logger.js"),l=t("./Observer.js");r.prototype={observe:function(t,e,n,r){var i,s=u("observe",arguments,2,4);return t=s.nextFromReq(this._observableProps.eventsMonitored),t&&(e=s.nextReq("function"),n=s.next("function"),r=s.next("object"),i=new l(this,t,e,r,n),this._observableProps.observers[t].push(i),this._observableProps.onAdd(t,i),this.isOneTimeEvent(t)&&a(t,this._observableProps,i)),i},hasObservers:function(t){return this.getObservers(t).length>0},stopObserving:function(t,e,n){var r=u("stopObserving",arguments);t=r.next(["array","string"],this._observableProps.eventsMonitored),e=r.next(["function"]),n=r.next(["object"]),h.each(t,function(t){var r=[],s=this.getObservers(t);h.each(s,function(i){i.matches(t,e,n)&&(i.notifyCancelled(null),r.push(i))},this),i(this._observableProps.observers[t],r),r.length&&this._observableProps.onRemove(t,r)},this)},abortObservers:function(t){var e=[];if(this.hasObservers()){var n=this.getObservers().slice();h.each(n,function(n){n.notifyCancelled(t),e.push(n)},this),this.resetObservers(),e.length&&this._observableProps.onRemove(this.event,e)}},getObservers:function(t){return t=u("getObservers",arguments).listFrom(this._observableProps.eventsMonitored,!0),s(this._observableProps,t)},triggerEvent:function(t){var e=u("triggerEvent",arguments),n=e.listFromWarn(this._observableProps.eventsMonitored,!0),r=e.restAsList();n&&h.each(n,function(e){if(this.isOneTimeEvent(t)){if(h.isArray(this._observableProps.oneTimeResults,t))return void c.warn("One time event was triggered twice, should by definition be triggered once",t);this._observableProps.oneTimeResults[t]=r}var n=this.getObservers(e),i=0;h.each(n,function(t){t.notify.apply(t,r.slice(0)),i++}),this._observableProps.onEvent.apply(null,[e,i].concat(r.slice(0)))},this)},resetObservers:function(){h.each(this._observableProps.eventsMonitored,function(t){this._observableProps.observers[t]=[]},this)},isOneTimeEvent:function(t){return h.contains(this._observableProps.oneTimeEvents,t)},observeOnce:function(t,e,n,r){var i,s=u("observeOnce",arguments,2,4);return t=s.nextFromWarn(this._observableProps.eventsMonitored),t&&(e=s.nextReq("function"),n=s.next("function"),r=s.next("object"),i=new l(this,t,e,r,n,!0),this._observableProps.observers[t].push(i),this._observableProps.onAdd(t,i),this.isOneTimeEvent(t)&&a(t,this._observableProps,i)),i}},e.exports=r},{"./Observer.js":20,"./args.js":21,"./logger.js":22,"./util.js":24}],20:[function(t,e,n){"use strict";function r(t,e,n,r,i,s){if("function"!=typeof n)throw new Error("Must provide a valid notifyFn");this.observable=t,this.fn=n,this.event=e,this.cancelFn=i||function(){},this.context=r,this.oneTimeEvent=!!s}var i=t("./util.js");r.prototype={notify:function(){var t=i.toArray(arguments);this.fn.apply(this.context,t),this.oneTimeEvent&&this.observable.stopObserving(this.event,this.fn,this.context)},matches:function(t,e,n){return i.isArray(t)?i.contains(t,function(t){return this.matches(t,e,n)},this):!(t&&t!==this.event||e&&e!==this&&e!==this.fn||n&&n!==this.context)},notifyCancelled:function(t){this.cancelFn.call(this.context,t||null)}},e.exports=r},{"./util.js":24}],21:[function(t,e,n){"use strict";function r(t,e,n,i){if("string"!=typeof t||!h.isObject(e))throw new Error("Args requires at least 2 args: fnName, arguments[, minArgs, maxArgs]");if(!(this instanceof r))return new r(t,e,n,i);this.fnName=t,this.argList=h.toArray(e),this.origArgs=h.toArray(e);var s=this.length=this.argList.length;if(this.pos=-1,h.isUndefined(n)&&(n=0),h.isUndefined(i)&&(i=this.argList.length),n>s||s>i){var a=i>n?h.printf("%d to %d",n,i):n;throw Error(h.printf("%s must be called with %s arguments, but received %d",t,a,s))}}function i(t,e){return e===!0?!0:(h.isArray(e)||(e=[e]),h.contains(e,function(e){switch(e){case"array":return h.isArray(t);case"string":return"string"==typeof t;case"number":return isFinite(parseInt(t,10));case"int":case"integer":return isFinite(parseFloat(t));case"object":return h.isObject(t);case"function":return"function"==typeof t;case"bool":case"boolean":return"boolean"==typeof t;case"boolean-like":return!h.isObject(t);default:throw new Error("Args received an invalid data type: "+e)}}))}function s(t,e,n,r){if(r=h.printf("%s: invalid argument at pos %d, %s (received %s)",e,n,r),t===!0)throw new Error(r);if(!h.has(u,t))throw new Error("The `required` value passed to Args methods must either be true or a method name from logger");u[t](r)}function a(t,e,n){u.warn("%s: invalid choice %s, must be one of [%s]",t,e,n)}function o(t,e){if(e===!0)return t;var n=h.isArray(e)?e[0]:e;switch(n){case"array":return h.isArray(t)?t:[t];case"string":return t+"";case"number":return parseFloat(t);case"int":case"integer":return parseInt(t,10);case"bool":case"boolean":case"boolean-like":return!!t;case"function":case"object":return t;default:throw new Error("Args received an invalid data type: "+n)}}var h=t("./util.js"),u=t("./logger.js");r.prototype={orig:function(){return this.origArgs.slice(0)},restAsList:function(t,e){var n=this.argList.slice(0);if(t||e)for(var r=0,i=n.length;i>r;r++)this._arg(e||!0,null,t>r);return n},skip:function(){return this.argList.length&&(this.pos++,this.argList.shift()),this},next:function(t,e){return this._arg(t,e,!1)},nextWarn:function(t,e){return this._arg(t,e,"warn")},nextReq:function(t){return this._arg(t,null,!0)},nextFrom:function(t,e){return this._from(t,e,!1)},nextFromWarn:function(t,e){return this._from(t,e,"warn")},nextFromReq:function(t){return this._from(t,null,!0)},listFrom:function(t,e){return this._list(t,e,!1)},listFromWarn:function(t,e){return this._list(t,e,"warn")},listFromReq:function(t){return this._list(t,null,!0)},_arg:function(t,e,n){return this.pos++,(h.isUndefined(t)||null===t)&&(t=!0),this.argList.length&&i(this.argList[0],t)?o(this.argList.shift(),t):(n&&s(n,this.fnName,this.pos,h.printf("must be of type %s",t)),e)},_from:function(t,e,n){return this.pos++,this.argList.length&&h.contains(t,this.argList[0])?this.argList.shift():(n&&s(n,this.fnName,this.pos,h.printf("must be one of %s",t)),e)},_list:function(t,e,n){this.pos++;var r=[],i=this.argList[0];return!this.argList.length||h.isEmpty(i)||!h.isArray(i)&&h.isObject(i)||(this.argList.shift(),h.isArray(i)?r=h.map(i,function(e){return h.contains(t,e)?e:void a(this.fnName,e,t)},this):h.contains(t,i)?r=[i]:a(this.fnName,i,t)),h.isEmpty(r)?(n&&s(n,this.fnName,this.pos,h.printf("choices must be in [%s]",t)),e===!0?t:e):r}},e.exports=r},{"./logger.js":22,"./util.js":24}],22:[function(t,e,n){"use strict";function r(t){return t.charAt(0).toUpperCase()+t.substr(1)}function i(){var t;return"undefined"!=typeof window&&window.location&&window.location.search&&(t=window.location.search.match("\bdebugLevel=([0-9]+)\b")),t?parseInt(t[1],10):h}function s(){return!0}function a(t,e){return e.length?t instanceof RegExp?!t.test(e[0]+""):!(e[0]+"").match(t):!0}function o(t){switch(t){case!1:return 0;case"off":return 0;case"none":return 0;case"error":return 1;case"warn":return 2;case"warning":return 2;case"info":return 3;case"log":return 4;case"debug":return 5;case!0:return h;case"on":return h;case"all":return h;default:return h}}var h=2,u=!1,c={error:s,warn:s,info:s,log:s,debug:s,time:s,timeEnd:s,group:s,groupEnd:s},l=t("./util.js"),f=function(){f.log.apply(null,l.toArray(arguments))};f.warn=s,f.error=s,f.info=s,f.log=s,f.debug=s,f.isErrorEnabled=s,f.isWarnEnabled=s,f.isInfoEnabled=s,f.isLogEnabled=s,f.isDebugEnabled=s,f.logLevel=function(t,e){if("number"!=typeof t&&(t=o(t)),u===t)return function(){};l.each(["error","warn","info","log","debug"],function(n,i){var o="undefined"!=typeof console&&t>=i+1;if(o){var h=l.bind(console["debug"===n?"log":n],console);f[n]=function(){var t=l.toArray(arguments);if(t.length>1&&"string"==typeof t[0]){var n=t[0].match(/(%s|%d|%j)/g);if(n){var r=[l.printf.apply(l,t)];t=t.length>n.length+1?r.concat(t.slice(n.length+1)):r}}e&&a(e,t)||h.apply("undefined"==typeof console?c:console,t)}}else f[n]=s;f["is"+r(n)+"Enabled"]=function(){return o}});var n=function(t){return function(){f.logLevel(t)}}(u);return u=t,n},f.logLevel(i()),e.exports=f},{"./util.js":24}],23:[function(t,e,n){"use strict";function r(t){this.needs=0,this.met=0,this.queued=[],this.errors=[],this.criteria=[],this.processing=!1,i.each(t,this.addCriteria,this)}var i=t("./util.js");r.prototype={addCriteria:function(t,e){if(this.processing)throw new Error("Cannot call addCriteria() after invoking done(), fail(), or handler() methods");return this.criteria.push(e?[t,e]:t),this},getHandler:function(){var t,e;return this.addCriteria(function(n){e!==i.undef?n(e):t=n}),function(n){t?t(n):e=n}},ready:function(){return this.needs===this.met},done:function(t,e){return t&&this._runOrStore(function(){this.hasErrors()||t.call(e)}),this},fail:function(t,e){return this._runOrStore(function(){this.hasErrors()&&t.apply(e,this.getErrors())}),this},handler:function(t,e){return this._runOrStore(function(){t.apply(e,this.hasErrors()?this.getErrors():null)}),this},chain:function(t){return this.addCriteria(t.handler,t),this},when:function(t){this._runOrStore(function(){this.hasErrors()?t.reject.apply(t,this.getErrors()):t.resolve()})},addError:function(t){this.errors.push(t)},hasErrors:function(){return this.errors.length},getErrors:function(){return this.errors.slice(0)},_process:function(){this.processing=!0,this.needs=this.criteria.length,i.each(this.criteria,this._evaluateCriteria,this)},_evaluateCriteria:function(t){var e=null;i.isArray(t)&&(e=t[1],t=t[0]);try{t.call(e,i.bind(this._criteriaMet,this))}catch(n){this.addError(n)}},_criteriaMet:function(t){t&&this.addError(t),this.met++,this.ready()&&i.each(this.queued,this._run,this)},_runOrStore:function(t){this.processing||this._process(),this.ready()?this._run(t):this.queued.push(t)},_run:function(t){t.call(this)}},e.exports=function(t,e){var n=new r(t);return e&&n.done(e),n}},{"./util.js":24}],24:[function(t,e,n){(function(e){"use strict";function r(t,e){switch(e){case"%d":return parseInt(t,10);case"%j":return t=c.isObject(t)?JSON.stringify(t):t+"",t.length>500&&(t=t.substr(0,500)+".../*truncated*/...}"),t;case"%s":return t+"";default:return t}}function i(t){return c.isObject(t)&&t+""=="[object Arguments]"}function s(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),n=this,r=function(){},i=function(){return n.apply(this instanceof r&&t?this:t,e.concat(Array.prototype.slice.call(arguments)))};return r.prototype=this.prototype,i.prototype=new r,i}function a(t,e){var n,r;for(n=0,r=this.length;r>n;++n)n in this&&t.call(e,this[n],n,this)}function o(t){return"[object Array]"===Object.prototype.toString.call(t)}function h(t,e){if(null===this)throw new TypeError;var n,r,i=Object(this),s=i.length>>>0;if(0===s)return-1;if(n=0,arguments.length>1&&(n=Number(arguments[1]),n!==n?n=0:0!==n&&n!==1/0&&n!==-(1/0)&&(n=(n>0||-1)*Math.floor(Math.abs(n)))),n>=s)return-1;for(r=n>=0?n:Math.max(s-Math.abs(n),0);s>r;r++)if(r in i&&i[r]===t)return r;return-1}var u,c=n,l=["value","child_added","child_removed","child_updated","child_changed"];c.undef=u,c.Firebase=e.Firebase||t("firebase"),c.isDefined=function(t){return t!==u},c.isUndefined=function(t){return t===u},c.isObject=function(t){return Object.prototype.isPrototypeOf(t)},c.isArray=function(t){return(Array.isArray||o).call(null,t)},c.isFunction=function(t,e){return"string"==typeof e?c.isObject(t)&&c.has(t,e)&&"function"==typeof t[e]:"function"==typeof t},c.toArray=function(t,e){var n=c.map(t,function(t,e){return t});return e>0?n.slice(e):n},c.extend=function(t,e){var n=c.toArray(arguments),r="boolean"==typeof n[0]&&n.shift(),i=n.shift();return c.each(n,function(t){c.isObject(t)&&c.each(t,function(t,e){i[e]=r&&c.isObject(i[e])?c.extend(!0,i[e],t):t})}),i},c.bind=function(t,e){var n=Array.prototype.slice.call(arguments,1);return(t.bind||s).apply(t,n)},c.isEmpty=function(t){return t===u||null===t||c.isArray(t)&&0===t.length||c.isObject(t)&&!c.contains(t,function(t){return!0})},c.keys=function(t){var e=[];return c.each(t,function(t,n){e.push(n)}),e},c.map=function(t,e,n){var r=[];return c.each(t,function(i,s){var a=e.call(n,i,s,t);a!==u&&r.push(a)}),r},c.mapObject=function(t,e,n){var r={};return c.each(t,function(i,s){var a=e.call(n,i,s,t);a!==u&&(r[s]=a)}),r},c.find=function(t,e,n){if(c.isArray(t)){for(var r=0,i=t.length;i>r;r++)if(e.call(n,t[r],r,t)===!0)return t[r]}else if(c.isObject(t)){var s;for(s in t)if(t.hasOwnProperty(s)&&e.call(n,t[s],s,t)===!0)return t[s]}return u},c.filter=function(t,e,n){var r=c.isArray(t),i=r?[]:{};return c.each(t,function(s,a){e.call(n,s,a,t)&&(r?i.push(s):i[a]=s)}),i},c.reduce=function(t,e,n){return c.each(t,function(r,i){e=n(e,r,i,t)}),e},c.has=function(t,e){return c.isObject(t)&&t[e]!==u},c.val=function(t,e){return c.has(t,e)?t[e]:u},c.contains=function(t,e,n){if("function"!=typeof e){if(c.isArray(t))return c.indexOf(t,e)>-1;e=function(t){return function(e){return e===t}}(e)}return c.find(t,e,n)!==u},c.each=function(t,e,n){if(c.isArray(t)||i(t))(t.forEach||a).call(t,e,n);else if(c.isObject(t)){var r;for(r in t)t.hasOwnProperty(r)&&e.call(n,t[r],r,t)}},c.indexOf=function(t,e){return(t.indexOf||h).call(t,e)},c.remove=function(t,e){var n=!1;if(c.isArray(t)){var r=c.indexOf(t,e);r>-1&&(t.splice(r,1),n=!0)}else if(c.isObject(t)){var i;for(i in t)if(t.hasOwnProperty(i)&&e===t[i]){n=!0,delete t[i];break}}return n},c.defer=function(t,e){var n=c.toArray(arguments);setTimeout(c.bind.apply(null,n),0)},c.call=function(t,e){var n=c.toArray(arguments,2),r=[];return c.each(t,function(t){return"function"!=typeof t||e?void(c.isObject(t)&&"function"==typeof t[e]&&r.push(t[e].apply(t,n))):r.push(t.apply(null,n))}),r},c.isEqual=function(t,e,n){if(t===e)return!0;if(typeof t!=typeof e)return!1;if(c.isObject(t)&&c.isObject(e)){var r=c.isArray(t),i=c.isArray(e);if(r||i)return r&&i&&t.length===e.length&&!c.contains(t,function(t,n){return!c.isEqual(t,e[n])});var s=n?c.keys(t):c.keys(t).sort(),a=n?c.keys(e):c.keys(e).sort();return c.isEqual(s,a)&&!c.contains(t,function(t,n){return!c.isEqual(t,e[n])})}return!1},c.bindAll=function(t,e){return c.each(e,function(n,r){"function"==typeof n&&(e[r]=c.bind(n,t))}),e},c.printf=function(){var t=c.toArray(arguments),e=t.shift(),n=e.match(/(%s|%d|%j)/g);return n&&t.length&&c.find(n,function(n){return e=e.replace(n,r(t.shift(),n)),0===t.length}),e},c.mergeToString=function(t){return 0===t.length?null:1===t.length?t[0]:"["+t.join("][")+"]"},c.construct=function(t,e){function n(){return t.apply(this,e)}return n.prototype=t.prototype,new n},c.noop=function(){};var f=[];c.isFirebaseRef=function(t){var e=c.isObject(t),n=e?Object.getPrototypeOf(t):!1;return n&&n.constructor===c.Firebase.prototype.constructor?!0:e&&"function"==typeof t.ref&&"function"==typeof t.ref().transaction?!0:c.find(f,function(n){return e?t instanceof n:t===n})},c.registerFirebaseWrapper=function(t){f.push(t)},c._mockFirebaseRef=function(t){c.Firebase=t},c.escapeEmail=function(t){return(t||"").replace(".",",")},c.assertValidEvent=function(t){if(!c.contains(l,t))throw new Error("Event must be one of "+l+", but received "+t)},c.inherits=function(t,e){var n=[t.prototype].concat(c.toArray(arguments).slice(2));return t.prototype=new e,t.prototype.constructor=e,c.each(n,function(e){c.each(e,function(e,n){t.prototype[n]=e})}),t.prototype._super=function(){e.apply(this,arguments)},t},c.deepCopy=function(t){if(!c.isObject(t))return t;var e=c.isArray(t)?[]:{};return c.each(t,function(t,n){e[n]=c.deepCopy(t)}),e},c.pick=function(t,e){if(!c.isObject(t))return{};var n=c.isArray(t)?[]:{};return c.each(e,function(e){n[e]=t[e]}),n},c.eachByPath=function(t,e,n,r){var i={};c.each(e,function(e,n){var r=t.pathFor(n),s=t.getField(n),a=s?s.id:n;c.has(i,r.name())||(i[r.name()]={path:r,data:{}}),i[r.name()].data[a]=e}),c.each(i,function(t){n.call(r,t.path,t.data)})}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{firebase:"firebase"}],"firebase-util":[function(t,e,n){"use strict";var r=t("./common/index.js");r.extend(n,t("./common/exports.js"),t("./NormalizedCollection/exports.js")),"undefined"!=typeof window&&(window.hasOwnProperty("Firebase")?window.Firebase.util=r:console.warn("Firebase not found on the global window instance. Cannot add Firebase.util namespace."))},{"./NormalizedCollection/exports.js":2,"./common/exports.js":17,"./common/index.js":18}]},{},[1]); \ No newline at end of file diff --git a/dist/firebase-util-paginate.min.js b/dist/firebase-util-paginate.min.js new file mode 100644 index 0000000..739c8ca --- /dev/null +++ b/dist/firebase-util-paginate.min.js @@ -0,0 +1,11 @@ +/*! + * Firebase-util: A set of experimental power tools for Firebase. + * + * Version: 0.2.4 + * URL: https://github.com/firebase/firebase-util + * Date: 2015-04-15T01:10:42.290Z + * License: MIT http://firebase.mit-license.org/ + */ +require=function t(e,n,r){function i(o,a){if(!n[o]){if(!e[o]){var u="function"==typeof require&&require;if(!a&&u)return u(o,!0);if(s)return s(o,!0);var h=new Error("Cannot find module '"+o+"'");throw h.code="MODULE_NOT_FOUND",h}var c=n[o]={exports:{}};e[o][0].call(c.exports,function(t){var n=e[o][1][t];return i(n?n:t)},c,c.exports,t,e,n,r)}return n[o].exports}for(var s="function"==typeof require&&require,o=0;o2&&!s.isObject(n))throw new Error("Optional third argument to Firebase.util.Scroll must be an object of key/value pairs");var i=new u(t);return i.scroll=new o(i,e,r(n,"windowSize","Scroll")),i},n.Paginate=function(t,e,n){if(!s.isFirebaseRef(t))throw new Error("First argument to Firebase.util.Paginate must be a valid Firebase ref");if("string"!=typeof e)throw new Error("Second argument to Firebase.util.Paginate must be a valid string");if(arguments.length>2&&!s.isObject(n))throw new Error("Optional third argument to Firebase.util.Paginate must be an object of key/value pairs");var i=new u(t);return i.page=new a(i,e,r(n,"pageSize","Paginate")),i}},{"../common":9,"./libs/Paginate.js":5,"./libs/ReadOnlyRef.js":6,"./libs/Scroll.js":7}],3:[function(t,e,n){"use strict";function r(t,e,n){i.log.debug("Cache: caching %s using field=%s maxRecordsLoaded=%d",t.toString(),e,n),this.offset=new s({field:e,max:n,ref:t.ref()}),this.outRef=t,this.inRef=null,this.queryRef=null,this.countRef=null,this.keys={},this.start=0,this.count=-1,this.endCount=-1,this.nextListeners=[],this.offset.observe(this._keyChange,this)}var i=t("../../common"),s=t("./Offset");r.prototype.moveTo=function(t,e){i.log.debug("Cache.moveTo: startOffset=%d, numberOfRecords=%d",t,e);var n=this.start,r=this.count;this.start=t,this.count=e,this.endCount=this.start+this.count,n!==this.start?this.offset.goTo(t,e):r!==this.count&&this._refChange()},r.prototype.hasNext=function(){return-1===this.count||this.endCount>this.start+this.count},r.prototype.hasPrev=function(){return this.start>0},r.prototype.observeHasNext=function(t,e){var n=this.nextListeners,r=[t,e];return n.push(r),function(){i.remove(n,r)}},r.prototype.destroy=function(){this._unsubscribe(),this.offset.destroy(),this.offset=null,this.start=0,this.count=-1,this.inRef=null,this.outRef=null,this.queryRef=null,this.countRef=null,this.keys=null,this.nextListeners=null},r.prototype._keyChange=function(t,e,n){this.inRef=n,i.log.debug("Cache._keyChange: %s %s %s",t,e,n.toString()),this._refChange()},r.prototype._unsubscribe=function(){this.queryRef&&(this.queryRef.off("child_added",this._add,this),this.queryRef.off("child_removed",this._remove,this),this.queryRef.off("child_moved",this._move,this),this.queryRef.off("child_changed",this._change,this),this.queryRef.off("value",this._value,this),this.queryRef.off("value",this._removeOrphans,this),this.queryRef=null),this.countRef&&(this.countRef.off("value",this._count,this),this.countRef=null)},r.prototype._refChange=function(){this._unsubscribe(),this.inRef&&this.count>-1&&(this.countRef=this.inRef.limitToFirst(this.count+1),this.countRef.on("value",this._count,this),this.queryRef=this.inRef.limitToFirst(this.count),this.queryRef.on("child_added",this._add,this),this.queryRef.on("child_removed",this._remove,this),this.queryRef.on("child_moved",this._move,this),this.queryRef.on("child_changed",this._change,this),this.queryRef.on("value",this._value,this),this.queryRef.once("value",this._removeOrphans,this))},r.prototype._add=function(t,e){var n=t.key();i.has(this.keys,n)?i.isEqual(this.keys[n],t.val())||this._change(t):(this.keys[n]=t,this.outRef.$trigger("child_added",t,e))},r.prototype._remove=function(t){var e=t.key();i.has(this.keys,e)&&(this.outRef.$trigger("child_removed",t),delete this.keys[e])},r.prototype._move=function(t,e){var n=t.key();i.has(this.keys,n)&&(this.keys[n]=t,this.outRef.$trigger("child_moved",t,e))},r.prototype._change=function(t){this.keys[t.key()]=t,this.outRef.$trigger("child_changed",t)},r.prototype._value=function(t){this.outRef.$trigger("value",t)},r.prototype._count=function(t){this.endCount=this.start+t.numChildren();var e=this.hasNext();i.each(this.nextListeners,function(t){t[0].call(t[1],e)})},r.prototype._removeOrphans=function(t){i.each(this.keys,function(e,n){t.hasChild(n)||(this.outRef.$trigger("child_removed",e),delete this.keys[n])},this)},e.exports=r},{"../../common":9,"./Offset":4}],4:[function(t,e,n){"use strict";function r(t){this.keys=[],this.field=t.field,this.ref=o(t.ref,t.field),this.max=t.max,this.listeners=[],this.curr=0,this.sub=null,this.isSubscribing=!1,this.lastNotifyValue=h.undef,this._debouncedRecache=a(function(){h.log.debug("Offset._debouncedRecache: recaching keys for offset %d",this.curr),this.keys=[],this._grow(this._listen)},this,100,1e3)}function i(t,e){var n;switch(e){case"$key":n=t.key();break;case"$priority":n=t.getPriority();break;default:var r=t.val();if(!h.isObject(r))throw new Error("A value of type "+typeof r+'Was found. But we are attempting to order by child field "'+e+"\". Pagination requires all records to be objects or it can't determine an appropriate offset value.");n=r[e]}return{val:n,key:t.key()}}function s(t,e){return e===!1?null:null===e?t:t.startAt(e.val,e.key)}function o(t,e){return"$key"===e?t.orderByKey():"$priority"===e?t.orderByPriority():t.orderByChild(e)}function a(t,e,n,r){function i(){if(u&&(u(),u=null),a&&Date.now()-a>r)c||(c=!0,setTimeout(s,0));else{a||(a=Date.now());var t=setTimeout(s,n);u=function(){clearTimeout(t)}}}function s(){u=null,a=null,c=!1,t.apply(e,h)}function o(){h=Array.prototype.slice.call(arguments,0),i()}var a,u,h,c;if("number"==typeof e&&(r=n,n=e,e=null),"number"!=typeof n)throw new Error("Must provide a valid integer for wait. Try 0 for a default");if("function"!=typeof t)throw new Error("Must provide a valid function to debounce");return r||(r=10*n||100),o.running=function(){return a>0},o}function u(t){var e=t.length;return e?t[e-1]:null}var h=t("../../common");r.prototype.goTo=function(t){t!==this.curr&&(h.log("Offset.goTo: offset changed from %d to %d",this.curr,t),this.curr=t,this.lastNotifyValue=h.undef,this._listen())},r.prototype.observe=function(t,e){this.listeners.push([t,e]);var n=this.getKey(),r=s(this.ref,n);t.call(e,n&&n.val,n&&n.key,r)},r.prototype.getKey=function(t){return arguments.length||(t=this.curr),0===t?null:this.keys.length>t&&this.keys[t]},r.prototype.destroy=function(){this._unsubscribe(),this.curr=0,this.keys=[],this.lastNotifyValue=h.undef,this.isSubscribing=!1},r.prototype._notify=function(){var t=this.getKey();if(!h.isEqual(this.lastNotifyValue,t)){h.log("Offset._notify: key at offset %d is %s",this.curr,t&&t.key),this.lastNotifyValue=t;var e=s(this.ref,t);h.each(this.listeners,function(n){n[0].call(n[1],t&&t.val,t&&t.key,e)})}},r.prototype._recache=function(){this.isSubscribing||this._debouncedRecache()};var c=0;r.prototype._grow=function(t){var e=this,n=e.keys.length;if(e.curr>=n){var r=e.getKey(),s=u(e.keys),o=Math.min(e.curr+(s?2:1)-n,e.max),a=null!==s?e.ref.startAt(s.val,s.key):e.ref;a.limitToFirst(o).once("value",function(n){var a=null!==s;if(n.forEach(function(t){return a?void(a=!1):void e.keys.push(i(t,e.field))}),c++>1e4)throw new Error("Tried to fetch more than 10,000 pages to determine the correct offset. Giving up now. Sorry.");e.curr>=e.keys.length&&n.numChildren()===o?setTimeout(h.bind(e._grow,e,t),0):(c=0,h.log.debug("Offset._grow: Cached %d keys",e.keys.length),t.call(e,!h.isEqual(e.getKey(),r)))})}else t.call(e,!1)},r.prototype._startOffset=function(){return Math.max(0,this.curr-this.max,this.curr-10)},r.prototype._queryRef=function(){var t=this._startOffset(),e=this.ref;if(t>0){var n=this.getKey(t);e=e.startAt(n.val,n.key)}return e.limitToLast(Math.max(this.curr-t,1))},r.prototype._moved=function(t){t.key()===this.getKey()&&this._recache()},r.prototype._unsubscribe=function(){this.sub&&(this.sub.off("child_added",this._recache,this),this.sub.off("child_moved",this._moved,this),this.sub.off("child_removed",this._recache,this),this.sub.off("value",this._doneSubscribing,this),this.sub=null)},r.prototype._subscribe=function(){this._unsubscribe(),this.sub=this._queryRef(),this.isSubscribing=!0,this.sub.on("child_added",this._recache,this),this.sub.on("child_moved",this._moved,this),this.sub.on("child_removed",this._recache,this),this.sub.once("value",this._doneSubscribing,this)},r.prototype._doneSubscribing=function(){this.isSubscribing=!1,this._notify()},r.prototype._monitorEmptyOffset=function(){function t(i){var s=i.numChildren();s>(null===r?0:1)&&(h.log.debug("Offset._monitorEmptyOffset: A value exists now."),n.off("value",t),e._grow())}var e=this,n=e.ref,r=null;this.keys.length&&(r=u(this.keys),n=n.startAt(r.val,r.key)),h.log.debug("Offset._monitorEmptyOffset: No value exists at offset %d, currently %d keys at this path. Watching for a new value.",this.curr,this.keys.length),n.limitToFirst(2).on("value",t)},r.prototype._listen=function(){this._unsubscribe(),this.curr>this.keys.length?this._grow(function(){this.keys.length>this.curr?this._subscribe():(this._monitorEmptyOffset(),this._notify())}):this._subscribe()},e.exports=r},{"../../common":9}],5:[function(t,e,n){"use strict";function r(t,e,n){this.currPage=0,this.field=e,this.ref=t,this.pageSize=n.pageSize,this.max=n.maxCacheSize,this.subs=[],this.pageChangeListeners=[],this.pageCountListeners=[],this.cache=new a(t,e,n.maxCacheSize),this.pageCount=-1,this.couldHaveMore=!1,this.cache.observeHasNext(this._countPages,this)}function i(t,e){return 0===t?0:Math.ceil(t/e)}function s(t,e){var n={};if(n.bindFunction=function(t,e){return function(){return t.apply(e,[e])}},n.stateChange=function(t){4==n.request.readyState&&n.callbackFunction(n.request.responseText)},n.getRequest=function(){return window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):window.XMLHttpRequest?new XMLHttpRequest:!1},n.postBody=arguments[2]||"",n.callbackFunction=e,n.url=t,n.request=n.getRequest(),n.request){var r=n.request;r.onreadystatechange=n.bindFunction(n.stateChange,n),""!==n.postBody?(r.open("POST",t,!0),r.setRequestHeader("X-Requested-With","XMLHttpRequest"),r.setRequestHeader("Content-type","application/x-www-form-urlencoded"),r.setRequestHeader("Connection","close")):r.open("GET",t,!0),r.send(n.postBody)}return n}var o=t("../../common"),a=t("./Cache");r.prototype.next=function(){return this.hasNext()&&(this.currPage++,o.log.debug("Paginate.next: current page is %d",this.currPage),this._pageChange()),this},r.prototype.prev=function(){return this.hasPrev()&&(this.currPage--,o.log.debug("Paginate.prev: current page is %d",this.currPage),this._pageChange()),this},r.prototype.setPage=function(t){t>0&&t<=this.pageCount?(this.currPage=t,o.log.debug("Paginate.setPage: current page is %d",this.currPage),this._pageChange()):o.log.warn("Paginate.setPage: invalid page number %d",t)},r.prototype.hasNext=function(){return this.cache.hasNext()},r.prototype.hasPrev=function(){return this.currPage>1},r.prototype.onPageChange=function(t,e){var n=this.pageChangeListeners,r=[t,e];return n.push(r),t.call(e,this.currPage),function(){o.remove(n,r)}},r.prototype.onPageCount=function(t,e){var n=this.pageCountListeners,r=[t,e];return n.push(r),this.pageCount>-1?t.call(e,this.pageCount,this.couldHaveMore):this._countPages(),function(){o.remove(n,r)}},r.prototype.getCountByDowloadingAllKeys=function(t,e){var n=this;n.downloadingEverything=!0;var r=n.ref.ref().toString();r.match(/\/$/)||(r+="/"),r+=".json?shallow=true",s(r,function(r){var s=0;try{s=o.keys(JSON.parse(r)).length}catch(a){o.log.warn(a)}o.log.debug("Paginate.getCountByDownloadingAllKeys: found %d keys",s),n.downloadingEverything=!1,n.pageCount=i(s,n.pageSize),n.couldHaveMore=!1,n._notifyPageCount(),t&&t.call(e,s)})},r.prototype.destroy=function(){this.cache.destroy(),this.cache=null,this.ref=null,o.each(this.subs,function(t){t()}),this.subs=[],this.pageCountListeners.length=0,this.pageChangeListeners.length=0},r.prototype._countPages=function(){var t=this,e=t.currPage;if(!this.downloadingEverything)if(-1===t.pageCount){var n=t.max,r=t.pageSize,i=this.ref.ref().limitToFirst(n);i.once("value",function(e){-1===t.pageCount&&(t.couldHaveMore=e.numChildren()===n,t.pageCount=Math.ceil(e.numChildren()/r),t._notifyPageCount(),t._countPages())})}else e>=t.pageCount&&(t.pageCount=e,t.couldHaveMore=t.cache.hasNext(),t._notifyPageCount())},r.prototype._pageChange=function(){var t=this.currPage,e=(t-1)*this.pageSize;this.cache.moveTo(e,this.pageSize),this._countPages(),o.each(this.pageChangeListeners,function(e){e[0].call(e[1],t)})},r.prototype._notifyPageCount=function(){var t=this.pageCount,e=this.couldHaveMore;o.each(this.pageCountListeners,function(n){n[0].call(n[1],t,e)})},e.exports=r},{"../../common":9,"./Cache":3}],6:[function(t,e,n){"use strict";function r(t){this._ref=t,this._obs=new a.Observable(["value","child_added","child_removed","child_moved","child_changed"])}function i(t){return function(){var e=a.toArray(arguments),n=this.ref();return n[t].apply(n,e)}}function s(t){return function(){throw new Error(t+" is not supported. This is a read-only reference. You can modify child records after calling .child(), or work with the original by using .ref().")}}function o(t){return function(){throw new Error(t+" is not supported for Paginate and Scroll references. Try calling it on the original reference used to create the instance instead.")}}var a=t("../../common");r.prototype={on:function(t,e,n,r){this._obs.observe(t,e,n,r)},once:function(t,e,n,r){function i(n){this.off(t,i,this),e.call(r,n)}this.on(t,i,n,this)},off:function(t,e,n){this._obs.stopObserving(t,e,n)},ref:function(){return this._ref},child:i("child"),parent:i("parent"),root:i("root"),name:i("name"),key:i("key"),toString:i("toString"),auth:i("auth"),unauth:i("unauth"),authWithCustomToken:i("authWithCustomToken"),authAnonymously:i("authAnonymously"),authWithPassword:i("authWithPassword"),authWithOAuthPopup:i("authWithOAuthPopup"),authWithOAuthRedirect:i("authWithOAuthRedirect"),authWithOAuthToken:i("authWithOAuthToken"),getAuth:i("getAuth"),onAuth:i("onAuth"),offAuth:i("offAuth"),createUser:i("createUser"),changePassword:i("changePassword"),removeUser:i("removeUser"),resetPassword:i("resetPassword"),changeEmail:i("changeEmail"),goOffline:i("goOffline"),goOnline:i("goOnline"),set:s("set"),update:s("update"),remove:s("remove"),push:s("push"),setWithPriority:s("setWithPriority"),setPriority:s("setPriority"),transaction:s("transaction"),limit:o("limit"),onDisconnect:o("onDisconnect"),orderByChild:o("orderByChild"),orderByKey:o("orderByKey"),orderByPriority:o("orderByPriority"),limitToFirst:o("limitToFirst"),limitToLast:o("limitToLast"),startAt:o("startAt"),endAt:o("endAt"),equalTo:o("equalTo"),$trigger:function(){this._obs.triggerEvent.apply(this._obs,a.toArray(arguments))}},e.exports=r},{"../../common":9}],7:[function(t,e,n){"use strict";function r(t,e,n){this.max=n.windowSize,this.start=0,this.end=0,this.cache=new i(t,e,n.maxCacheSize)}var i=t("./Cache");r.prototype.next=function(t){this.hasNext()&&(this.end=this.end+t,this.start=Math.max(0,this.end-this.max,this.start),this.cache.moveTo(this.start,this.end-this.start))},r.prototype.prev=function(t){this.hasPrev()&&(this.start=Math.max(0,this.start-t),this.end=Math.min(this.start+this.max,this.end),this.cache.moveTo(this.start,this.end-this.start))},r.prototype.hasNext=function(){return this.cache.hasNext()},r.prototype.hasPrev=function(){return this.start>0},r.prototype.observeHasNext=function(t,e){return this.cache.observeHasNext(t,e)},r.prototype.destroy=function(){this.cache.destroy(),this.ref=null,this.cache=null},e.exports=r},{"./Cache":3}],8:[function(t,e,n){var r=t("./index.js");n.log=r.log,n.logLevel=r.logLevel,n.escapeEmail=r.escapeEmail},{"./index.js":9}],9:[function(t,e,n){var r=t("./libs/util.js"),i=t("./libs/logger.js");r.extend(n,r,{args:t("./libs/args.js"),log:i,logLevel:i.logLevel,Observable:t("./libs/Observable.js"),Observer:t("./libs/Observer.js"),queue:t("./libs/queue.js")})},{"./libs/Observable.js":10,"./libs/Observer.js":11,"./libs/args.js":12,"./libs/logger.js":13,"./libs/queue.js":14,"./libs/util.js":15}],10:[function(t,e,n){"use strict";function r(t,e){e||(e={}),this._observableProps=a(t,e),this.resetObservers()}function i(t,e){u.each(e,function(e){var n=u.indexOf(t,e);n>=0&&t.splice(n,1)})}function s(t,e){var n=[];return u.each(e,function(e){u.has(t.observers,e)?t.observers[e].length&&(n=n.concat(t.observers[e])):c.warn("Observable.hasObservers: invalid event type %s",e)}),n}function o(t,e,n){u.has(e.oneTimeResults,t)&&n.notify.apply(n,e.oneTimeResults[t])}function a(t,e){return u.extend({onAdd:u.noop,onRemove:u.noop,onEvent:u.noop,oneTimeEvents:[]},e,{eventsMonitored:t,observers:{},oneTimeResults:{}})}var u=t("./util.js"),h=t("./args.js"),c=t("./logger.js"),f=t("./Observer.js");r.prototype={observe:function(t,e,n,r){var i,s=h("observe",arguments,2,4);return t=s.nextFromReq(this._observableProps.eventsMonitored),t&&(e=s.nextReq("function"),n=s.next("function"),r=s.next("object"),i=new f(this,t,e,r,n),this._observableProps.observers[t].push(i),this._observableProps.onAdd(t,i),this.isOneTimeEvent(t)&&o(t,this._observableProps,i)),i},hasObservers:function(t){return this.getObservers(t).length>0},stopObserving:function(t,e,n){var r=h("stopObserving",arguments);t=r.next(["array","string"],this._observableProps.eventsMonitored),e=r.next(["function"]),n=r.next(["object"]),u.each(t,function(t){var r=[],s=this.getObservers(t);u.each(s,function(i){i.matches(t,e,n)&&(i.notifyCancelled(null),r.push(i))},this),i(this._observableProps.observers[t],r),r.length&&this._observableProps.onRemove(t,r)},this)},abortObservers:function(t){var e=[];if(this.hasObservers()){var n=this.getObservers().slice();u.each(n,function(n){n.notifyCancelled(t),e.push(n)},this),this.resetObservers(),e.length&&this._observableProps.onRemove(this.event,e)}},getObservers:function(t){return t=h("getObservers",arguments).listFrom(this._observableProps.eventsMonitored,!0),s(this._observableProps,t)},triggerEvent:function(t){var e=h("triggerEvent",arguments),n=e.listFromWarn(this._observableProps.eventsMonitored,!0),r=e.restAsList();n&&u.each(n,function(e){if(this.isOneTimeEvent(t)){if(u.isArray(this._observableProps.oneTimeResults,t))return void c.warn("One time event was triggered twice, should by definition be triggered once",t);this._observableProps.oneTimeResults[t]=r}var n=this.getObservers(e),i=0;u.each(n,function(t){t.notify.apply(t,r.slice(0)),i++}),this._observableProps.onEvent.apply(null,[e,i].concat(r.slice(0)))},this)},resetObservers:function(){u.each(this._observableProps.eventsMonitored,function(t){this._observableProps.observers[t]=[]},this)},isOneTimeEvent:function(t){return u.contains(this._observableProps.oneTimeEvents,t)},observeOnce:function(t,e,n,r){var i,s=h("observeOnce",arguments,2,4);return t=s.nextFromWarn(this._observableProps.eventsMonitored),t&&(e=s.nextReq("function"),n=s.next("function"),r=s.next("object"),i=new f(this,t,e,r,n,!0),this._observableProps.observers[t].push(i),this._observableProps.onAdd(t,i),this.isOneTimeEvent(t)&&o(t,this._observableProps,i)),i}},e.exports=r},{"./Observer.js":11,"./args.js":12,"./logger.js":13,"./util.js":15}],11:[function(t,e,n){"use strict";function r(t,e,n,r,i,s){if("function"!=typeof n)throw new Error("Must provide a valid notifyFn");this.observable=t,this.fn=n,this.event=e,this.cancelFn=i||function(){},this.context=r,this.oneTimeEvent=!!s}var i=t("./util.js");r.prototype={notify:function(){var t=i.toArray(arguments);this.fn.apply(this.context,t),this.oneTimeEvent&&this.observable.stopObserving(this.event,this.fn,this.context)},matches:function(t,e,n){return i.isArray(t)?i.contains(t,function(t){return this.matches(t,e,n)},this):!(t&&t!==this.event||e&&e!==this&&e!==this.fn||n&&n!==this.context)},notifyCancelled:function(t){this.cancelFn.call(this.context,t||null)}},e.exports=r},{"./util.js":15}],12:[function(t,e,n){"use strict";function r(t,e,n,i){if("string"!=typeof t||!u.isObject(e))throw new Error("Args requires at least 2 args: fnName, arguments[, minArgs, maxArgs]");if(!(this instanceof r))return new r(t,e,n,i);this.fnName=t,this.argList=u.toArray(e),this.origArgs=u.toArray(e);var s=this.length=this.argList.length;if(this.pos=-1,u.isUndefined(n)&&(n=0),u.isUndefined(i)&&(i=this.argList.length),n>s||s>i){var o=i>n?u.printf("%d to %d",n,i):n;throw Error(u.printf("%s must be called with %s arguments, but received %d",t,o,s))}}function i(t,e){return e===!0?!0:(u.isArray(e)||(e=[e]),u.contains(e,function(e){switch(e){case"array":return u.isArray(t);case"string":return"string"==typeof t;case"number":return isFinite(parseInt(t,10));case"int":case"integer":return isFinite(parseFloat(t));case"object":return u.isObject(t);case"function":return"function"==typeof t;case"bool":case"boolean":return"boolean"==typeof t;case"boolean-like":return!u.isObject(t);default:throw new Error("Args received an invalid data type: "+e)}}))}function s(t,e,n,r){if(r=u.printf("%s: invalid argument at pos %d, %s (received %s)",e,n,r),t===!0)throw new Error(r);if(!u.has(h,t))throw new Error("The `required` value passed to Args methods must either be true or a method name from logger");h[t](r)}function o(t,e,n){h.warn("%s: invalid choice %s, must be one of [%s]",t,e,n)}function a(t,e){if(e===!0)return t;var n=u.isArray(e)?e[0]:e;switch(n){case"array":return u.isArray(t)?t:[t];case"string":return t+"";case"number":return parseFloat(t);case"int":case"integer":return parseInt(t,10);case"bool":case"boolean":case"boolean-like":return!!t;case"function":case"object":return t;default:throw new Error("Args received an invalid data type: "+n)}}var u=t("./util.js"),h=t("./logger.js");r.prototype={orig:function(){return this.origArgs.slice(0)},restAsList:function(t,e){var n=this.argList.slice(0);if(t||e)for(var r=0,i=n.length;i>r;r++)this._arg(e||!0,null,t>r);return n},skip:function(){return this.argList.length&&(this.pos++,this.argList.shift()),this},next:function(t,e){return this._arg(t,e,!1)},nextWarn:function(t,e){return this._arg(t,e,"warn")},nextReq:function(t){return this._arg(t,null,!0)},nextFrom:function(t,e){return this._from(t,e,!1)},nextFromWarn:function(t,e){return this._from(t,e,"warn")},nextFromReq:function(t){return this._from(t,null,!0)},listFrom:function(t,e){return this._list(t,e,!1)},listFromWarn:function(t,e){return this._list(t,e,"warn")},listFromReq:function(t){return this._list(t,null,!0)},_arg:function(t,e,n){return this.pos++,(u.isUndefined(t)||null===t)&&(t=!0),this.argList.length&&i(this.argList[0],t)?a(this.argList.shift(),t):(n&&s(n,this.fnName,this.pos,u.printf("must be of type %s",t)),e)},_from:function(t,e,n){return this.pos++,this.argList.length&&u.contains(t,this.argList[0])?this.argList.shift():(n&&s(n,this.fnName,this.pos,u.printf("must be one of %s",t)),e)},_list:function(t,e,n){this.pos++;var r=[],i=this.argList[0];return!this.argList.length||u.isEmpty(i)||!u.isArray(i)&&u.isObject(i)||(this.argList.shift(),u.isArray(i)?r=u.map(i,function(e){return u.contains(t,e)?e:void o(this.fnName,e,t)},this):u.contains(t,i)?r=[i]:o(this.fnName,i,t)),u.isEmpty(r)?(n&&s(n,this.fnName,this.pos,u.printf("choices must be in [%s]",t)),e===!0?t:e):r}},e.exports=r},{"./logger.js":13,"./util.js":15}],13:[function(t,e,n){"use strict";function r(t){return t.charAt(0).toUpperCase()+t.substr(1)}function i(){var t;return"undefined"!=typeof window&&window.location&&window.location.search&&(t=window.location.search.match("\bdebugLevel=([0-9]+)\b")),t?parseInt(t[1],10):u}function s(){return!0}function o(t,e){return e.length?t instanceof RegExp?!t.test(e[0]+""):!(e[0]+"").match(t):!0}function a(t){switch(t){case!1:return 0;case"off":return 0;case"none":return 0;case"error":return 1;case"warn":return 2;case"warning":return 2;case"info":return 3;case"log":return 4;case"debug":return 5;case!0:return u;case"on":return u;case"all":return u;default:return u}}var u=2,h=!1,c={error:s,warn:s,info:s,log:s,debug:s,time:s,timeEnd:s,group:s,groupEnd:s},f=t("./util.js"),l=function(){l.log.apply(null,f.toArray(arguments))};l.warn=s,l.error=s,l.info=s,l.log=s,l.debug=s,l.isErrorEnabled=s,l.isWarnEnabled=s,l.isInfoEnabled=s,l.isLogEnabled=s,l.isDebugEnabled=s,l.logLevel=function(t,e){if("number"!=typeof t&&(t=a(t)),h===t)return function(){};f.each(["error","warn","info","log","debug"],function(n,i){var a="undefined"!=typeof console&&t>=i+1;if(a){var u=f.bind(console["debug"===n?"log":n],console);l[n]=function(){var t=f.toArray(arguments);if(t.length>1&&"string"==typeof t[0]){var n=t[0].match(/(%s|%d|%j)/g);if(n){var r=[f.printf.apply(f,t)];t=t.length>n.length+1?r.concat(t.slice(n.length+1)):r}}e&&o(e,t)||u.apply("undefined"==typeof console?c:console,t)}}else l[n]=s;l["is"+r(n)+"Enabled"]=function(){return a}});var n=function(t){return function(){l.logLevel(t)}}(h);return h=t,n},l.logLevel(i()),e.exports=l},{"./util.js":15}],14:[function(t,e,n){"use strict";function r(t){this.needs=0,this.met=0,this.queued=[],this.errors=[],this.criteria=[],this.processing=!1,i.each(t,this.addCriteria,this)}var i=t("./util.js");r.prototype={addCriteria:function(t,e){if(this.processing)throw new Error("Cannot call addCriteria() after invoking done(), fail(), or handler() methods");return this.criteria.push(e?[t,e]:t),this},getHandler:function(){var t,e;return this.addCriteria(function(n){e!==i.undef?n(e):t=n}),function(n){t?t(n):e=n}},ready:function(){return this.needs===this.met},done:function(t,e){return t&&this._runOrStore(function(){this.hasErrors()||t.call(e)}),this},fail:function(t,e){return this._runOrStore(function(){this.hasErrors()&&t.apply(e,this.getErrors())}),this},handler:function(t,e){return this._runOrStore(function(){t.apply(e,this.hasErrors()?this.getErrors():null)}),this},chain:function(t){return this.addCriteria(t.handler,t),this},when:function(t){this._runOrStore(function(){this.hasErrors()?t.reject.apply(t,this.getErrors()):t.resolve()})},addError:function(t){this.errors.push(t)},hasErrors:function(){return this.errors.length},getErrors:function(){return this.errors.slice(0)},_process:function(){this.processing=!0,this.needs=this.criteria.length,i.each(this.criteria,this._evaluateCriteria,this)},_evaluateCriteria:function(t){var e=null;i.isArray(t)&&(e=t[1],t=t[0]);try{t.call(e,i.bind(this._criteriaMet,this))}catch(n){this.addError(n)}},_criteriaMet:function(t){t&&this.addError(t),this.met++,this.ready()&&i.each(this.queued,this._run,this)},_runOrStore:function(t){this.processing||this._process(),this.ready()?this._run(t):this.queued.push(t)},_run:function(t){t.call(this)}},e.exports=function(t,e){var n=new r(t);return e&&n.done(e),n}},{"./util.js":15}],15:[function(t,e,n){(function(e){"use strict";function r(t,e){switch(e){case"%d":return parseInt(t,10);case"%j":return t=c.isObject(t)?JSON.stringify(t):t+"",t.length>500&&(t=t.substr(0,500)+".../*truncated*/...}"),t;case"%s":return t+"";default:return t}}function i(t){return c.isObject(t)&&t+""=="[object Arguments]"}function s(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),n=this,r=function(){},i=function(){return n.apply(this instanceof r&&t?this:t,e.concat(Array.prototype.slice.call(arguments)))};return r.prototype=this.prototype,i.prototype=new r,i}function o(t,e){var n,r;for(n=0,r=this.length;r>n;++n)n in this&&t.call(e,this[n],n,this)}function a(t){return"[object Array]"===Object.prototype.toString.call(t)}function u(t,e){if(null===this)throw new TypeError;var n,r,i=Object(this),s=i.length>>>0;if(0===s)return-1;if(n=0,arguments.length>1&&(n=Number(arguments[1]),n!==n?n=0:0!==n&&n!==1/0&&n!==-(1/0)&&(n=(n>0||-1)*Math.floor(Math.abs(n)))),n>=s)return-1;for(r=n>=0?n:Math.max(s-Math.abs(n),0);s>r;r++)if(r in i&&i[r]===t)return r;return-1}var h,c=n,f=["value","child_added","child_removed","child_updated","child_changed"];c.undef=h,c.Firebase=e.Firebase||t("firebase"),c.isDefined=function(t){return t!==h},c.isUndefined=function(t){return t===h},c.isObject=function(t){return Object.prototype.isPrototypeOf(t)},c.isArray=function(t){return(Array.isArray||a).call(null,t)},c.isFunction=function(t,e){return"string"==typeof e?c.isObject(t)&&c.has(t,e)&&"function"==typeof t[e]:"function"==typeof t},c.toArray=function(t,e){var n=c.map(t,function(t,e){return t});return e>0?n.slice(e):n},c.extend=function(t,e){var n=c.toArray(arguments),r="boolean"==typeof n[0]&&n.shift(),i=n.shift();return c.each(n,function(t){c.isObject(t)&&c.each(t,function(t,e){i[e]=r&&c.isObject(i[e])?c.extend(!0,i[e],t):t})}),i},c.bind=function(t,e){var n=Array.prototype.slice.call(arguments,1);return(t.bind||s).apply(t,n)},c.isEmpty=function(t){return t===h||null===t||c.isArray(t)&&0===t.length||c.isObject(t)&&!c.contains(t,function(t){return!0})},c.keys=function(t){var e=[];return c.each(t,function(t,n){e.push(n)}),e},c.map=function(t,e,n){var r=[];return c.each(t,function(i,s){var o=e.call(n,i,s,t);o!==h&&r.push(o)}),r},c.mapObject=function(t,e,n){var r={};return c.each(t,function(i,s){var o=e.call(n,i,s,t);o!==h&&(r[s]=o)}),r},c.find=function(t,e,n){if(c.isArray(t)){for(var r=0,i=t.length;i>r;r++)if(e.call(n,t[r],r,t)===!0)return t[r]}else if(c.isObject(t)){var s;for(s in t)if(t.hasOwnProperty(s)&&e.call(n,t[s],s,t)===!0)return t[s]}return h},c.filter=function(t,e,n){var r=c.isArray(t),i=r?[]:{};return c.each(t,function(s,o){e.call(n,s,o,t)&&(r?i.push(s):i[o]=s)}),i},c.reduce=function(t,e,n){return c.each(t,function(r,i){e=n(e,r,i,t)}),e},c.has=function(t,e){return c.isObject(t)&&t[e]!==h},c.val=function(t,e){return c.has(t,e)?t[e]:h},c.contains=function(t,e,n){if("function"!=typeof e){if(c.isArray(t))return c.indexOf(t,e)>-1;e=function(t){return function(e){return e===t}}(e)}return c.find(t,e,n)!==h},c.each=function(t,e,n){if(c.isArray(t)||i(t))(t.forEach||o).call(t,e,n);else if(c.isObject(t)){var r;for(r in t)t.hasOwnProperty(r)&&e.call(n,t[r],r,t)}},c.indexOf=function(t,e){return(t.indexOf||u).call(t,e)},c.remove=function(t,e){var n=!1;if(c.isArray(t)){var r=c.indexOf(t,e);r>-1&&(t.splice(r,1),n=!0)}else if(c.isObject(t)){var i;for(i in t)if(t.hasOwnProperty(i)&&e===t[i]){n=!0,delete t[i];break}}return n},c.defer=function(t,e){var n=c.toArray(arguments);setTimeout(c.bind.apply(null,n),0)},c.call=function(t,e){var n=c.toArray(arguments,2),r=[];return c.each(t,function(t){return"function"!=typeof t||e?void(c.isObject(t)&&"function"==typeof t[e]&&r.push(t[e].apply(t,n))):r.push(t.apply(null,n))}),r},c.isEqual=function(t,e,n){if(t===e)return!0;if(typeof t!=typeof e)return!1;if(c.isObject(t)&&c.isObject(e)){var r=c.isArray(t),i=c.isArray(e);if(r||i)return r&&i&&t.length===e.length&&!c.contains(t,function(t,n){return!c.isEqual(t,e[n])});var s=n?c.keys(t):c.keys(t).sort(),o=n?c.keys(e):c.keys(e).sort();return c.isEqual(s,o)&&!c.contains(t,function(t,n){return!c.isEqual(t,e[n])})}return!1},c.bindAll=function(t,e){return c.each(e,function(n,r){"function"==typeof n&&(e[r]=c.bind(n,t))}),e},c.printf=function(){var t=c.toArray(arguments),e=t.shift(),n=e.match(/(%s|%d|%j)/g); + +return n&&t.length&&c.find(n,function(n){return e=e.replace(n,r(t.shift(),n)),0===t.length}),e},c.mergeToString=function(t){return 0===t.length?null:1===t.length?t[0]:"["+t.join("][")+"]"},c.construct=function(t,e){function n(){return t.apply(this,e)}return n.prototype=t.prototype,new n},c.noop=function(){};var l=[];c.isFirebaseRef=function(t){var e=c.isObject(t),n=e?Object.getPrototypeOf(t):!1;return n&&n.constructor===c.Firebase.prototype.constructor?!0:e&&"function"==typeof t.ref&&"function"==typeof t.ref().transaction?!0:c.find(l,function(n){return e?t instanceof n:t===n})},c.registerFirebaseWrapper=function(t){l.push(t)},c._mockFirebaseRef=function(t){c.Firebase=t},c.escapeEmail=function(t){return(t||"").replace(".",",")},c.assertValidEvent=function(t){if(!c.contains(f,t))throw new Error("Event must be one of "+f+", but received "+t)},c.inherits=function(t,e){var n=[t.prototype].concat(c.toArray(arguments).slice(2));return t.prototype=new e,t.prototype.constructor=e,c.each(n,function(e){c.each(e,function(e,n){t.prototype[n]=e})}),t.prototype._super=function(){e.apply(this,arguments)},t},c.deepCopy=function(t){if(!c.isObject(t))return t;var e=c.isArray(t)?[]:{};return c.each(t,function(t,n){e[n]=c.deepCopy(t)}),e},c.pick=function(t,e){if(!c.isObject(t))return{};var n=c.isArray(t)?[]:{};return c.each(e,function(e){n[e]=t[e]}),n},c.eachByPath=function(t,e,n,r){var i={};c.each(e,function(e,n){var r=t.pathFor(n),s=t.getField(n),o=s?s.id:n;c.has(i,r.name())||(i[r.name()]={path:r,data:{}}),i[r.name()].data[o]=e}),c.each(i,function(t){n.call(r,t.path,t.data)})}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{firebase:"firebase"}],"firebase-util":[function(t,e,n){"use strict";var r=t("./common/index.js");r.extend(n,t("./common/exports.js"),t("./Paginate/exports.js")),"undefined"!=typeof window&&(window.hasOwnProperty("Firebase")?window.Firebase.util=r:console.warn("Firebase not found on the global window instance. Cannot add Firebase.util namespace."))},{"./Paginate/exports.js":2,"./common/exports.js":8,"./common/index.js":9}]},{},[1]); \ No newline at end of file diff --git a/dist/firebase-util.js b/dist/firebase-util.js new file mode 100644 index 0000000..c58dc90 --- /dev/null +++ b/dist/firebase-util.js @@ -0,0 +1,4680 @@ +/*! + * Firebase-util: A set of experimental power tools for Firebase. + * + * Version: 0.2.4 + * URL: https://github.com/firebase/firebase-util + * Date: 2015-04-15T01:10:41.880Z + * License: MIT http://firebase.mit-license.org/ + */ + +require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;odata which + * is flattened for each key. + * + * @param {object} data + * @returns {object} an array containing [ [Path, Object]... ] where each object is a key/value store + */ + denest: function(data) { + var out = {}; + util.each(this.getPathManager().getPaths(), function(p) { + out[p.name()] = { path: p, data: {} }; + }); + + this.forEach(function(field) { + var val = getOut(data, field.alias); + if( val !== util.undef ) { + switch(field.id) { + case '$value': + out[field.pathName].data = val; + break; + case '$key': + // do nothing + break; + default: + out[field.pathName].data[field.id] = val; + } + } + }); + return out; + }, + + idFor: function(fieldName) { + var f = this.getField(fieldName); + if( !f ) { return fieldName; } + return f.id; + } +}; + +FieldMap.key = function(path, field) { + if( typeof path !== 'string' ) { + path = path.name(); + } + return path + '.' + field; +}; + +FieldMap.fieldMap = function(map, fieldName) { + var childPath; + var field = map.getField(fieldName); + if( field ) { + childPath = field.path; + if( field.id !== '$value' ) { + childPath = childPath.child(field.id); + } + } + else { + childPath = map.pathFor(fieldName).child(fieldName); + } + var pm = new PathManager([childPath]); + var fm = new FieldMap(pm); + fm.add({key: FieldMap.key(childPath, '$value'), alias: fieldName}); + return fm; +}; + +/** + * Fetch a list of paths suitable for use in a Record. + * + * @param {FieldMap} map to be copied + * @param {string} recordId the push id for the record + * @returns {FieldMap} a copy of the field map with paths ajusted down to the child node + */ +FieldMap.recordMap = function(map, recordId) { + var mgr = map.getPathManager(); + var paths = util.map(mgr.getPaths(), function(p) { + return p.normChild(recordId); + }); + var childMap = new FieldMap(new PathManager(paths)); + map.forEach(function(field) { + childMap.add({key: field.key, alias: field.alias}); + }); + return childMap; +}; + +function getDepField(fields, dep) { + return util.find(fields, function(field) { + return field.pathName === dep.path && field.id === dep.field; + }) || null; +} + +function Field(props) { + // these properties are considered public and accessed directly by other classes + this.path = props.path; + this.id = props.id; + this.key = props.key; + this.alias = props.alias; + this.url = props.url; + this.pathName = props.pathName; + this.isNested = props.alias.indexOf('.') >= 0; +} + +function parseProps(propsRaw, pathMgr) { + if( propsRaw instanceof Field ) { + return util.pick(propsRaw, ['path', 'id', 'key', 'alias', 'pathName', 'url']); + } + else if( typeof(propsRaw) === 'string' ) { + propsRaw = { key: propsRaw }; + } + var parts = propsRaw.key.split('.'); + var path = pathMgr.getPath(parts[0]); + return { + pathName: parts[0], + id: parts[1], + key: propsRaw.key, + alias: propsRaw.alias || parts[1], + path: path, + //todo-dynamic-keys this isn't correct for dynamic fields :( + //todo-dynamic-keys and probably not for $value and $key either + url: path? path.url() + '/' + parts[1] : null + }; +} + +function putIn(data, alias, val) { + if( val === null ) { return; } + if( alias.indexOf('.') > 0 ) { + var parts = alias.split('.').reverse(), p; + while(parts.length > 1 && (p = parts.pop())) { + data = data[p] = util.has(data, p)? data[p] : {}; + } + alias = parts.pop(); + } + data[alias] = val; +} + +function getOut(data, alias) { + var key = alias; + if( !util.isObject(data) ) { return util.undef; } + var val = data[alias]; + if( alias.indexOf('.') > 0 ) { + var parts = alias.split('.').reverse(); + val = data; + while(parts.length) { + key = parts.pop(); + val = util.isObject(val) && val.hasOwnProperty(key)? val[key] : util.undef; + } + } + return val; +} + +module.exports = FieldMap; +},{"../../common":24,"./PathManager":10}],5:[function(require,module,exports){ +'use strict'; + +var util = require('../../common'); + +function Filter() { + this.criteria = []; + util.each(arguments, this.add, this); +} + +Filter.prototype = { + add: function(fn) { + this.criteria.push( + new Condition(fn) + ); + }, + test: function(recordData, key, priority) { + return util.contains(this.criteria, function(cond) { + return !cond.test(recordData, key, priority); + }) === false; + } +}; + +function Condition(fn) { + this.match = fn; +} + +Condition.prototype.test = function(data, key, priority) { + return this.match(data, key, priority) === true; +}; + +module.exports = Filter; +},{"../../common":24}],6:[function(require,module,exports){ +'use strict'; + +var util = require('../../common'); +var PathManager = require('./PathManager'); +var Filter = require('./Filter'); +var FieldMap = require('./FieldMap'); +var NormalizedRef = require('./NormalizedRef'); +var RecordSet = require('./RecordSet'); + +/** + * @param {...object} path + * @constructor + */ +function NormalizedCollection(path) { //jshint unused:vars + assertPaths(arguments); + this.pathMgr = new PathManager(arguments); + this.map = new FieldMap(this.pathMgr); + this.filters = new Filter(); + this.finalized = false; +} + +NormalizedCollection.prototype = { + select: function(fieldName) { //jshint unused:vars + assertNotFinalized(this, 'select'); + var args = util.args('NormalizedCollection.select', arguments, 1); + util.each(args.restAsList(0, ['string', 'object']), function(f) { + assertValidField(f); + this.map.add(f); + }, this); + return this; + }, + + filter: function(matchFn) { //jshint unused:vars + assertNotFinalized(this, 'filter'); + var args = util.args('NormalizedCollection.filter', arguments, 1, 1); + this.filters.add( + args.nextReq('function') + ); + return this; + }, + + ref: function() { + if( !this.map.length ) { + throw new Error('Must call select() with at least one field' + + ' before creating a ref'); + } + this.finalized = true; + if( util.log.isInfoEnabled() ) { + util.log.info('NormalizedRef created using %s', buildDebugString(this)); + } + var recordSet = new RecordSet(this.map, this.filters); + return new NormalizedRef(recordSet); + } +}; + +function assertPaths(args) { + if( args.length < 1 ) { + throw new Error('Must provide at least one path definition'); + } + function notValidRef(p) { + if( util.isArray(p) ) { + p = p[0]; + } + return !util.isFirebaseRef(p); + } + if( util.contains(args, notValidRef) ) { + throw new Error('Each argument to the NormalizedCollection constructor must be a ' + + 'valid Firebase reference or an Array containing a Firebase ref as the first argument'); + } +} + +function assertNotFinalized(self, m) { + if( self.finalized ) { + throw new Error('Cannot call ' + m + '() after ref() has been invoked'); + } +} + +function buildDebugString(nc) { + var paths = []; + var selects = []; + var filter = ''; + + util.each(nc.pathMgr.getPaths(), function(p) { + var dep = p.getDependency(); + paths.push( + util.printf('\t"%s%s"%s', + p.url(), + p.id() === p.name()? '' : ' as ' + p.name(), + dep? '-> ' + dep.path + '.' + dep.field : '' + ) + ); + }); + + nc.map.forEach(function(f) { + selects.push(util.printf('"%s%s"', f.key, f.alias === f.id? '' : ' as ' + f.alias)); + if( selects.length % 5 === 0 ) { + selects.push('\n'); + } + }); + + if(nc.filters.criteria.length > 0) { + filter = util.printf('<%s filters applied>', nc.filters.criteria.length); + } + + return util.printf('NormalizedCollection(\n%s\n).select(%s)%s.ref()', paths.join('\n'), selects.join(', '), filter); +} + +function assertValidField(f) { + var k; + if( typeof f === 'string' ) { + k = f; + } + else { + k = util.has(f, 'key')? f.key : util.undef; + } + if( typeof k !== 'string' || k.indexOf('.') <= 0 ) { + throw new Error('Each field passed to NormalizedCollection.select() must either be a string ' + + 'in the format "pathAlias.fieldId", or an object in the format ' + + '{key: "pathAlias.fieldId", alias: "any_name_for_field"}, but I received ' + JSON.stringify(f)); + } +} + +module.exports = NormalizedCollection; +},{"../../common":24,"./FieldMap":4,"./Filter":5,"./NormalizedRef":7,"./PathManager":10,"./RecordSet":14}],7:[function(require,module,exports){ +'use strict'; + +var util = require('../../common'); +var Query = require('./Query'); + +function NormalizedRef(record, parent) { + this._super(this, record); + this._parent = parent||null; + this._key = record.getName(); + this._toString = record.getUrl(); +} + +util.inherits(NormalizedRef, Query, { + 'child': function(fieldName) { + var parts = fieldName.split('/').reverse(); // pop is faster than shift + var parent = this; + var ref = this; + while(parts.length) { + var key = parts.pop(); + ref = new NormalizedRef(ref.$getRecord().makeChild(key), parent); + parent = ref; + } + return ref; + }, + + 'parent': function() { + return this._parent; + }, + + 'root': function() { + var p = this; + while(p.parent() !== null) { + p = p.parent(); + } + return p; + }, + + /** @deprecated */ + 'name': function() { + console.warn('The name() function has been deprecated. Use key() instead.'); + return this.key(); + }, + + 'key': function() { + return this._key; + }, + + 'toString': function() { + return this._toString; + }, + + //todo have set, update, push, remove attempt to revert any partial commits + //todo by running a transaction and, if the value is still the "new" partial + //todo value, then revert it to the old complete value + + 'set': function(data, callback, context) { + this.$getRecord().saveData(data, {callback: callback, context: context, isUpdate: false}); + }, + + 'update': function(data, callback, context) { + this.$getRecord().saveData(data, {callback: callback, context: context, isUpdate: true}); + }, + + 'remove': function(callback, context) { + this.$getRecord().saveData(null, {callback: callback, context: context, isUpdate: false}); + }, + + 'push': function(data, callback, context) { // jshint unused:false + var uid = this.$getMaster().push().key(); + var child = this.child(uid); + if( arguments.length ) { + child.set.apply(child, arguments); + } + return child; + }, + + 'setWithPriority': function(data, priority, callback, context) { + this.$getRecord().saveData(data, { + callback: callback, context: context, isUpdate: false, priority: priority + }); + }, + + 'setPriority': function(priority, callback, context) { + this.$getMaster().setPriority(priority, callback, context); + }, + + /**************************** + * WRAPPER FUNCTIONS + ****************************/ + 'auth': wrapMaster('auth'), + 'unauth': wrapMaster('unauth'), + 'authWithCustomToken': wrapMaster('authWithCustomToken'), + 'authAnonymously': wrapMaster('authAnonymously'), + 'authWithPassword': wrapMaster('authWithPassword'), + 'authWithOAuthPopup': wrapMaster('authWithOAuthPopup'), + 'authWithOAuthRedirect': wrapMaster('authWithOAuthRedirect'), + 'authWithOAuthToken': wrapMaster('authWithOAuthToken'), + 'getAuth': wrapMaster('getAuth'), + 'onAuth': wrapMaster('onAuth'), + 'offAuth': wrapMaster('offAuth'), + 'createUser': wrapMaster('createUser'), + 'changePassword': wrapMaster('changePassword'), + 'removeUser': wrapMaster('removeUser'), + 'resetPassword': wrapMaster('resetPassword'), + 'changeEmail': wrapMaster('changeEmail'), + + 'goOffline': wrapAll('goOffline'), + 'goOnline': wrapAll('goOnline'), + + /**************************** + * UNSUPPORTED FUNCTIONS + ***************************/ + 'transaction': notSupported('transaction'), //todo use field map to pick fields and apply to each + 'onDisconnect': notSupported('onDisconnect') //todo use field map to pick fields and apply to each +}); + +function wrapAll(method) { + return function() { + var args = util.toArray(arguments); + util.each(this.$getPaths(), function(p) { + var ref = p.ref(); + ref[method].apply(ref, args); + }); + }; +} + +function wrapMaster(method) { + return function() { + var args = util.toArray(arguments); + var ref = this.$getMaster(); + return ref[method].apply(ref, args); + }; +} + +function notSupported(method) { + return function() { + throw new Error(method + ' is not supported for NormalizedCollection references. ' + + 'Try calling it on the original reference used to create the NormalizedCollection instead.'); + }; +} + +module.exports = NormalizedRef; +},{"../../common":24,"./Query":11}],8:[function(require,module,exports){ +'use strict'; + +var util = require('../../common'); + +function NormalizedSnapshot(ref, snaps) { + this._ref = ref; + this._rec = ref.$getRecord(); + if( !util.isArray(snaps) ) { + throw new Error('Must provide an array of snapshots to merge'); + } + this._pri = this._rec.getPriority(snaps); + this._snaps = snaps; +} + +NormalizedSnapshot.prototype = { + val: function() { + if( !this._snaps.length ) { + return null; + } + return this._rec.mergeData(this._snaps, false); + }, + + child: function(key) { + var snap; + // keys may contain / to separate nested child paths + // so make a list of child keys (we reverse it once + // as this is faster than unshift() on each iteration) + var childParts = key.split('/').reverse(); + // grab the first key and get the child snapshot + var firstChildName = childParts.pop(); + snap = new NormalizedSnapshot( + this._ref.child(firstChildName), + this._rec.getChildSnaps(this._snaps, firstChildName) + ); + // iterate any nested keys and keep calling child on them + while(childParts.length) { + snap = snap.child(childParts.pop()); + } + return snap; + }, + + forEach: function(cb, context) { + return this._rec.forEachKey(this._snaps, function(childId, childAlias) { + if( childId === '$value' || childId === '$key' ) { return false; } + return cb.call(context, this.child(childAlias)); + }, this); + }, + + hasChild: function(key) { + //todo optimize and/or memoize? + var parts = key.split('/').reverse(); + var res = parts.length > 0; + var nextRef = this._ref; + var nsnap = this; + while(res && parts.length) { + var nextKey = parts.pop(); + res = nextRef.$getRecord().hasChild(nsnap._snaps, nextKey); + if( res && parts.length ) { + nextRef = nextRef.child(nextKey); + nsnap = nsnap.child(nextKey); + } + } + return res; + }, + + /** + * Returns true if this snapshot has any child data. Does not return true for $key or $value + * fields. + * + * @returns {boolean} + */ + hasChildren: function() { + // if there are any keys to iterate, and that key is not $key or $value + // then we have children + return this._rec.forEachKey(this._snaps, function(id) { + return id !== '$key' && id !== '$value'; + }); + }, + + /** @deprecated */ + name: function() { + console.warn('name() has been deprecated. Use key() instead.'); + return this.key(); + }, + + key: function() { + return this._rec.getName(); + }, + + numChildren: function() { + //todo-bug does not account for nested aliases (they will change the count here) + var ct = 0; + this._rec.forEachKey(this._snaps, function(id) { + if( id !== '$key' && id !== '$value' ) { ct++; } + }); + return ct; + }, + + ref: function() { return this._ref.ref(); }, + + getPriority: function() { return this._pri; }, + + exportVal: function() { + return this._rec.mergeData(this._snaps, true); + }, + + exists: function() { + return this.val() !== null; + } +}; + +module.exports = NormalizedSnapshot; +},{"../../common":24}],9:[function(require,module,exports){ +'use strict'; + +var util = require('../../common'); + +function Path(pathProps, parent) { + var props = parseProps(pathProps); + this._ref = props.ref; + this._alias = props.alias; + this._dep = props.dep; + this._parent = parent || null; +} + +Path.prototype = { + ref: function() { return this._ref; }, + reff: function() { return this.ref().ref(); }, + child: function(key) { + return new Path(this.reff().child(key), this); + }, + normChild: function(key) { + var dep = this.getDependency(); + if( dep !== null ) { + return new Path([this.reff(), this.name(), dep.path+'.'+dep.field], this); + } + else { + return new Path([this.reff().child(key), this.name()], this); + } + }, + hasDependency: function() { + return this._dep !== null; + }, + getDependency: function() { + return this._dep; + }, + url: function() { return this.reff().toString(); }, + name: function() { return this._alias; }, + id: function() { return this.reff().key(); }, + parent: function() { return this._parent; }, + clone: function() { + return new Path([this._ref, this._alias, this._dep], this._parent); + } +}; + +function parseProps(props) { + var ref, alias, dep = null; + if( util.isArray(props) ) { + ref = props[0]; + alias = props[1]; + dep = props[2]; + } + else if( util.isFunction(props.ref) ) { + ref = props.ref(); + } + else { + ref = props; + } + return { + ref: ref, alias: alias||ref.key(), dep: parseDep(dep) + }; +} + +function parseDep(dep) { + if(util.isObject(dep) ) { + return dep; + } + else if( dep ) { + var parts = dep.split('.'); + return { path: parts[0], field: parts[1] }; + } + return null; +} + +module.exports = Path; +},{"../../common":24}],10:[function(require,module,exports){ +'use strict'; + +var Path = require('./Path'); +var util = require('../../common'); + +function PathManager(paths) { + this.paths = []; + this.pathsByUrl = {}; + this.deps = {}; + this.pathNames = []; + util.each(paths, this.add, this); +} + +PathManager.prototype = { + add: function(pathProps) { + var path = pathProps instanceof Path? pathProps.clone() : new Path(pathProps); + if( !this.paths.length && path.hasDependency() ) { + throw new Error('The master path (i.e. the first) may not declare a dependency.' + + ' Perhaps you have put the wrong path first in the list?'); + } + if( util.has(this.pathsByUrl, path.url()) ) { + throw new Error('Duplicate path: ' + path.url()); + } + if( util.contains(this.pathNames, path.name()) ) { + throw new Error('Duplicate path name. The .key() value for each path must be unique, or you ' + + 'can give each a path an alias by using [firebaseRef, alias] in the constructor. The aliases ' + + 'must also be unique.'); + } + this._map(path); + this.paths.push(path); + this.pathsByUrl[path.url()] = path.name(); + this.pathNames.push(path.name()); + }, + + count: function() { + return this.paths.length; + }, + + first: function() { + return this.paths[0]; + }, + + getPath: function(pathName) { + return util.find(this.paths, function(p) { + return p.name() === pathName; + })||null; + }, + + getPathFor: function(url) { + var n = this.getPathName(url); + return n? this.getPath(n) : null; + }, + + getPaths: function() { + return this.paths.slice(); + }, + + getPathName: function(url) { + return this.pathsByUrl[url] || null; + }, + + getPathNames: function() { + return this.pathNames.slice(); + }, + + getUrls: function() { + return util.keys(this.pathsByUrl); + }, + + //todo remove? + getDependencyGraph: function() { + return util.extend(true, this.deps); + }, + + _map: function(path) { + var first = this.first(); + var dep = path.getDependency(); + if( !dep && first ) { + dep = { path: first.name(), field: '$key' }; + } + if( dep ) { + this.deps[path.name()] = dep; + this._assertNotCircularDep(path.name()); + } + }, + + _assertNotCircularDep: function(pathName) { + var map = [pathName], dep = this.deps[pathName]; + while(util.isDefined(dep)) { + var p = dep.path; + if(util.contains(map, p)) { + map.push(p); // adds it into the error message chain + throw new Error('Circular dependencies in paths: ' + depChain(map, this.deps)); + } + map.push(p); + dep = util.val(this.deps, p); + } + } +}; + +function depChain(map, deps) { + return util.map(map, function(p) { + return deps[p].path + '.' + deps[p].field; + }).join(' >> '); +} + +module.exports = PathManager; +},{"../../common":24,"./Path":9}],11:[function(require,module,exports){ +'use strict'; + +var util = require('../../common'); +var Transmogrifier = require('./Transmogrifier'); + +function Query(ref, record) { + var self = this; + self._ref = ref; + self._rec = record; + // necessary because util.inherit() can only call classes with an empty constructor + // so we can't depend on the params existing for that call + if( record ) { record.setRef(self); } //todo don't like this here, is awkward coupling +} + +Query.prototype = { + 'on': function(event, callback, cancel, context) { + if( arguments.length === 3 && util.isObject(cancel) ) { + context = cancel; + cancel = util.undef; + } + this.$getRecord().watch(event, callback, cancel, context); + }, + + 'once': function(event, callback, cancel, context) { + var self = this; + if( arguments.length === 3 && util.isObject(cancel) ) { + context = cancel; + cancel = util.undef; + } + function successHandler(snap) { + self.off(event, successHandler); + callback.call(context, snap); + } + + function cancelHandler(err) { + if( typeof(cancel) === 'function' && err !== null ) { + cancel.call(context, err); + } + } + + this.on(event, successHandler, cancelHandler); + }, + + 'off': function(event, callback, context) { + this.$getRecord().unwatch(event, callback, context); + }, + + /************************************ + * Wrapped functions + ************************************/ + + 'orderByChild': function() { + return this.$replicate('orderByChild', util.toArray(arguments)); + }, + + 'orderByKey': function() { + return this.$replicate('orderByKey', util.toArray(arguments)); + }, + + 'orderByPriority': function() { + return this.$replicate('orderByPriority', util.toArray(arguments)); + }, + + 'limitToFirst': function() { + return this.$replicate('limitToFirst', util.toArray(arguments)); + }, + + 'limitToLast': function() { + return this.$replicate('limitToLast', util.toArray(arguments)); + }, + + /** @deprecated */ + 'limit': function() { + return this.$replicate('limit', util.toArray(arguments)); + }, + + 'startAt': function() { + return this.$replicate('startAt', util.toArray(arguments)); + }, + + 'endAt': function() { + return this.$replicate('endAt', util.toArray(arguments)); + }, + + 'equalTo': function() { + return this.$replicate('equalTo', util.toArray(arguments)); + }, + + 'ref': function() { return this._ref; }, + + /**************************** + * PACKAGE FUNCTIONS (not API) + ***************************/ + + /** @returns {Record} */ + '$getRecord': function() { return this._rec; }, + + /** @return {Firebase} */ + '$getMaster': function() { return this._rec.getPathManager().first().ref(); }, + + /** @return {Array} */ + '$getPaths': function() { return this._rec.getPathManager().getPaths(); }, + + /** + * @param {String} method + * @param {Array|arguments} args + * @returns {Query} + */ + '$replicate': function(method, args) { + var rec = this.$getRecord(); + var ref = this.$getMaster(); + ref = ref[method].apply(ref, args); + return new Query(this._ref, Transmogrifier.replicate(rec, ref)); + } +}; + +util.registerFirebaseWrapper(Query); +module.exports = Query; +},{"../../common":24,"./Transmogrifier":16}],12:[function(require,module,exports){ +'use strict'; + +var FieldMap = require('./FieldMap'); +var RecordField = require('./RecordField'); +var AbstractRecord = require('./AbstractRecord'); +var util = require('../../common'); + +function Record(fieldMap) { + var name = fieldMap.getPathManager().first().id(); + var url = util.mergeToString(fieldMap.getPathManager().getUrls()); + this._super(fieldMap, name, url); + this._eventManagers = {}; + util.log.debug('Record created', this.getName(), this.getUrl()); +} + +util.inherits(Record, AbstractRecord, { + makeChild: function(key) { + var fm = FieldMap.fieldMap(this.getFieldMap(), key); + return new RecordField(fm); + }, + + hasChild: function(snaps, key) { + var field = this.getFieldMap().getField(key); + if( !field ) { return false; } + var snap = this.getFieldMap().snapFor(snaps, key); + return snap !== null && snap.hasChild(key); + }, + + getChildSnaps: function(snaps, fieldName) { + var child; + var snap = this.getFieldMap().snapFor(snaps, fieldName); + var field = this.getFieldMap().getField(fieldName); + if( !field ) { + child = snap.child(fieldName); + } + else { + switch(field.id) { + case '$key': + throw new Error('Cannot get child snapshot from key (not a real child element)'); + case '$value': + child = snap; + break; + default: + child = snap.child(field.id); + } + } + return [child]; + }, + + /** + * Given a list of snapshots to iterate, returns the valid keys + * which exist in both the snapshots and the field map, in the + * order they should be iterated. + * + * Calls iterator with a {string|number} key for the next field to + * iterate only. + * + * If iterator returns true, this method should abort and return true, + * otherwise it should return false (same as Snapshot.forEach). + * + * @param {Array} snaps + * @param {function} iterator + * @param {object} [context] + * @return {boolean} true if aborted + * @abstract + */ + forEachKey: function(snaps, iterator, context) { + function shouldIterate(snap, fieldId) { + switch(fieldId) { + case '$key': + return true; + case '$value': + return snap && snap.val() !== null; + default: + return snap && snap.hasChild(fieldId); + } + } + var map = this.getFieldMap(); + return map.forEach(function(field) { + var snap = map.snapFor(snaps, field.alias); + if( shouldIterate(snap, field.id) ) { + return iterator.call(context, field.id, field.alias) === true; + } + return false; + }); + }, + + /** + * Merge the data by iterating the snapshots in reverse order + * so that keys from later paths do not overwrite keys from earlier paths + * + * @param {Array} snaps list of snapshots to be merged + * @param {boolean} isExport true if exportVal() was called + * @returns {Object} + */ + mergeData: function(snaps, isExport) { + var map = this.getFieldMap(); + var data = util.extend.apply(null, util.map(snaps, function(ss) { + return map.extractData(ss, isExport); + })); + if( isExport && snaps.length > 0 && snaps[0].getPriority() !== null ) { + if( !util.isObject(data) ) { + data = {'.value': data}; + } + data['.priority'] = snaps[0].getPriority(); + } + return data; + }, + + getPriority: function(snaps) { + return snaps[0].getPriority(); + }, + + getClass: function() { return Record; }, + + saveData: function(data, props) { + var q = util.queue(); + var map = this.getFieldMap(); + var paths = this.getPathManager().getPaths(); + if( props.isUpdate && !util.isObject(data) ) { + throw new Error('First argument to update() command must be an object'); + } + if( data === null ) { + util.each(paths, function(p) { + if( !p.hasDependency() ) { + p.reff().remove(q.getHandler()); + } + }); + } + else if(util.isObject(data)) { + var denestedData = map.denest(data); + util.each(denestedData, function(parts) { + var path = parts.path; + var dataForPath = parts.data; + var ref = this._writeRef(denestedData, path); + if( ref !== null ) { + if( !util.isEmpty(dataForPath) || !props.isUpdate ) { + if( !util.isObject(dataForPath) ) { + dataForPath = {'.value': dataForPath}; + } + if( !props.isUpdate ) { + addEmptyFields(map, path, dataForPath); + } + if( util.isDefined(props.priority) ) { + dataForPath['.priority'] = props.priority; + } + if( util.has(dataForPath, '.value') ) { + ref.set(dataForPath, q.getHandler()); + } + else { + ref.update(dataForPath, q.getHandler()); + } + } + } + else { + util.log.info('No dynamic key found for master', paths[0].ref().toString(), 'with dynamic path', path.ref().toString()); + } + }, this); + } + else if( paths.length === 1 ) { + if( util.isDefined(props.priority) ) { + paths[0].ref().setWithPriority(data, props.priority, q.getHandler()); + } + else { + paths[0].ref().set(data, q.getHandler()); + } + } + else { + throw new Error('Cannot set multiple paths to a non-object value. ' + + 'Since this is a NormalizedCollection, the data will be split between the paths. ' + + 'But I can\'t split a primitive value'); + } + q.handler(props.callback||util.noop, props.context); + }, + + getName: function() { + return this._name; + }, + + getUrl: function() { + return this._url; + }, + + _start: function(event) { + if( !util.has(this._eventManagers, event) ) { + util.log.debug('Record._start: event=%s, url=%s', event, this.getUrl()); + this._eventManagers[event] = event === 'value'? + new ValueEventManager(this) : new ChildEventManager(event, this); + } + this._eventManagers[event].start(); + }, + + _stop: function(event) { + if (util.has(this._eventManagers, event)) { + util.log.debug('Record._stop: event=%s, url=%s', event, this.getUrl()); + this._eventManagers[event].stop(); + } + }, + + _writeRef: function(denestedData, path) { + var ref = path.reff(); + var dep = path.getDependency(); + if( dep !== null ) { + var depPath = this.getPathManager().getPath(dep.path); + var key = this._depKey(denestedData, depPath, dep.field); + ref = key === null? null : ref.child(key); + } + return ref; + }, + + _depKey: function(denestedData, path, fieldId) { + var key; + var dat = denestedData[path.name()].data; + switch(fieldId) { + case '$key': + key = path.id(); + break; + case '$value': + key = util.has(dat, '.value')? dat['.value'] : util.isEmpty(dat)? null : dat; + break; + default: + key = util.has(dat, fieldId)? dat[fieldId] : null; + } + var type = typeof key; + if( key !== null && type !== 'string' ) { + throw new Error( + 'Dynamic key values must be a string. Type was ' + + type + ' for ' + path.ref().toString() + '->' + fieldId + ); + } + return key; + } +}); + +function ValueEventManager(rec) { + this.rec = rec; + this.pm = rec.getPathManager(); + this.running = false; + this._init(); +} + +ValueEventManager.prototype = { + start: function() { + if( !this.running ) { + this.running = true; + util.each(this.pm.getPathNames(), this._startPath, this); + } + }, + + stop: function() { + if( this.running ) { + this.running = false; + util.each(this.subs, function(fn) { + fn(); + }); + this._init(); + } + }, + + update: function(pathName, snap) { + this.snaps[pathName] = snap; + this._checkLoadState(); + util.log('Record.ValueEventManager.update: url=%s, loadCompleted=%s', snap.ref().toString(), this.loadCompleted); + if( this.loadCompleted ) { + this.rec.handler('value')(util.toArray(this.snaps)); + } + }, + + _startPath: function(pathName) { + var self = this; + var path = self.pm.getPath(pathName); + var fn = util.bind(self.update, self, pathName); + if( path.hasDependency() ) { + var dyno = new Dyno(path, this.rec.getFieldMap(), 'value', fn); + this.subs.push(dyno.dispose); + } + else { + path.ref().on('value', fn); + self.subs.push(function() { + path.ref().off('value', fn); + }); + } + }, + + _checkLoadState: function() { + if( this.loadCompleted ) { return; } + var snaps = this.snaps; + var pathNames = this.pm.getPathNames(); + this.loadCompleted = !util.contains(pathNames, function(p) { + return !snaps.hasOwnProperty(p); + }); + }, + + _init: function() { + this.loadCompleted = false; + this.snaps = {}; + this.subs = []; + } +}; + +function ChildEventManager(event, rec) { + this.event = event; + this.rec = rec; + this.map = rec.getFieldMap(); + this.pm = rec.getPathManager(); + this.subs = []; + this.dyno = null; +} + +ChildEventManager.prototype = { + start: function() { + util.each(this.pm.getPathNames(), function(pathName) { + var event = this.event; + var path = this.pm.getPath(pathName); + var fn = util.bind(this.update, this); + if( path.hasDependency() ) { + this.dyno = new Dyno(path, this.map, event, fn); + this.subs.push(this.dyno.dispose); + } + else { + path.ref().on(event, fn); + this.subs.push(function() { + path.ref().off(event, fn); + }); + } + }, this); + }, + + stop: function() { + util.each(this.subs, function(fn) { + fn(); + }); + this.subs = []; + }, + + update: function(snap, prev) { + if( snap !== null && this.map.aliasFor(snap.ref().toString()) !== null ) { + var args = [snap.key(), snap]; + if( prev !== util.undef ) { args.push(prev); } + util.log('Record.ChildEventManager.update: event=%s, key=%s/%s', this.event, snap.ref().parent().key(), snap.key()); + this.rec.handler(this.event).apply(this.rec, args); + } + } +}; + +/** + * Process a path which depends on the value of another field. We have + * to monitor the field it depends on for value events and update + * the ref that we listen on whenever the id is modified. + * + * @param {Path} path + * @param {FieldMap } fieldMap + * @param {string} event + * @param {function} updateFn + * @constructor + */ +function Dyno(path, fieldMap, event, updateFn) { + var dep = path.getDependency(); + var depPath = fieldMap.getPath(dep.path); + var depRef = depPath.ref(); + if( dep.field === '$key' ) { + throw new Error('Dynamic paths do not support $key (you should probably just join on this path)'); + } + if( dep.field !== '$value' ) { + depRef = depRef.child(dep.field); + } + var ref; + + // establish our listener at the field which contains the id of our ref + var depFn = depRef.on('value', function(snap) { + if( ref && ref.key() !== snap.val() ) { + util.log.debug('Record.Dyno: stopped monitoring %s', ref.toString()); + // any time the id changes, remove the old listener + ref.off(event, updateFn); + updateFn(null); + } + if( snap.val() !== null ) { + // establish our listener at the correct dynamic id for values + ref = path.ref().child(snap.val()); + ref.on(event, updateFn); + util.log('Record.Dyno: monitoring %s', ref.toString()); //debug + } + }); + + // create a dispose method that can turn off all our event listeners + // when _stop is called + this.dispose = function() { + depRef.off('value', depFn); + if( ref ) { + ref.off(event, updateFn); + } + }; +} + +function addEmptyFields(map, path, dataToSave) { + util.each(map.fieldsFor(path.name()), function(f) { + switch(f.id) { + case '$key': + // ignore key + break; + case '$value': + if( !util.has(dataToSave, '.value') ) { + dataToSave['.value'] = null; + } + break; + default: + if( !util.has(dataToSave, f.id) ) { + dataToSave[f.id] = null; + } + } + }); +} + +module.exports = Record; +},{"../../common":24,"./AbstractRecord":3,"./FieldMap":4,"./RecordField":13}],13:[function(require,module,exports){ +'use strict'; + +var PathManager = require('./PathManager'); +var FieldMap = require('./FieldMap'); +var AbstractRecord = require('./AbstractRecord'); +var util = require('../../common'); + +function RecordField(fieldMap) { + this.path = fieldMap.getPathManager().first(); + this._super(fieldMap, this.path.name(), this.path.url()); + if( fieldMap.getPathManager().count() !== 1 ) { + throw new Error('RecordField must have exactly one path, but we got '+ fieldMap.getPathManager().count()); + } + if( fieldMap.length !== 1 ) { + throw new Error('RecordField must have exactly one field, but we found '+ fieldMap.length); + } + util.log.debug('RecordField created', this.getName(), this.getUrl()); +} + +util.inherits(RecordField, AbstractRecord, { + makeChild: function(key) { + var pm = new PathManager([this.path.child(key)]); + var fm = new FieldMap(pm); + fm.add({key: FieldMap.key(pm.first(), '$value'), alias: key}); + return new RecordField(fm); + }, + + hasChild: function(snaps, key) { + return snaps[0].hasChild(key); + }, + + getChildSnaps: function(snaps, fieldName) { + // there is exactly one snap and there are no aliases to deal with + return [snaps[0].child(fieldName)]; + }, + + /** + * There is nothing to merge at this level because there is only one + * path and no field map + * + * @param {Array} snaps list of snapshots to be merged + * @param {boolean} isExport true if exportVal() was called + * @returns {Object} + */ + mergeData: function(snaps, isExport) { + return isExport? snaps[0].exportVal() : snaps[0].val(); + }, + + getPriority: function(snaps) { + return snaps[0].getPriority(); + }, + + /** + * Iterates all keys of snapshot. + * + * Calls iterator with a {string|number} key for the next field to + * iterate only. + * + * If iterator returns true, this method should abort and return true, + * otherwise it should return false (same as Snapshot.forEach). + * + * @param {Array} snaps + * @param {function} iterator + * @param {object} [context] + * @return {boolean} true if aborted + * @abstract + */ + forEachKey: function(snaps, iterator, context) { + var firstSnap = snaps[0]; + return firstSnap.forEach(function(ss) { + iterator.call(context, ss.key(), ss.key()); + }); + }, + + saveData: function(data, opts) { + var ref = this.path.ref(); + if( opts.isUpdate ) { + if( !util.isObject(data) ) { + throw new Error('When using update(), the data must be an object.'); + } + if( util.has(opts, 'priority') ) { + data['.priority'] = opts.priority; + } + ref.update(data, wrapCallback(opts)); + } + else if( util.has(opts, 'priority') ) { + ref.setWithPriority(data, opts.priority, wrapCallback(opts)); + } + else { + ref.set(data, wrapCallback(opts)); + } + }, + + getClass: function() { return RecordField; }, + + _start: function(event) { + this.path.ref().on(event, this.handler(event), this._cancel, this); + }, + + _stop: function(event) { + this.path.ref().off(event, this.handler(event), this); + } +}); + +function wrapCallback(opts) { + if( opts.callback ) { + return function() { + opts.callback.apply(opts.context, arguments); + }; + } + else { + return util.noop; + } +} + +module.exports = RecordField; +},{"../../common":24,"./AbstractRecord":3,"./FieldMap":4,"./PathManager":10}],14:[function(require,module,exports){ +'use strict'; + +var Record = require('./Record'); +var AbstractRecord = require('./AbstractRecord'); +var util = require('../../common'); +var FieldMap = require('./FieldMap'); +var RecordSetEventManager = require('./RecordSetEventManager'); + +/** + * A "Record" (see AbstractRecord) represents a merged set of data used by NormalizedRef. + * It is used by NormalizedRef to create snapshots and monitor Firebase for data changes. + * + * A RecordSet represents the root level of NormalizedCollection's output. It is a list of + * collections (from multiple paths in Firebase) to be joined together. + * + * This is, for the purposes of a NormalizedCollections, the root of the data. Calls to parent() + * from here should return null, just like they would from the root of a Firebase. This is because + * the parent of a normalized collection is ambiguous, so there is no higher level of data. + * + * @param fieldMap this is the field map to be applied to each Record created when calling child() + * @param whereClause this filters the output data and events + * @constructor + */ +function RecordSet(fieldMap, whereClause) { + var name = util.mergeToString(fieldMap.getPathManager().getPathNames()); + var url = util.mergeToString(fieldMap.getPathManager().getUrls()); + + // AbstractRecord makes this observable and abstracts some common impl details + // between RecordSet, Record, and RecordField + this._super(fieldMap, name, url); + + // Used to filter the merged data and determine which merged Records should trigger events and + // which ones should be ignored + this.filters = whereClause; + + // the RecordSetEventManager handles Firebase events and calls event handlers on + // this RecordSet appropriately. See RecordSetEventManager for more details + this.monitor = new RecordSetEventManager(this); +} + +util.inherits(RecordSet, AbstractRecord, { + makeChild: function(key) { + var fm = FieldMap.recordMap(this.getFieldMap(), key); + return new Record(fm); + }, + + /** + * Override AbstractRecord's hasChild since we are dealing + * with record ids at this level. We use the master list to determine + * if records exist, so it's a pretty straightforward hasChild + * on the first snapshot. + * + * @param {Array} snaps a list of array snapshots to test for child + * @param {string} key + */ + hasChild: function(snaps, key) { + return util.contains(snaps, function(s) { + return s.key() === key; + }); + }, + + getChildSnaps: function(snapsArray, recordId) { + return util.filter(snapsArray, function(s) { + return s.key() === recordId; + }); + }, + + /** + * Since the snapshots attached to this level are records, there isn't much + * to do for a merge. Just put them all together. + * + * @param {Array} snaps list of snapshots to be merged + * @param {boolean} isExport true if exportVal() was called + * @returns {Object} + */ + mergeData: function(snaps, isExport) { + var self = this; + var out = {}; + util.each(snaps, function(snap) { + if( snap.val() !== null && self.filters.test(snap.val(), snap.key(), snap.getPriority()) ) { + out[snap.key()] = isExport? snap.exportVal() : snap.val(); + } + }); + return out; + }, + + getPriority: function() { + return null; + }, + + /** + * Given a list of snapshots to iterate, returns the valid keys + * which exist in both the snapshots and the field map, in the + * order they should be iterated. + * + * Calls iterator with a {string|number} key for the next field to + * iterate only. + * + * If iterator returns true, this method should abort and return true, + * otherwise it should return false (same as Snapshot.forEach). + * + * @param {Array} snaps + * @param {function} iterator + * @param {object} [context] + * @return {boolean} true if aborted + * @abstract + */ + forEachKey: function(snaps, iterator, context) { + snaps.forEach(function(snap) { + return iterator.call(context, snap.key(), snap.key()); + }); + }, + + getClass: function() { return RecordSet; }, + + /** + * Saving a record set is done by grabbing each child record and calling save against that. + * This is the easiest approach since we must distribute fields to each appropriate path. + * It might be more efficient to bulk these into a single write op, and perhaps we should + * explore that if this proves to be slow. + * + * @param data + * @param {Object} opts + */ + saveData: function(data, opts) { + var q = util.queue(); + if( data === null ) { + util.each(this.getPathManager().getPaths(), function(path) { + path.ref().remove(q.getHandler()); + }); + } + else if( !util.isObject(data) ) { + throw new Error('Calls to set() or update() on a NormalizedCollection must pass either ' + + 'null or an object value. There is no way to split a primitive value between the paths'); + } + else { + util.each(data, function(v, k) { + if( k === '.value' || k === '.priority' ) { + throw new Error('Cannot use .priority or .value on the root path of a NormalizedCollection. ' + + 'You probably meant to sort the records anyway (i.e. one level lower).'); + } + this.child(k).saveData(v, {isUpdate: opts.isUpdate, callback: q.getHandler()}); + }, this); + if( opts.priority ) { + this.getPathManager().first().ref().setPriority(opts.priority, q.getHandler()); + } + } + q.handler(opts.callback||util.noop, opts.context); + }, + + /** + * Return the correct child key for a snapshot by determining if its corresponding path + * has dependencies. If so, we look up the id and return that child, otherwise, we just + * return the child for the recordId. + * + * If a dependency exists, but the required field is null or invalid, then we just return + * null in place of the snapshot. + * + * @private + */ + _getChildKey: function(snap, snapsArray, recordId) { + var key = recordId; + var path = this.getPathManager().getPathFor(snap.ref().toString()); + // resolve any dependencies to determine the child key's value + if( path.hasDependency() ) { + var dep = path.getDependency(); + var depPath = this.getFieldMap().getPath(dep.path); + if( !depPath ) { + throw new Error('Invalid dependency path. ' + snap.ref.toString() + + ' depends on ' + dep.path + + ', but that alias does not exist in the paths provided.'); + } + var depSnap = util.find(snapsArray, function(snap) { + return snap.ref().toString() === depPath.url(); + }); + if( depSnap ) { + depSnap = depSnap.child(recordId); + if( dep.field !== '$value' ) { + depSnap = depSnap.child(dep.field); + } + key = depSnap.val(); + } + else { + key = null; + } + } + return key; + }, + + _start: function() { + this.monitor.start(); + }, + + _stop: function(event, count) { + if( count === 0 ) { this.monitor.stop(); } + } +}); + +module.exports = RecordSet; +},{"../../common":24,"./AbstractRecord":3,"./FieldMap":4,"./Record":12,"./RecordSetEventManager":15}],15:[function(require,module,exports){ +'use strict'; + +var util = require('../../common'); + +/** + * Monitors the references attached to a RecordSet and maintains a cache of + * current snapshots (inside RecordList below). Any time there is an update, this calls + * RecordSet.handler() to invoke the correct event types (child_added, child_removed, value, et al) + * on the RecordSet object. + * + * @param parentRec + * @constructor + */ +function RecordSetEventManager(parentRec) { + var pm = parentRec.getPathManager(); + this.masterRef = pm.first().ref(); + this.url = this.masterRef.toString(); + this.recList = new RecordList(parentRec, this.url); + this.running = false; +} + +RecordSetEventManager.prototype = { + start: function() { + if( !this.running ) { + util.log('RecordSetEventManager: Loading normalized records from master list %s', this.url); + this.running = true; + this.masterRef.on('child_added', this._add, this); + this.masterRef.on('child_removed', this._remove, this); + this.masterRef.on('child_moved', this._move, this); + /** + * This depends on the fact that all child_added events on a given path will be triggered + * before + */ + this.masterRef.once('value', this.recList.masterPathLoaded, this.recList); + } + return this; + }, + + stop: function() { + if( this.running ) { + util.log('RecordSetEventManager: Stopped monitoring master list %s', this.url); + this.running = false; + this.masterRef.off('child_added', this._add, this); + this.masterRef.off('child_removed', this._remove, this); + this.masterRef.off('child_moved', this._move, this); + this.recList.unloaded(); + } + return this; + }, + + _add: function(snap, prevChild) { + this.recList.add(snap.key(), prevChild); + }, + + _remove: function(snap) { + this.recList.remove(snap.key()); + }, + + _move: function(snap, prevChild) { + this.recList.move(snap.key(), prevChild); + } +}; + +function RecordList(observable, url) { + this.obs = observable; + this.url = url; + this._reset(); +} + +RecordList.prototype = { + add: function(key, prevChild) { + util.log.debug('RecordList.add: key=%s, prevChild=%s', key, prevChild); + var rec = this.obs.child(key); + var fn = util.bind(this._valueUpdated, this, key); + this.loading[key] = {rec: rec, prev: prevChild, fn: fn}; + if( !this.loadComplete ) { + this.initialKeysLeft.push(key); + } + rec.watch('value', fn); + }, + + remove: function(key) { + util.log.debug('RecordList.remove: key=%s', key); + var oldSnap = this._dropRecord(key); + if( oldSnap !== null ) { + this._notify('child_removed', key, oldSnap); + } + }, + + move: function(key, prevChild) { + if(util.has(this.recs, key)) { + var currPos = util.indexOf(this.recIds, key); + this.recIds.splice(currPos, 1); + this._putAfter(key, prevChild); + this._notify('child_moved', key); + } + }, + + masterPathLoaded: function() { + util.log.debug('RecordList: Initial data has been loaded from master list at %s', this.url); + this.masterLoaded = true; + this._checkLoadState(); + }, + + unloaded: function() { + this._reset(); + }, + + findKey: function(key) { + //todo cache these lookups in a weak map? + return util.indexOf(this.recIds, key); + }, + + _reset: function() { + util.each(this.recs, function(rec, key) { + this.remove(key); + }, this); + this.recs = {}; + this.recIds = []; + this.snaps = {}; + this.loading = {}; + this.loadComplete = false; + this.initialKeysLeft = []; + this.masterLoaded = false; + }, + + _valueUpdated: function(key, snap) { + this.snaps[key] = snap; + if(util.has(this.loading, key)) { + // newly added record + var r = this.loading[key]; + delete this.loading[key]; + if( this.obs.filters.test(snap.val(), key, snap.getPriority()) ) { + this.recs[key] = r.rec; + this._putAfter(key, r.prev); + this._checkLoadState(key); + this._notify('child_added', key); + } + else { + util.log('RecordList: Filtered key %s', key); + r.rec.unwatch('value', r.fn); + } + } + else if(util.has(this.recs, key)) { + // a changed record + this._notify('child_changed', key); + } + else { + util.log('RecordList: Orphan key %s ignored. Probably a concurrent edit.', key); + } + }, + + _notify: function(event, key, oldSnap) { + var args = [key]; + // do not fetch prev child for other events as it costs an indexOf + switch(event) { + case 'child_added': + case 'child_moved': + var prev = this._getPrevChild(key); + args.push(this.snaps[key], prev); + break; + case 'child_changed': + args.push(this.snaps[key]); + break; + case 'child_removed': + args.push(oldSnap); + break; + default: + throw new Error('Invalid event type ' + event + ' for key ' + key); + } + util.log('RecordList._notify: event=%s, key=%s, prev=%s', event, key, prev); + this.obs.handler(event).apply(this.obs, args); + this._notifyValue(); + }, + + _notifyValue: function() { + util.log.debug('RecordList._notifyValue: snap_keys=%s, loadComplete=%s', util.keys(this.snaps), this.loadComplete); + if( this.loadComplete ) { + this.obs.handler('value')(util.toArray(this.snaps)); + } + }, + + _getPrevChild: function(key) { + if( !this.recIds.length ) { return null; } + var pos = this.findKey(key); + if( pos === -1 ) { + return this.recIds[this.recIds.length-1]; + } + else if( pos === 0 ) { + return null; + } + else { + return this.recIds[pos-1]; + } + }, + + _posFor: function(prevKey) { + var pos, x; + if( prevKey === null ) { + pos = 0; + } + else { + x = this.findKey(prevKey); + pos = x === -1? this.recIds.length : x+1; + } + return pos; + }, + + _putAfter: function(key, prevChild) { + var newPos = this._posFor(prevChild); + this.recIds.splice(newPos, 0, key); + }, + + _dropRecord: function(key) { + if(util.has(this.recs, key)) { + var snap = this.snaps[key]; + this.recs[key].unwatch('value', this._valueUpdated, this); + delete this.recs[key]; + delete this.snaps[key]; + delete this.loading[key]; + util.remove(this.recIds, key); + return snap; + } + return null; + }, + + /** + * Because the initial once('value') will probably trigger before all the child paths + * are retrieved (remember that we are monitoring multiple paths per child), we need + * to wait for them to load in before triggering our first value event. + * @private + */ + _checkLoadState: function(key) { + if( this.loadComplete ) { return; } + if( key ) { + util.remove(this.initialKeysLeft, key); + } + if( !this.initialKeysLeft.length && this.masterLoaded ) { + this.loadComplete = true; + if( !key ) { + this._notifyValue(); + } + } + } +}; + +module.exports = RecordSetEventManager; +},{"../../common":24}],16:[function(require,module,exports){ +'use strict'; + +var util = require('../../common'); +var PathManager = require('./PathManager'); +var Path = require('./Path'); +var FieldMap = require('./FieldMap'); +var RecordSet = require('./RecordSet'); + +module.exports = { + replicate: function(record, newMasterRef) { + // create a new set of paths + var paths = record.getPathManager().getPaths().slice(0); + var firstPath = paths[0]; + paths[0] = new Path([newMasterRef, firstPath.name(), firstPath.getDependency()]); + var mgr = new PathManager(util.map(paths, function(p) { return p.clone(); })); + + // create a new field map from the updated paths + var fieldMap = new FieldMap(mgr); + record.getFieldMap().forEach(fieldMap.add, fieldMap); + + // recreate the AbstractRecord instance + var Clazz = record.getClass(); + var rec; + if( Clazz === RecordSet ) { + rec = new Clazz(fieldMap, record.filters); + } + else { + rec = new Clazz(fieldMap); + } + + // done! + return rec; + } +}; +},{"../../common":24,"./FieldMap":4,"./Path":9,"./PathManager":10,"./RecordSet":14}],17:[function(require,module,exports){ +'use strict'; +var util = require('../common'); +var Scroll = require('./libs/Scroll.js'); +var Paginate = require('./libs/Paginate.js'); +var ReadOnlyRef = require('./libs/ReadOnlyRef.js'); + +var DEFAULTS = { + field: null, + pageSize: 10, + windowSize: 250 +}; + +exports.Scroll = function(baseRef, sortField, opts) { + if( !util.isFirebaseRef(baseRef) ) { + throw new Error('First argument to Firebase.util.Scroll must be a valid Firebase ref'); + } + if( typeof sortField !== 'string' ) { + throw new Error('Second argument to Firebase.util.Scroll must be a valid string'); + } + if( arguments.length > 2 && !util.isObject(opts) ) { + throw new Error('Optional third argument to Firebase.util.Scroll must be an object of key/value pairs'); + } + var ref = new ReadOnlyRef(baseRef); + ref.scroll = new Scroll(ref, sortField, calcOpts(opts, 'windowSize', 'Scroll')); + return ref; +}; + +exports.Paginate = function(baseRef, sortField, opts) { + if( !util.isFirebaseRef(baseRef) ) { + throw new Error('First argument to Firebase.util.Paginate must be a valid Firebase ref'); + } + if( typeof sortField !== 'string' ) { + throw new Error('Second argument to Firebase.util.Paginate must be a valid string'); + } + if( arguments.length > 2 && !util.isObject(opts) ) { + throw new Error('Optional third argument to Firebase.util.Paginate must be an object of key/value pairs'); + } + var ref = new ReadOnlyRef(baseRef); + ref.page = new Paginate(ref, sortField, calcOpts(opts, 'pageSize', 'Paginate')); + return ref; +}; + +function calcOpts(opts, maxFromKey, method) { + var res = util.extend({}, DEFAULTS, opts); + if( !res.maxCacheSize ) { + res.maxCacheSize = res[maxFromKey] * 3; + } + assertNumber(res, maxFromKey, method); + assertNumber(res, 'maxCacheSize', method); + return res; +} + +function assertNumber(obj, key, method) { + if( typeof obj[key] !== 'number' ) { + throw new Error('Argument ' + key + ' passed into opts for ' + method + 'must be a number' ); + } +} +},{"../common":24,"./libs/Paginate.js":20,"./libs/ReadOnlyRef.js":21,"./libs/Scroll.js":22}],18:[function(require,module,exports){ +'use strict'; +var util = require('../../common'); +var Offset = require('./Offset'); + +function Cache(outRef, sortField, maxRecordsLoaded) { + util.log.debug('Cache: caching %s using field=%s maxRecordsLoaded=%d', outRef.toString(), sortField, maxRecordsLoaded); + this.offset = new Offset({field: sortField, max: maxRecordsLoaded, ref: outRef.ref()}); + this.outRef = outRef; + this.inRef = null; + this.queryRef = null; + this.countRef = null; + this.keys = {}; + this.start = 0; + this.count = -1; + this.endCount = -1; + this.nextListeners = []; + this.offset.observe(this._keyChange, this); +} + +Cache.prototype.moveTo = function(startOffset, numberOfRecords) { + util.log.debug('Cache.moveTo: startOffset=%d, numberOfRecords=%d', startOffset, numberOfRecords); + var s = this.start, e = this.count; + this.start = startOffset; + this.count = numberOfRecords; + this.endCount = this.start + this.count; + if( s !== this.start ) { + this.offset.goTo(startOffset, numberOfRecords); + } + else if( e !== this.count ) { + this._refChange(); + } +}; + +Cache.prototype.hasNext = function() { + return this.count === -1 || this.endCount > this.start + this.count; +}; + +Cache.prototype.hasPrev = function() { + return this.start > 0; +}; + +Cache.prototype.observeHasNext = function(callback, context) { + var list = this.nextListeners; + var parts = [callback, context]; + list.push(parts); + return function() { + util.remove(list, parts); + }; +}; + +Cache.prototype.destroy = function() { + this._unsubscribe(); + this.offset.destroy(); + this.offset = null; + this.start = 0; + this.count = -1; + this.inRef = null; + this.outRef = null; + this.queryRef = null; + this.countRef = null; + this.keys = null; + this.nextListeners = null; +}; + +Cache.prototype._keyChange = function(val, key, ref) { + this.inRef = ref; + util.log.debug('Cache._keyChange: %s %s %s', val, key, ref.toString()); + this._refChange(); +}; + +Cache.prototype._unsubscribe = function() { + if( this.queryRef ) { + this.queryRef.off('child_added', this._add, this); + this.queryRef.off('child_removed', this._remove, this); + this.queryRef.off('child_moved', this._move, this); + this.queryRef.off('child_changed', this._change, this); + this.queryRef.off('value', this._value, this); + this.queryRef.off('value', this._removeOrphans, this); + this.queryRef = null; + } + if( this.countRef ) { + this.countRef.off('value', this._count, this); + this.countRef = null; + } +}; + +Cache.prototype._refChange = function() { + this._unsubscribe(); + if( this.inRef && this.count > -1 ) { + this.countRef = this.inRef.limitToFirst(this.count+1); + this.countRef.on('value', this._count, this); + //todo we should queue all the events until the once('value') is completed + //todo so that we can trigger removed before added + this.queryRef = this.inRef.limitToFirst(this.count); + this.queryRef.on('child_added', this._add, this); + this.queryRef.on('child_removed', this._remove, this); + this.queryRef.on('child_moved', this._move, this); + this.queryRef.on('child_changed', this._change, this); + this.queryRef.on('value', this._value, this); + this.queryRef.once('value', this._removeOrphans, this); + } +}; + +Cache.prototype._add = function(snap, prevChild) { + var key = snap.key(); + if( !util.has(this.keys, key) ) { + this.keys[key] = snap; + this.outRef.$trigger('child_added', snap, prevChild); + } + else if( !util.isEqual(this.keys[key], snap.val()) ) { + this._change(snap); + } +}; + +Cache.prototype._remove = function(snap) { + var key = snap.key(); + if( util.has(this.keys, key) ) { + this.outRef.$trigger('child_removed', snap); + delete this.keys[key]; + } +}; + +Cache.prototype._move = function(snap, prevChild) { + var key = snap.key(); + if( util.has(this.keys, key) ) { + this.keys[key] = snap; + this.outRef.$trigger('child_moved', snap, prevChild); + } +}; + +Cache.prototype._change = function(snap) { + this.keys[snap.key()] = snap; + this.outRef.$trigger('child_changed', snap); +}; + +Cache.prototype._value = function(snap) { + this.outRef.$trigger('value', snap); +}; + +Cache.prototype._count = function(snap) { + this.endCount = this.start + snap.numChildren(); + var hasNext = this.hasNext(); + util.each(this.nextListeners, function(parts) { + parts[0].call(parts[1], hasNext); + }); +}; + +Cache.prototype._removeOrphans = function(valueSnap) { + util.each(this.keys, function(cachedSnap, key) { + if( !valueSnap.hasChild(key) ) { + this.outRef.$trigger('child_removed', cachedSnap); + delete this.keys[key]; + } + }, this); +}; + +module.exports = Cache; +},{"../../common":24,"./Offset":19}],19:[function(require,module,exports){ +'use strict'; +var util = require('../../common'); + +function Offset(opts) { + this.keys = []; + this.field = opts.field; + this.ref = baseRef(opts.ref, opts.field); + this.max = opts.max; + this.listeners = []; + this.curr = 0; + this.sub = null; + this.isSubscribing = false; + this.lastNotifyValue = util.undef; + this._debouncedRecache = debounce(function() { + util.log.debug('Offset._debouncedRecache: recaching keys for offset %d', this.curr); + this.keys = []; + this._grow(this._listen); + }, this, 100, 1000); +} + +Offset.prototype.goTo = function(newOffset) { + if( newOffset !== this.curr ) { + util.log('Offset.goTo: offset changed from %d to %d', this.curr, newOffset); + this.curr = newOffset; + this.lastNotifyValue = util.undef; + this._listen(); + } +}; + +Offset.prototype.observe = function(callback, context) { + this.listeners.push([callback, context]); + var key = this.getKey(); + var ref = offsetRef(this.ref, key); + callback.call(context, key && key.val, key && key.key, ref); +}; + +Offset.prototype.getKey = function(offset) { + if( !arguments.length ) { offset = this.curr; } + if( offset === 0 ) { return null; } + return this.keys.length > offset && this.keys[offset]; +}; + +Offset.prototype.destroy = function() { + this._unsubscribe(); + this.curr = 0; + this.keys = []; + this.lastNotifyValue = util.undef; + this.isSubscribing = false; +}; + +Offset.prototype._notify = function() { + var key = this.getKey(); + if( !util.isEqual(this.lastNotifyValue, key) ) { + util.log('Offset._notify: key at offset %d is %s', this.curr, key && key.key); + this.lastNotifyValue = key; + var ref = offsetRef(this.ref, key); + util.each(this.listeners, function(parts) { + parts[0].call(parts[1], key && key.val, key && key.key, ref); + }); + } +}; + +Offset.prototype._recache = function() { + if( !this.isSubscribing ) { + this._debouncedRecache(); + } +}; + +var killCount = 0; +Offset.prototype._grow = function(callback) { + var self = this; + var len = self.keys.length; + if( self.curr >= len ) { + var oldKey = self.getKey(); + var startAt = lastKey(self.keys); + var limit = Math.min(self.curr + (startAt? 2 : 1) - len, self.max); + var ref = startAt !== null? self.ref.startAt(startAt.val, startAt.key) : self.ref; + ref.limitToFirst(limit).once('value', function(snap) { + var skipFirst = startAt !== null; + snap.forEach(function(ss) { + if( skipFirst ) { + skipFirst = false; + return; + } + self.keys.push(extractKey(ss, self.field)); + }); + if( killCount++ > 10000 ) { + throw new Error('Tried to fetch more than 10,000 pages to determine the correct offset. Giving up now. Sorry.'); + } + if( self.curr >= self.keys.length && snap.numChildren() === limit ) { + // prevents recursive scoping + setTimeout(util.bind(self._grow, self, callback), 0); + } + else { + killCount = 0; + util.log.debug('Offset._grow: Cached %d keys', self.keys.length); + callback.call(self, !util.isEqual(self.getKey(), oldKey)); + } + }); + } + else { + callback.call(self, false); + } +}; + +Offset.prototype._startOffset = function() { + return Math.max(0, this.curr - this.max, this.curr - 10); +}; + +Offset.prototype._queryRef = function() { + var start = this._startOffset(); + var ref = this.ref; + if( start > 0 ) { + var key = this.getKey(start); + ref = ref.startAt(key.val, key.key); + } + return ref.limitToLast(Math.max(this.curr - start, 1)); +}; + +Offset.prototype._moved = function(snap) { + if( snap.key() === this.getKey() ) { + this._recache(); + } +}; + +Offset.prototype._unsubscribe = function() { + if( this.sub ) { + this.sub.off('child_added', this._recache, this); + this.sub.off('child_moved', this._moved, this); + this.sub.off('child_removed', this._recache, this); + this.sub.off('value', this._doneSubscribing, this); + this.sub = null; + } +}; + +Offset.prototype._subscribe = function() { + this._unsubscribe(); + this.sub = this._queryRef(); + this.isSubscribing = true; + this.sub.on('child_added', this._recache, this); + this.sub.on('child_moved', this._moved, this); + this.sub.on('child_removed', this._recache, this); + this.sub.once('value', this._doneSubscribing, this); +}; + +Offset.prototype._doneSubscribing = function() { + this.isSubscribing = false; + this._notify(); +}; + +Offset.prototype._monitorEmptyOffset = function() { + function fn(snap) { + var count = snap.numChildren(); + if( count > (key === null? 0 : 1) ) { + util.log.debug('Offset._monitorEmptyOffset: A value exists now.'); + ref.off('value', fn); + self._grow(); + } + } + var self = this; + var ref = self.ref; + var key = null; + if( this.keys.length ) { + key = lastKey(this.keys); + ref = ref.startAt(key.val, key.key); + } + util.log.debug('Offset._monitorEmptyOffset: No value exists at offset %d, currently %d keys at this path. Watching for a new value.', this.curr, this.keys.length); + ref.limitToFirst(2).on('value', fn); +}; + +Offset.prototype._listen = function() { + this._unsubscribe(); + if( this.curr > this.keys.length ) { + this._grow(function(/*changed*/) { + if( this.keys.length > this.curr ) { + this._subscribe(); + } + else { + this._monitorEmptyOffset(); + this._notify(); + } + }); + } + else { + this._subscribe(); + } +}; + +function extractKey(snap, field) { + var v; + switch(field) { + case '$key': + v = snap.key(); + break; + case '$priority': + v = snap.getPriority(); + break; + default: + var obj = snap.val(); + if( !util.isObject(obj) ) { + throw new Error('A value of type ' + typeof obj + 'Was found. ' + + 'But we are attempting to order by child field "' + field + '". ' + + 'Pagination requires all records to be objects or it can\'t determine an ' + + 'appropriate offset value.'); + } + else { + v = obj[field]; + } + } + return {val: v, key: snap.key()}; +} + +function offsetRef(baseRef, startKey) { + if( startKey === false ) { + return null; + } + else if( startKey === null ) { + return baseRef; + } + else { + return baseRef.startAt(startKey.val, startKey.key); + } +} + +function baseRef(ref, field) { + if( field === '$key' ) { + return ref.orderByKey(); + } + else if( field === '$priority' ) { + return ref.orderByPriority(); + } + else { + return ref.orderByChild(field); + } +} + +/** + * A rudimentary debounce method + * @param {function} fn the function to debounce + * @param {object} [ctx] the `this` context to set in fn + * @param {int} wait number of milliseconds to pause before sending out after each invocation + * @param {int} [maxWait] max milliseconds to wait before sending out, defaults to wait * 10 or 100 + */ +function debounce(fn, ctx, wait, maxWait) { + var start, cancelTimer, args, runScheduledForNextTick; + if( typeof(ctx) === 'number' ) { + maxWait = wait; + wait = ctx; + ctx = null; + } + + if( typeof wait !== 'number' ) { + throw new Error('Must provide a valid integer for wait. Try 0 for a default'); + } + if( typeof(fn) !== 'function' ) { + throw new Error('Must provide a valid function to debounce'); + } + if( !maxWait ) { maxWait = wait*10 || 100; } + + // clears the current wait timer and creates a new one + // however, if maxWait is exceeded, calls runNow() on the next tick. + function resetTimer() { + if( cancelTimer ) { + cancelTimer(); + cancelTimer = null; + } + if( start && Date.now() - start > maxWait ) { + if(!runScheduledForNextTick){ + runScheduledForNextTick = true; + setTimeout(runNow, 0); + } + } + else { + if( !start ) { start = Date.now(); } + var to = setTimeout(runNow, wait); + cancelTimer = function() { clearTimeout(to); }; + } + } + + // Clears the queue and invokes the debounced function with the most recent arguments + function runNow() { + cancelTimer = null; + start = null; + runScheduledForNextTick = false; + fn.apply(ctx, args); + } + + function debounced() { + args = Array.prototype.slice.call(arguments, 0); + resetTimer(); + } + debounced.running = function() { + return start > 0; + }; + + return debounced; +} + +function lastKey(list) { + var len = list.length; + return len? list[len-1] : null; +} + +module.exports = Offset; +},{"../../common":24}],20:[function(require,module,exports){ +'use strict'; +var util = require('../../common'); +var Cache = require('./Cache'); + +/** + * @param {Firebase} ref + * @param {String} field + * @param {object} [opts] + * @constructor + */ +function Paginate(ref, field, opts) { + this.currPage = 0; + this.field = field; + this.ref = ref; + this.pageSize = opts.pageSize; + this.max = opts.maxCacheSize; + this.subs = []; + this.pageChangeListeners = []; + this.pageCountListeners = []; + this.cache = new Cache(ref, field, opts.maxCacheSize); + this.pageCount = -1; + this.couldHaveMore = false; + this.cache.observeHasNext(this._countPages, this); +} + +/** + * Unload current records and load the next page into the PaginatedRef + * + * @return {Paginate} returns `this` + */ +Paginate.prototype.next = function() { + if( this.hasNext() ) { + this.currPage++; + util.log.debug('Paginate.next: current page is %d', this.currPage); + this._pageChange(); + } + return this; +}; + +/** + * Unload current records and load the previous page into the PaginatedRef. + * + * @return {Paginate} returns `this` + */ +Paginate.prototype.prev = function() { + if( this.hasPrev() ) { + this.currPage--; + util.log.debug('Paginate.prev: current page is %d', this.currPage); + this._pageChange(); + } + return this; +}; + +/** + * Skip to a specific page. The pageNumber must be less than pageCount. + * + * @param {int} pageNumber + * @return {Paginate} returns `this` + */ +Paginate.prototype.setPage = function(pageNumber) { + if( pageNumber > 0 && pageNumber <= this.pageCount ) { + this.currPage = pageNumber; + util.log.debug('Paginate.setPage: current page is %d', this.currPage); + this._pageChange(); + } + else { + util.log.warn('Paginate.setPage: invalid page number %d', pageNumber); + } +}; + +/** + * @return {boolean} true if there are more records after the currently loaded page + */ +Paginate.prototype.hasNext = function() { + return this.cache.hasNext(); +}; + +/** + * @return {boolean} true if there are more records before the currently loaded page + */ +Paginate.prototype.hasPrev = function() { + return this.currPage > 1; +}; + +/** + * Invoked whenever the page count changes. This may not be accurate if number of pages + * exceeds the maxCacheSize. + * + * The callback is delivered two arguments. The first is the {int} count, and the second + * is a {boolean}couldHaveMore which is true whenever we have run into maxCacheSize (i.e. there + * could be more) + * + * @param {Function} callback + * @param {Object} [context] + * @return {Function} a dispose function that cancels the listener + */ +Paginate.prototype.onPageChange = function(callback, context) { + var listeners = this.pageChangeListeners; + var parts = [callback, context]; + listeners.push(parts); + callback.call(context, this.currPage); + return function() { + util.remove(listeners, parts); + }; +}; + +/** + * Invoked whenever the local page count is changed. This may not include + * all records that exist on the remote server, as it is limited by maxCacheSize + */ +Paginate.prototype.onPageCount = function(callback, context) { + var listeners = this.pageCountListeners; + var parts = [callback, context]; + listeners.push(parts); + if( this.pageCount > -1 ) { + callback.call(context, this.pageCount, this.couldHaveMore); + } + else { + this._countPages(); + } + return function() { + util.remove(listeners, parts); + }; +}; + +/** + * Asynchronously fetch the total page count. This maxes a REST API + * call using shallow=true. All the keys must be able to fit in memory at the same time. + * + * @param {Function} [callback] + * @param {Object} [context] + */ +Paginate.prototype.getCountByDowloadingAllKeys = function(callback, context) { + var self = this; + self.downloadingEverything = true; + var url = self.ref.ref().toString(); + if( !url.match(/\/$/) ) { url += '/'; } + url += '.json?shallow=true'; + microAjax(url, function(data) { + var count = 0; + try { + count = util.keys(JSON.parse(data)).length; + } + catch(e) { + util.log.warn(e); + } + util.log.debug('Paginate.getCountByDownloadingAllKeys: found %d keys', count); + self.downloadingEverything = false; + self.pageCount = countPages(count, self.pageSize); + self.couldHaveMore = false; + self._notifyPageCount(); + if( callback ) { callback.call(context, count); } + }); +}; + +/** + * Deletes locally cached data and cancels all listeners. Unloads + * records and triggers child_removed events. + */ +Paginate.prototype.destroy = function() { + this.cache.destroy(); + this.cache = null; + this.ref = null; + util.each(this.subs, function(fn) { fn(); }); + this.subs = []; + this.pageCountListeners.length = 0; + this.pageChangeListeners.length = 0; +}; + +Paginate.prototype._countPages = function() { + var self = this; + var currPage = self.currPage; + if( !this.downloadingEverything ) { + if( self.pageCount === -1 ) { + var max = self.max; + var pageSize = self.pageSize; + var ref = this.ref.ref().limitToFirst(max); + ref.once('value', function(snap) { + if( self.pageCount === -1 ) { // double-null check pattern (may have changed during async op) + self.couldHaveMore = snap.numChildren() === max; + self.pageCount = Math.ceil(snap.numChildren() / pageSize); + self._notifyPageCount(); + self._countPages(); + } + }); + } + else if( currPage >= self.pageCount ) { + self.pageCount = currPage; + self.couldHaveMore = self.cache.hasNext(); + self._notifyPageCount(); + } + } +}; + +Paginate.prototype._pageChange = function() { + var currPage = this.currPage; + var start = (currPage -1) * this.pageSize; + this.cache.moveTo(start, this.pageSize); + this._countPages(); + util.each(this.pageChangeListeners, function(parts) { + parts[0].call(parts[1], currPage); + }); +}; + +Paginate.prototype._notifyPageCount = function() { + var pageCount = this.pageCount; + var couldHaveMore = this.couldHaveMore; + util.each(this.pageCountListeners, function(parts) { + parts[0].call(parts[1], pageCount, couldHaveMore); + }); +}; + +function countPages(recordCount, pageSize) { + if( recordCount === 0 ) { + return 0; + } + else { + return Math.ceil(recordCount / pageSize); + } +} + +// https://code.google.com/p/microajax/ +// new BSD license: http://opensource.org/licenses/BSD-3-Clause +function microAjax(url,callbackFunction){var o={};o.bindFunction=function(caller,object){return function(){return caller.apply(object,[object]);};};o.stateChange=function(object){if(o.request.readyState==4) o.callbackFunction(o.request.responseText);};o.getRequest=function(){if(window.ActiveXObject) return new ActiveXObject('Microsoft.XMLHTTP');else if(window.XMLHttpRequest) return new XMLHttpRequest();return false;};o.postBody=(arguments[2]||"");o.callbackFunction=callbackFunction;o.url=url;o.request=o.getRequest();if(o.request){var req=o.request;req.onreadystatechange=o.bindFunction(o.stateChange,o);if(o.postBody!==""){req.open("POST",url,true);req.setRequestHeader('X-Requested-With','XMLHttpRequest');req.setRequestHeader('Content-type','application/x-www-form-urlencoded');req.setRequestHeader('Connection','close');}else{req.open("GET",url,true);} req.send(o.postBody);} return o;} // jshint ignore:line + +module.exports = Paginate; + +},{"../../common":24,"./Cache":18}],21:[function(require,module,exports){ +'use strict'; +var util = require('../../common'); + +function ReadOnlyRef(ref) { + this._ref = ref; + this._obs = new util.Observable( + ['value', 'child_added', 'child_removed', 'child_moved', 'child_changed'] + ); +} + + +ReadOnlyRef.prototype = { + 'on': function(event, callback, cancel, context) { + this._obs.observe(event, callback, cancel, context); + }, + + 'once': function(event, callback, cancel, context) { + function fn(snap) { + /*jshint validthis:true */ + this.off(event, fn, this); + callback.call(context, snap); + } + this.on(event, fn, cancel, this); + }, + + 'off': function(event, callback, context) { + this._obs.stopObserving(event, callback, context); + }, + + /**************************** + * WRAPPER FUNCTIONS + ****************************/ + 'ref': function() { + return this._ref; + }, + 'child': wrapMaster('child'), + 'parent': wrapMaster('parent'), + 'root': wrapMaster('root'), + 'name': wrapMaster('name'), + 'key': wrapMaster('key'), + 'toString': wrapMaster('toString'), + + 'auth': wrapMaster('auth'), + 'unauth': wrapMaster('unauth'), + 'authWithCustomToken': wrapMaster('authWithCustomToken'), + 'authAnonymously': wrapMaster('authAnonymously'), + 'authWithPassword': wrapMaster('authWithPassword'), + 'authWithOAuthPopup': wrapMaster('authWithOAuthPopup'), + 'authWithOAuthRedirect': wrapMaster('authWithOAuthRedirect'), + 'authWithOAuthToken': wrapMaster('authWithOAuthToken'), + 'getAuth': wrapMaster('getAuth'), + 'onAuth': wrapMaster('onAuth'), + 'offAuth': wrapMaster('offAuth'), + 'createUser': wrapMaster('createUser'), + 'changePassword': wrapMaster('changePassword'), + 'removeUser': wrapMaster('removeUser'), + 'resetPassword': wrapMaster('resetPassword'), + 'changeEmail': wrapMaster('changeEmail'), + + 'goOffline': wrapMaster('goOffline'), + 'goOnline': wrapMaster('goOnline'), + + /**************************** + * UNSUPPORTED FUNCTIONS + ***************************/ + 'set': isReadOnly('set'), + 'update': isReadOnly('update'), + 'remove': isReadOnly('remove'), + 'push': isReadOnly('push'), + 'setWithPriority': isReadOnly('setWithPriority'), + 'setPriority': isReadOnly('setPriority'), + 'transaction': isReadOnly('transaction'), + + /** @deprecated */ + 'limit': notSupported('limit'), + + 'onDisconnect': notSupported('onDisconnect'), + 'orderByChild': notSupported('orderByChild'), + 'orderByKey': notSupported('orderByKey'), + 'orderByPriority': notSupported('orderByPriority'), + 'limitToFirst': notSupported('limitToFirst'), + 'limitToLast': notSupported('limitToLast'), + 'startAt': notSupported('startAt'), + 'endAt': notSupported('endAt'), + 'equalTo': notSupported('equalTo'), + + /** INTERNAL METHODS */ + $trigger: function() { + this._obs.triggerEvent.apply(this._obs, util.toArray(arguments)); + } +}; + +function wrapMaster(method) { + return function() { + var args = util.toArray(arguments); + var ref = this.ref(); + return ref[method].apply(ref, args); + }; +} + +function isReadOnly(method) { + return function() { + throw new Error(method + ' is not supported. This is a read-only reference. You can ' + + 'modify child records after calling .child(), or work with the original by using .ref().'); + }; +} + +function notSupported(method) { + return function() { + throw new Error(method + ' is not supported for Paginate and Scroll references. ' + + 'Try calling it on the original reference used to create the instance instead.'); + }; +} + +module.exports = ReadOnlyRef; +},{"../../common":24}],22:[function(require,module,exports){ +'use strict'; +var Cache = require('./Cache'); + +/** + * @param {ReadOnlyRef} readOnlyRef + * @param {String} field + * @param {Object} [opts] + * @constructor + */ +function Scroll(readOnlyRef, field, opts) { + this.max = opts.windowSize; + this.start = 0; + this.end = 0; + this.cache = new Cache(readOnlyRef, field, opts.maxCacheSize); +} + +/** + * Load the next numberToAppend records and trigger child_added events + * for them. If the total number of records exceeds maxRecords, then + * child_removed events will be triggered for the first items in the list. + * + * @param {int} numberToAppend + */ +Scroll.prototype.next = function(numberToAppend) { + if( this.hasNext() ) { + this.end = this.end + numberToAppend; + this.start = Math.max(0, this.end - this.max, this.start); + this.cache.moveTo(this.start, this.end - this.start); + } +}; + +/** + * Load the previous numberToAppend records and trigger child_added events + * for them. If the total number of records exceeds maxRecords, then + * child_removed events will be triggered for the last items in the list. + * + * @param {int} numberToPrepend + */ +Scroll.prototype.prev = function(numberToPrepend) { + if( this.hasPrev() ) { + this.start = Math.max(0, this.start - numberToPrepend); + this.end = Math.min(this.start + this.max, this.end); + this.cache.moveTo(this.start, this.end-this.start); + } +}; + +/** + * @return {boolean} true if there are more records after the currently loaded page + */ +Scroll.prototype.hasNext = function() { + return this.cache.hasNext(); +}; + +/** + * @return {boolean} true if there are more records before the currently loaded page + */ +Scroll.prototype.hasPrev = function() { + return this.start > 0; +}; + +Scroll.prototype.observeHasNext = function(callback, context) { + return this.cache.observeHasNext(callback, context); +}; + +/** + * Deletes locally cached data and cancels all listeners. Unloads + * records and triggers child_removed events. + */ +Scroll.prototype.destroy = function() { + this.cache.destroy(); + this.ref = null; + this.cache = null; +}; + +module.exports = Scroll; +},{"./Cache":18}],23:[function(require,module,exports){ +/** + * This file loads all the public methods from + * the common/ library. To fetch all methods + * for internal use, just do require('./src/common'), + * which loads index.js and includes the private methods. + */ + +var util = require('./index.js'); +exports.log = util.log; +exports.logLevel = util.logLevel; +exports.escapeEmail = util.escapeEmail; +},{"./index.js":24}],24:[function(require,module,exports){ +/** + * This file loads the entire common/ package for INTERNAL USE. + * The public methods are specified by exports.js + */ + +var util = require('./libs/util.js'); +var log = require('./libs/logger.js'); + +util.extend( + exports, + util, + { + args: require('./libs/args.js'), + log: log, + logLevel: log.logLevel, + Observable: require('./libs/Observable.js'), + Observer: require('./libs/Observer.js'), + queue: require('./libs/queue.js') + } +); +},{"./libs/Observable.js":25,"./libs/Observer.js":26,"./libs/args.js":27,"./libs/logger.js":28,"./libs/queue.js":29,"./libs/util.js":30}],25:[function(require,module,exports){ +'use strict'; + +var util = require('./util.js'); +var getArgs = require('./args.js'); +var log = require('./logger.js'); +var Observer = require('./Observer.js'); + +/** + * A simple observer model for watching events. + * @param eventsMonitored + * @param [opts] can contain callbacks for onAdd, onRemove, and onEvent, as well as a list of oneTimeEvents + * @constructor + */ +function Observable(eventsMonitored, opts) { + if( !opts ) { opts = {}; } + this._observableProps = parseProps(eventsMonitored, opts); + this.resetObservers(); +} +Observable.prototype = { + /** + * @param {String} event + * @param {Function|util.Observer} callback + * @param {Function} [cancelFn] + * @param {Object} [scope] + */ + observe: function(event, callback, cancelFn, scope) { + var args = getArgs('observe', arguments, 2, 4), obs; + event = args.nextFromReq(this._observableProps.eventsMonitored); + if( event ) { + callback = args.nextReq('function'); + cancelFn = args.next('function'); + scope = args.next('object'); + obs = new Observer(this, event, callback, scope, cancelFn); + this._observableProps.observers[event].push(obs); + this._observableProps.onAdd(event, obs); + if( this.isOneTimeEvent(event) ) { + checkOneTimeEvents(event, this._observableProps, obs); + } + } + return obs; + }, + + /** + * @param {String|Array} [event] + * @returns {boolean} + */ + hasObservers: function(event) { + return this.getObservers(event).length > 0; + }, + + /** + * @param {String|Array} events + * @param {Function|Observer} callback + * @param {Object} [scope] + */ + stopObserving: function(events, callback, scope) { + var args = getArgs('stopObserving', arguments); + events = args.next(['array', 'string'], this._observableProps.eventsMonitored); + callback = args.next(['function']); + scope = args.next(['object']); + util.each(events, function(event) { + var removes = []; + var observers = this.getObservers(event); + util.each(observers, function(obs) { + if( obs.matches(event, callback, scope) ) { + obs.notifyCancelled(null); + removes.push(obs); + } + }, this); + removeAll(this._observableProps.observers[event], removes); + if( removes.length ) { + this._observableProps.onRemove(event, removes); + } + }, this); + }, + + /** + * Turn off all observers and call cancel callbacks with an error + * @param {String} error + * @returns {*} + */ + abortObservers: function(error) { + var removes = []; + if( this.hasObservers() ) { + var observers = this.getObservers().slice(); + util.each(observers, function(obs) { + obs.notifyCancelled(error); + removes.push(obs); + }, this); + this.resetObservers(); + if( removes.length ) { + this._observableProps.onRemove(this.event, removes); + } + } + }, + + /** + * @param {String|Array} [events] + * @returns {*} + */ + getObservers: function(events) { + events = getArgs('getObservers', arguments).listFrom(this._observableProps.eventsMonitored, true); + return getObserversFor(this._observableProps, events); + }, + + triggerEvent: function(event) { + var args = getArgs('triggerEvent', arguments); + var events = args.listFromWarn(this._observableProps.eventsMonitored, true); + var passThruArgs = args.restAsList(); + if( events ) { + util.each(events, function(e) { + if( this.isOneTimeEvent(event) ) { + if( util.isArray(this._observableProps.oneTimeResults, event) ) { + log.warn('One time event was triggered twice, should by definition be triggered once', event); + return; + } + this._observableProps.oneTimeResults[event] = passThruArgs; + } + var observers = this.getObservers(e), ct = 0; + util.each(observers, function(obs) { + obs.notify.apply(obs, passThruArgs.slice(0)); + ct++; + }); + this._observableProps.onEvent.apply(null, [e, ct].concat(passThruArgs.slice(0))); + }, this); + } + }, + + resetObservers: function() { + util.each(this._observableProps.eventsMonitored, function(key) { + this._observableProps.observers[key] = []; + }, this); + }, + + isOneTimeEvent: function(event) { + return util.contains(this._observableProps.oneTimeEvents, event); + }, + + observeOnce: function(event, callback, cancelFn, scope) { + var args = getArgs('observeOnce', arguments, 2, 4), obs; + event = args.nextFromWarn(this._observableProps.eventsMonitored); + if( event ) { + callback = args.nextReq('function'); + cancelFn = args.next('function'); + scope = args.next('object'); + obs = new Observer(this, event, callback, scope, cancelFn, true); + this._observableProps.observers[event].push(obs); + this._observableProps.onAdd(event, obs); + if( this.isOneTimeEvent(event) ) { + checkOneTimeEvents(event, this._observableProps, obs); + } + } + return obs; + } +}; + +function removeAll(list, items) { + util.each(items, function(x) { + var i = util.indexOf(list, x); + if( i >= 0 ) { + list.splice(i, 1); + } + }); +} + +function getObserversFor(props, events) { + var out = []; + util.each(events, function(event) { + if( !util.has(props.observers, event) ) { + log.warn('Observable.hasObservers: invalid event type %s', event); + } + else { + if( props.observers[event].length ) { + out = out.concat(props.observers[event]); + } + } + }); + return out; +} + +function checkOneTimeEvents(event, props, obs) { + if( util.has(props.oneTimeResults, event) ) { + obs.notify.apply(obs, props.oneTimeResults[event]); + } +} + +function parseProps(eventsMonitored, opts) { + return util.extend( + { onAdd: util.noop, onRemove: util.noop, onEvent: util.noop, oneTimeEvents: [] }, + opts, + { eventsMonitored: eventsMonitored, observers: {}, oneTimeResults: {} } + ); +} + +module.exports = Observable; +},{"./Observer.js":26,"./args.js":27,"./logger.js":28,"./util.js":30}],26:[function(require,module,exports){ +'use strict'; +var util = require('./util.js'); + +/** Observer + *************************************************** + * @private + * @constructor + */ +function Observer(observable, event, notifyFn, context, cancelFn, oneTimeEvent) { + if( typeof(notifyFn) !== 'function' ) { + throw new Error('Must provide a valid notifyFn'); + } + this.observable = observable; + this.fn = notifyFn; + this.event = event; + this.cancelFn = cancelFn||function() {}; + this.context = context; + this.oneTimeEvent = !!oneTimeEvent; +} + +Observer.prototype = { + notify: function() { + var args = util.toArray(arguments); + this.fn.apply(this.context, args); + if( this.oneTimeEvent ) { + this.observable.stopObserving(this.event, this.fn, this.context); + } + }, + + matches: function(event, fn, context) { + if( util.isArray(event) ) { + return util.contains(event, function(e) { + return this.matches(e, fn, context); + }, this); + } + return (!event || event === this.event) && + (!fn || fn === this || fn === this.fn) && + (!context || context === this.context); + }, + + notifyCancelled: function(err) { + this.cancelFn.call(this.context, err||null); + } +}; + +module.exports = Observer; +},{"./util.js":30}],27:[function(require,module,exports){ +'use strict'; +var util = require('./util.js'); +var log = require('./logger.js'); + +function Args(fnName, args, minArgs, maxArgs) { + if( typeof(fnName) !== 'string' || !util.isObject(args) ) { + throw new Error('Args requires at least 2 args: fnName, arguments[, minArgs, maxArgs]'); + } + if( !(this instanceof Args) ) { // allow it to be called without `new` prefix + return new Args(fnName, args, minArgs, maxArgs); + } + this.fnName = fnName; + this.argList = util.toArray(args); + this.origArgs = util.toArray(args); + var len = this.length = this.argList.length; + this.pos = -1; + if(util.isUndefined(minArgs)) { minArgs = 0; } + if(util.isUndefined(maxArgs)) { maxArgs = this.argList.length; } + if( len < minArgs || len > maxArgs ) { + var rangeText = maxArgs > minArgs? util.printf('%d to %d', minArgs, maxArgs) : minArgs; + throw Error(util.printf('%s must be called with %s arguments, but received %d', fnName, rangeText, len)); + } +} + +Args.prototype = { + /** + * Grab the original list of args + * @return {Array} containing the original arguments + */ + orig: function() { return this.origArgs.slice(0); }, + + /** + * Return whatever args remain as a list + * @returns {Array|string|Buffer|Blob|*} + */ + restAsList: function(minLength, types) { + var list = this.argList.slice(0); + if( minLength || types ) { + for (var i = 0, len = list.length; i < len; i++) { + this._arg(types||true, null, i < minLength); + } + } + return list; + }, + + /** + * Advance the argument list by one and discard the value + * @return {Args} + */ + skip: function() { + if( this.argList.length ) { + this.pos++; + this.argList.shift(); + } + return this; + }, + + /** + * Read the next optional argument, but only if `types` is true, or it is of a type specified + * In the case that it is not present, return `defaultValue` + * @param {boolean|Array|string} types either `true` or one of array, string, object, number, int, boolean, boolean-like, or function + * @param [defaultValue] + */ + next: function(types, defaultValue) { + return this._arg(types, defaultValue, false); + }, + + /** + * Read the next optional argument, but only if `types` is true, or it is of a type specified. In the case + * that it is not present, return `defaultValue` and log a warning to the console + * @param {boolean|Array|string} types either `true` or one of array, string, object, number, int, boolean, boolean-like, or function + * @param [defaultValue] + */ + nextWarn: function(types, defaultValue) { + return this._arg(types, defaultValue, 'warn'); + }, + + /** + * Read the next required argument, but only if `types` is true, or it is of a type specified. In the case + * that it is not present, throw an Error + * @param {boolean|Array|string} types either `true` or one of array, string, object, number, int, boolean, boolean-like, or function + */ + nextReq: function(types) { + return this._arg(types, null, true); + }, + + /** + * Read the next optional argument, which must be one of the values in choices. If it is not present, + * return defaultValue. + * @param {Array} choices a list of allowed values + * @param [defaultValue] + */ + nextFrom: function(choices, defaultValue) { + return this._from(choices, defaultValue, false); + }, + + /** + * Read the next optional argument, which must be one of the values in choices. If it is not present, + * return defaultValue and log a warning to the console. + * @param {Array} choices a list of allowed values + * @param [defaultValue] + */ + nextFromWarn: function(choices, defaultValue) { + return this._from(choices, defaultValue, 'warn'); + }, + + /** + * Read the next optional argument, which must be one of the values in choices. If it is not present, + * throw an Error. + * @param {Array} choices a list of allowed values + */ + nextFromReq: function(choices) { + return this._from(choices, null, true); + }, + + /** + * Read the next optional argument and return it as an array (it can optionally be an array or a single value + * which will be coerced into an array). All values in the argument must be in choices or they are removed + * from the choices and a warning is logged. If no valid value is present, return defaultValue. + * @param {Array} choices a list of allowed values + * @param [defaultValue] a set of defaults, setting this to true uses the `choices` as default + */ + listFrom: function(choices, defaultValue) { + return this._list(choices, defaultValue, false); + }, + + /** + * Read the next optional argument and return it as an array (it can optionally be an array or a single value + * which will be coerced into an array). All values in the argument must be in choices or they are removed + * from the choices and a warning is logged. If no valid value is present, return defaultValue and log a warning. + * @param {Array} choices a list of allowed values + * @param [defaultValue] a set of defaults, setting this to true uses the `choices` as default + */ + listFromWarn: function(choices, defaultValue) { + return this._list(choices, defaultValue, 'warn'); + }, + + /** + * Read the next optional argument and return it as an array (it can optionally be an array or a single value + * which will be coerced into an array). All values in the argument must be in choices or they are removed + * from the choices and a warning is logged. If no valid value is present, throw an Error. + * @param {Array} choices a list of allowed values + */ + listFromReq: function(choices) { + return this._list(choices, null, true); + }, + + _arg: function(types, defaultValue, required) { + this.pos++; + if( util.isUndefined(types) || types === null ) { types = true; } + if( this.argList.length && isOfType(this.argList[0], types) ) { + return format(this.argList.shift(), types); + } + else { + if( required ) { + assertRequired(required, this.fnName, this.pos, util.printf('must be of type %s', types)); + } + return defaultValue; + } + }, + + _from: function(choices, defaultValue, required) { + this.pos++; + if( this.argList.length && util.contains(choices, this.argList[0]) ) { + return this.argList.shift(); + } + else { + if( required ) { + assertRequired(required, this.fnName, this.pos, util.printf('must be one of %s', choices)); + } + return defaultValue; + } + }, + + _list: function(choices, defaultValue, required) { + this.pos++; + var out = []; + var list = this.argList[0]; + if( this.argList.length && !util.isEmpty(list) && (util.isArray(list) || !util.isObject(list)) ) { + this.argList.shift(); + if( util.isArray(list) ) { + out = util.map(list, function(v) { + if( util.contains(choices, v) ) { + return v; + } + else { + badChoiceWarning(this.fnName, v, choices); + return undefined; + } + }, this); + } + else { + if( util.contains(choices, list) ) { + out = [list]; + } + else { + badChoiceWarning(this.fnName, list, choices); + } + } + } + if( util.isEmpty(out) ) { + if( required ) { + assertRequired(required, this.fnName, this.pos, + util.printf('choices must be in [%s]', choices)); + } + return defaultValue === true? choices : defaultValue; + } + return out; + } + +}; + +function isOfType(val, types) { + if( types === true ) { return true; } + if( !util.isArray(types) ) { types = [types]; } + return util.contains(types, function(type) { + switch(type) { + case 'array': + return util.isArray(val); + case 'string': + return typeof(val) === 'string'; + case 'number': + return isFinite(parseInt(val, 10)); + case 'int': + case 'integer': + return isFinite(parseFloat(val)); + case 'object': + return util.isObject(val); + case 'function': + return typeof(val) === 'function'; + case 'bool': + case 'boolean': + return typeof(val) === 'boolean'; + case 'boolean-like': + return !util.isObject(val); // be lenient here + default: + throw new Error('Args received an invalid data type: '+type); + } + }); +} + +function assertRequired(required, fnName, pos, msg) { + msg = util.printf('%s: invalid argument at pos %d, %s (received %s)', fnName, pos, msg); + if( required === true ) { + throw new Error(msg); + } + else if( util.has(log, required) ) { + log[required](msg); + } + else { + throw new Error('The `required` value passed to Args methods must either be true or a method name from logger'); + } +} + +function badChoiceWarning(fnName, val, choices) { + log.warn('%s: invalid choice %s, must be one of [%s]', fnName, val, choices); +} + +function format(val, types) { + if( types === true ) { return val; } + var type = util.isArray(types)? types[0] : types; + switch(type) { + case 'array': + return util.isArray(val)? val : [val]; + case 'string': + return val + ''; + case 'number': + return parseFloat(val); + case 'int': + case 'integer': + return parseInt(val, 10); + case 'bool': + case 'boolean': + case 'boolean-like': + return !!val; + case 'function': + case 'object': + return val; + default: + throw new Error('Args received an invalid data type: '+type); + } +} + +module.exports = Args; +},{"./logger.js":28,"./util.js":30}],28:[function(require,module,exports){ +'use strict'; +/*global window*/ +var DEFAULT_LEVEL = 2; // errors and warnings +var oldDebuggingLevel = false; +var fakeConsole = { + error: noop, warn: noop, info: noop, log: noop, debug: noop, time: noop, timeEnd: noop, group: noop, groupEnd: noop +}; +var util = require('./util.js'); + +var logger = function() { + logger.log.apply(null, util.toArray(arguments)); +}; + +/** hints for the IDEs */ +logger.warn = noop; +logger.error = noop; +logger.info = noop; +logger.log = noop; +logger.debug = noop; +logger.isErrorEnabled = noop; +logger.isWarnEnabled = noop; +logger.isInfoEnabled = noop; +logger.isLogEnabled = noop; +logger.isDebugEnabled = noop; + +/** + * @param {int} level use -1 to turn off all logging, use 5 for maximum debugging + * @param {string|RegExp} [grep] filter logs to those whose first value matches this text or expression + */ +logger.logLevel = function(level, grep) { + if( typeof(level) !== 'number' ) { level = levelInt(level); } + + if( oldDebuggingLevel === level ) { return function() {}; } + + util.each(['error', 'warn', 'info', 'log', 'debug'], function(k, i) { + var isEnabled = typeof(console) !== 'undefined' && level >= i+1; + if( isEnabled ) { + // binding is necessary to prevent IE 8/9 from having a spaz when + // .apply and .call are used on console methods + var fn = util.bind(console[k==='debug'? 'log' : k], console); + logger[k] = function() { + var args = util.toArray(arguments); + if( args.length > 1 && typeof args[0] === 'string' ) { + var m = args[0].match(/(%s|%d|%j)/g); + if( m ) { + var newArgs = [util.printf.apply(util, args)]; + args = args.length > m.length+1? newArgs.concat(args.slice(m.length+1)) : newArgs; + } + } + if( !grep || !filterThis(grep, args) ) { + fn.apply(typeof(console) === 'undefined'? fakeConsole : console, args); + } + }; + } + else { + logger[k] = noop; + } + logger['is' + ucfirst(k) + 'Enabled'] = function() { return isEnabled; }; + }); + + // provide a way to revert the debugging level if I want to change it temporarily + var off = (function(x) { + return function() { logger.logLevel(x); }; + })(oldDebuggingLevel); + oldDebuggingLevel = level; + + return off; +}; + +function ucfirst(s) { + return s.charAt(0).toUpperCase() + s.substr(1); +} + +function getDefaultLevel() { + var m; + if( typeof(window) !== 'undefined' && window.location && window.location.search ) { + m = window.location.search.match('\bdebugLevel=([0-9]+)\b'); + } + return m? parseInt(m[1], 10) : DEFAULT_LEVEL; +} + +function noop() { return true; } + +function filterThis(expr, args) { + if( !args.length ) { + return true; + } + else if( expr instanceof RegExp ) { + return !expr.test(args[0]+''); + } + else { + return !(args[0]+'').match(expr); + } +} + +function levelInt(x) { + switch(x) { + case false: return 0; + case 'off': return 0; + case 'none': return 0; + case 'error': return 1; + case 'warn': return 2; + case 'warning': return 2; + case 'info': return 3; + case 'log': return 4; + case 'debug': return 5; + case true: return DEFAULT_LEVEL; + case 'on': return DEFAULT_LEVEL; + case 'all': return DEFAULT_LEVEL; + default: return DEFAULT_LEVEL; + } +} + +logger.logLevel(getDefaultLevel()); +module.exports = logger; +},{"./util.js":30}],29:[function(require,module,exports){ +'use strict'; +var util = require('./util.js'); + +function Queue(criteriaFunctions) { + this.needs = 0; + this.met = 0; + this.queued = []; + this.errors = []; + this.criteria = []; + this.processing = false; + util.each(criteriaFunctions, this.addCriteria, this); +} + +Queue.prototype = { + /** + * @param {Function} criteriaFn + * @param {Object} [scope] + */ + addCriteria: function(criteriaFn, scope) { + if( this.processing ) { + throw new Error('Cannot call addCriteria() after invoking done(), fail(), or handler() methods'); + } + this.criteria.push(scope? [criteriaFn, scope] : criteriaFn); + return this; + }, + + /** + * Returns a node-like callback to be invoked when an op is completed. + * @returns {function} + */ + getHandler: function() { + var doneCallback, result; + this.addCriteria(function(done) { + if( result !== util.undef ) { + done(result); + } + else { + doneCallback = done; + } + }); + return function(err) { + if( doneCallback ) { doneCallback(err); } + else { result = err; } + }; + }, + + ready: function() { + return this.needs === this.met; + }, + + done: function(fn, context) { + if( fn ) { + this._runOrStore(function() { + if( !this.hasErrors() ) { fn.call(context); } + }); + } + return this; + }, + + fail: function(fn, context) { + this._runOrStore(function() { + if( this.hasErrors() ) { fn.apply(context, this.getErrors()); } + }); + return this; + }, + + handler: function(fn, context) { + this._runOrStore(function() { + fn.apply(context, this.hasErrors()? this.getErrors() : null); + }); + return this; + }, + + /** + * @param {Queue} queue + */ + chain: function(queue) { + this.addCriteria(queue.handler, queue); + return this; + }, + + when: function(def) { + this._runOrStore(function() { + if( this.hasErrors() ) { + def.reject.apply(def, this.getErrors()); + } + else { + def.resolve(); + } + }); + }, + + addError: function(e) { + this.errors.push(e); + }, + + hasErrors: function() { + return this.errors.length; + }, + + getErrors: function() { + return this.errors.slice(0); + }, + + _process: function() { + this.processing = true; + this.needs = this.criteria.length; + util.each(this.criteria, this._evaluateCriteria, this); + }, + + _evaluateCriteria: function(criteriaFn) { + var scope = null; + if( util.isArray(criteriaFn) ) { + scope = criteriaFn[1]; + criteriaFn = criteriaFn[0]; + } + try { + criteriaFn.call(scope, util.bind(this._criteriaMet, this)); + } + catch(e) { + this.addError(e); + } + }, + + _criteriaMet: function(error) { + if( error ) { this.addError(error); } + this.met++; + if( this.ready() ) { + util.each(this.queued, this._run, this); + } + }, + + _runOrStore: function(fn) { + if( !this.processing ) { this._process(); } + if( this.ready() ) { + this._run(fn); + } + else { + this.queued.push(fn); + } + }, + + _run: function(fn) { + fn.call(this); + } +}; + +module.exports = function(criteriaFns, callback) { + var q = new Queue(criteriaFns); + if( callback ) { q.done(callback); } + return q; +}; +},{"./util.js":30}],30:[function(require,module,exports){ +(function (global){ +/*jshint unused:vars */ +/*jshint bitwise:false */ + +'use strict'; + +var undef; +var util = exports; +var READ_EVENTS = ['value', 'child_added', 'child_removed', 'child_updated', 'child_changed']; + +util.undef = undef; +util.Firebase = global.Firebase || require('firebase'); + +util.isDefined = function(v) { + return v !== undef; +}; + +util.isUndefined = function(v) { + return v === undef; +}; + +util.isObject = function(v) { + return Object.prototype.isPrototypeOf(v); +}; + +util.isArray = function(v) { + return (Array.isArray || isArray).call(null, v); +}; + +/** + * @param v value to test or if `key` is provided, the object containing method + * @param {string} [key] if provided, v is an object and this is the method we want to find + * @returns {*} + */ +util.isFunction = function(v, key) { + if( typeof(key) === 'string' ) { + return util.isObject(v) && util.has(v, key) && typeof(v[key]) === 'function'; + } + else { + return typeof(v) === 'function'; + } +}; + +util.toArray = function(vals, startFrom) { + var newVals = util.map(vals, function(v, k) { return v; }); + return startFrom > 0? newVals.slice(startFrom) : newVals; +}; + +/** + * @param {boolean} [recursive] if true, keys are merged recursively, otherwise, they replace the base + * @param {...object} base + * @returns {Object} + */ +util.extend = function(recursive, base) { + var args = util.toArray(arguments); + var recurse = typeof args[0] === 'boolean' && args.shift(); + var out = args.shift(); + util.each(args, function(o) { + if( util.isObject(o) ) { + util.each(o, function(v,k) { + out[k] = recurse && util.isObject(out[k])? util.extend(true, out[k], v) : v; + }); + } + }); + return out; +}; + +util.bind = function(fn, scope) { + var args = Array.prototype.slice.call(arguments, 1); + return (fn.bind || bind).apply(fn, args); +}; + +/** + * @param {Object|Array} vals + * @returns {boolean} + */ +util.isEmpty = function(vals) { + return vals === undef || vals === null || + (util.isArray(vals) && vals.length === 0) || + (util.isObject(vals) && !util.contains(vals, function(v) { return true; })); +}; + +/** + * @param {Object|Array} vals + * @returns {Array} of keys + */ +util.keys = function(vals) { + var keys = []; + util.each(vals, function(v, k) { keys.push(k); }); + return keys; +}; + +/** + * Create an array using values returned by an iterator. Undefined values + * are discarded. + * + * @param vals + * @param iterator + * @param [scope] + * @returns {*} + */ +util.map = function(vals, iterator, scope) { + var out = []; + util.each(vals, function(v, k) { + var res = iterator.call(scope, v, k, vals); + if( res !== undef ) { out.push(res); } + }); + return out; +}; + +/** + * + * @param {Object} list + * @param {Function} iterator + * @param {Object} [scope] + */ +util.mapObject = function(list, iterator, scope) { + var out = {}; + util.each(list, function(v,k) { + var res = iterator.call(scope, v, k, list); + if( res !== undef ) { + out[k] = res; + } + }); + return out; +}; + +/** + * Returns the first match + * @param {Object|Array} vals + * @param {Function} iterator + * @param {Object} [scope] set `this` in the callback or undefined + */ +util.find = function(vals, iterator, scope) { + if( util.isArray(vals) ) { + for(var i = 0, len = vals.length; i < len; i++) { + if( iterator.call(scope, vals[i], i, vals) === true ) { return vals[i]; } + } + } + else if( util.isObject(vals) ) { + var key; + for (key in vals) { + if (vals.hasOwnProperty(key) && iterator.call(scope, vals[key], key, vals) === true) { + return vals[key]; + } + } + } + return undef; +}; + +util.filter = function(list, iterator, scope) { + var isArray = util.isArray(list); + var out = isArray? [] : {}; + util.each(list, function(v,k) { + if( iterator.call(scope, v, k, list) ) { + if( isArray ) { + out.push(v); + } + else { + out[k] = v; + } + } + }); + return out; +}; + +util.reduce = function(list, accumulator, iterator) { + util.each(list, function(v, k) { + accumulator = iterator(accumulator, v, k, list); + }); + return accumulator; +}; + +util.has = function(vals, key) { + return util.isObject(vals) && vals[key] !== undef; +}; + +util.val = function(list, key) { + return util.has(list, key)? list[key] : undef; +}; + +util.contains = function(vals, iterator, scope) { + if( typeof(iterator) !== 'function' ) { + if( util.isArray(vals) ) { + return util.indexOf(vals, iterator) > -1; + } + iterator = (function(testVal) { + return function(v) { return v === testVal; }; + })(iterator); + } + return util.find(vals, iterator, scope) !== undef; +}; + +util.each = function(vals, cb, scope) { + if( util.isArray(vals) || isArguments(vals) ) { + (vals.forEach || forEach).call(vals, cb, scope); + } + else if( util.isObject(vals) ) { + var key; + for (key in vals) { + if (vals.hasOwnProperty(key)) { + cb.call(scope, vals[key], key, vals); + } + } + } +}; + +util.indexOf = function(list, item) { + return (list.indexOf || indexOf).call(list, item); +}; + +util.remove = function(list, item) { + var res = false; + if( util.isArray(list) ) { + var i = util.indexOf(list, item); + if( i > -1 ) { + list.splice(i, 1); + res = true; + } + } + else if( util.isObject(list) ) { + var key; + for (key in list) { + if (list.hasOwnProperty(key) && item === list[key]) { + res = true; + delete list[key]; + break; + } + } + } + return res; +}; + +/** + * Invoke a function after a setTimeout(..., 0), to help convert synch callbacks to async ones. + * Additional args after `scope` will be passed to the fn when it is invoked + * + * @param {Function} fn + * @param {Object} scope the `this` scope inside `fn` + */ +util.defer = function(fn, scope) { + var args = util.toArray(arguments); + setTimeout(util.bind.apply(null, args), 0); +}; + +/** + * Call a method on each instance contained in the list. Any additional args are passed into the method. + * + * @param {Object|Array} list contains instantiated objects + * @param {String} methodName + * @return {Array} + */ +util.call = function(list, methodName) { + var args = util.toArray(arguments, 2); + var res = []; + util.each(list, function(o) { + if( typeof(o) === 'function' && !methodName ) { + return res.push(o.apply(null, args)); + } + if( util.isObject(o) && typeof(o[methodName]) === 'function' ) { + res.push(o[methodName].apply(o, args)); + } + }); + return res; +}; + +/** + * Determine if two variables are equal. They must be: + * - of the same type + * - arrays must be same length and all values pass isEqual() + * - arrays must be in same order + * - objects must contain the same keys and their values pass isEqual() + * - object keys do not have to be in same order unless objectsSameOrder === true + * - primitives must pass === + * + * @param a + * @param b + * @param {boolean} [objectsSameOrder] + * @returns {boolean} + */ +util.isEqual = function(a, b, objectsSameOrder) { + if( a === b ) { return true; } + else if( typeof(a) !== typeof(b) ) { + return false; + } + else if( util.isObject(a) && util.isObject(b) ) { + var isA = util.isArray(a); + var isB = util.isArray(b); + if( isA || isB ) { + return isA && isB && a.length === b.length && !util.contains(a, function(v, i) { + return !util.isEqual(v, b[i]); + }); + } + else { + var aKeys = objectsSameOrder? util.keys(a) : util.keys(a).sort(); + var bKeys = objectsSameOrder? util.keys(b) : util.keys(b).sort(); + return util.isEqual(aKeys, bKeys) && + !util.contains(a, function(v, k) { return !util.isEqual(v, b[k]); }); + } + } + else { + return false; + } +}; + +util.bindAll = function(context, methods) { + util.each(methods, function(m,k) { + if( typeof(m) === 'function' ) { + methods[k] = util.bind(m, context); + } + }); + return methods; +}; + +util.printf = function() { + var localArgs = util.toArray(arguments); + var template = localArgs.shift(); + var matches = template.match(/(%s|%d|%j)/g); + if( matches && localArgs.length ) { + util.find(matches, function (m) { + template = template.replace(m, format(localArgs.shift(), m)); + return localArgs.length === 0; + }); + } + return template; +}; + +util.mergeToString = function(list) { + if( list.length === 0 ) { return null; } + else if( list.length === 1 ) { return list[0]; } + else { + return '[' + list.join('][') + ']'; + } +}; + +// credits: http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible +util.construct = function(constructor, args) { + function F() { + return constructor.apply(this, args); + } + F.prototype = constructor.prototype; + return new F(); +}; + +util.noop = function() {}; + +var wrappingClasses = []; +util.isFirebaseRef = function(x) { + // necessary because instanceof won't work on Firebase Query objects + // so we can't simply do instanceof here + var isObject = util.isObject(x); + var proto = isObject? Object.getPrototypeOf(x) : false; + if( proto && proto.constructor === util.Firebase.prototype.constructor ) { + return true; + } + + //todo-hack: SDK 2.2.x no longer works with the above. This is a hack to make that work until fixed + if( isObject && typeof(x.ref) === 'function' && typeof(x.ref().transaction) === 'function' ) { + return true; + } + + return util.find(wrappingClasses, function(C) { + return isObject? x instanceof C : x === C; + }); +}; + +// Add a Class as a valid substitute for a Firebase reference, so that it will +// pass util.isFirebaseRef(). This means that it must provide all the Firebase +// API methods and behave exactly like a Firebase instance in all cases. +util.registerFirebaseWrapper = function(WrappingClass) { + wrappingClasses.push(WrappingClass); +}; + +// for test units +util._mockFirebaseRef = function(mock) { + util.Firebase = mock; +}; + +util.escapeEmail = function(email) { + return (email||'').replace('.', ','); +}; + + +util.assertValidEvent = function(event) { + if( !util.contains(READ_EVENTS, event) ) { + throw new Error('Event must be one of ' + READ_EVENTS + ', but received ' + event); + } +}; + +/** + * Inherit prototype of another JS class. Adds an _super() method for the constructor to call. + * It takes any number of arguments (whatever the inherited classes constructor methods are), + * + * Limitations: + * 1. Inherited constructor must be callable with no arguments (to make instanceof work), but can be called + * properly during instantiation with arguments by using _super(this, args...) + * 2. Can only inherit one super class, no exceptions + * 3. Cannot call prototype = {} after using this method + * + * @param {Function} Child + * @param {Function} Parent a class which can be constructed without passing any arguments + * @returns {Function} + */ +util.inherits = function(Child, Parent) { + var methodSets = [Child.prototype].concat(util.toArray(arguments).slice(2)); + Child.prototype = new Parent(); + Child.prototype.constructor = Parent; + util.each(methodSets, function(fnSet) { + util.each(fnSet, function(fn, key) { + Child.prototype[key] = fn; + }); + }); + Child.prototype._super = function() { + Parent.apply(this, arguments); + }; + return Child; +}; + +util.deepCopy = function(data) { + if( !util.isObject(data) ) { return data; } + var out = util.isArray(data)? [] : {}; + util.each(data, function(v,k) { + out[k] = util.deepCopy(v); + }); + return out; +}; + +util.pick = function(obj, keys) { + if( !util.isObject(obj) ) { return {}; } + var out = util.isArray(obj)? [] : {}; + util.each(keys, function(k) { + out[k] = obj[k]; + }); + return out; +}; + +util.eachByPath = function(map, data, callback, context) { + var dataByPath = {}; + util.each(data, function(v,k) { + var p = map.pathFor(k); + var f = map.getField(k); + var key = f? f.id : k; + if( !util.has(dataByPath, p.name()) ) { + dataByPath[p.name()] = { path: p, data: {} }; + } + dataByPath[p.name()].data[key] = v; + }); + + util.each(dataByPath, function(collated) { + callback.call(context, collated.path, collated.data); + }); +}; + +function format(v, type) { + switch(type) { + case '%d': + return parseInt(v, 10); + case '%j': + v = util.isObject(v)? JSON.stringify(v) : v+''; + if(v.length > 500) { + v = v.substr(0, 500)+'.../*truncated*/...}'; + } + return v; + case '%s': + return v + ''; + default: + return v; + } +} + +function isArguments(o) { + return util.isObject(o) && o+'' === '[object Arguments]'; +} + +/**************************************** + * POLYFILLS + ****************************************/ + +// a polyfill for Function.prototype.bind (invoke using call or apply!) +// credits: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind +function bind(oThis) { + /*jshint validthis:true */ + if (typeof this !== 'function') { + // closest thing possible to the ECMAScript 5 internal IsCallable function + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + FNOP = function () {}, + fBound = function () { + return fToBind.apply( + this instanceof FNOP && oThis? this : oThis, + aArgs.concat(Array.prototype.slice.call(arguments)) + ); + }; + + FNOP.prototype = this.prototype; + fBound.prototype = new FNOP(); + + return fBound; +} + +// a polyfill for Array.prototype.forEach (invoke using call or apply!) +// credits: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach +function forEach(fn, scope) { + /*jshint validthis:true */ + var i, len; + for (i = 0, len = this.length; i < len; ++i) { + if (i in this) { + fn.call(scope, this[i], i, this); + } + } +} + +// a polyfill for Array.isArray +// credits: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray +function isArray(vArg) { + return Object.prototype.toString.call(vArg) === '[object Array]'; +} + +// a polyfill for Array.indexOf +// credits: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf +function indexOf(searchElement, fromIndex) { + /*jshint validthis:true */ + if (this === null) { + throw new TypeError(); + } + var n, k, t = Object(this), + len = t.length >>> 0; + + if (len === 0) { + return -1; + } + n = 0; + if (arguments.length > 1) { + n = Number(arguments[1]); + if (n !== n) { // shortcut for verifying if it's NaN + n = 0; + } else if (n !== 0 && n !== Infinity && n !== -Infinity) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + if (n >= len) { + return -1; + } + for (k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); k < len; k++) { + if (k in t && t[k] === searchElement) { + return k; + } + } + return -1; +} + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"firebase":"firebase"}],"firebase-util":[function(require,module,exports){ +/** + * This file establishes the Firebase.util namespace and + * defines the exports for all packages when using node.js + */ +'use strict'; + +var util = require('./common/index.js'); + +// put all our public methods into the exported scope +util.extend(exports, + require('./common/exports.js'), + require('./NormalizedCollection/exports.js'), + require('./Paginate/exports.js') +); + +/*global window */ +if( typeof window !== 'undefined' ) { + if( !window.hasOwnProperty('Firebase') ) { + console.warn('Firebase not found on the global window instance. Cannot add Firebase.util namespace.'); + } + else { + window.Firebase.util = util; + } +} +},{"./NormalizedCollection/exports.js":2,"./Paginate/exports.js":17,"./common/exports.js":23,"./common/index.js":24}]},{},[1]); diff --git a/dist/firebase-util.min.js b/dist/firebase-util.min.js new file mode 100644 index 0000000..6fedb96 --- /dev/null +++ b/dist/firebase-util.min.js @@ -0,0 +1,12 @@ +/*! + * Firebase-util: A set of experimental power tools for Firebase. + * + * Version: 0.2.4 + * URL: https://github.com/firebase/firebase-util + * Date: 2015-04-15T01:10:44.521Z + * License: MIT http://firebase.mit-license.org/ + */ +require=function t(e,n,r){function i(o,a){if(!n[o]){if(!e[o]){var h="function"==typeof require&&require;if(!a&&h)return h(o,!0);if(s)return s(o,!0);var u=new Error("Cannot find module '"+o+"'");throw u.code="MODULE_NOT_FOUND",u}var c=n[o]={exports:{}};e[o][0].call(c.exports,function(t){var n=e[o][1][t];return i(n?n:t)},c,c.exports,t,e,n,r)}return n[o].exports}for(var s="function"==typeof require&&require,o=0;o=0}function o(t,e){if(t instanceof s)return u.pick(t,["path","id","key","alias","pathName","url"]);"string"==typeof t&&(t={key:t});var n=t.key.split("."),r=e.getPath(n[0]);return{pathName:n[0],id:n[1],key:t.key,alias:t.alias||n[1],path:r,url:r?r.url()+"/"+n[1]:null}}function a(t,e,n){if(null!==n){if(e.indexOf(".")>0){for(var r,i=e.split(".").reverse();i.length>1&&(r=i.pop());)t=t[r]=u.has(t,r)?t[r]:{};e=i.pop()}t[e]=n}}function h(t,e){var n=e;if(!u.isObject(t))return u.undef;var r=t[e];if(e.indexOf(".")>0){var i=e.split(".").reverse();for(r=t;i.length;)n=i.pop(),r=u.isObject(r)&&r.hasOwnProperty(n)?r[n]:u.undef}return r}var u=t("../../common"),c=t("./PathManager");r.prototype={add:function(t){var e=new s(o(t,this.pathMgr));if(this.fields.hasOwnProperty(e.alias))throw new Error("Duplicate field alias "+e.alias+"("+e.key+")");if(null===e.path)throw new Error("Invalid path specified for field "+e.key+"; it was not in the paths provided, which are : "+this.pathMgr.getPathNames().join(","));var n=e.path.getDependency();if(null!==n&&null===i(this.fields,n))throw new Error("Dynamic paths must reference a field declared in the map. Please add "+r.key(n.path,n.field)+" to the select() criteria before using it in a dynamic field");this.fields[e.alias]=e,this.length++},forEach:function(t,e){return u.find(this.fields,t,e)!==u.undef},getField:function(t){return this.fields[t]||null},getPath:function(t){return this.getPathManager().getPath(t)},getPathManager:function(){return this.pathMgr},pathFor:function(t){var e=this.getField(t);return e?e.path:this.pathMgr.first()},fieldsFor:function(t){return u.filter(u.toArray(this.fields),function(e){return e.pathName===t})},aliasFor:function(t){var e=u.find(this.fields,function(e){return e.url===t},this);return e?e.alias:null},extractData:function(t,e){var n={},r=this.pathMgr.getPathName(t.ref().toString());if(null===r&&null!==t.ref().parent()){var i=this.pathMgr.getPathFor(t.ref().parent().toString());i&&i.hasDependency()&&(r=i.name())}var s=e?"exportVal":"val";return u.each(this.fieldsFor(r),function(e){switch(e.id){case"$key":a(n,e.alias,t.key());break;case"$value":a(n,e.alias,t[s]());break;default:t.hasChild(e.id)&&a(n,e.alias,t.child(e.id)[s]())}}),n},snapFor:function(t,e){var n,r=this.pathFor(e);if(!r)return null;var s=r.getDependency();if(null!==s){var o=i(this.fields,s),a=this.snapFor(t,o.alias);a&&(n="$key"===s.field?r.child(a.key()).url():"$value"===s.field?r.child(a.val()).url():r.child(a.child(s.field).val()).url())}else n=r.url();return n?u.find(t,function(t){return t.ref().toString()===n})||null:null},denest:function(t){var e={};return u.each(this.getPathManager().getPaths(),function(t){e[t.name()]={path:t,data:{}}}),this.forEach(function(n){var r=h(t,n.alias);if(r!==u.undef)switch(n.id){case"$value":e[n.pathName].data=r;break;case"$key":break;default:e[n.pathName].data[n.id]=r}}),e},idFor:function(t){var e=this.getField(t);return e?e.id:t}},r.key=function(t,e){return"string"!=typeof t&&(t=t.name()),t+"."+e},r.fieldMap=function(t,e){var n,i=t.getField(e);i?(n=i.path,"$value"!==i.id&&(n=n.child(i.id))):n=t.pathFor(e).child(e);var s=new c([n]),o=new r(s);return o.add({key:r.key(n,"$value"),alias:e}),o},r.recordMap=function(t,e){var n=t.getPathManager(),i=u.map(n.getPaths(),function(t){return t.normChild(e)}),s=new r(new c(i));return t.forEach(function(t){s.add({key:t.key,alias:t.alias})}),s},e.exports=r},{"../../common":24,"./PathManager":10}],5:[function(t,e,n){"use strict";function r(){this.criteria=[],s.each(arguments,this.add,this)}function i(t){this.match=t}var s=t("../../common");r.prototype={add:function(t){this.criteria.push(new i(t))},test:function(t,e,n){return s.contains(this.criteria,function(r){return!r.test(t,e,n)})===!1}},i.prototype.test=function(t,e,n){return this.match(t,e,n)===!0},e.exports=r},{"../../common":24}],6:[function(t,e,n){"use strict";function r(t){i(arguments),this.pathMgr=new u(arguments),this.map=new l(this.pathMgr),this.filters=new c,this.finalized=!1}function i(t){function e(t){return h.isArray(t)&&(t=t[0]),!h.isFirebaseRef(t)}if(t.length<1)throw new Error("Must provide at least one path definition");if(h.contains(t,e))throw new Error("Each argument to the NormalizedCollection constructor must be a valid Firebase reference or an Array containing a Firebase ref as the first argument")}function s(t,e){if(t.finalized)throw new Error("Cannot call "+e+"() after ref() has been invoked")}function o(t){var e=[],n=[],r="";return h.each(t.pathMgr.getPaths(),function(t){var n=t.getDependency();e.push(h.printf(' "%s%s"%s',t.url(),t.id()===t.name()?"":" as "+t.name(),n?"-> "+n.path+"."+n.field:""))}),t.map.forEach(function(t){n.push(h.printf('"%s%s"',t.key,t.alias===t.id?"":" as "+t.alias)),n.length%5===0&&n.push("\n")}),t.filters.criteria.length>0&&(r=h.printf("<%s filters applied>",t.filters.criteria.length)),h.printf("NormalizedCollection(\n%s\n).select(%s)%s.ref()",e.join("\n"),n.join(", "),r)}function a(t){var e;if(e="string"==typeof t?t:h.has(t,"key")?t.key:h.undef,"string"!=typeof e||e.indexOf(".")<=0)throw new Error('Each field passed to NormalizedCollection.select() must either be a string in the format "pathAlias.fieldId", or an object in the format {key: "pathAlias.fieldId", alias: "any_name_for_field"}, but I received '+JSON.stringify(t))}var h=t("../../common"),u=t("./PathManager"),c=t("./Filter"),l=t("./FieldMap"),f=t("./NormalizedRef"),d=t("./RecordSet");r.prototype={select:function(t){s(this,"select");var e=h.args("NormalizedCollection.select",arguments,1);return h.each(e.restAsList(0,["string","object"]),function(t){a(t),this.map.add(t)},this),this},filter:function(t){s(this,"filter");var e=h.args("NormalizedCollection.filter",arguments,1,1);return this.filters.add(e.nextReq("function")),this},ref:function(){if(!this.map.length)throw new Error("Must call select() with at least one field before creating a ref");this.finalized=!0,h.log.isInfoEnabled()&&h.log.info("NormalizedRef created using %s",o(this));var t=new d(this.map,this.filters);return new f(t)}},e.exports=r},{"../../common":24,"./FieldMap":4,"./Filter":5,"./NormalizedRef":7,"./PathManager":10,"./RecordSet":14}],7:[function(t,e,n){"use strict";function r(t,e){this._super(this,t),this._parent=e||null,this._key=t.getName(),this._toString=t.getUrl()}function i(t){return function(){var e=a.toArray(arguments);a.each(this.$getPaths(),function(n){var r=n.ref();r[t].apply(r,e)})}}function s(t){return function(){var e=a.toArray(arguments),n=this.$getMaster();return n[t].apply(n,e)}}function o(t){return function(){throw new Error(t+" is not supported for NormalizedCollection references. Try calling it on the original reference used to create the NormalizedCollection instead.")}}var a=t("../../common"),h=t("./Query");a.inherits(r,h,{child:function(t){for(var e=t.split("/").reverse(),n=this,i=this;e.length;){var s=e.pop();i=new r(i.$getRecord().makeChild(s),n),n=i}return i},parent:function(){return this._parent},root:function(){for(var t=this;null!==t.parent();)t=t.parent();return t},name:function(){return console.warn("The name() function has been deprecated. Use key() instead."),this.key()},key:function(){return this._key},toString:function(){return this._toString},set:function(t,e,n){this.$getRecord().saveData(t,{callback:e,context:n,isUpdate:!1})},update:function(t,e,n){this.$getRecord().saveData(t,{callback:e,context:n,isUpdate:!0})},remove:function(t,e){this.$getRecord().saveData(null,{callback:t,context:e,isUpdate:!1})},push:function(t,e,n){var r=this.$getMaster().push().key(),i=this.child(r);return arguments.length&&i.set.apply(i,arguments),i},setWithPriority:function(t,e,n,r){this.$getRecord().saveData(t,{callback:n,context:r,isUpdate:!1,priority:e})},setPriority:function(t,e,n){this.$getMaster().setPriority(t,e,n)},auth:s("auth"),unauth:s("unauth"),authWithCustomToken:s("authWithCustomToken"),authAnonymously:s("authAnonymously"),authWithPassword:s("authWithPassword"),authWithOAuthPopup:s("authWithOAuthPopup"),authWithOAuthRedirect:s("authWithOAuthRedirect"),authWithOAuthToken:s("authWithOAuthToken"),getAuth:s("getAuth"),onAuth:s("onAuth"),offAuth:s("offAuth"),createUser:s("createUser"),changePassword:s("changePassword"),removeUser:s("removeUser"),resetPassword:s("resetPassword"),changeEmail:s("changeEmail"),goOffline:i("goOffline"),goOnline:i("goOnline"),transaction:o("transaction"),onDisconnect:o("onDisconnect")}),e.exports=r},{"../../common":24,"./Query":11}],8:[function(t,e,n){"use strict";function r(t,e){if(this._ref=t,this._rec=t.$getRecord(),!i.isArray(e))throw new Error("Must provide an array of snapshots to merge");this._pri=this._rec.getPriority(e),this._snaps=e}var i=t("../../common");r.prototype={val:function(){return this._snaps.length?this._rec.mergeData(this._snaps,!1):null},child:function(t){var e,n=t.split("/").reverse(),i=n.pop();for(e=new r(this._ref.child(i),this._rec.getChildSnaps(this._snaps,i));n.length;)e=e.child(n.pop());return e},forEach:function(t,e){return this._rec.forEachKey(this._snaps,function(n,r){return"$value"===n||"$key"===n?!1:t.call(e,this.child(r))},this)},hasChild:function(t){for(var e=t.split("/").reverse(),n=e.length>0,r=this._ref,i=this;n&&e.length;){var s=e.pop();n=r.$getRecord().hasChild(i._snaps,s),n&&e.length&&(r=r.child(s),i=i.child(s))}return n},hasChildren:function(){return this._rec.forEachKey(this._snaps,function(t){return"$key"!==t&&"$value"!==t})},name:function(){return console.warn("name() has been deprecated. Use key() instead."),this.key()},key:function(){return this._rec.getName()},numChildren:function(){var t=0;return this._rec.forEachKey(this._snaps,function(e){"$key"!==e&&"$value"!==e&&t++}),t},ref:function(){return this._ref.ref()},getPriority:function(){return this._pri},exportVal:function(){return this._rec.mergeData(this._snaps,!0)},exists:function(){return null!==this.val()}},e.exports=r},{"../../common":24}],9:[function(t,e,n){"use strict";function r(t,e){var n=i(t);this._ref=n.ref,this._alias=n.alias,this._dep=n.dep,this._parent=e||null}function i(t){var e,n,r=null;return o.isArray(t)?(e=t[0],n=t[1],r=t[2]):e=o.isFunction(t.ref)?t.ref():t,{ref:e,alias:n||e.key(),dep:s(r)}}function s(t){if(o.isObject(t))return t;if(t){var e=t.split(".");return{path:e[0],field:e[1]}}return null}var o=t("../../common");r.prototype={ref:function(){return this._ref},reff:function(){return this.ref().ref()},child:function(t){return new r(this.reff().child(t),this)},normChild:function(t){var e=this.getDependency();return null!==e?new r([this.reff(),this.name(),e.path+"."+e.field],this):new r([this.reff().child(t),this.name()],this)},hasDependency:function(){return null!==this._dep},getDependency:function(){return this._dep},url:function(){return this.reff().toString()},name:function(){return this._alias},id:function(){return this.reff().key()},parent:function(){return this._parent},clone:function(){return new r([this._ref,this._alias,this._dep],this._parent)}},e.exports=r},{"../../common":24}],10:[function(t,e,n){"use strict";function r(t){this.paths=[],this.pathsByUrl={},this.deps={},this.pathNames=[],o.each(t,this.add,this)}function i(t,e){return o.map(t,function(t){return e[t].path+"."+e[t].field}).join(" >> ")}var s=t("./Path"),o=t("../../common");r.prototype={add:function(t){var e=t instanceof s?t.clone():new s(t);if(!this.paths.length&&e.hasDependency())throw new Error("The master path (i.e. the first) may not declare a dependency. Perhaps you have put the wrong path first in the list?");if(o.has(this.pathsByUrl,e.url()))throw new Error("Duplicate path: "+e.url());if(o.contains(this.pathNames,e.name()))throw new Error("Duplicate path name. The .key() value for each path must be unique, or you can give each a path an alias by using [firebaseRef, alias] in the constructor. The aliases must also be unique.");this._map(e),this.paths.push(e),this.pathsByUrl[e.url()]=e.name(),this.pathNames.push(e.name())},count:function(){return this.paths.length},first:function(){return this.paths[0]},getPath:function(t){return o.find(this.paths,function(e){return e.name()===t})||null},getPathFor:function(t){var e=this.getPathName(t);return e?this.getPath(e):null},getPaths:function(){return this.paths.slice()},getPathName:function(t){return this.pathsByUrl[t]||null},getPathNames:function(){return this.pathNames.slice()},getUrls:function(){return o.keys(this.pathsByUrl)},getDependencyGraph:function(){return o.extend(!0,this.deps)},_map:function(t){var e=this.first(),n=t.getDependency();!n&&e&&(n={path:e.name(),field:"$key"}),n&&(this.deps[t.name()]=n,this._assertNotCircularDep(t.name()))},_assertNotCircularDep:function(t){for(var e=[t],n=this.deps[t];o.isDefined(n);){var r=n.path;if(o.contains(e,r))throw e.push(r),new Error("Circular dependencies in paths: "+i(e,this.deps));e.push(r),n=o.val(this.deps,r)}}},e.exports=r},{"../../common":24,"./Path":9}],11:[function(t,e,n){"use strict";function r(t,e){var n=this;n._ref=t,n._rec=e,e&&e.setRef(n)}var i=t("../../common"),s=t("./Transmogrifier");r.prototype={on:function(t,e,n,r){3===arguments.length&&i.isObject(n)&&(r=n,n=i.undef),this.$getRecord().watch(t,e,n,r)},once:function(t,e,n,r){function s(n){a.off(t,s),e.call(r,n)}function o(t){"function"==typeof n&&null!==t&&n.call(r,t)}var a=this;3===arguments.length&&i.isObject(n)&&(r=n,n=i.undef),this.on(t,s,o)},off:function(t,e,n){this.$getRecord().unwatch(t,e,n)},orderByChild:function(){return this.$replicate("orderByChild",i.toArray(arguments))},orderByKey:function(){return this.$replicate("orderByKey",i.toArray(arguments))},orderByPriority:function(){return this.$replicate("orderByPriority",i.toArray(arguments))},limitToFirst:function(){return this.$replicate("limitToFirst",i.toArray(arguments))},limitToLast:function(){return this.$replicate("limitToLast",i.toArray(arguments))},limit:function(){return this.$replicate("limit",i.toArray(arguments))},startAt:function(){return this.$replicate("startAt",i.toArray(arguments))},endAt:function(){return this.$replicate("endAt",i.toArray(arguments))},equalTo:function(){return this.$replicate("equalTo",i.toArray(arguments))},ref:function(){return this._ref},$getRecord:function(){return this._rec},$getMaster:function(){return this._rec.getPathManager().first().ref()},$getPaths:function(){return this._rec.getPathManager().getPaths()},$replicate:function(t,e){var n=this.$getRecord(),i=this.$getMaster();return i=i[t].apply(i,e),new r(this._ref,s.replicate(n,i))}},i.registerFirebaseWrapper(r),e.exports=r},{"../../common":24,"./Transmogrifier":16}],12:[function(t,e,n){"use strict";function r(t){var e=t.getPathManager().first().id(),n=l.mergeToString(t.getPathManager().getUrls());this._super(t,e,n),this._eventManagers={},l.log.debug("Record created",this.getName(),this.getUrl())}function i(t){this.rec=t,this.pm=t.getPathManager(),this.running=!1,this._init()}function s(t,e){this.event=t,this.rec=e,this.map=e.getFieldMap(),this.pm=e.getPathManager(),this.subs=[],this.dyno=null}function o(t,e,n,r){var i=t.getDependency(),s=e.getPath(i.path),o=s.ref();if("$key"===i.field)throw new Error("Dynamic paths do not support $key (you should probably just join on this path)");"$value"!==i.field&&(o=o.child(i.field));var a,h=o.on("value",function(e){a&&a.key()!==e.val()&&(l.log.debug("Record.Dyno: stopped monitoring %s",a.toString()),a.off(n,r),r(null)),null!==e.val()&&(a=t.ref().child(e.val()),a.on(n,r),l.log("Record.Dyno: monitoring %s",a.toString()))});this.dispose=function(){o.off("value",h),a&&a.off(n,r)}}function a(t,e,n){l.each(t.fieldsFor(e.name()),function(t){switch(t.id){case"$key":break;case"$value":l.has(n,".value")||(n[".value"]=null);break;default:l.has(n,t.id)||(n[t.id]=null)}})}var h=t("./FieldMap"),u=t("./RecordField"),c=t("./AbstractRecord"),l=t("../../common");l.inherits(r,c,{makeChild:function(t){var e=h.fieldMap(this.getFieldMap(),t);return new u(e)},hasChild:function(t,e){var n=this.getFieldMap().getField(e);if(!n)return!1;var r=this.getFieldMap().snapFor(t,e);return null!==r&&r.hasChild(e)},getChildSnaps:function(t,e){var n,r=this.getFieldMap().snapFor(t,e),i=this.getFieldMap().getField(e);if(i)switch(i.id){case"$key":throw new Error("Cannot get child snapshot from key (not a real child element)");case"$value":n=r;break;default:n=r.child(i.id)}else n=r.child(e);return[n]},forEachKey:function(t,e,n){function r(t,e){switch(e){case"$key":return!0;case"$value":return t&&null!==t.val();default:return t&&t.hasChild(e)}}var i=this.getFieldMap();return i.forEach(function(s){var o=i.snapFor(t,s.alias);return r(o,s.id)?e.call(n,s.id,s.alias)===!0:!1})},mergeData:function(t,e){var n=this.getFieldMap(),r=l.extend.apply(null,l.map(t,function(t){return n.extractData(t,e)}));return e&&t.length>0&&null!==t[0].getPriority()&&(l.isObject(r)||(r={".value":r}),r[".priority"]=t[0].getPriority()),r},getPriority:function(t){return t[0].getPriority()},getClass:function(){return r},saveData:function(t,e){var n=l.queue(),r=this.getFieldMap(),i=this.getPathManager().getPaths();if(e.isUpdate&&!l.isObject(t))throw new Error("First argument to update() command must be an object");if(null===t)l.each(i,function(t){t.hasDependency()||t.reff().remove(n.getHandler())});else if(l.isObject(t)){var s=r.denest(t);l.each(s,function(t){var o=t.path,h=t.data,u=this._writeRef(s,o);null!==u?l.isEmpty(h)&&e.isUpdate||(l.isObject(h)||(h={".value":h}),e.isUpdate||a(r,o,h),l.isDefined(e.priority)&&(h[".priority"]=e.priority),l.has(h,".value")?u.set(h,n.getHandler()):u.update(h,n.getHandler())):l.log.info("No dynamic key found for master",i[0].ref().toString(),"with dynamic path",o.ref().toString())},this)}else{if(1!==i.length)throw new Error("Cannot set multiple paths to a non-object value. Since this is a NormalizedCollection, the data will be split between the paths. But I can't split a primitive value");l.isDefined(e.priority)?i[0].ref().setWithPriority(t,e.priority,n.getHandler()):i[0].ref().set(t,n.getHandler())}n.handler(e.callback||l.noop,e.context)},getName:function(){return this._name},getUrl:function(){return this._url},_start:function(t){l.has(this._eventManagers,t)||(l.log.debug("Record._start: event=%s, url=%s",t,this.getUrl()),this._eventManagers[t]="value"===t?new i(this):new s(t,this)),this._eventManagers[t].start()},_stop:function(t){l.has(this._eventManagers,t)&&(l.log.debug("Record._stop: event=%s, url=%s",t,this.getUrl()),this._eventManagers[t].stop())},_writeRef:function(t,e){var n=e.reff(),r=e.getDependency();if(null!==r){var i=this.getPathManager().getPath(r.path),s=this._depKey(t,i,r.field);n=null===s?null:n.child(s)}return n},_depKey:function(t,e,n){var r,i=t[e.name()].data;switch(n){case"$key":r=e.id();break;case"$value":r=l.has(i,".value")?i[".value"]:l.isEmpty(i)?null:i;break;default:r=l.has(i,n)?i[n]:null}var s=typeof r;if(null!==r&&"string"!==s)throw new Error("Dynamic key values must be a string. Type was "+s+" for "+e.ref().toString()+"->"+n);return r}}),i.prototype={start:function(){this.running||(this.running=!0,l.each(this.pm.getPathNames(),this._startPath,this))},stop:function(){this.running&&(this.running=!1,l.each(this.subs,function(t){t()}),this._init())},update:function(t,e){this.snaps[t]=e,this._checkLoadState(),l.log("Record.ValueEventManager.update: url=%s, loadCompleted=%s",e.ref().toString(),this.loadCompleted),this.loadCompleted&&this.rec.handler("value")(l.toArray(this.snaps))},_startPath:function(t){var e=this,n=e.pm.getPath(t),r=l.bind(e.update,e,t);if(n.hasDependency()){var i=new o(n,this.rec.getFieldMap(),"value",r);this.subs.push(i.dispose)}else n.ref().on("value",r),e.subs.push(function(){n.ref().off("value",r)})},_checkLoadState:function(){if(!this.loadCompleted){var t=this.snaps,e=this.pm.getPathNames();this.loadCompleted=!l.contains(e,function(e){return!t.hasOwnProperty(e)})}},_init:function(){this.loadCompleted=!1,this.snaps={},this.subs=[]}},s.prototype={start:function(){l.each(this.pm.getPathNames(),function(t){var e=this.event,n=this.pm.getPath(t),r=l.bind(this.update,this);n.hasDependency()?(this.dyno=new o(n,this.map,e,r),this.subs.push(this.dyno.dispose)):(n.ref().on(e,r),this.subs.push(function(){n.ref().off(e,r)}))},this)},stop:function(){l.each(this.subs,function(t){t()}),this.subs=[]},update:function(t,e){if(null!==t&&null!==this.map.aliasFor(t.ref().toString())){var n=[t.key(),t];e!==l.undef&&n.push(e),l.log("Record.ChildEventManager.update: event=%s, key=%s/%s",this.event,t.ref().parent().key(),t.key()),this.rec.handler(this.event).apply(this.rec,n)}}},e.exports=r},{"../../common":24,"./AbstractRecord":3,"./FieldMap":4,"./RecordField":13}],13:[function(t,e,n){"use strict";function r(t){if(this.path=t.getPathManager().first(),this._super(t,this.path.name(),this.path.url()),1!==t.getPathManager().count())throw new Error("RecordField must have exactly one path, but we got "+t.getPathManager().count());if(1!==t.length)throw new Error("RecordField must have exactly one field, but we found "+t.length);h.log.debug("RecordField created",this.getName(),this.getUrl())}function i(t){return t.callback?function(){t.callback.apply(t.context,arguments)}:h.noop}var s=t("./PathManager"),o=t("./FieldMap"),a=t("./AbstractRecord"),h=t("../../common");h.inherits(r,a,{makeChild:function(t){var e=new s([this.path.child(t)]),n=new o(e);return n.add({key:o.key(e.first(),"$value"),alias:t}),new r(n)},hasChild:function(t,e){return t[0].hasChild(e)},getChildSnaps:function(t,e){return[t[0].child(e)]},mergeData:function(t,e){return e?t[0].exportVal():t[0].val()},getPriority:function(t){return t[0].getPriority()},forEachKey:function(t,e,n){var r=t[0];return r.forEach(function(t){e.call(n,t.key(),t.key())})},saveData:function(t,e){var n=this.path.ref();if(e.isUpdate){if(!h.isObject(t))throw new Error("When using update(), the data must be an object.");h.has(e,"priority")&&(t[".priority"]=e.priority),n.update(t,i(e))}else h.has(e,"priority")?n.setWithPriority(t,e.priority,i(e)):n.set(t,i(e))},getClass:function(){return r},_start:function(t){this.path.ref().on(t,this.handler(t),this._cancel,this)},_stop:function(t){this.path.ref().off(t,this.handler(t),this)}}),e.exports=r},{"../../common":24,"./AbstractRecord":3,"./FieldMap":4,"./PathManager":10}],14:[function(t,e,n){"use strict";function r(t,e){var n=o.mergeToString(t.getPathManager().getPathNames()),r=o.mergeToString(t.getPathManager().getUrls());this._super(t,n,r),this.filters=e,this.monitor=new h(this)}var i=t("./Record"),s=t("./AbstractRecord"),o=t("../../common"),a=t("./FieldMap"),h=t("./RecordSetEventManager");o.inherits(r,s,{makeChild:function(t){var e=a.recordMap(this.getFieldMap(),t);return new i(e)},hasChild:function(t,e){return o.contains(t,function(t){return t.key()===e})},getChildSnaps:function(t,e){return o.filter(t,function(t){return t.key()===e})},mergeData:function(t,e){var n=this,r={};return o.each(t,function(t){null!==t.val()&&n.filters.test(t.val(),t.key(),t.getPriority())&&(r[t.key()]=e?t.exportVal():t.val())}),r},getPriority:function(){return null},forEachKey:function(t,e,n){t.forEach(function(t){return e.call(n,t.key(),t.key())})},getClass:function(){return r},saveData:function(t,e){var n=o.queue();if(null===t)o.each(this.getPathManager().getPaths(),function(t){t.ref().remove(n.getHandler())});else{if(!o.isObject(t))throw new Error("Calls to set() or update() on a NormalizedCollection must pass either null or an object value. There is no way to split a primitive value between the paths");o.each(t,function(t,r){if(".value"===r||".priority"===r)throw new Error("Cannot use .priority or .value on the root path of a NormalizedCollection. You probably meant to sort the records anyway (i.e. one level lower).");this.child(r).saveData(t,{isUpdate:e.isUpdate,callback:n.getHandler()})},this),e.priority&&this.getPathManager().first().ref().setPriority(e.priority,n.getHandler())}n.handler(e.callback||o.noop,e.context)},_getChildKey:function(t,e,n){var r=n,i=this.getPathManager().getPathFor(t.ref().toString());if(i.hasDependency()){var s=i.getDependency(),a=this.getFieldMap().getPath(s.path);if(!a)throw new Error("Invalid dependency path. "+t.ref.toString()+" depends on "+s.path+", but that alias does not exist in the paths provided.");var h=o.find(e,function(t){return t.ref().toString()===a.url()});h?(h=h.child(n),"$value"!==s.field&&(h=h.child(s.field)),r=h.val()):r=null}return r},_start:function(){this.monitor.start()},_stop:function(t,e){0===e&&this.monitor.stop()}}),e.exports=r},{"../../common":24,"./AbstractRecord":3,"./FieldMap":4,"./Record":12,"./RecordSetEventManager":15}],15:[function(t,e,n){"use strict";function r(t){var e=t.getPathManager();this.masterRef=e.first().ref(),this.url=this.masterRef.toString(),this.recList=new i(t,this.url),this.running=!1}function i(t,e){this.obs=t,this.url=e,this._reset()}var s=t("../../common");r.prototype={start:function(){return this.running||(s.log("RecordSetEventManager: Loading normalized records from master list %s",this.url),this.running=!0,this.masterRef.on("child_added",this._add,this),this.masterRef.on("child_removed",this._remove,this),this.masterRef.on("child_moved",this._move,this),this.masterRef.once("value",this.recList.masterPathLoaded,this.recList)),this},stop:function(){return this.running&&(s.log("RecordSetEventManager: Stopped monitoring master list %s",this.url),this.running=!1,this.masterRef.off("child_added",this._add,this),this.masterRef.off("child_removed",this._remove,this),this.masterRef.off("child_moved",this._move,this),this.recList.unloaded()),this},_add:function(t,e){this.recList.add(t.key(),e)},_remove:function(t){this.recList.remove(t.key())},_move:function(t,e){this.recList.move(t.key(),e)}},i.prototype={add:function(t,e){s.log.debug("RecordList.add: key=%s, prevChild=%s",t,e);var n=this.obs.child(t),r=s.bind(this._valueUpdated,this,t);this.loading[t]={rec:n,prev:e,fn:r},this.loadComplete||this.initialKeysLeft.push(t),n.watch("value",r)},remove:function(t){s.log.debug("RecordList.remove: key=%s",t);var e=this._dropRecord(t);null!==e&&this._notify("child_removed",t,e)},move:function(t,e){if(s.has(this.recs,t)){var n=s.indexOf(this.recIds,t);this.recIds.splice(n,1),this._putAfter(t,e),this._notify("child_moved",t)}},masterPathLoaded:function(){s.log.debug("RecordList: Initial data has been loaded from master list at %s",this.url),this.masterLoaded=!0,this._checkLoadState()},unloaded:function(){this._reset()},findKey:function(t){return s.indexOf(this.recIds,t)},_reset:function(){s.each(this.recs,function(t,e){this.remove(e)},this),this.recs={},this.recIds=[],this.snaps={},this.loading={},this.loadComplete=!1,this.initialKeysLeft=[],this.masterLoaded=!1},_valueUpdated:function(t,e){if(this.snaps[t]=e,s.has(this.loading,t)){var n=this.loading[t];delete this.loading[t],this.obs.filters.test(e.val(),t,e.getPriority())?(this.recs[t]=n.rec,this._putAfter(t,n.prev),this._checkLoadState(t),this._notify("child_added",t)):(s.log("RecordList: Filtered key %s",t),n.rec.unwatch("value",n.fn))}else s.has(this.recs,t)?this._notify("child_changed",t):s.log("RecordList: Orphan key %s ignored. Probably a concurrent edit.",t)},_notify:function(t,e,n){var r=[e];switch(t){case"child_added":case"child_moved":var i=this._getPrevChild(e);r.push(this.snaps[e],i);break;case"child_changed":r.push(this.snaps[e]);break;case"child_removed":r.push(n);break;default:throw new Error("Invalid event type "+t+" for key "+e)}s.log("RecordList._notify: event=%s, key=%s, prev=%s",t,e,i),this.obs.handler(t).apply(this.obs,r),this._notifyValue()},_notifyValue:function(){s.log.debug("RecordList._notifyValue: snap_keys=%s, loadComplete=%s",s.keys(this.snaps),this.loadComplete),this.loadComplete&&this.obs.handler("value")(s.toArray(this.snaps))},_getPrevChild:function(t){if(!this.recIds.length)return null;var e=this.findKey(t);return-1===e?this.recIds[this.recIds.length-1]:0===e?null:this.recIds[e-1]},_posFor:function(t){var e,n;return null===t?e=0:(n=this.findKey(t),e=-1===n?this.recIds.length:n+1),e},_putAfter:function(t,e){var n=this._posFor(e);this.recIds.splice(n,0,t)},_dropRecord:function(t){if(s.has(this.recs,t)){var e=this.snaps[t];return this.recs[t].unwatch("value",this._valueUpdated,this),delete this.recs[t],delete this.snaps[t],delete this.loading[t],s.remove(this.recIds,t),e}return null},_checkLoadState:function(t){this.loadComplete||(t&&s.remove(this.initialKeysLeft,t),!this.initialKeysLeft.length&&this.masterLoaded&&(this.loadComplete=!0,t||this._notifyValue()))}},e.exports=r},{"../../common":24}],16:[function(t,e,n){"use strict";var r=t("../../common"),i=t("./PathManager"),s=t("./Path"),o=t("./FieldMap"),a=t("./RecordSet");e.exports={replicate:function(t,e){var n=t.getPathManager().getPaths().slice(0),h=n[0];n[0]=new s([e,h.name(),h.getDependency()]);var u=new i(r.map(n,function(t){return t.clone()})),c=new o(u);t.getFieldMap().forEach(c.add,c); + +var l,f=t.getClass();return l=f===a?new f(c,t.filters):new f(c)}}},{"../../common":24,"./FieldMap":4,"./Path":9,"./PathManager":10,"./RecordSet":14}],17:[function(t,e,n){"use strict";function r(t,e,n){var r=s.extend({},u,t);return r.maxCacheSize||(r.maxCacheSize=3*r[e]),i(r,e,n),i(r,"maxCacheSize",n),r}function i(t,e,n){if("number"!=typeof t[e])throw new Error("Argument "+e+" passed into opts for "+n+"must be a number")}var s=t("../common"),o=t("./libs/Scroll.js"),a=t("./libs/Paginate.js"),h=t("./libs/ReadOnlyRef.js"),u={field:null,pageSize:10,windowSize:250};n.Scroll=function(t,e,n){if(!s.isFirebaseRef(t))throw new Error("First argument to Firebase.util.Scroll must be a valid Firebase ref");if("string"!=typeof e)throw new Error("Second argument to Firebase.util.Scroll must be a valid string");if(arguments.length>2&&!s.isObject(n))throw new Error("Optional third argument to Firebase.util.Scroll must be an object of key/value pairs");var i=new h(t);return i.scroll=new o(i,e,r(n,"windowSize","Scroll")),i},n.Paginate=function(t,e,n){if(!s.isFirebaseRef(t))throw new Error("First argument to Firebase.util.Paginate must be a valid Firebase ref");if("string"!=typeof e)throw new Error("Second argument to Firebase.util.Paginate must be a valid string");if(arguments.length>2&&!s.isObject(n))throw new Error("Optional third argument to Firebase.util.Paginate must be an object of key/value pairs");var i=new h(t);return i.page=new a(i,e,r(n,"pageSize","Paginate")),i}},{"../common":24,"./libs/Paginate.js":20,"./libs/ReadOnlyRef.js":21,"./libs/Scroll.js":22}],18:[function(t,e,n){"use strict";function r(t,e,n){i.log.debug("Cache: caching %s using field=%s maxRecordsLoaded=%d",t.toString(),e,n),this.offset=new s({field:e,max:n,ref:t.ref()}),this.outRef=t,this.inRef=null,this.queryRef=null,this.countRef=null,this.keys={},this.start=0,this.count=-1,this.endCount=-1,this.nextListeners=[],this.offset.observe(this._keyChange,this)}var i=t("../../common"),s=t("./Offset");r.prototype.moveTo=function(t,e){i.log.debug("Cache.moveTo: startOffset=%d, numberOfRecords=%d",t,e);var n=this.start,r=this.count;this.start=t,this.count=e,this.endCount=this.start+this.count,n!==this.start?this.offset.goTo(t,e):r!==this.count&&this._refChange()},r.prototype.hasNext=function(){return-1===this.count||this.endCount>this.start+this.count},r.prototype.hasPrev=function(){return this.start>0},r.prototype.observeHasNext=function(t,e){var n=this.nextListeners,r=[t,e];return n.push(r),function(){i.remove(n,r)}},r.prototype.destroy=function(){this._unsubscribe(),this.offset.destroy(),this.offset=null,this.start=0,this.count=-1,this.inRef=null,this.outRef=null,this.queryRef=null,this.countRef=null,this.keys=null,this.nextListeners=null},r.prototype._keyChange=function(t,e,n){this.inRef=n,i.log.debug("Cache._keyChange: %s %s %s",t,e,n.toString()),this._refChange()},r.prototype._unsubscribe=function(){this.queryRef&&(this.queryRef.off("child_added",this._add,this),this.queryRef.off("child_removed",this._remove,this),this.queryRef.off("child_moved",this._move,this),this.queryRef.off("child_changed",this._change,this),this.queryRef.off("value",this._value,this),this.queryRef.off("value",this._removeOrphans,this),this.queryRef=null),this.countRef&&(this.countRef.off("value",this._count,this),this.countRef=null)},r.prototype._refChange=function(){this._unsubscribe(),this.inRef&&this.count>-1&&(this.countRef=this.inRef.limitToFirst(this.count+1),this.countRef.on("value",this._count,this),this.queryRef=this.inRef.limitToFirst(this.count),this.queryRef.on("child_added",this._add,this),this.queryRef.on("child_removed",this._remove,this),this.queryRef.on("child_moved",this._move,this),this.queryRef.on("child_changed",this._change,this),this.queryRef.on("value",this._value,this),this.queryRef.once("value",this._removeOrphans,this))},r.prototype._add=function(t,e){var n=t.key();i.has(this.keys,n)?i.isEqual(this.keys[n],t.val())||this._change(t):(this.keys[n]=t,this.outRef.$trigger("child_added",t,e))},r.prototype._remove=function(t){var e=t.key();i.has(this.keys,e)&&(this.outRef.$trigger("child_removed",t),delete this.keys[e])},r.prototype._move=function(t,e){var n=t.key();i.has(this.keys,n)&&(this.keys[n]=t,this.outRef.$trigger("child_moved",t,e))},r.prototype._change=function(t){this.keys[t.key()]=t,this.outRef.$trigger("child_changed",t)},r.prototype._value=function(t){this.outRef.$trigger("value",t)},r.prototype._count=function(t){this.endCount=this.start+t.numChildren();var e=this.hasNext();i.each(this.nextListeners,function(t){t[0].call(t[1],e)})},r.prototype._removeOrphans=function(t){i.each(this.keys,function(e,n){t.hasChild(n)||(this.outRef.$trigger("child_removed",e),delete this.keys[n])},this)},e.exports=r},{"../../common":24,"./Offset":19}],19:[function(t,e,n){"use strict";function r(t){this.keys=[],this.field=t.field,this.ref=o(t.ref,t.field),this.max=t.max,this.listeners=[],this.curr=0,this.sub=null,this.isSubscribing=!1,this.lastNotifyValue=u.undef,this._debouncedRecache=a(function(){u.log.debug("Offset._debouncedRecache: recaching keys for offset %d",this.curr),this.keys=[],this._grow(this._listen)},this,100,1e3)}function i(t,e){var n;switch(e){case"$key":n=t.key();break;case"$priority":n=t.getPriority();break;default:var r=t.val();if(!u.isObject(r))throw new Error("A value of type "+typeof r+'Was found. But we are attempting to order by child field "'+e+"\". Pagination requires all records to be objects or it can't determine an appropriate offset value.");n=r[e]}return{val:n,key:t.key()}}function s(t,e){return e===!1?null:null===e?t:t.startAt(e.val,e.key)}function o(t,e){return"$key"===e?t.orderByKey():"$priority"===e?t.orderByPriority():t.orderByChild(e)}function a(t,e,n,r){function i(){if(h&&(h(),h=null),a&&Date.now()-a>r)c||(c=!0,setTimeout(s,0));else{a||(a=Date.now());var t=setTimeout(s,n);h=function(){clearTimeout(t)}}}function s(){h=null,a=null,c=!1,t.apply(e,u)}function o(){u=Array.prototype.slice.call(arguments,0),i()}var a,h,u,c;if("number"==typeof e&&(r=n,n=e,e=null),"number"!=typeof n)throw new Error("Must provide a valid integer for wait. Try 0 for a default");if("function"!=typeof t)throw new Error("Must provide a valid function to debounce");return r||(r=10*n||100),o.running=function(){return a>0},o}function h(t){var e=t.length;return e?t[e-1]:null}var u=t("../../common");r.prototype.goTo=function(t){t!==this.curr&&(u.log("Offset.goTo: offset changed from %d to %d",this.curr,t),this.curr=t,this.lastNotifyValue=u.undef,this._listen())},r.prototype.observe=function(t,e){this.listeners.push([t,e]);var n=this.getKey(),r=s(this.ref,n);t.call(e,n&&n.val,n&&n.key,r)},r.prototype.getKey=function(t){return arguments.length||(t=this.curr),0===t?null:this.keys.length>t&&this.keys[t]},r.prototype.destroy=function(){this._unsubscribe(),this.curr=0,this.keys=[],this.lastNotifyValue=u.undef,this.isSubscribing=!1},r.prototype._notify=function(){var t=this.getKey();if(!u.isEqual(this.lastNotifyValue,t)){u.log("Offset._notify: key at offset %d is %s",this.curr,t&&t.key),this.lastNotifyValue=t;var e=s(this.ref,t);u.each(this.listeners,function(n){n[0].call(n[1],t&&t.val,t&&t.key,e)})}},r.prototype._recache=function(){this.isSubscribing||this._debouncedRecache()};var c=0;r.prototype._grow=function(t){var e=this,n=e.keys.length;if(e.curr>=n){var r=e.getKey(),s=h(e.keys),o=Math.min(e.curr+(s?2:1)-n,e.max),a=null!==s?e.ref.startAt(s.val,s.key):e.ref;a.limitToFirst(o).once("value",function(n){var a=null!==s;if(n.forEach(function(t){return a?void(a=!1):void e.keys.push(i(t,e.field))}),c++>1e4)throw new Error("Tried to fetch more than 10,000 pages to determine the correct offset. Giving up now. Sorry.");e.curr>=e.keys.length&&n.numChildren()===o?setTimeout(u.bind(e._grow,e,t),0):(c=0,u.log.debug("Offset._grow: Cached %d keys",e.keys.length),t.call(e,!u.isEqual(e.getKey(),r)))})}else t.call(e,!1)},r.prototype._startOffset=function(){return Math.max(0,this.curr-this.max,this.curr-10)},r.prototype._queryRef=function(){var t=this._startOffset(),e=this.ref;if(t>0){var n=this.getKey(t);e=e.startAt(n.val,n.key)}return e.limitToLast(Math.max(this.curr-t,1))},r.prototype._moved=function(t){t.key()===this.getKey()&&this._recache()},r.prototype._unsubscribe=function(){this.sub&&(this.sub.off("child_added",this._recache,this),this.sub.off("child_moved",this._moved,this),this.sub.off("child_removed",this._recache,this),this.sub.off("value",this._doneSubscribing,this),this.sub=null)},r.prototype._subscribe=function(){this._unsubscribe(),this.sub=this._queryRef(),this.isSubscribing=!0,this.sub.on("child_added",this._recache,this),this.sub.on("child_moved",this._moved,this),this.sub.on("child_removed",this._recache,this),this.sub.once("value",this._doneSubscribing,this)},r.prototype._doneSubscribing=function(){this.isSubscribing=!1,this._notify()},r.prototype._monitorEmptyOffset=function(){function t(i){var s=i.numChildren();s>(null===r?0:1)&&(u.log.debug("Offset._monitorEmptyOffset: A value exists now."),n.off("value",t),e._grow())}var e=this,n=e.ref,r=null;this.keys.length&&(r=h(this.keys),n=n.startAt(r.val,r.key)),u.log.debug("Offset._monitorEmptyOffset: No value exists at offset %d, currently %d keys at this path. Watching for a new value.",this.curr,this.keys.length),n.limitToFirst(2).on("value",t)},r.prototype._listen=function(){this._unsubscribe(),this.curr>this.keys.length?this._grow(function(){this.keys.length>this.curr?this._subscribe():(this._monitorEmptyOffset(),this._notify())}):this._subscribe()},e.exports=r},{"../../common":24}],20:[function(t,e,n){"use strict";function r(t,e,n){this.currPage=0,this.field=e,this.ref=t,this.pageSize=n.pageSize,this.max=n.maxCacheSize,this.subs=[],this.pageChangeListeners=[],this.pageCountListeners=[],this.cache=new a(t,e,n.maxCacheSize),this.pageCount=-1,this.couldHaveMore=!1,this.cache.observeHasNext(this._countPages,this)}function i(t,e){return 0===t?0:Math.ceil(t/e)}function s(t,e){var n={};if(n.bindFunction=function(t,e){return function(){return t.apply(e,[e])}},n.stateChange=function(t){4==n.request.readyState&&n.callbackFunction(n.request.responseText)},n.getRequest=function(){return window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):window.XMLHttpRequest?new XMLHttpRequest:!1},n.postBody=arguments[2]||"",n.callbackFunction=e,n.url=t,n.request=n.getRequest(),n.request){var r=n.request;r.onreadystatechange=n.bindFunction(n.stateChange,n),""!==n.postBody?(r.open("POST",t,!0),r.setRequestHeader("X-Requested-With","XMLHttpRequest"),r.setRequestHeader("Content-type","application/x-www-form-urlencoded"),r.setRequestHeader("Connection","close")):r.open("GET",t,!0),r.send(n.postBody)}return n}var o=t("../../common"),a=t("./Cache");r.prototype.next=function(){return this.hasNext()&&(this.currPage++,o.log.debug("Paginate.next: current page is %d",this.currPage),this._pageChange()),this},r.prototype.prev=function(){return this.hasPrev()&&(this.currPage--,o.log.debug("Paginate.prev: current page is %d",this.currPage),this._pageChange()),this},r.prototype.setPage=function(t){t>0&&t<=this.pageCount?(this.currPage=t,o.log.debug("Paginate.setPage: current page is %d",this.currPage),this._pageChange()):o.log.warn("Paginate.setPage: invalid page number %d",t)},r.prototype.hasNext=function(){return this.cache.hasNext()},r.prototype.hasPrev=function(){return this.currPage>1},r.prototype.onPageChange=function(t,e){var n=this.pageChangeListeners,r=[t,e];return n.push(r),t.call(e,this.currPage),function(){o.remove(n,r)}},r.prototype.onPageCount=function(t,e){var n=this.pageCountListeners,r=[t,e];return n.push(r),this.pageCount>-1?t.call(e,this.pageCount,this.couldHaveMore):this._countPages(),function(){o.remove(n,r)}},r.prototype.getCountByDowloadingAllKeys=function(t,e){var n=this;n.downloadingEverything=!0;var r=n.ref.ref().toString();r.match(/\/$/)||(r+="/"),r+=".json?shallow=true",s(r,function(r){var s=0;try{s=o.keys(JSON.parse(r)).length}catch(a){o.log.warn(a)}o.log.debug("Paginate.getCountByDownloadingAllKeys: found %d keys",s),n.downloadingEverything=!1,n.pageCount=i(s,n.pageSize),n.couldHaveMore=!1,n._notifyPageCount(),t&&t.call(e,s)})},r.prototype.destroy=function(){this.cache.destroy(),this.cache=null,this.ref=null,o.each(this.subs,function(t){t()}),this.subs=[],this.pageCountListeners.length=0,this.pageChangeListeners.length=0},r.prototype._countPages=function(){var t=this,e=t.currPage;if(!this.downloadingEverything)if(-1===t.pageCount){var n=t.max,r=t.pageSize,i=this.ref.ref().limitToFirst(n);i.once("value",function(e){-1===t.pageCount&&(t.couldHaveMore=e.numChildren()===n,t.pageCount=Math.ceil(e.numChildren()/r),t._notifyPageCount(),t._countPages())})}else e>=t.pageCount&&(t.pageCount=e,t.couldHaveMore=t.cache.hasNext(),t._notifyPageCount())},r.prototype._pageChange=function(){var t=this.currPage,e=(t-1)*this.pageSize;this.cache.moveTo(e,this.pageSize),this._countPages(),o.each(this.pageChangeListeners,function(e){e[0].call(e[1],t)})},r.prototype._notifyPageCount=function(){var t=this.pageCount,e=this.couldHaveMore;o.each(this.pageCountListeners,function(n){n[0].call(n[1],t,e)})},e.exports=r},{"../../common":24,"./Cache":18}],21:[function(t,e,n){"use strict";function r(t){this._ref=t,this._obs=new a.Observable(["value","child_added","child_removed","child_moved","child_changed"])}function i(t){return function(){var e=a.toArray(arguments),n=this.ref();return n[t].apply(n,e)}}function s(t){return function(){throw new Error(t+" is not supported. This is a read-only reference. You can modify child records after calling .child(), or work with the original by using .ref().")}}function o(t){return function(){throw new Error(t+" is not supported for Paginate and Scroll references. Try calling it on the original reference used to create the instance instead.")}}var a=t("../../common");r.prototype={on:function(t,e,n,r){this._obs.observe(t,e,n,r)},once:function(t,e,n,r){function i(n){this.off(t,i,this),e.call(r,n)}this.on(t,i,n,this)},off:function(t,e,n){this._obs.stopObserving(t,e,n)},ref:function(){return this._ref},child:i("child"),parent:i("parent"),root:i("root"),name:i("name"),key:i("key"),toString:i("toString"),auth:i("auth"),unauth:i("unauth"),authWithCustomToken:i("authWithCustomToken"),authAnonymously:i("authAnonymously"),authWithPassword:i("authWithPassword"),authWithOAuthPopup:i("authWithOAuthPopup"),authWithOAuthRedirect:i("authWithOAuthRedirect"),authWithOAuthToken:i("authWithOAuthToken"),getAuth:i("getAuth"),onAuth:i("onAuth"),offAuth:i("offAuth"),createUser:i("createUser"),changePassword:i("changePassword"),removeUser:i("removeUser"),resetPassword:i("resetPassword"),changeEmail:i("changeEmail"),goOffline:i("goOffline"),goOnline:i("goOnline"),set:s("set"),update:s("update"),remove:s("remove"),push:s("push"),setWithPriority:s("setWithPriority"),setPriority:s("setPriority"),transaction:s("transaction"),limit:o("limit"),onDisconnect:o("onDisconnect"),orderByChild:o("orderByChild"),orderByKey:o("orderByKey"),orderByPriority:o("orderByPriority"),limitToFirst:o("limitToFirst"),limitToLast:o("limitToLast"),startAt:o("startAt"),endAt:o("endAt"),equalTo:o("equalTo"),$trigger:function(){this._obs.triggerEvent.apply(this._obs,a.toArray(arguments))}},e.exports=r},{"../../common":24}],22:[function(t,e,n){"use strict";function r(t,e,n){this.max=n.windowSize,this.start=0,this.end=0,this.cache=new i(t,e,n.maxCacheSize)}var i=t("./Cache");r.prototype.next=function(t){this.hasNext()&&(this.end=this.end+t,this.start=Math.max(0,this.end-this.max,this.start),this.cache.moveTo(this.start,this.end-this.start))},r.prototype.prev=function(t){this.hasPrev()&&(this.start=Math.max(0,this.start-t),this.end=Math.min(this.start+this.max,this.end),this.cache.moveTo(this.start,this.end-this.start))},r.prototype.hasNext=function(){return this.cache.hasNext()},r.prototype.hasPrev=function(){return this.start>0},r.prototype.observeHasNext=function(t,e){return this.cache.observeHasNext(t,e)},r.prototype.destroy=function(){this.cache.destroy(),this.ref=null,this.cache=null},e.exports=r},{"./Cache":18}],23:[function(t,e,n){var r=t("./index.js");n.log=r.log,n.logLevel=r.logLevel,n.escapeEmail=r.escapeEmail},{"./index.js":24}],24:[function(t,e,n){var r=t("./libs/util.js"),i=t("./libs/logger.js");r.extend(n,r,{args:t("./libs/args.js"),log:i,logLevel:i.logLevel,Observable:t("./libs/Observable.js"),Observer:t("./libs/Observer.js"),queue:t("./libs/queue.js")})},{"./libs/Observable.js":25,"./libs/Observer.js":26,"./libs/args.js":27,"./libs/logger.js":28,"./libs/queue.js":29,"./libs/util.js":30}],25:[function(t,e,n){"use strict";function r(t,e){e||(e={}),this._observableProps=a(t,e),this.resetObservers()}function i(t,e){h.each(e,function(e){var n=h.indexOf(t,e);n>=0&&t.splice(n,1)})}function s(t,e){var n=[];return h.each(e,function(e){h.has(t.observers,e)?t.observers[e].length&&(n=n.concat(t.observers[e])):c.warn("Observable.hasObservers: invalid event type %s",e)}),n}function o(t,e,n){h.has(e.oneTimeResults,t)&&n.notify.apply(n,e.oneTimeResults[t])}function a(t,e){return h.extend({onAdd:h.noop,onRemove:h.noop,onEvent:h.noop,oneTimeEvents:[]},e,{eventsMonitored:t,observers:{},oneTimeResults:{}})}var h=t("./util.js"),u=t("./args.js"),c=t("./logger.js"),l=t("./Observer.js");r.prototype={observe:function(t,e,n,r){var i,s=u("observe",arguments,2,4);return t=s.nextFromReq(this._observableProps.eventsMonitored),t&&(e=s.nextReq("function"),n=s.next("function"),r=s.next("object"),i=new l(this,t,e,r,n),this._observableProps.observers[t].push(i),this._observableProps.onAdd(t,i),this.isOneTimeEvent(t)&&o(t,this._observableProps,i)),i},hasObservers:function(t){return this.getObservers(t).length>0},stopObserving:function(t,e,n){var r=u("stopObserving",arguments);t=r.next(["array","string"],this._observableProps.eventsMonitored),e=r.next(["function"]),n=r.next(["object"]),h.each(t,function(t){var r=[],s=this.getObservers(t);h.each(s,function(i){i.matches(t,e,n)&&(i.notifyCancelled(null),r.push(i))},this),i(this._observableProps.observers[t],r),r.length&&this._observableProps.onRemove(t,r)},this)},abortObservers:function(t){var e=[];if(this.hasObservers()){var n=this.getObservers().slice();h.each(n,function(n){n.notifyCancelled(t),e.push(n)},this),this.resetObservers(),e.length&&this._observableProps.onRemove(this.event,e)}},getObservers:function(t){return t=u("getObservers",arguments).listFrom(this._observableProps.eventsMonitored,!0),s(this._observableProps,t)},triggerEvent:function(t){var e=u("triggerEvent",arguments),n=e.listFromWarn(this._observableProps.eventsMonitored,!0),r=e.restAsList();n&&h.each(n,function(e){if(this.isOneTimeEvent(t)){if(h.isArray(this._observableProps.oneTimeResults,t))return void c.warn("One time event was triggered twice, should by definition be triggered once",t);this._observableProps.oneTimeResults[t]=r}var n=this.getObservers(e),i=0;h.each(n,function(t){t.notify.apply(t,r.slice(0)),i++}),this._observableProps.onEvent.apply(null,[e,i].concat(r.slice(0)))},this)},resetObservers:function(){h.each(this._observableProps.eventsMonitored,function(t){this._observableProps.observers[t]=[]},this)},isOneTimeEvent:function(t){return h.contains(this._observableProps.oneTimeEvents,t)},observeOnce:function(t,e,n,r){var i,s=u("observeOnce",arguments,2,4);return t=s.nextFromWarn(this._observableProps.eventsMonitored),t&&(e=s.nextReq("function"),n=s.next("function"),r=s.next("object"),i=new l(this,t,e,r,n,!0),this._observableProps.observers[t].push(i),this._observableProps.onAdd(t,i),this.isOneTimeEvent(t)&&o(t,this._observableProps,i)),i}},e.exports=r},{"./Observer.js":26,"./args.js":27,"./logger.js":28,"./util.js":30}],26:[function(t,e,n){"use strict";function r(t,e,n,r,i,s){if("function"!=typeof n)throw new Error("Must provide a valid notifyFn");this.observable=t,this.fn=n,this.event=e,this.cancelFn=i||function(){},this.context=r,this.oneTimeEvent=!!s}var i=t("./util.js");r.prototype={notify:function(){var t=i.toArray(arguments);this.fn.apply(this.context,t),this.oneTimeEvent&&this.observable.stopObserving(this.event,this.fn,this.context)},matches:function(t,e,n){return i.isArray(t)?i.contains(t,function(t){return this.matches(t,e,n)},this):!(t&&t!==this.event||e&&e!==this&&e!==this.fn||n&&n!==this.context)},notifyCancelled:function(t){this.cancelFn.call(this.context,t||null)}},e.exports=r},{"./util.js":30}],27:[function(t,e,n){"use strict";function r(t,e,n,i){if("string"!=typeof t||!h.isObject(e))throw new Error("Args requires at least 2 args: fnName, arguments[, minArgs, maxArgs]");if(!(this instanceof r))return new r(t,e,n,i);this.fnName=t,this.argList=h.toArray(e),this.origArgs=h.toArray(e);var s=this.length=this.argList.length;if(this.pos=-1,h.isUndefined(n)&&(n=0),h.isUndefined(i)&&(i=this.argList.length),n>s||s>i){var o=i>n?h.printf("%d to %d",n,i):n;throw Error(h.printf("%s must be called with %s arguments, but received %d",t,o,s))}}function i(t,e){return e===!0?!0:(h.isArray(e)||(e=[e]),h.contains(e,function(e){switch(e){case"array":return h.isArray(t);case"string":return"string"==typeof t;case"number":return isFinite(parseInt(t,10));case"int":case"integer":return isFinite(parseFloat(t));case"object":return h.isObject(t);case"function":return"function"==typeof t;case"bool":case"boolean":return"boolean"==typeof t;case"boolean-like":return!h.isObject(t);default:throw new Error("Args received an invalid data type: "+e)}}))}function s(t,e,n,r){if(r=h.printf("%s: invalid argument at pos %d, %s (received %s)",e,n,r),t===!0)throw new Error(r);if(!h.has(u,t))throw new Error("The `required` value passed to Args methods must either be true or a method name from logger");u[t](r)}function o(t,e,n){u.warn("%s: invalid choice %s, must be one of [%s]",t,e,n)}function a(t,e){if(e===!0)return t;var n=h.isArray(e)?e[0]:e;switch(n){case"array":return h.isArray(t)?t:[t];case"string":return t+"";case"number":return parseFloat(t);case"int":case"integer":return parseInt(t,10);case"bool":case"boolean":case"boolean-like":return!!t;case"function":case"object":return t;default:throw new Error("Args received an invalid data type: "+n)}}var h=t("./util.js"),u=t("./logger.js");r.prototype={orig:function(){return this.origArgs.slice(0)},restAsList:function(t,e){var n=this.argList.slice(0);if(t||e)for(var r=0,i=n.length;i>r;r++)this._arg(e||!0,null,t>r);return n},skip:function(){return this.argList.length&&(this.pos++,this.argList.shift()),this},next:function(t,e){return this._arg(t,e,!1)},nextWarn:function(t,e){return this._arg(t,e,"warn")},nextReq:function(t){return this._arg(t,null,!0)},nextFrom:function(t,e){return this._from(t,e,!1)},nextFromWarn:function(t,e){return this._from(t,e,"warn")},nextFromReq:function(t){return this._from(t,null,!0)},listFrom:function(t,e){return this._list(t,e,!1)},listFromWarn:function(t,e){return this._list(t,e,"warn")},listFromReq:function(t){return this._list(t,null,!0)},_arg:function(t,e,n){return this.pos++,(h.isUndefined(t)||null===t)&&(t=!0),this.argList.length&&i(this.argList[0],t)?a(this.argList.shift(),t):(n&&s(n,this.fnName,this.pos,h.printf("must be of type %s",t)),e)},_from:function(t,e,n){return this.pos++,this.argList.length&&h.contains(t,this.argList[0])?this.argList.shift():(n&&s(n,this.fnName,this.pos,h.printf("must be one of %s",t)),e)},_list:function(t,e,n){this.pos++;var r=[],i=this.argList[0];return!this.argList.length||h.isEmpty(i)||!h.isArray(i)&&h.isObject(i)||(this.argList.shift(),h.isArray(i)?r=h.map(i,function(e){return h.contains(t,e)?e:void o(this.fnName,e,t)},this):h.contains(t,i)?r=[i]:o(this.fnName,i,t)),h.isEmpty(r)?(n&&s(n,this.fnName,this.pos,h.printf("choices must be in [%s]",t)),e===!0?t:e):r}},e.exports=r},{"./logger.js":28,"./util.js":30}],28:[function(t,e,n){"use strict";function r(t){return t.charAt(0).toUpperCase()+t.substr(1)}function i(){var t;return"undefined"!=typeof window&&window.location&&window.location.search&&(t=window.location.search.match("\bdebugLevel=([0-9]+)\b")),t?parseInt(t[1],10):h}function s(){return!0}function o(t,e){return e.length?t instanceof RegExp?!t.test(e[0]+""):!(e[0]+"").match(t):!0}function a(t){switch(t){case!1:return 0;case"off":return 0;case"none":return 0;case"error":return 1;case"warn":return 2;case"warning":return 2;case"info":return 3;case"log":return 4;case"debug":return 5;case!0:return h;case"on":return h;case"all":return h;default:return h}}var h=2,u=!1,c={error:s,warn:s,info:s,log:s,debug:s,time:s,timeEnd:s,group:s,groupEnd:s},l=t("./util.js"),f=function(){f.log.apply(null,l.toArray(arguments))};f.warn=s,f.error=s,f.info=s,f.log=s,f.debug=s,f.isErrorEnabled=s,f.isWarnEnabled=s,f.isInfoEnabled=s,f.isLogEnabled=s,f.isDebugEnabled=s,f.logLevel=function(t,e){if("number"!=typeof t&&(t=a(t)),u===t)return function(){};l.each(["error","warn","info","log","debug"],function(n,i){var a="undefined"!=typeof console&&t>=i+1;if(a){var h=l.bind(console["debug"===n?"log":n],console);f[n]=function(){var t=l.toArray(arguments);if(t.length>1&&"string"==typeof t[0]){var n=t[0].match(/(%s|%d|%j)/g);if(n){var r=[l.printf.apply(l,t)];t=t.length>n.length+1?r.concat(t.slice(n.length+1)):r}}e&&o(e,t)||h.apply("undefined"==typeof console?c:console,t)}}else f[n]=s;f["is"+r(n)+"Enabled"]=function(){return a}});var n=function(t){return function(){f.logLevel(t)}}(u);return u=t,n},f.logLevel(i()),e.exports=f},{"./util.js":30}],29:[function(t,e,n){"use strict";function r(t){this.needs=0,this.met=0,this.queued=[],this.errors=[],this.criteria=[],this.processing=!1,i.each(t,this.addCriteria,this)}var i=t("./util.js");r.prototype={addCriteria:function(t,e){if(this.processing)throw new Error("Cannot call addCriteria() after invoking done(), fail(), or handler() methods");return this.criteria.push(e?[t,e]:t),this},getHandler:function(){var t,e;return this.addCriteria(function(n){e!==i.undef?n(e):t=n}),function(n){t?t(n):e=n}},ready:function(){return this.needs===this.met},done:function(t,e){return t&&this._runOrStore(function(){this.hasErrors()||t.call(e)}),this},fail:function(t,e){return this._runOrStore(function(){this.hasErrors()&&t.apply(e,this.getErrors())}),this},handler:function(t,e){return this._runOrStore(function(){t.apply(e,this.hasErrors()?this.getErrors():null)}),this},chain:function(t){return this.addCriteria(t.handler,t),this},when:function(t){this._runOrStore(function(){this.hasErrors()?t.reject.apply(t,this.getErrors()):t.resolve()})},addError:function(t){this.errors.push(t)},hasErrors:function(){return this.errors.length},getErrors:function(){return this.errors.slice(0)},_process:function(){this.processing=!0,this.needs=this.criteria.length,i.each(this.criteria,this._evaluateCriteria,this)},_evaluateCriteria:function(t){var e=null;i.isArray(t)&&(e=t[1],t=t[0]);try{t.call(e,i.bind(this._criteriaMet,this))}catch(n){this.addError(n)}},_criteriaMet:function(t){t&&this.addError(t),this.met++,this.ready()&&i.each(this.queued,this._run,this)},_runOrStore:function(t){this.processing||this._process(),this.ready()?this._run(t):this.queued.push(t)},_run:function(t){t.call(this)}},e.exports=function(t,e){var n=new r(t);return e&&n.done(e),n}},{"./util.js":30}],30:[function(t,e,n){(function(e){"use strict";function r(t,e){switch(e){case"%d":return parseInt(t,10);case"%j":return t=c.isObject(t)?JSON.stringify(t):t+"",t.length>500&&(t=t.substr(0,500)+".../*truncated*/...}"),t;case"%s":return t+"";default:return t}}function i(t){return c.isObject(t)&&t+""=="[object Arguments]"}function s(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),n=this,r=function(){},i=function(){return n.apply(this instanceof r&&t?this:t,e.concat(Array.prototype.slice.call(arguments)))};return r.prototype=this.prototype,i.prototype=new r,i}function o(t,e){var n,r;for(n=0,r=this.length;r>n;++n)n in this&&t.call(e,this[n],n,this)}function a(t){return"[object Array]"===Object.prototype.toString.call(t)}function h(t,e){if(null===this)throw new TypeError;var n,r,i=Object(this),s=i.length>>>0;if(0===s)return-1;if(n=0,arguments.length>1&&(n=Number(arguments[1]),n!==n?n=0:0!==n&&n!==1/0&&n!==-(1/0)&&(n=(n>0||-1)*Math.floor(Math.abs(n)))),n>=s)return-1;for(r=n>=0?n:Math.max(s-Math.abs(n),0);s>r;r++)if(r in i&&i[r]===t)return r;return-1}var u,c=n,l=["value","child_added","child_removed","child_updated","child_changed"];c.undef=u,c.Firebase=e.Firebase||t("firebase"),c.isDefined=function(t){return t!==u},c.isUndefined=function(t){return t===u},c.isObject=function(t){return Object.prototype.isPrototypeOf(t)},c.isArray=function(t){return(Array.isArray||a).call(null,t)},c.isFunction=function(t,e){return"string"==typeof e?c.isObject(t)&&c.has(t,e)&&"function"==typeof t[e]:"function"==typeof t},c.toArray=function(t,e){var n=c.map(t,function(t,e){return t});return e>0?n.slice(e):n},c.extend=function(t,e){var n=c.toArray(arguments),r="boolean"==typeof n[0]&&n.shift(),i=n.shift();return c.each(n,function(t){c.isObject(t)&&c.each(t,function(t,e){i[e]=r&&c.isObject(i[e])?c.extend(!0,i[e],t):t})}),i},c.bind=function(t,e){var n=Array.prototype.slice.call(arguments,1);return(t.bind||s).apply(t,n)},c.isEmpty=function(t){return t===u||null===t||c.isArray(t)&&0===t.length||c.isObject(t)&&!c.contains(t,function(t){return!0})},c.keys=function(t){var e=[];return c.each(t,function(t,n){e.push(n)}),e},c.map=function(t,e,n){var r=[];return c.each(t,function(i,s){var o=e.call(n,i,s,t);o!==u&&r.push(o)}),r},c.mapObject=function(t,e,n){var r={};return c.each(t,function(i,s){var o=e.call(n,i,s,t);o!==u&&(r[s]=o)}),r},c.find=function(t,e,n){if(c.isArray(t)){for(var r=0,i=t.length;i>r;r++)if(e.call(n,t[r],r,t)===!0)return t[r]}else if(c.isObject(t)){var s;for(s in t)if(t.hasOwnProperty(s)&&e.call(n,t[s],s,t)===!0)return t[s]}return u},c.filter=function(t,e,n){var r=c.isArray(t),i=r?[]:{};return c.each(t,function(s,o){e.call(n,s,o,t)&&(r?i.push(s):i[o]=s)}),i},c.reduce=function(t,e,n){return c.each(t,function(r,i){e=n(e,r,i,t)}),e},c.has=function(t,e){return c.isObject(t)&&t[e]!==u},c.val=function(t,e){return c.has(t,e)?t[e]:u},c.contains=function(t,e,n){if("function"!=typeof e){if(c.isArray(t))return c.indexOf(t,e)>-1;e=function(t){return function(e){return e===t}}(e)}return c.find(t,e,n)!==u},c.each=function(t,e,n){if(c.isArray(t)||i(t))(t.forEach||o).call(t,e,n);else if(c.isObject(t)){var r;for(r in t)t.hasOwnProperty(r)&&e.call(n,t[r],r,t)}},c.indexOf=function(t,e){return(t.indexOf||h).call(t,e)},c.remove=function(t,e){var n=!1;if(c.isArray(t)){var r=c.indexOf(t,e);r>-1&&(t.splice(r,1),n=!0)}else if(c.isObject(t)){var i;for(i in t)if(t.hasOwnProperty(i)&&e===t[i]){n=!0,delete t[i];break}}return n},c.defer=function(t,e){var n=c.toArray(arguments);setTimeout(c.bind.apply(null,n),0)},c.call=function(t,e){var n=c.toArray(arguments,2),r=[];return c.each(t,function(t){return"function"!=typeof t||e?void(c.isObject(t)&&"function"==typeof t[e]&&r.push(t[e].apply(t,n))):r.push(t.apply(null,n))}),r},c.isEqual=function(t,e,n){if(t===e)return!0;if(typeof t!=typeof e)return!1;if(c.isObject(t)&&c.isObject(e)){var r=c.isArray(t),i=c.isArray(e);if(r||i)return r&&i&&t.length===e.length&&!c.contains(t,function(t,n){return!c.isEqual(t,e[n])});var s=n?c.keys(t):c.keys(t).sort(),o=n?c.keys(e):c.keys(e).sort();return c.isEqual(s,o)&&!c.contains(t,function(t,n){return!c.isEqual(t,e[n])})}return!1},c.bindAll=function(t,e){return c.each(e,function(n,r){"function"==typeof n&&(e[r]=c.bind(n,t))}),e},c.printf=function(){var t=c.toArray(arguments),e=t.shift(),n=e.match(/(%s|%d|%j)/g);return n&&t.length&&c.find(n,function(n){return e=e.replace(n,r(t.shift(),n)),0===t.length}),e},c.mergeToString=function(t){return 0===t.length?null:1===t.length?t[0]:"["+t.join("][")+"]"},c.construct=function(t,e){function n(){return t.apply(this,e)}return n.prototype=t.prototype,new n},c.noop=function(){};var f=[];c.isFirebaseRef=function(t){var e=c.isObject(t),n=e?Object.getPrototypeOf(t):!1;return n&&n.constructor===c.Firebase.prototype.constructor?!0:e&&"function"==typeof t.ref&&"function"==typeof t.ref().transaction?!0:c.find(f,function(n){ +return e?t instanceof n:t===n})},c.registerFirebaseWrapper=function(t){f.push(t)},c._mockFirebaseRef=function(t){c.Firebase=t},c.escapeEmail=function(t){return(t||"").replace(".",",")},c.assertValidEvent=function(t){if(!c.contains(l,t))throw new Error("Event must be one of "+l+", but received "+t)},c.inherits=function(t,e){var n=[t.prototype].concat(c.toArray(arguments).slice(2));return t.prototype=new e,t.prototype.constructor=e,c.each(n,function(e){c.each(e,function(e,n){t.prototype[n]=e})}),t.prototype._super=function(){e.apply(this,arguments)},t},c.deepCopy=function(t){if(!c.isObject(t))return t;var e=c.isArray(t)?[]:{};return c.each(t,function(t,n){e[n]=c.deepCopy(t)}),e},c.pick=function(t,e){if(!c.isObject(t))return{};var n=c.isArray(t)?[]:{};return c.each(e,function(e){n[e]=t[e]}),n},c.eachByPath=function(t,e,n,r){var i={};c.each(e,function(e,n){var r=t.pathFor(n),s=t.getField(n),o=s?s.id:n;c.has(i,r.name())||(i[r.name()]={path:r,data:{}}),i[r.name()].data[o]=e}),c.each(i,function(t){n.call(r,t.path,t.data)})}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{firebase:"firebase"}],"firebase-util":[function(t,e,n){"use strict";var r=t("./common/index.js");r.extend(n,t("./common/exports.js"),t("./NormalizedCollection/exports.js"),t("./Paginate/exports.js")),"undefined"!=typeof window&&(window.hasOwnProperty("Firebase")?window.Firebase.util=r:console.warn("Firebase not found on the global window instance. Cannot add Firebase.util namespace."))},{"./NormalizedCollection/exports.js":2,"./Paginate/exports.js":17,"./common/exports.js":23,"./common/index.js":24}]},{},[1]); \ No newline at end of file diff --git a/package.json b/package.json index 5c3ef43..3aaa52b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "firebase-util", "description": "A set of experimental power tools for Firebase.", - "version": "0.0.0", + "version": "0.2.4", "author": "Firebase (https://www.firebase.com/)", "homepage": "https://github.com/firebase/firebase-util", "repository": {