)]}'
{"version":3,"sources":["minimongo/minimongo.js","minimongo/wrap_transform.js","minimongo/helpers.js","minimongo/selector.js","minimongo/sort.js","minimongo/projection.js","minimongo/modify.js","minimongo/diff.js","minimongo/id_map.js","minimongo/observe.js","minimongo/objectid.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+D;;AAEA,2E;;AAEA,mE;AACA,sF;;AAEA,mD;;AAEA,mC;AACA,kB;AACA,mB;AACA,yC;AACA,0C;;AAEA,sD;;AAEA,+C;;AAEA,oC;AACA,4E;AACA,uE;AACA,kC;AACA,+D;AACA,0C;AACA,8C;AACA,oB;;AAEA,iF;AACA,2D;AACA,8B;;AAEA,qE;AACA,sB;AACA,E;;AAEA,e;;AAEA,yC;AACA,0D;AACA,mB;;AAEA,8D;AACA,8C;AACA,4B;AACA,sB;AACA,Q;AACA,uB;AACA,K;AACA,E;;AAEA,qC;AACA,6B;AACA,4B;AACA,W;AACA,E;;;AAGA,kD;AACA,kC;AACA,oB;AACA,oC;AACA,2B;AACA,mE;AACA,wB;AACA,E;AACA,sE;AACA,W;AACA,E;AACA,iE;AACA,iE;AACA,Q;AACA,E;AACA,6D;AACA,oC;AACA,Y;AACA,+D;AACA,oE;AACA,sE;AACA,0C;AACA,6B;AACA,kB;;AAEA,6D;AACA,E;;AAEA,8D;;AAEA,mE;AACA,kB;AACA,6B;;AAEA,+B;AACA,qB;;AAEA,gD;AACA,0B;AACA,gC;AACA,mD;AACA,U;AACA,iC;AACA,mD;AACA,qD;AACA,4D;AACA,oE;AACA,K;AACA,G;AACA,2B;AACA,6B;AACA,+B;;AAEA,6E;;AAEA,qE;;AAEA,kE;AACA,qC;AACA,+E;AACA,E;;AAEA,2E;AACA,2E;AACA,uD;AACA,wB;AACA,uD;AACA,E;;AAEA,kE;AACA,6B;AACA,kB;;AAEA,oE;AACA,uE;AACA,8D;AACA,qE;AACA,sE;AACA,qE;AACA,uE;AACA,iB;AACA,0B;AACA,oB;;AAEA,iD;AACA,E;;AAEA,G;AACA,8B;AACA,sB;AACA,wB;AACA,G;AACA,G;AACA,4F;AACA,kB;AACA,mB;AACA,Y;AACA,yB;AACA,2J;AACA,wF;AACA,G;AACA,yE;AACA,kB;;AAEA,qD;;AAEA,sB;AACA,kB;AACA,wB;AACA,oB;AACA,oB;AACA,0B;AACA,G;;AAEA,qC;AACA,yC;AACA,kC;;AAEA,wB;AACA,iC;AACA,yC;AACA,K;AACA,E;;AAEA,6D;AACA,yB;AACA,E;;AAEA,G;AACA,wE;AACA,kB;AACA,c;AACA,Y;AACA,yB;AACA,2J;AACA,wF;AACA,G;AACA,qE;AACA,kB;AACA,e;AACA,sC;AACA,uD;AACA,K;AACA,a;AACA,E;;AAEA,G;AACA,sD;AACA,yB;AACA,iB;AACA,Y;AACA,kB;AACA,sB;AACA,G;AACA,sD;AACA,kB;AACA,e;AACA,+B;AACA,kB;AACA,K;AACA,a;AACA,E;;AAEA,G;AACA,+D;AACA,yB;AACA,iB;AACA,Y;AACA,kB;AACA,oB;AACA,G;AACA,sD;AACA,kB;;AAEA,oB;AACA,8C;AACA,+D;;AAEA,qD;AACA,E;;AAEA,kE;AACA,kB;AACA,6B;AACA,gF;AACA,wC;;AAEA,uD;AACA,gE;AACA,E;;AAEA,mE;AACA,kB;AACA,8B;AACA,E;;AAEA,2E;AACA,+C;AACA,4E;AACA,4D;AACA,E;;AAEA,oE;AACA,2C;AACA,wE;AACA,+C;AACA,4E;AACA,+C;AACA,4E;;AAEA,yE;AACA,oC;AACA,E;;AAEA,2C;AACA,+C;;AAEA,sB;AACA,8B;AACA,mC;AACA,wB;AACA,qD;AACA,0C;AACA,qC;AACA,0B;AACA,8C;AACA,E;AACA,iD;AACA,yB;AACA,uD;AACA,E;AACA,kD;AACA,yC;AACA,E;AACA,mD;AACA,6E;AACA,sE;;AAEA,4C;AACA,K;AACA,0E;AACA,oB;AACA,2B;AACA,c;AACA,wF;AACA,K;AACA,+B;AACA,oB;AACA,qE;AACA,I;;AAEA,K;AACA,+J;AACA,oB;AACA,2B;AACA,c;AACA,wF;AACA,K;AACA,sC;AACA,oB;;AAEA,8E;;AAEA,gF;AACA,gF;AACA,kC;AACA,kD;AACA,2E;AACA,4G;;AAEA,4E;AACA,0E;;AAEA,iB;AACA,+C;AACA,qC;AACA,kB;AACA,6E;AACA,4B;AACA,uB;AACA,mB;AACA,sC;AACA,M;AACA,Y;;AAEA,2E;AACA,Y;AACA,wB;AACA,uC;AACA,2C;AACA,K;AACA,yC;AACA,qD;AACA,+B;AACA,0E;;AAEA,6E;AACA,0B;AACA,uE;AACA,gC;;AAEA,6E;AACA,Y;AACA,qC;AACA,a;AACA,8B;AACA,kC;AACA,2B;AACA,6B;;AAEA,mC;AACA,iB;;AAEA,6D;AACA,iC;AACA,W;AACA,Q;AACA,M;AACA,8C;AACA,kD;AACA,kD;AACA,kB;AACA,4D;AACA,4D;AACA,K;;AAEA,gE;AACA,kD;AACA,wB;AACA,iD;AACA,2D;AACA,2B;AACA,sC;;AAEA,0B;AACA,oB;AACA,uE;AACA,yD;AACA,S;AACA,K;;AAEA,mD;AACA,sB;AACA,kC;AACA,yB;AACA,0B;AACA,8C;AACA,O;AACA,O;;AAEA,0C;AACA,mE;AACA,6D;AACA,2D;AACA,iE;AACA,6C;AACA,wC;AACA,sB;AACA,S;AACA,K;AACA,oE;AACA,mC;AACA,0C;;AAEA,kB;AACA,G;AACA,G;;AAEA,wE;AACA,E;AACA,gF;AACA,gF;AACA,S;AACA,E;AACA,gF;AACA,gC;AACA,E;AACA,4E;AACA,6E;AACA,2E;AACA,+E;AACA,8E;AACA,8D;AACA,sE;AACA,kB;AACA,0B;;AAEA,yE;AACA,e;AACA,kE;;AAEA,kC;AACA,uC;AACA,iE;AACA,sE;AACA,Y;AACA,kB;AACA,qB;;AAEA,kE;AACA,sB;AACA,0B;AACA,kC;AACA,U;AACA,mD;AACA,K;AACA,mB;AACA,G;;AAEA,wD;;AAEA,+E;AACA,6E;AACA,mB;AACA,gB;AACA,sD;AACA,4B;AACA,oC;AACA,wB;AACA,Y;AACA,+C;AACA,K;AACA,G;;AAEA,oD;AACA,wD;AACA,6B;AACA,4B;AACA,0B;AACA,4D;AACA,kD;AACA,c;AACA,6B;AACA,O;AACA,K;AACA,8C;AACA,sD;AACA,mD;AACA,sC;AACA,6B;AACA,6B;AACA,K;;AAEA,uB;AACA,mB;;AAEA,oB;AACA,uE;AACA,6B;AACA,G;;AAEA,iC;AACA,uE;AACA,2C;AACA,E;;AAEA,uE;AACA,oB;AACA,kF;AACA,kB;;AAEA,uB;AACA,mC;AACA,e;AACA,4C;;AAEA,mB;AACA,8B;AACA,wC;AACA,M;AACA,yE;AACA,8B;AACA,kC;AACA,8C;AACA,c;;AAEA,sE;AACA,iC;AACA,G;AACA,E;;AAEA,wE;AACA,2C;AACA,mE;AACA,4C;AACA,6D;AACA,kB;AACA,yB;;AAEA,2B;AACA,4D;AACA,yE;AACA,uE;AACA,oD;AACA,G;AACA,mB;;AAEA,yB;AACA,uD;;AAEA,oC;AACA,0B;;AAEA,8B;AACA,oC;AACA,iC;AACA,kC;AACA,yD;AACA,6B;AACA,gE;AACA,sD;AACA,kD;AACA,qC;AACA,U;AACA,qD;AACA,K;AACA,G;;AAEA,6C;AACA,0B;AACA,gD;AACA,K;AACA,6B;;AAEA,0E;AACA,iB;AACA,e;AACA,8B;AACA,yB;AACA,O;AACA,Y;AACA,E;;AAEA,uE;AACA,mE;AACA,0E;AACA,gC;AACA,6E;AACA,kB;AACA,oE;AACA,oB;AACA,kD;AACA,8B;AACA,mC;AACA,gB;AACA,sC;AACA,mC;AACA,gB;AACA,O;AACA,K;AACA,U;AACA,0B;AACA,G;AACA,E;;AAEA,kE;AACA,kB;;AAEA,+E;AACA,gF;AACA,yB;AACA,2E;AACA,mC;AACA,uB;AACA,2C;AACA,0B;AACA,2B;AACA,c;AACA,8B;AACA,O;AACA,O;AACA,mB;AACA,gC;AACA,+B;AACA,S;AACA,K;AACA,kB;AACA,G;;AAEA,gD;AACA,kB;AACA,8D;AACA,4C;AACA,sB;AACA,K;;AAEA,8B;AACA,uB;AACA,2C;AACA,6B;AACA,6C;AACA,gD;AACA,4D;AACA,oD;AACA,uC;AACA,Y;AACA,uD;AACA,O;AACA,O;AACA,4C;AACA,gC;AACA,G;;AAEA,kE;AACA,yC;AACA,yC;AACA,gB;AACA,gE;AACA,4D;AACA,K;AACA,K;AACA,6C;AACA,kC;AACA,c;AACA,oC;AACA,K;AACA,6B;AACA,yB;AACA,e;AACA,8B;AACA,6B;AACA,O;AACA,gB;AACA,E;;AAEA,kE;AACA,4C;AACA,gF;AACA,kB;AACA,kD;AACA,uB;AACA,mB;AACA,G;AACA,6B;;AAEA,gD;;AAEA,iE;AACA,8E;AACA,8E;AACA,mE;AACA,wB;AACA,gC;AACA,8C;AACA,2E;AACA,sB;AACA,mE;AACA,6D;AACA,K;AACA,yB;;AAEA,sB;;AAEA,8D;AACA,mD;AACA,6B;AACA,2E;AACA,kC;AACA,+E;AACA,oB;AACA,yB;AACA,+B;AACA,K;AACA,gB;AACA,K;;AAEA,+C;AACA,kC;AACA,c;AACA,+D;AACA,K;AACA,6B;;AAEA,4E;AACA,8E;AACA,2B;AACA,iB;AACA,4C;AACA,kE;AACA,2D;AACA,2C;AACA,sC;AACA,qC;AACA,oB;AACA,G;;AAEA,8E;AACA,0E;AACA,sB;AACA,a;AACA,8B;AACA,c;AACA,iC;AACA,M;AACA,iC;AACA,qC;AACA,U;AACA,yB;AACA,G;;AAEA,e;AACA,8B;AACA,6B;AACA,O;AACA,gB;AACA,E;;AAEA,uE;AACA,gF;AACA,W;AACA,gF;AACA,kB;AACA,oD;AACA,uB;AACA,iB;AACA,G;AACA,2D;AACA,iB;AACA,uB;AACA,gB;AACA,E;;AAEA,uD;AACA,4C;AACA,kB;;AAEA,0B;AACA,iC;AACA,kC;AACA,wB;AACA,sE;AACA,Y;AACA,8E;AACA,qC;AACA,uD;AACA,K;AACA,G;;AAEA,iC;;AAEA,kE;;AAEA,6B;AACA,8B;AACA,qC;AACA,wD;AACA,kC;AACA,sE;AACA,wD;;AAEA,kD;AACA,0E;AACA,8E;AACA,4E;AACA,6E;AACA,8E;AACA,2E;AACA,yB;AACA,0B;AACA,kC;AACA,kC;AACA,qD;AACA,kC;AACA,mD;AACA,iC;AACA,4D;AACA,K;AACA,G;AACA,E;;AAEA,mE;AACA,wD;AACA,E;AACA,kE;AACA,oE;;AAEA,0D;AACA,gC;AACA,oB;AACA,sB;AACA,wB;AACA,mE;AACA,8B;AACA,Y;AACA,kD;AACA,iE;AACA,4B;AACA,oC;AACA,e;AACA,wB;AACA,U;AACA,oB;AACA,mE;AACA,K;AACA,qD;AACA,U;AACA,qD;AACA,oC;AACA,G;AACA,E;;AAEA,4D;AACA,sB;AACA,8D;AACA,2B;AACA,+B;AACA,U;AACA,sD;AACA,2B;AACA,6B;AACA,G;AACA,E;;AAEA,mE;AACA,0C;AACA,+D;AACA,wC;AACA,yD;AACA,8C;;AAEA,uB;AACA,oC;AACA,4C;AACA,sC;AACA,K;AACA,W;AACA,G;;AAEA,mE;;AAEA,gC;AACA,0C;AACA,oB;AACA,W;;AAEA,oE;AACA,Y;AACA,oC;AACA,oD;AACA,6D;AACA,wB;AACA,6B;AACA,wC;AACA,a;AACA,sB;AACA,Q;AACA,kB;AACA,0D;AACA,G;AACA,E;;AAEA,uE;AACA,0E;AACA,wC;AACA,E;AACA,+E;AACA,yE;AACA,gF;AACA,0E;AACA,E;AACA,qE;AACA,4E;AACA,kB;AACA,oC;AACA,+B;AACA,sB;AACA,4B;AACA,+C;AACA,yD;;AAEA,sB;AACA,sC;AACA,sD;AACA,4C;AACA,G;AACA,E;;;AAGA,+D;AACA,qB;AACA,2E;AACA,gD;AACA,iC;AACA,e;AACA,2C;AACA,E;;AAEA,0E;AACA,gB;AACA,8D;AACA,4C;;AAEA,2B;AACA,8C;AACA,oD;AACA,6B;AACA,mC;AACA,Y;AACA,8B;AACA,K;AACA,G;AACA,e;AACA,E;;AAEA,oE;AACA,2B;AACA,sB;AACA,a;AACA,G;;AAEA,6D;AACA,8B;AACA,a;AACA,E;;AAEA,gF;AACA,2E;AACA,8E;AACA,+E;AACA,gF;AACA,yE;AACA,uB;AACA,uD;AACA,kB;AACA,2B;AACA,4E;AACA,oD;AACA,E;AACA,2D;AACA,kB;AACA,4B;AACA,sE;;AAEA,uC;AACA,8B;AACA,mB;AACA,E;;AAEA,8D;AACA,kB;AACA,0C;AACA,4B;AACA,W;AACA,0E;AACA,4E;AACA,6B;AACA,mC;AACA,W;AACA,iD;AACA,E;;AAEA,mE;AACA,+B;AACA,wD;AACA,6B;AACA,kB;AACA,W;;AAEA,sE;AACA,qB;;AAEA,yD;AACA,iC;AACA,kC;;AAEA,uD;AACA,G;AACA,E;;AAEA,6D;AACA,0D;AACA,sE;AACA,+D;AACA,yD;AACA,kB;AACA,yB;AACA,mB;AACA,W;;AAEA,mE;AACA,+D;AACA,sB;;AAEA,iC;AACA,kC;AACA,2E;AACA,wD;AACA,sC;AACA,iE;AACA,4C;AACA,iC;AACA,G;AACA,6B;AACA,E;;;AAGA,uB;AACA,8C;AACA,gD;AACA,wB;AACA,sC;AACA,oB;AACA,gB;AACA,6E;AACA,gF;AACA,yF;AACA,qG;AACA,sB;AACA,Y;AACA,uD;AACA,K;AACA,gC;AACA,e;AACA,qD;AACA,4F;AACA,wC;AACA,oC;AACA,G;AACA,E;;;AAGA,uB;AACA,0C;AACA,kB;AACA,c;AACA,0B;AACA,qB;AACA,uC;AACA,wB;AACA,uC;AACA,oC;AACA,sD;AACA,6C;AACA,U;AACA,c;AACA,G;AACA,E;;AAEA,gE;AACA,kB;AACA,gD;AACA,qC;AACA,8B;AACA,M;AACA,sC;AACA,0B;AACA,M;AACA,iD;AACA,+C;AACA,iC;AACA,K;AACA,K;AACA,gB;AACA,E;;;;;;;;;;;;;;;;;;;ACvlCA,sE;AACA,sE;AACA,qE;AACA,4B;AACA,E;AACA,oC;AACA,qE;AACA,uB;AACA,gE;AACA,sD;AACA,kB;AACA,gB;;AAEA,uC;AACA,qC;AACA,qB;;AAEA,gC;AACA,6B;AACA,gF;AACA,+B;AACA,+D;AACA,K;;AAEA,qB;AACA,sF;AACA,uD;AACA,4B;AACA,O;;AAEA,sC;AACA,sD;AACA,K;;AAEA,oC;AACA,+C;AACA,yE;AACA,O;AACA,Y;AACA,2B;AACA,K;AACA,uB;AACA,I;AACA,sC;AACA,iB;AACA,E;;;;;;;;;;;;;;;;;;;AC7CA,+E;AACA,U;AACA,yC;AACA,wB;AACA,4C;AACA,E;;AAEA,2E;AACA,S;AACA,2C;AACA,+D;AACA,gD;AACA,E;;AAEA,4B;AACA,wC;AACA,E;;AAEA,6E;AACA,8E;AACA,gB;AACA,6D;AACA,oC;AACA,iB;;AAEA,oC;AACA,kD;AACA,qD;AACA,0C;AACA,yC;AACA,sD;AACA,0B;AACA,mD;AACA,uD;AACA,gC;AACA,K;AACA,K;AACA,qD;AACA,E;;;AAGA,qC;AACA,6B;AACA,4B;AACA,E;;;;;;;;;;;;;;;;;;;AC5CA,mC;;AAEA,e;AACA,8D;AACA,wE;AACA,sE;AACA,oE;AACA,gC;AACA,oE;AACA,uC;AACA,4E;AACA,4E;AACA,oC;AACA,4E;AACA,a;AACA,8D;;AAEA,oB;AACA,wD;AACA,6C;AACA,yC;AACA,kB;AACA,2E;AACA,6E;AACA,uB;AACA,mB;AACA,8C;AACA,4B;AACA,+C;AACA,yB;AACA,+E;AACA,gF;AACA,yB;AACA,wB;AACA,8E;AACA,0C;AACA,qC;AACA,4E;AACA,yE;AACA,yE;AACA,4B;AACA,wB;AACA,qD;AACA,E;;AAEA,uC;AACA,mC;AACA,0C;AACA,sD;AACA,K;AACA,iC;AACA,I;AACA,4B;AACA,6B;AACA,I;AACA,yB;AACA,0B;AACA,I;AACA,yB;AACA,0B;AACA,I;;AAEA,mE;AACA,0C;AACA,yC;AACA,oB;AACA,4D;AACA,uC;AACA,6B;AACA,gC;AACA,+B;AACA,6B;AACA,8C;AACA,Q;AACA,K;;AAEA,qC;AACA,kD;AACA,uC;AACA,kC;AACA,6B;AACA,yD;AACA,Q;AACA,K;;AAEA,8E;AACA,uE;AACA,8B;AACA,8D;AACA,6B;AACA,4B;AACA,K;;AAEA,qD;AACA,8D;AACA,iC;AACA,uD;;AAEA,2C;AACA,mE;AACA,I;AACA,oC;AACA,6B;AACA,I;AACA,+E;AACA,2C;AACA,0B;AACA,+B;AACA,G;AACA,G;;;AAGA,yE;AACA,iE;AACA,E;AACA,kD;AACA,E;AACA,+E;AACA,gD;AACA,wE;AACA,0B;AACA,uB;AACA,mD;AACA,mC;AACA,6E;AACA,mC;AACA,yC;AACA,iE;AACA,gC;AACA,mE;AACA,oE;AACA,Y;AACA,+E;AACA,8E;AACA,c;AACA,+B;AACA,qC;AACA,kD;AACA,wB;AACA,mE;AACA,uC;AACA,8C;AACA,0C;AACA,S;AACA,K;AACA,K;;AAEA,0C;AACA,E;;AAEA,8E;AACA,8E;AACA,sE;AACA,mC;AACA,sE;AACA,wC;AACA,8B;AACA,kD;AACA,2C;AACA,+C;AACA,mE;AACA,U;AACA,kD;AACA,6C;AACA,G;AACA,E;;AAEA,gF;AACA,+E;AACA,iE;AACA,uD;AACA,8B;AACA,0B;AACA,8B;AACA,4B;AACA,wC;AACA,wC;AACA,iD;AACA,K;AACA,iB;AACA,qD;AACA,kD;;AAEA,8E;AACA,6C;AACA,wC;AACA,4E;AACA,8E;AACA,6C;AACA,kC;AACA,2C;AACA,uB;AACA,O;;AAEA,6E;AACA,4C;AACA,0C;AACA,gD;;AAEA,qB;AACA,O;AACA,e;AACA,I;AACA,E;;AAEA,wD;AACA,0C;AACA,2B;AACA,kC;AACA,wE;AACA,yC;AACA,sC;AACA,K;AACA,yC;AACA,kC;AACA,mB;;AAEA,+E;AACA,+E;AACA,gF;AACA,6E;AACA,6B;AACA,yB;;AAEA,8B;AACA,I;AACA,E;;AAEA,gF;AACA,gC;AACA,qD;AACA,wC;AACA,0E;;AAEA,8E;AACA,6E;AACA,oE;AACA,sD;AACA,6B;AACA,iD;AACA,M;AACA,G;;AAEA,2B;AACA,6D;AACA,I;AACA,E;;AAEA,0E;AACA,kB;AACA,yE;AACA,yE;AACA,8E;AACA,W;;AAEA,4B;AACA,sD;AACA,gE;AACA,6E;AACA,0B;AACA,sE;AACA,kE;AACA,wD;;AAEA,+C;AACA,kD;AACA,gC;AACA,K;;AAEA,2C;AACA,4B;AACA,4E;AACA,oD;AACA,gD;AACA,4B;AACA,+C;AACA,yC;AACA,6C;AACA,oB;AACA,Y;AACA,4D;AACA,K;AACA,K;;AAEA,+C;AACA,E;;AAEA,gD;AACA,sC;AACA,kD;AACA,wD;AACA,kD;AACA,oC;AACA,mE;AACA,mC;AACA,wD;AACA,K;AACA,E;;AAEA,iE;AACA,yB;AACA,sD;AACA,mD;AACA,yC;AACA,yC;AACA,I;;AAEA,qD;AACA,mD;AACA,yC;;AAEA,gF;AACA,mC;AACA,8B;AACA,yB;;AAEA,2B;AACA,iD;AACA,6B;AACA,S;AACA,2D;AACA,mD;AACA,8B;AACA,M;AACA,I;;AAEA,sD;AACA,mD;AACA,yC;AACA,2B;AACA,iD;AACA,8B;AACA,S;AACA,+E;AACA,iE;AACA,8B;AACA,M;AACA,I;;AAEA,6C;AACA,0C;AACA,gC;AACA,6B;AACA,+C;AACA,+E;AACA,sD;AACA,iE;AACA,K;AACA,2B;AACA,iE;AACA,sD;AACA,oD;AACA,M;AACA,I;;AAEA,gF;AACA,2D;AACA,yB;AACA,wB;AACA,4B;AACA,M;AACA,G;AACA,E;;AAEA,0E;AACA,yE;AACA,2E;AACA,wD;AACA,kC;AACA,iD;AACA,gF;AACA,6E;AACA,qB;AACA,sC;AACA,I;AACA,E;;AAEA,6E;AACA,8E;AACA,4D;AACA,2C;AACA,uB;AACA,oD;AACA,yE;AACA,I;AACA,2B;AACA,wE;AACA,wC;AACA,I;AACA,4B;AACA,wE;AACA,8D;AACA,I;AACA,+B;AACA,0E;AACA,iC;AACA,O;AACA,4D;AACA,I;AACA,0E;AACA,+C;AACA,wC;AACA,6C;AACA,6B;AACA,I;AACA,mD;AACA,mD;AACA,6B;AACA,gD;AACA,6B;AACA,I;AACA,oD;AACA,0B;AACA,yC;AACA,4D;AACA,2B;AACA,4B;;AAEA,8B;AACA,0C;AACA,+C;AACA,sC;AACA,gD;AACA,sD;AACA,sE;AACA,O;AACA,+E;AACA,mB;AACA,iD;AACA,I;AACA,6D;AACA,gB;AACA,8D;AACA,gC;;AAEA,sE;AACA,6E;AACA,wC;;AAEA,qC;AACA,gE;AACA,iC;AACA,yC;AACA,gC;AACA,mC;AACA,6E;AACA,6E;AACA,sB;AACA,kC;AACA,sB;AACA,qC;AACA,qD;AACA,gB;AACA,wE;AACA,kC;AACA,S;AACA,Q;AACA,Y;AACA,+C;AACA,uD;AACA,yE;AACA,oC;AACA,mC;AACA,qD;AACA,sB;AACA,qD;AACA,Q;AACA,K;;AAEA,sC;AACA,4E;AACA,gF;AACA,2E;AACA,0E;AACA,Q;AACA,gF;AACA,gF;AACA,kD;AACA,8D;AACA,mC;AACA,gD;AACA,iD;AACA,qE;AACA,8D;AACA,iB;AACA,sC;AACA,4E;AACA,iB;AACA,6B;AACA,sC;AACA,iC;AACA,qC;AACA,Y;AACA,oD;AACA,S;AACA,oB;AACA,M;AACA,G;AACA,E;;AAEA,qB;AACA,+C;AACA,sB;AACA,sB;AACA,sB;AACA,sB;AACA,+B;AACA,gB;AACA,kC;AACA,E;AACA,0E;AACA,kD;AACA,wD;AACA,qC;AACA,kC;AACA,E;;AAEA,gC;AACA,oD;AACA,U;AACA,gD;AACA,uE;AACA,0E;AACA,4C;AACA,6D;AACA,6B;AACA,4B;AACA,uB;AACA,U;AACA,O;;AAEA,yE;AACA,oB;AACA,gC;AACA,uB;;AAEA,0D;;AAEA,+B;AACA,gC;AACA,uB;AACA,4E;AACA,8B;AACA,4D;AACA,uB;AACA,2E;AACA,Q;AACA,K;AACA,I;AACA,E;;AAEA,kC;AACA,mD;AACA,uD;AACA,+E;AACA,iB;AACA,gF;AACA,oB;AACA,0D;AACA,6E;AACA,kB;AACA,4E;AACA,4C;AACA,qB;AACA,2C;AACA,wB;AACA,K;AACA,2C;AACA,wB;AACA,K;AACA,4C;AACA,yB;AACA,K;AACA,4C;AACA,yB;AACA,K;AACA,S;AACA,gD;AACA,oD;AACA,8C;AACA,kD;AACA,wE;AACA,O;AACA,2D;AACA,+B;AACA,iC;AACA,+B;AACA,0E;AACA,Q;AACA,K;AACA,I;AACA,Q;AACA,gD;AACA,4B;AACA,0C;;AAEA,+B;AACA,yC;AACA,qC;AACA,6D;AACA,0C;AACA,iD;AACA,Y;AACA,+D;AACA,S;;AAEA,+B;AACA,qE;AACA,gC;AACA,uB;AACA,oD;AACA,0B;AACA,W;AACA,Q;AACA,K;AACA,I;AACA,U;AACA,8E;AACA,8E;AACA,sB;AACA,+B;AACA,gD;AACA,wC;AACA,gF;AACA,gB;AACA,oB;AACA,+C;AACA,4C;AACA,O;AACA,+B;AACA,0D;AACA,Q;AACA,K;AACA,I;AACA,U;AACA,6E;AACA,6E;AACA,6E;AACA,sC;AACA,gC;AACA,gD;AACA,sC;AACA,4C;AACA,+B;AACA,kC;AACA,yD;AACA,Q;AACA,K;AACA,I;AACA,W;AACA,+D;AACA,sE;AACA,2D;;AAEA,iB;AACA,iD;AACA,8E;AACA,yD;AACA,8C;;AAEA,+E;AACA,gF;AACA,uD;AACA,kD;AACA,+E;;AAEA,+E;AACA,iE;AACA,6C;AACA,yB;AACA,c;AACA,qC;AACA,O;AACA,0C;AACA,K;AACA,I;AACA,e;AACA,+B;AACA,wE;AACA,kC;AACA,iD;;AAEA,mC;AACA,4C;AACA,4D;AACA,6B;AACA,c;AACA,8E;AACA,gE;AACA,uE;AACA,+E;AACA,8D;AACA,kE;AACA,4B;AACA,O;;AAEA,+B;AACA,4B;AACA,uB;AACA,gD;AACA,sC;AACA,kB;AACA,6B;AACA,sE;AACA,6E;AACA,oE;AACA,uE;AACA,2B;AACA,+B;AACA,kB;AACA,2E;AACA,0C;AACA,6D;AACA,W;AACA,sE;AACA,qC;AACA,6E;AACA,S;AACA,qB;AACA,Q;AACA,K;AACA,G;AACA,E;;AAEA,qD;AACA,E;AACA,yE;AACA,8E;AACA,8E;AACA,kB;AACA,E;AACA,gF;AACA,4E;AACA,+E;AACA,sD;AACA,E;AACA,0E;AACA,uE;AACA,wE;AACA,wC;AACA,E;AACA,sC;AACA,oC;AACA,+E;AACA,8E;AACA,+D;AACA,mE;AACA,uB;AACA,+E;AACA,+E;AACA,0E;AACA,6E;AACA,mE;AACA,E;AACA,uE;AACA,4E;AACA,c;AACA,E;AACA,gF;AACA,mE;AACA,yE;AACA,wE;AACA,+E;AACA,6E;AACA,4E;AACA,6E;AACA,oD;AACA,E;AACA,yE;AACA,2E;AACA,E;AACA,E;AACA,kE;AACA,E;AACA,+E;AACA,U;AACA,8C;AACA,0B;AACA,6B;AACA,+C;AACA,mD;AACA,sE;AACA,iB;AACA,yB;AACA,8D;AACA,G;;AAEA,iD;AACA,4B;AACA,gC;AACA,2D;AACA,iC;AACA,kB;AACA,I;;AAEA,mD;AACA,+C;AACA,uC;AACA,sB;AACA,wB;;AAEA,uB;AACA,gF;AACA,gF;AACA,gF;AACA,0D;AACA,kB;;AAEA,gF;AACA,2E;AACA,+B;AACA,0D;AACA,K;;AAEA,2B;AACA,oC;;AAEA,0D;AACA,M;AACA,8E;AACA,4E;AACA,gF;AACA,gF;AACA,kB;AACA,M;AACA,4E;AACA,uE;AACA,+E;AACA,oE;AACA,sB;AACA,qC;AACA,0B;AACA,yD;AACA,sC;AACA,K;;AAEA,+E;AACA,gF;AACA,2E;AACA,8E;AACA,8E;AACA,oB;AACA,mC;AACA,uB;AACA,kB;AACA,sD;AACA,oE;AACA,K;;AAEA,oB;AACA,0C;AACA,+C;AACA,M;;AAEA,yE;AACA,wE;AACA,oC;AACA,yD;;AAEA,gF;AACA,gF;AACA,+D;AACA,M;AACA,4E;AACA,6E;AACA,yE;AACA,gF;AACA,gF;AACA,kD;AACA,M;AACA,8E;AACA,+E;AACA,6E;AACA,kC;AACA,yE;AACA,wD;AACA,oC;AACA,oC;AACA,mB;AACA,8C;AACA,S;AACA,S;AACA,K;;AAEA,kB;AACA,I;AACA,E;AACA,sD;;AAEA,6D;AACA,uB;AACA,sC;AACA,4C;AACA,+E;AACA,gF;AACA,8E;AACA,mC;AACA,iE;AACA,wB;AACA,4B;AACA,yC;AACA,S;AACA,K;AACA,6C;AACA,+C;AACA,0B;AACA,sB;AACA,6D;AACA,W;AACA,S;AACA,K;AACA,K;AACA,qB;AACA,E;;AAEA,qD;AACA,yB;AACA,E;;AAEA,wD;AACA,wB;AACA,E;;;AAGA,0E;AACA,+E;AACA,8E;AACA,iD;AACA,8C;AACA,+B;AACA,6B;AACA,+B;AACA,0B;;AAEA,mC;AACA,iB;AACA,kD;AACA,uC;AACA,uE;AACA,0E;AACA,+E;AACA,e;AACA,8D;AACA,0C;AACA,0C;AACA,O;AACA,4E;AACA,6E;AACA,c;AACA,uD;AACA,kD;AACA,O;AACA,8B;AACA,O;;AAEA,8E;AACA,sB;AACA,0B;AACA,8B;AACA,K;AACA,e;AACA,I;AACA,E;;AAEA,0C;AACA,0C;;;AAGA,yC;AACA,sB;AACA,wE;;AAEA,uB;AACA,8B;AACA,e;AACA,8B;AACA,e;AACA,+B;AACA,e;AACA,mB;AACA,e;AACA,mB;AACA,gB;AACA,4B;AACA,2C;AACA,gB;AACA,gC;AACA,gB;AACA,0B;AACA,e;AACA,0B;AACA,e;AACA,+C;AACA,e;AACA,uB;;AAEA,qC;AACA,iB;AACA,qC;AACA,oC;AACA,oB;AACA,kB;AACA,kB;AACA,I;;AAEA,mE;AACA,2B;AACA,yD;AACA,I;;AAEA,mE;AACA,oB;AACA,4B;AACA,mF;AACA,iE;AACA,kC;AACA,wB;AACA,gC;AACA,0B;AACA,0B;AACA,0B;AACA,yB;AACA,0B;AACA,8B;AACA,4B;AACA,wB;AACA,wB;AACA,wB;AACA,0B;AACA,8B;AACA,2B;AACA,uC;AACA,2B;AACA,8B;AACA,mC;AACA,8B;AACA,gB;AACA,I;;AAEA,kE;AACA,sE;AACA,qE;AACA,wB;AACA,yB;AACA,wB;AACA,sC;AACA,wB;AACA,e;AACA,yC;AACA,yC;AACA,+C;AACA,+C;AACA,kB;AACA,8B;AACA,kB;AACA,0E;AACA,kB;AACA,yD;AACA,+B;AACA,2B;AACA,kB;AACA,0B;AACA,0B;AACA,K;AACA,2B;AACA,2B;AACA,kB;AACA,sB;AACA,sB;AACA,K;;AAEA,2B;AACA,mB;AACA,2B;AACA,4C;AACA,6B;AACA,mE;AACA,qC;AACA,qB;AACA,8B;AACA,wB;AACA,6B;AACA,S;AACA,mB;AACA,Q;AACA,+D;AACA,K;AACA,4B;AACA,8B;AACA,2B;AACA,2C;AACA,2B;AACA,mB;AACA,oD;AACA,oB;AACA,mB;AACA,O;AACA,K;AACA,6B;AACA,6E;AACA,e;AACA,gC;AACA,mC;AACA,sC;AACA,wB;AACA,oB;AACA,wB;AACA,mB;AACA,O;AACA,e;AACA,K;AACA,8B;AACA,8B;AACA,wB;AACA,K;AACA,0B;AACA,e;AACA,4B;AACA,wE;AACA,0B;AACA,iB;AACA,qC;AACA,yB;AACA,oB;AACA,yB;AACA,kB;AACA,kB;AACA,qC;AACA,qE;AACA,wC;AACA,G;AACA,E;;AAEA,mC;AACA,8D;AACA,uB;AACA,yB;AACA,+B;AACA,mC;AACA,qB;AACA,E;;;;;;;;;;;;;;;;;;;ACpnCA,wD;AACA,4B;AACA,wC;AACA,+B;AACA,E;AACA,iE;AACA,sE;AACA,0D;AACA,E;AACA,kE;AACA,kE;AACA,wD;;AAEA,6C;AACA,kB;AACA,0B;;AAEA,2B;;AAEA,gD;AACA,c;AACA,iD;AACA,+B;AACA,mD;AACA,8B;AACA,iB;AACA,wD;AACA,0B;AACA,O;AACA,I;;AAEA,8B;AACA,2C;AACA,wC;AACA,mC;AACA,c;AACA,uD;AACA,O;AACA,K;AACA,wC;AACA,wC;AACA,mC;AACA,O;AACA,U;AACA,mE;AACA,G;;AAEA,uE;AACA,+E;AACA,0E;AACA,gC;AACA,sB;AACA,iD;AACA,8B;AACA,O;AACA,0E;AACA,G;;AAEA,2C;AACA,mD;AACA,yC;AACA,Q;;AAEA,yE;AACA,8E;AACA,yE;AACA,yB;AACA,2D;AACA,E;;AAEA,gF;AACA,sB;AACA,sC;AACA,qC;AACA,oB;;AAEA,uE;AACA,8D;AACA,yC;AACA,uC;AACA,K;;AAEA,sC;;AAEA,gF;AACA,4D;AACA,2E;AACA,gC;AACA,qD;AACA,gC;AACA,qD;AACA,yD;AACA,Q;AACA,I;;AAEA,0B;AACA,oB;AACA,gD;AACA,I;;AAEA,8E;AACA,8E;AACA,4D;AACA,I;AACA,0E;AACA,gE;AACA,gF;AACA,8E;AACA,gF;AACA,sE;AACA,qC;AACA,oB;AACA,sB;;AAEA,mD;AACA,gD;AACA,e;;AAEA,4B;AACA,qB;AACA,e;AACA,O;AACA,+C;AACA,qB;AACA,O;AACA,O;;AAEA,gF;AACA,2C;AACA,wB;AACA,yD;AACA,kB;AACA,I;;AAEA,8C;AACA,oB;AACA,oD;AACA,I;;AAEA,+E;AACA,uB;AACA,4C;AACA,oB;;AAEA,yC;AACA,4D;;AAEA,uD;AACA,kC;;AAEA,8C;AACA,qC;AACA,M;;AAEA,0B;;AAEA,6D;AACA,qE;AACA,+D;AACA,oE;;AAEA,2E;AACA,yC;AACA,2B;AACA,mC;;AAEA,4B;AACA,4C;AACA,0C;AACA,mC;AACA,4E;AACA,gF;AACA,kC;AACA,kC;AACA,gE;AACA,8D;AACA,iB;AACA,S;;AAEA,yB;AACA,wD;AACA,0D;AACA,iD;AACA,8D;;AAEA,2E;AACA,yE;AACA,2E;AACA,4E;AACA,mD;AACA,U;AACA,6E;AACA,wE;AACA,2B;AACA,8C;AACA,qD;AACA,sD;AACA,S;AACA,S;;AAEA,uB;AACA,4E;AACA,2B;AACA,2D;AACA,8E;AACA,uD;AACA,S;AACA,6B;AACA,wB;AACA,qE;AACA,kC;AACA,W;AACA,O;AACA,O;;AAEA,sB;AACA,qC;AACA,mE;AACA,+B;AACA,oD;AACA,0B;AACA,S;AACA,kB;AACA,a;AACA,K;;AAEA,2C;AACA,+D;AACA,8B;AACA,4B;AACA,iC;AACA,uC;AACA,4B;AACA,S;AACA,c;AACA,O;AACA,I;;AAEA,qE;AACA,4E;AACA,oB;AACA,uC;AACA,oB;AACA,qD;AACA,qD;AACA,0C;AACA,K;;AAEA,2C;AACA,I;;AAEA,gF;AACA,kB;AACA,qC;AACA,oB;AACA,mD;AACA,kC;AACA,8D;AACA,iB;AACA,2B;AACA,qB;AACA,M;AACA,I;;AAEA,yE;AACA,yD;AACA,mC;AACA,oB;;AAEA,wE;AACA,2B;AACA,sC;AACA,oC;AACA,iB;AACA,Q;AACA,K;;AAEA,kC;AACA,8C;AACA,8C;AACA,2C;AACA,M;AACA,I;;AAEA,sC;AACA,kC;AACA,+B;AACA,wE;AACA,2E;AACA,gD;AACA,I;AACA,wE;AACA,sD;AACA,mD;AACA,2D;AACA,sE;AACA,wE;AACA,yB;AACA,I;AACA,8E;AACA,2E;AACA,8E;AACA,8C;AACA,uC;AACA,oB;;AAEA,wB;AACA,mD;;AAEA,4E;AACA,0B;AACA,6D;AACA,uC;AACA,a;;AAEA,qC;;AAEA,gF;AACA,0B;AACA,qC;AACA,a;;AAEA,+B;AACA,oD;AACA,wC;AACA,O;;AAEA,kD;AACA,iC;;AAEA,+C;AACA,uB;AACA,e;;AAEA,0E;AACA,yE;AACA,yE;AACA,yD;AACA,0C;AACA,8E;AACA,6E;AACA,6E;AACA,2E;AACA,8E;AACA,wE;AACA,4D;AACA,iB;AACA,4D;AACA,e;AACA,O;;AAEA,0C;AACA,0D;AACA,qE;AACA,gF;AACA,6E;AACA,6B;AACA,2E;AACA,W;;AAEA,oD;AACA,+D;AACA,6B;AACA,8D;AACA,uC;AACA,W;;AAEA,sE;AACA,W;AACA,e;AACA,O;;AAEA,oC;AACA,4D;AACA,O;;AAEA,2E;AACA,8E;AACA,gF;AACA,yB;AACA,kE;AACA,a;;AAEA,sC;AACA,oE;AACA,qE;AACA,+B;AACA,W;AACA,S;AACA,M;AACA,G;AACA,G;;AAEA,gC;AACA,sE;AACA,uE;AACA,kB;AACA,qD;AACA,0B;AACA,sD;AACA,6C;AACA,wB;AACA,uB;AACA,K;AACA,a;AACA,I;AACA,E;;;;;;;;;;;;;;;;;;;ACtZA,oE;AACA,6E;AACA,sC;AACA,0D;AACA,wE;AACA,gF;AACA,4C;AACA,wD;AACA,oD;;AAEA,oE;AACA,0C;;AAEA,kD;AACA,4C;AACA,8B;AACA,uB;AACA,mF;;AAEA,wD;AACA,2C;AACA,2B;AACA,e;AACA,6B;AACA,4C;AACA,iC;AACA,+C;AACA,sD;AACA,mC;AACA,yC;AACA,U;AACA,wB;AACA,O;;AAEA,e;AACA,I;;AAEA,yB;AACA,2C;;AAEA,2C;AACA,wB;AACA,4C;AACA,qB;AACA,e;AACA,I;AACA,E;;AAEA,0E;AACA,0C;AACA,mB;AACA,wE;AACA,oE;AACA,yE;AACA,uC;AACA,kF;AACA,6E;AACA,gE;AACA,yC;;AAEA,4E;AACA,4E;AACA,oC;AACA,qF;AACA,gF;;AAEA,kC;;AAEA,yC;AACA,iC;AACA,2B;AACA,uB;AACA,2B;AACA,wD;AACA,uF;AACA,K;;;AAGA,wC;AACA,e;AACA,0C;AACA,qC;AACA,4E;AACA,2E;AACA,4E;AACA,yE;AACA,2E;AACA,8E;AACA,yC;AACA,Q;AACA,kD;AACA,kD;AACA,iD;AACA,6E;AACA,iD;AACA,uF;AACA,Q;AACA,mE;;AAEA,iC;AACA,6B;AACA,0E;AACA,uF;AACA,yF;AACA,O;;AAEA,U;AACA,8B;AACA,wB;AACA,I;AACA,E;;AAEA,2C;AACA,+E;AACA,4D;AACA,0E;AACA,0E;AACA,+E;AACA,8C;AACA,iD;AACA,iE;AACA,6D;AACA,oB;AACA,oC;AACA,uB;AACA,qC;;AAEA,8C;AACA,mE;AACA,+B;AACA,0B;AACA,2C;AACA,+C;AACA,sE;AACA,2C;AACA,4D;AACA,sC;AACA,uB;AACA,O;;AAEA,6B;AACA,kB;AACA,O;;AAEA,kB;AACA,oC;AACA,mC;AACA,8C;AACA,U;AACA,0E;AACA,K;AACA,K;;AAEA,c;AACA,E;;AAEA,+D;AACA,+C;AACA,4D;;AAEA,0C;AACA,4C;AACA,uF;AACA,mD;AACA,sF;AACA,K;AACA,E;;;;;;;;;;;;;;;;;;;;ACtKA,6D;AACA,uC;AACA,E;AACA,6D;AACA,E;AACA,sE;AACA,U;AACA,E;AACA,W;AACA,8E;AACA,6E;AACA,8D;AACA,wD;AACA,0B;AACA,0B;AACA,uD;AACA,yC;;AAEA,a;;AAEA,oB;AACA,mD;AACA,kE;;AAEA,iC;AACA,wB;AACA,uB;AACA,6B;AACA,qE;AACA,K;AACA,iB;AACA,U;AACA,kC;AACA,8B;;AAEA,wC;AACA,kC;AACA,yD;AACA,oD;AACA,oC;AACA,mB;AACA,iE;AACA,+C;AACA,6B;AACA,qE;AACA,S;;AAEA,gC;AACA,yD;AACA,S;;AAEA,0C;;AAEA,4C;AACA,+B;AACA,2C;AACA,uE;AACA,S;;AAEA,sD;AACA,6C;AACA,sD;AACA,4C;AACA,0C;AACA,4C;AACA,W;AACA,mC;AACA,qD;AACA,S;AACA,O;AACA,G;;AAEA,kC;AACA,oC;AACA,sE;AACA,uE;AACA,qD;AACA,oB;AACA,oB;AACA,K;AACA,kC;AACA,e;AACA,K;AACA,E;;AAEA,sE;AACA,iE;AACA,U;AACA,E;AACA,gE;AACA,oE;AACA,iE;AACA,sD;AACA,E;AACA,gF;AACA,2E;AACA,4B;AACA,E;AACA,4E;AACA,E;AACA,+E;AACA,Y;AACA,uD;AACA,0B;AACA,6B;AACA,6C;AACA,2C;AACA,8B;AACA,qC;AACA,qB;AACA,2B;AACA,yB;AACA,6B;AACA,oE;AACA,gC;AACA,c;AACA,K;AACA,+B;AACA,8B;AACA,oB;AACA,4B;AACA,2B;AACA,0E;AACA,oE;AACA,4E;AACA,8D;AACA,S;AACA,0C;AACA,8B;AACA,yC;AACA,oC;AACA,c;AACA,6B;AACA,2B;AACA,6B;AACA,2D;AACA,qC;AACA,O;AACA,e;AACA,wB;AACA,8B;AACA,oD;AACA,yB;AACA,kC;AACA,uB;AACA,kB;AACA,mC;AACA,uB;AACA,kD;AACA,yE;AACA,yE;AACA,O;AACA,Y;AACA,yD;AACA,iE;AACA,8B;AACA,6B;AACA,2B;AACA,kB;AACA,4B;AACA,O;AACA,K;;AAEA,a;AACA,iB;AACA,uB;AACA,G;;AAEA,e;AACA,E;;AAEA,2B;AACA,e;AACA,a;AACA,gB;AACA,c;AACA,gB;AACA,E;;AAEA,iB;AACA,uC;AACA,gC;AACA,qE;AACA,0B;AACA,4C;AACA,yE;AACA,2B;AACA,Y;AACA,0B;AACA,K;AACA,I;AACA,uC;AACA,2D;AACA,wE;AACA,gC;AACA,c;AACA,K;AACA,0B;AACA,4D;AACA,gC;AACA,c;AACA,K;AACA,qC;AACA,I;AACA,+C;AACA,uC;AACA,I;AACA,yC;AACA,+B;AACA,oC;AACA,4B;AACA,+B;AACA,Y;AACA,6B;AACA,K;AACA,I;AACA,wC;AACA,oC;AACA,yB;AACA,0C;AACA,uE;;AAEA,8B;AACA,+B;AACA,2C;AACA,a;AACA,K;;AAEA,qD;AACA,2B;AACA,mC;AACA,qD;;AAEA,oB;AACA,0B;AACA,0B;AACA,yC;AACA,+D;AACA,8C;AACA,yB;AACA,yE;AACA,yB;AACA,K;;AAEA,mB;AACA,iC;AACA,oB;AACA,8B;AACA,oE;AACA,8E;AACA,mE;AACA,yC;AACA,2D;AACA,qE;AACA,+C;AACA,wD;AACA,oE;AACA,4D;AACA,S;AACA,O;AACA,K;;AAEA,qB;AACA,2C;AACA,iD;;AAEA,qB;AACA,qB;AACA,uC;;AAEA,sB;AACA,8B;AACA,sB;AACA,yD;AACA,U;AACA,mD;AACA,K;AACA,I;AACA,2C;AACA,2D;AACA,gF;AACA,0B;AACA,wB;AACA,0B;AACA,mC;AACA,0E;AACA,U;AACA,0C;AACA,uB;AACA,K;AACA,I;AACA,4C;AACA,uB;AACA,kC;AACA,qC;AACA,0B;AACA,0B;AACA,wB;AACA,c;AACA,O;AACA,K;AACA,+C;AACA,0B;AACA,wB;AACA,6B;AACA,mC;AACA,2E;AACA,U;AACA,uC;AACA,0C;AACA,qD;AACA,mB;AACA,mC;AACA,S;AACA,K;AACA,I;AACA,uC;AACA,6B;AACA,a;AACA,0B;AACA,wB;AACA,a;AACA,mC;AACA,sE;AACA,U;AACA,6C;AACA,uB;AACA,U;AACA,gB;AACA,K;AACA,I;AACA,wC;AACA,6B;AACA,a;AACA,0B;AACA,wB;AACA,a;AACA,mC;AACA,+E;AACA,U;AACA,mB;AACA,+D;AACA,oE;AACA,8D;AACA,mE;AACA,c;;AAEA,sE;AACA,kE;AACA,6D;AACA,6C;AACA,iD;AACA,0C;AACA,oD;AACA,2B;AACA,c;AACA,0C;AACA,oD;AACA,2B;AACA,O;AACA,0B;AACA,K;AACA,I;AACA,2C;AACA,2D;AACA,gF;AACA,6B;AACA,a;AACA,0B;AACA,wB;AACA,a;AACA,mC;AACA,+E;AACA,U;AACA,mB;AACA,0C;AACA,4B;AACA,8C;AACA,wD;AACA,2B;AACA,kB;AACA,W;AACA,S;AACA,qB;AACA,yB;AACA,O;AACA,0B;AACA,K;AACA,I;AACA,wD;AACA,wB;AACA,iD;AACA,qE;AACA,wB;AACA,2D;AACA,gC;AACA,8D;AACA,6B;AACA,a;AACA,0B;AACA,yB;;AAEA,kC;AACA,oE;AACA,yB;AACA,2D;AACA,gC;AACA,wB;AACA,I;AACA,uC;AACA,oE;AACA,2E;AACA,kD;AACA,G;AACA,E;;;;;;;;;;;;;;;;;;;AC9ZA,iB;AACA,yD;AACA,kC;AACA,mC;AACA,8E;AACA,kE;AACA,c;AACA,6C;AACA,iD;AACA,M;AACA,+C;AACA,iD;AACA,E;;AAEA,8E;AACA,2E;AACA,0B;AACA,yD;;AAEA,6B;AACA,+E;AACA,G;;AAEA,4C;AACA,oC;AACA,iB;AACA,8D;AACA,gD;AACA,gD;AACA,2B;AACA,6E;AACA,yC;AACA,8C;AACA,S;AACA,O;AACA,gC;AACA,wC;AACA,wB;AACA,yC;AACA,K;AACA,K;;AAEA,yB;AACA,8C;AACA,8B;AACA,6B;AACA,O;AACA,G;AACA,E;;;AAGA,8E;AACA,yE;AACA,0B;AACA,yD;;AAEA,8B;AACA,sC;AACA,oC;AACA,oD;AACA,uC;AACA,K;;AAEA,2B;AACA,yC;AACA,mC;AACA,oD;AACA,iC;AACA,K;;AAEA,e;AACA,I;AACA,oE;AACA,gE;AACA,oE;AACA,sE;AACA,sB;;AAEA,gF;AACA,e;;AAEA,0E;AACA,W;;AAEA,+E;AACA,8E;AACA,gF;AACA,+E;AACA,gF;AACA,4E;AACA,8E;AACA,S;;AAEA,oE;AACA,mE;;;AAGA,6D;AACA,8D;AACA,iE;AACA,yB;AACA,I;AACA,8D;AACA,4C;AACA,mB;AACA,4C;AACA,sB;AACA,iE;AACA,uD;AACA,6B;AACA,8B;AACA,qE;AACA,iE;AACA,kB;AACA,0B;AACA,mD;AACA,qC;AACA,mD;AACA,I;AACA,wE;AACA,+B;AACA,0B;AACA,4D;AACA,0B;AACA,gE;AACA,sE;AACA,qE;AACA,uE;AACA,oD;AACA,qB;AACA,wD;AACA,gB;AACA,Y;AACA,O;;AAEA,+C;AACA,sB;AACA,4B;AACA,0B;AACA,K;AACA,G;;AAEA,sC;AACA,+D;AACA,oB;AACA,sB;AACA,oB;AACA,G;AACA,0D;AACA,oB;;AAEA,8E;AACA,oB;AACA,mC;;AAEA,sC;AACA,qC;AACA,oD;AACA,K;AACA,+E;AACA,mD;AACA,uB;AACA,yC;AACA,+E;AACA,2D;AACA,qD;AACA,8B;AACA,gD;AACA,sC;AACA,0B;AACA,kF;AACA,6D;AACA,c;AACA,gB;AACA,0D;AACA,4C;AACA,4C;AACA,gF;AACA,iC;AACA,mE;AACA,S;AACA,0E;AACA,O;AACA,K;AACA,kB;AACA,uC;AACA,wD;AACA,0C;AACA,0C;AACA,8E;AACA,+B;AACA,iE;AACA,O;AACA,K;AACA,gC;AACA,K;;;AAGA,E;;;AAGA,2C;AACA,kC;AACA,+C;AACA,iD;AACA,uD;AACA,I;AACA,kE;AACA,0C;AACA,0B;AACA,mE;AACA,Q;AACA,+D;AACA,K;AACA,4B;AACA,6C;AACA,4B;AACA,6C;AACA,O;AACA,G;AACA,E;;;;;;;;;;;;;;;;;;;AC5NA,sC;AACA,kB;AACA,2E;AACA,E;;AAEA,gD;;;;;;;;;;;;;;;;;;;;ACLA,wE;;AAEA,8E;AACA,+E;AACA,8E;AACA,4E;AACA,gF;AACA,4E;AACA,0C;AACA,6D;AACA,kB;AACA,0B;;AAEA,iD;AACA,8E;AACA,kC;AACA,mC;AACA,sE;AACA,4D;AACA,iC;AACA,wC;AACA,U;AACA,qD;AACA,G;AACA,0C;;AAEA,qB;AACA,8D;AACA,wB;AACA,kD;AACA,sC;AACA,qB;AACA,4D;AACA,oC;AACA,mE;AACA,kE;AACA,yD;AACA,wD;AACA,2D;AACA,qD;AACA,Q;AACA,0C;AACA,oC;AACA,8E;AACA,iD;AACA,O;AACA,M;AACA,U;AACA,2C;AACA,wB;AACA,oC;AACA,sC;AACA,kE;AACA,qB;AACA,gC;AACA,O;AACA,M;AACA,G;;AAEA,sE;AACA,e;AACA,oD;AACA,gC;AACA,a;AACA,uD;AACA,gD;AACA,qC;AACA,+C;AACA,I;AACA,4C;AACA,0D;AACA,yB;AACA,I;AACA,E;;AAEA,kF;AACA,wE;AACA,wD;;AAEA,8B;AACA,sE;AACA,4E;AACA,8E;AACA,2E;AACA,6E;AACA,gD;AACA,+B;AACA,kD;AACA,wB;AACA,gF;AACA,iB;AACA,yD;AACA,uC;AACA,6B;AACA,+E;AACA,uD;AACA,gB;AACA,sC;AACA,S;AACA,Q;AACA,sC;AACA,wB;AACA,sE;AACA,iB;AACA,iD;AACA,iB;AACA,2D;AACA,iD;AACA,mD;AACA,6B;AACA,yC;AACA,2D;AACA,yD;AACA,gB;AACA,gD;AACA,S;AACA,Q;AACA,0C;AACA,wB;AACA,sC;AACA,iB;AACA,wD;;AAEA,wB;AACA,6E;AACA,2E;AACA,oD;AACA,sB;AACA,e;AACA,2E;AACA,2D;AACA,Q;AACA,8B;AACA,wB;AACA,sE;AACA,iB;AACA,gF;AACA,wC;AACA,+C;AACA,yC;AACA,2D;AACA,iD;AACA,gB;AACA,wC;AACA,S;AACA,O;AACA,M;AACA,U;AACA,+B;AACA,oC;AACA,oD;AACA,iD;AACA,iD;AACA,S;AACA,Q;AACA,sC;AACA,wB;AACA,uC;AACA,yC;AACA,wC;AACA,qD;AACA,kD;AACA,mE;AACA,S;AACA,Q;AACA,8B;AACA,wB;AACA,uC;AACA,iE;AACA,S;AACA,O;AACA,M;AACA,G;;AAEA,kE;AACA,0C;AACA,iE;AACA,qB;;AAEA,gB;AACA,E;;;;;;;;;;;;;;;;;;;ACpLA,qD;AACA,uD;AACA,E;;AAEA,kD;AACA,uC;AACA,kB;AACA,kB;AACA,wC;AACA,yD;AACA,6E;AACA,K;AACA,0E;AACA,0B;AACA,U;AACA,qC;AACA,G;AACA,E;;AAEA,4D;AACA,kB;AACA,2C;AACA,E;;AAEA,+D;AACA,kB;AACA,sD;AACA,uC;AACA,E;;AAEA,yD;AACA,kB;AACA,kD;AACA,E;;AAEA,2D;AACA,e;AACA,E;;AAEA,+D;AACA,kB;AACA,8C;AACA,E;;AAEA,6C;AACA,qD;AACA,qD;AACA,sC;;AAEA,qD;AACA,qD;AACA,0C;AACA,qC;AACA,kD;AACA,E;;AAEA,yD;AACA,oE;AACA,mD;AACA,gD;AACA,mE;AACA,6B;AACA,E;;AAEA,gF;AACA,uE;AACA,8E;AACA,8E;AACA,uC;AACA,6D;AACA,gC;AACA,8C;AACA,sB;AACA,gB;AACA,gB;;AAEA,8B;AACA,+B;AACA,oC;AACA,oD;AACA,4B;AACA,uD;AACA,wC;AACA,sC;AACA,uC;AACA,oE;AACA,8B;AACA,K;AACA,gB;AACA,G;;AAEA,wE;AACA,0E;AACA,wE;AACA,kD;AACA,oD;AACA,2E;AACA,iB;AACA,sB;AACA,K;AACA,G;;AAEA,c;AACA,E;;AAEA,sC;AACA,4C;AACA,G","file":"/packages/minimongo.js","sourcesContent":["// XXX type checking on selectors (graceful error if malformed)\n\n// LocalCollection: a set of documents that supports queries and modifiers.\n\n// Cursor: a specification for a particular subset of documents, w/\n// a defined order, limit, and offset. creating a Cursor with LocalCollection.find(),\n\n// ObserveHandle: the return value of a live query.\n\nLocalCollection = function (name) {\n var self = this;\n self.name = name;\n // _id -> document (also containing id)\n self._docs = new LocalCollection._IdMap;\n\n self._observeQueue = new Meteor._SynchronousQueue();\n\n self.next_qid = 1; // live query id generator\n\n // qid -> live query object. keys:\n // ordered: bool. ordered queries have addedBefore/movedBefore callbacks.\n // results: array (ordered) or object (unordered) of current results\n // (aliased with self._docs!)\n // resultsSnapshot: snapshot of results. null if not paused.\n // cursor: Cursor object for the query.\n // selector, sorter, (callbacks): functions\n self.queries = {};\n\n // null if not saving originals; an IdMap from id to original document value if\n // saving originals. See comments before saveOriginals().\n self._savedOriginals = null;\n\n // True when observers are paused and we should not send callbacks.\n self.paused = false;\n};\n\nMinimongo = {};\n\n// Object exported only for unit testing.\n// Use it to export private functions to test in Tinytest.\nMinimongoTest = {};\n\nLocalCollection._applyChanges = function (doc, changeFields) {\n _.each(changeFields, function (value, key) {\n if (value === undefined)\n delete doc[key];\n else\n doc[key] = value;\n });\n};\n\nMinimongoError = function (message) {\n var e = new Error(message);\n e.name = \"MinimongoError\";\n return e;\n};\n\n\n// options may include sort, skip, limit, reactive\n// sort may be any of these forms:\n// {a: 1, b: -1}\n// [[\"a\", \"asc\"], [\"b\", \"desc\"]]\n// [\"a\", [\"b\", \"desc\"]]\n// (in the first form you're beholden to key enumeration order in\n// your javascript VM)\n//\n// reactive: if given, and false, don't register with Tracker (default\n// is true)\n//\n// XXX possibly should support retrieving a subset of fields? and\n// have it be a hint (ignored on the client, when not copying the\n// doc?)\n//\n// XXX sort does not yet support subkeys ('a.b') .. fix that!\n// XXX add one more sort form: \"key\"\n// XXX tests\nLocalCollection.prototype.find = function (selector, options) {\n // default syntax for everything is to omit the selector argument.\n // but if selector is explicitly passed in as false or undefined, we\n // want a selector that matches nothing.\n if (arguments.length === 0)\n selector = {};\n\n return new LocalCollection.Cursor(this, selector, options);\n};\n\n// don't call this ctor directly. use LocalCollection.find().\n\nLocalCollection.Cursor = function (collection, selector, options) {\n var self = this;\n if (!options) options = {};\n\n self.collection = collection;\n self.sorter = null;\n\n if (LocalCollection._selectorIsId(selector)) {\n // stash for fast path\n self._selectorId = selector;\n self.matcher = new Minimongo.Matcher(selector);\n } else {\n self._selectorId = undefined;\n self.matcher = new Minimongo.Matcher(selector);\n if (self.matcher.hasGeoQuery() || options.sort) {\n self.sorter = new Minimongo.Sorter(options.sort || [],\n { matcher: self.matcher });\n }\n }\n self.skip = options.skip;\n self.limit = options.limit;\n self.fields = options.fields;\n\n self._projectionFn = LocalCollection._compileProjection(self.fields || {});\n\n self._transform = LocalCollection.wrapTransform(options.transform);\n\n // by default, queries register w/ Tracker when it is available.\n if (typeof Tracker !== \"undefined\")\n self.reactive = (options.reactive === undefined) ? true : options.reactive;\n};\n\n// Since we don't actually have a \"nextObject\" interface, there's really no\n// reason to have a \"rewind\" interface. All it did was make multiple calls\n// to fetch/map/forEach return nothing the second time.\n// XXX COMPAT WITH 0.8.1\nLocalCollection.Cursor.prototype.rewind = function () {\n};\n\nLocalCollection.prototype.findOne = function (selector, options) {\n if (arguments.length === 0)\n selector = {};\n\n // NOTE: by setting limit 1 here, we end up using very inefficient\n // code that recomputes the whole query on each update. The upside is\n // that when you reactively depend on a findOne you only get\n // invalidated when the found object changes, not any object in the\n // collection. Most findOne will be by id, which has a fast path, so\n // this might not be a big deal. In most cases, invalidation causes\n // the called to re-query anyway, so this should be a net performance\n // improvement.\n options = options || {};\n options.limit = 1;\n\n return this.find(selector, options).fetch()[0];\n};\n\n/**\n * @callback IterationCallback\n * @param {Object} doc\n * @param {Number} index\n */\n/**\n * @summary Call `callback` once for each matching document, sequentially and synchronously.\n * @locus Anywhere\n * @method forEach\n * @instance\n * @memberOf Mongo.Cursor\n * @param {IterationCallback} callback Function to call. It will be called with three arguments: the document, a 0-based index, and cursor itself.\n * @param {Any} [thisArg] An object which will be the value of `this` inside `callback`.\n */\nLocalCollection.Cursor.prototype.forEach = function (callback, thisArg) {\n var self = this;\n\n var objects = self._getRawObjects({ordered: true});\n\n if (self.reactive) {\n self._depend({\n addedBefore: true,\n removed: true,\n changed: true,\n movedBefore: true});\n }\n\n _.each(objects, function (elt, i) {\n // This doubles as a clone operation.\n elt = self._projectionFn(elt);\n\n if (self._transform)\n elt = self._transform(elt);\n callback.call(thisArg, elt, i, self);\n });\n};\n\nLocalCollection.Cursor.prototype.getTransform = function () {\n return this._transform;\n};\n\n/**\n * @summary Map callback over all matching documents. Returns an Array.\n * @locus Anywhere\n * @method map\n * @instance\n * @memberOf Mongo.Cursor\n * @param {IterationCallback} callback Function to call. It will be called with three arguments: the document, a 0-based index, and cursor itself.\n * @param {Any} [thisArg] An object which will be the value of `this` inside `callback`.\n */\nLocalCollection.Cursor.prototype.map = function (callback, thisArg) {\n var self = this;\n var res = [];\n self.forEach(function (doc, index) {\n res.push(callback.call(thisArg, doc, index, self));\n });\n return res;\n};\n\n/**\n * @summary Return all matching documents as an Array.\n * @memberOf Mongo.Cursor\n * @method fetch\n * @instance\n * @locus Anywhere\n * @returns {Object[]}\n */\nLocalCollection.Cursor.prototype.fetch = function () {\n var self = this;\n var res = [];\n self.forEach(function (doc) {\n res.push(doc);\n });\n return res;\n};\n\n/**\n * @summary Returns the number of documents that match a query.\n * @memberOf Mongo.Cursor\n * @method count\n * @instance\n * @locus Anywhere\n * @returns {Number}\n */\nLocalCollection.Cursor.prototype.count = function () {\n var self = this;\n\n if (self.reactive)\n self._depend({added: true, removed: true},\n true /* allow the observe to be unordered */);\n\n return self._getRawObjects({ordered: true}).length;\n};\n\nLocalCollection.Cursor.prototype._publishCursor = function (sub) {\n var self = this;\n if (! self.collection.name)\n throw new Error(\"Can't publish a cursor from a collection without a name.\");\n var collection = self.collection.name;\n\n // XXX minimongo should not depend on mongo-livedata!\n return Mongo.Collection._publishCursor(self, sub, collection);\n};\n\nLocalCollection.Cursor.prototype._getCollectionName = function () {\n var self = this;\n return self.collection.name;\n};\n\nLocalCollection._observeChangesCallbacksAreOrdered = function (callbacks) {\n if (callbacks.added && callbacks.addedBefore)\n throw new Error(\"Please specify only one of added() and addedBefore()\");\n return !!(callbacks.addedBefore || callbacks.movedBefore);\n};\n\nLocalCollection._observeCallbacksAreOrdered = function (callbacks) {\n if (callbacks.addedAt && callbacks.added)\n throw new Error(\"Please specify only one of added() and addedAt()\");\n if (callbacks.changedAt && callbacks.changed)\n throw new Error(\"Please specify only one of changed() and changedAt()\");\n if (callbacks.removed && callbacks.removedAt)\n throw new Error(\"Please specify only one of removed() and removedAt()\");\n\n return !!(callbacks.addedAt || callbacks.movedTo || callbacks.changedAt\n || callbacks.removedAt);\n};\n\n// the handle that comes back from observe.\nLocalCollection.ObserveHandle = function () {};\n\n// options to contain:\n// * callbacks for observe():\n// - addedAt (document, atIndex)\n// - added (document)\n// - changedAt (newDocument, oldDocument, atIndex)\n// - changed (newDocument, oldDocument)\n// - removedAt (document, atIndex)\n// - removed (document)\n// - movedTo (document, oldIndex, newIndex)\n//\n// attributes available on returned query handle:\n// * stop(): end updates\n// * collection: the collection this query is querying\n//\n// iff x is a returned query handle, (x instanceof\n// LocalCollection.ObserveHandle) is true\n//\n// initial results delivered through added callback\n// XXX maybe callbacks should take a list of objects, to expose transactions?\n// XXX maybe support field limiting (to limit what you're notified on)\n\n_.extend(LocalCollection.Cursor.prototype, {\n /**\n * @summary Watch a query. Receive callbacks as the result set changes.\n * @locus Anywhere\n * @memberOf Mongo.Cursor\n * @instance\n * @param {Object} callbacks Functions to call to deliver the result set as it changes\n */\n observe: function (options) {\n var self = this;\n return LocalCollection._observeFromObserveChanges(self, options);\n },\n\n /**\n * @summary Watch a query. Receive callbacks as the result set changes. Only the differences between the old and new documents are passed to the callbacks.\n * @locus Anywhere\n * @memberOf Mongo.Cursor\n * @instance\n * @param {Object} callbacks Functions to call to deliver the result set as it changes\n */\n observeChanges: function (options) {\n var self = this;\n\n var ordered = LocalCollection._observeChangesCallbacksAreOrdered(options);\n\n // there are several places that assume you aren't combining skip/limit with\n // unordered observe. eg, update's EJSON.clone, and the \"there are several\"\n // comment in _modifyAndNotify\n // XXX allow skip/limit with unordered observe\n if (!options._allow_unordered && !ordered && (self.skip || self.limit))\n throw new Error(\"must use ordered observe (ie, 'addedBefore' instead of 'added') with skip or limit\");\n\n if (self.fields && (self.fields._id === 0 || self.fields._id === false))\n throw Error(\"You may not observe a cursor with {fields: {_id: 0}}\");\n\n var query = {\n matcher: self.matcher, // not fast pathed\n sorter: ordered && self.sorter,\n distances: (\n self.matcher.hasGeoQuery() && ordered && new LocalCollection._IdMap),\n resultsSnapshot: null,\n ordered: ordered,\n cursor: self,\n projectionFn: self._projectionFn\n };\n var qid;\n\n // Non-reactive queries call added[Before] and then never call anything\n // else.\n if (self.reactive) {\n qid = self.collection.next_qid++;\n self.collection.queries[qid] = query;\n }\n query.results = self._getRawObjects({\n ordered: ordered, distances: query.distances});\n if (self.collection.paused)\n query.resultsSnapshot = (ordered ? [] : new LocalCollection._IdMap);\n\n // wrap callbacks we were passed. callbacks only fire when not paused and\n // are never undefined\n // Filters out blacklisted fields according to cursor's projection.\n // XXX wrong place for this?\n\n // furthermore, callbacks enqueue until the operation we're working on is\n // done.\n var wrapCallback = function (f) {\n if (!f)\n return function () {};\n return function (/*args*/) {\n var context = this;\n var args = arguments;\n\n if (self.collection.paused)\n return;\n\n self.collection._observeQueue.queueTask(function () {\n f.apply(context, args);\n });\n };\n };\n query.added = wrapCallback(options.added);\n query.changed = wrapCallback(options.changed);\n query.removed = wrapCallback(options.removed);\n if (ordered) {\n query.addedBefore = wrapCallback(options.addedBefore);\n query.movedBefore = wrapCallback(options.movedBefore);\n }\n\n if (!options._suppress_initial && !self.collection.paused) {\n // XXX unify ordered and unordered interface\n var each = ordered\n ? _.bind(_.each, null, query.results)\n : _.bind(query.results.forEach, query.results);\n each(function (doc) {\n var fields = EJSON.clone(doc);\n\n delete fields._id;\n if (ordered)\n query.addedBefore(doc._id, self._projectionFn(fields), null);\n query.added(doc._id, self._projectionFn(fields));\n });\n }\n\n var handle = new LocalCollection.ObserveHandle;\n _.extend(handle, {\n collection: self.collection,\n stop: function () {\n if (self.reactive)\n delete self.collection.queries[qid];\n }\n });\n\n if (self.reactive && Tracker.active) {\n // XXX in many cases, the same observe will be recreated when\n // the current autorun is rerun. we could save work by\n // letting it linger across rerun and potentially get\n // repurposed if the same observe is performed, using logic\n // similar to that of Meteor.subscribe.\n Tracker.onInvalidate(function () {\n handle.stop();\n });\n }\n // run the observe callbacks resulting from the initial contents\n // before we leave the observe.\n self.collection._observeQueue.drain();\n\n return handle;\n }\n});\n\n// Returns a collection of matching objects, but doesn't deep copy them.\n//\n// If ordered is set, returns a sorted array, respecting sorter, skip, and limit\n// properties of the query. if sorter is falsey, no sort -- you get the natural\n// order.\n//\n// If ordered is not set, returns an object mapping from ID to doc (sorter, skip\n// and limit should not be set).\n//\n// If ordered is set and this cursor is a $near geoquery, then this function\n// will use an _IdMap to track each distance from the $near argument point in\n// order to use it as a sort key. If an _IdMap is passed in the 'distances'\n// argument, this function will clear it and use it for this purpose (otherwise\n// it will just create its own _IdMap). The observeChanges implementation uses\n// this to remember the distances after this function returns.\nLocalCollection.Cursor.prototype._getRawObjects = function (options) {\n var self = this;\n options = options || {};\n\n // XXX use OrderedDict instead of array, and make IdMap and OrderedDict\n // compatible\n var results = options.ordered ? [] : new LocalCollection._IdMap;\n\n // fast path for single ID value\n if (self._selectorId !== undefined) {\n // If you have non-zero skip and ask for a single id, you get\n // nothing. This is so it matches the behavior of the '{_id: foo}'\n // path.\n if (self.skip)\n return results;\n\n var selectedDoc = self.collection._docs.get(self._selectorId);\n if (selectedDoc) {\n if (options.ordered)\n results.push(selectedDoc);\n else\n results.set(self._selectorId, selectedDoc);\n }\n return results;\n }\n\n // slow path for arbitrary selector, sort, skip, limit\n\n // in the observeChanges case, distances is actually part of the \"query\" (ie,\n // live results set) object. in other cases, distances is only used inside\n // this function.\n var distances;\n if (self.matcher.hasGeoQuery() && options.ordered) {\n if (options.distances) {\n distances = options.distances;\n distances.clear();\n } else {\n distances = new LocalCollection._IdMap();\n }\n }\n\n self.collection._docs.forEach(function (doc, id) {\n var matchResult = self.matcher.documentMatches(doc);\n if (matchResult.result) {\n if (options.ordered) {\n results.push(doc);\n if (distances && matchResult.distance !== undefined)\n distances.set(id, matchResult.distance);\n } else {\n results.set(id, doc);\n }\n }\n // Fast path for limited unsorted queries.\n // XXX 'length' check here seems wrong for ordered\n if (self.limit && !self.skip && !self.sorter &&\n results.length === self.limit)\n return false; // break\n return true; // continue\n });\n\n if (!options.ordered)\n return results;\n\n if (self.sorter) {\n var comparator = self.sorter.getComparator({distances: distances});\n results.sort(comparator);\n }\n\n var idx_start = self.skip || 0;\n var idx_end = self.limit ? (self.limit + idx_start) : results.length;\n return results.slice(idx_start, idx_end);\n};\n\n// XXX Maybe we need a version of observe that just calls a callback if\n// anything changed.\nLocalCollection.Cursor.prototype._depend = function (changers, _allow_unordered) {\n var self = this;\n\n if (Tracker.active) {\n var v = new Tracker.Dependency;\n v.depend();\n var notifyChange = _.bind(v.changed, v);\n\n var options = {\n _suppress_initial: true,\n _allow_unordered: _allow_unordered\n };\n _.each(['added', 'changed', 'removed', 'addedBefore', 'movedBefore'],\n function (fnName) {\n if (changers[fnName])\n options[fnName] = notifyChange;\n });\n\n // observeChanges will stop() when this computation is invalidated\n self.observeChanges(options);\n }\n};\n\n// XXX enforce rule that field names can't start with '$' or contain '.'\n// (real mongodb does in fact enforce this)\n// XXX possibly enforce that 'undefined' does not appear (we assume\n// this in our handling of null and $exists)\nLocalCollection.prototype.insert = function (doc, callback) {\n var self = this;\n doc = EJSON.clone(doc);\n\n if (!_.has(doc, '_id')) {\n // if you really want to use ObjectIDs, set this global.\n // Mongo.Collection specifies its own ids and does not use this code.\n doc._id = LocalCollection._useOID ? new LocalCollection._ObjectID()\n : Random.id();\n }\n var id = doc._id;\n\n if (self._docs.has(id))\n throw MinimongoError(\"Duplicate _id '\" + id + \"'\");\n\n self._saveOriginal(id, undefined);\n self._docs.set(id, doc);\n\n var queriesToRecompute = [];\n // trigger live queries that match\n for (var qid in self.queries) {\n var query = self.queries[qid];\n var matchResult = query.matcher.documentMatches(doc);\n if (matchResult.result) {\n if (query.distances && matchResult.distance !== undefined)\n query.distances.set(id, matchResult.distance);\n if (query.cursor.skip || query.cursor.limit)\n queriesToRecompute.push(qid);\n else\n LocalCollection._insertInResults(query, doc);\n }\n }\n\n _.each(queriesToRecompute, function (qid) {\n if (self.queries[qid])\n self._recomputeResults(self.queries[qid]);\n });\n self._observeQueue.drain();\n\n // Defer because the caller likely doesn't expect the callback to be run\n // immediately.\n if (callback)\n Meteor.defer(function () {\n callback(null, id);\n });\n return id;\n};\n\n// Iterates over a subset of documents that could match selector; calls\n// f(doc, id) on each of them. Specifically, if selector specifies\n// specific _id's, it only looks at those. doc is *not* cloned: it is the\n// same object that is in _docs.\nLocalCollection.prototype._eachPossiblyMatchingDoc = function (selector, f) {\n var self = this;\n var specificIds = LocalCollection._idsMatchedBySelector(selector);\n if (specificIds) {\n for (var i = 0; i < specificIds.length; ++i) {\n var id = specificIds[i];\n var doc = self._docs.get(id);\n if (doc) {\n var breakIfFalse = f(doc, id);\n if (breakIfFalse === false)\n break;\n }\n }\n } else {\n self._docs.forEach(f);\n }\n};\n\nLocalCollection.prototype.remove = function (selector, callback) {\n var self = this;\n\n // Easy special case: if we're not calling observeChanges callbacks and we're\n // not saving originals and we got asked to remove everything, then just empty\n // everything directly.\n if (self.paused && !self._savedOriginals && EJSON.equals(selector, {})) {\n var result = self._docs.size();\n self._docs.clear();\n _.each(self.queries, function (query) {\n if (query.ordered) {\n query.results = [];\n } else {\n query.results.clear();\n }\n });\n if (callback) {\n Meteor.defer(function () {\n callback(null, result);\n });\n }\n return result;\n }\n\n var matcher = new Minimongo.Matcher(selector);\n var remove = [];\n self._eachPossiblyMatchingDoc(selector, function (doc, id) {\n if (matcher.documentMatches(doc).result)\n remove.push(id);\n });\n\n var queriesToRecompute = [];\n var queryRemove = [];\n for (var i = 0; i < remove.length; i++) {\n var removeId = remove[i];\n var removeDoc = self._docs.get(removeId);\n _.each(self.queries, function (query, qid) {\n if (query.matcher.documentMatches(removeDoc).result) {\n if (query.cursor.skip || query.cursor.limit)\n queriesToRecompute.push(qid);\n else\n queryRemove.push({qid: qid, doc: removeDoc});\n }\n });\n self._saveOriginal(removeId, removeDoc);\n self._docs.remove(removeId);\n }\n\n // run live query callbacks _after_ we've removed the documents.\n _.each(queryRemove, function (remove) {\n var query = self.queries[remove.qid];\n if (query) {\n query.distances && query.distances.remove(remove.doc._id);\n LocalCollection._removeFromResults(query, remove.doc);\n }\n });\n _.each(queriesToRecompute, function (qid) {\n var query = self.queries[qid];\n if (query)\n self._recomputeResults(query);\n });\n self._observeQueue.drain();\n result = remove.length;\n if (callback)\n Meteor.defer(function () {\n callback(null, result);\n });\n return result;\n};\n\n// XXX atomicity: if multi is true, and one modification fails, do\n// we rollback the whole operation, or what?\nLocalCollection.prototype.update = function (selector, mod, options, callback) {\n var self = this;\n if (! callback && options instanceof Function) {\n callback = options;\n options = null;\n }\n if (!options) options = {};\n\n var matcher = new Minimongo.Matcher(selector);\n\n // Save the original results of any query that we might need to\n // _recomputeResults on, because _modifyAndNotify will mutate the objects in\n // it. (We don't need to save the original results of paused queries because\n // they already have a resultsSnapshot and we won't be diffing in\n // _recomputeResults.)\n var qidToOriginalResults = {};\n _.each(self.queries, function (query, qid) {\n // XXX for now, skip/limit implies ordered observe, so query.results is\n // always an array\n if ((query.cursor.skip || query.cursor.limit) && ! self.paused)\n qidToOriginalResults[qid] = EJSON.clone(query.results);\n });\n var recomputeQids = {};\n\n var updateCount = 0;\n\n self._eachPossiblyMatchingDoc(selector, function (doc, id) {\n var queryResult = matcher.documentMatches(doc);\n if (queryResult.result) {\n // XXX Should we save the original even if mod ends up being a no-op?\n self._saveOriginal(id, doc);\n self._modifyAndNotify(doc, mod, recomputeQids, queryResult.arrayIndices);\n ++updateCount;\n if (!options.multi)\n return false; // break\n }\n return true;\n });\n\n _.each(recomputeQids, function (dummy, qid) {\n var query = self.queries[qid];\n if (query)\n self._recomputeResults(query, qidToOriginalResults[qid]);\n });\n self._observeQueue.drain();\n\n // If we are doing an upsert, and we didn't modify any documents yet, then\n // it's time to do an insert. Figure out what document we are inserting, and\n // generate an id for it.\n var insertedId;\n if (updateCount === 0 && options.upsert) {\n var newDoc = LocalCollection._removeDollarOperators(selector);\n LocalCollection._modify(newDoc, mod, {isInsert: true});\n if (! newDoc._id && options.insertedId)\n newDoc._id = options.insertedId;\n insertedId = self.insert(newDoc);\n updateCount = 1;\n }\n\n // Return the number of affected documents, or in the upsert case, an object\n // containing the number of affected docs and the id of the doc that was\n // inserted, if any.\n var result;\n if (options._returnObject) {\n result = {\n numberAffected: updateCount\n };\n if (insertedId !== undefined)\n result.insertedId = insertedId;\n } else {\n result = updateCount;\n }\n\n if (callback)\n Meteor.defer(function () {\n callback(null, result);\n });\n return result;\n};\n\n// A convenience wrapper on update. LocalCollection.upsert(sel, mod) is\n// equivalent to LocalCollection.update(sel, mod, { upsert: true, _returnObject:\n// true }).\nLocalCollection.prototype.upsert = function (selector, mod, options, callback) {\n var self = this;\n if (! callback && typeof options === \"function\") {\n callback = options;\n options = {};\n }\n return self.update(selector, mod, _.extend({}, options, {\n upsert: true,\n _returnObject: true\n }), callback);\n};\n\nLocalCollection.prototype._modifyAndNotify = function (\n doc, mod, recomputeQids, arrayIndices) {\n var self = this;\n\n var matched_before = {};\n for (var qid in self.queries) {\n var query = self.queries[qid];\n if (query.ordered) {\n matched_before[qid] = query.matcher.documentMatches(doc).result;\n } else {\n // Because we don't support skip or limit (yet) in unordered queries, we\n // can just do a direct lookup.\n matched_before[qid] = query.results.has(doc._id);\n }\n }\n\n var old_doc = EJSON.clone(doc);\n\n LocalCollection._modify(doc, mod, {arrayIndices: arrayIndices});\n\n for (qid in self.queries) {\n query = self.queries[qid];\n var before = matched_before[qid];\n var afterMatch = query.matcher.documentMatches(doc);\n var after = afterMatch.result;\n if (after && query.distances && afterMatch.distance !== undefined)\n query.distances.set(doc._id, afterMatch.distance);\n\n if (query.cursor.skip || query.cursor.limit) {\n // We need to recompute any query where the doc may have been in the\n // cursor's window either before or after the update. (Note that if skip\n // or limit is set, \"before\" and \"after\" being true do not necessarily\n // mean that the document is in the cursor's output after skip/limit is\n // applied... but if they are false, then the document definitely is NOT\n // in the output. So it's safe to skip recompute if neither before or\n // after are true.)\n if (before || after)\n recomputeQids[qid] = true;\n } else if (before && !after) {\n LocalCollection._removeFromResults(query, doc);\n } else if (!before && after) {\n LocalCollection._insertInResults(query, doc);\n } else if (before && after) {\n LocalCollection._updateInResults(query, doc, old_doc);\n }\n }\n};\n\n// XXX the sorted-query logic below is laughably inefficient. we'll\n// need to come up with a better datastructure for this.\n//\n// XXX the logic for observing with a skip or a limit is even more\n// laughably inefficient. we recompute the whole results every time!\n\nLocalCollection._insertInResults = function (query, doc) {\n var fields = EJSON.clone(doc);\n delete fields._id;\n if (query.ordered) {\n if (!query.sorter) {\n query.addedBefore(doc._id, query.projectionFn(fields), null);\n query.results.push(doc);\n } else {\n var i = LocalCollection._insertInSortedList(\n query.sorter.getComparator({distances: query.distances}),\n query.results, doc);\n var next = query.results[i+1];\n if (next)\n next = next._id;\n else\n next = null;\n query.addedBefore(doc._id, query.projectionFn(fields), next);\n }\n query.added(doc._id, query.projectionFn(fields));\n } else {\n query.added(doc._id, query.projectionFn(fields));\n query.results.set(doc._id, doc);\n }\n};\n\nLocalCollection._removeFromResults = function (query, doc) {\n if (query.ordered) {\n var i = LocalCollection._findInOrderedResults(query, doc);\n query.removed(doc._id);\n query.results.splice(i, 1);\n } else {\n var id = doc._id; // in case callback mutates doc\n query.removed(doc._id);\n query.results.remove(id);\n }\n};\n\nLocalCollection._updateInResults = function (query, doc, old_doc) {\n if (!EJSON.equals(doc._id, old_doc._id))\n throw new Error(\"Can't change a doc's _id while updating\");\n var projectionFn = query.projectionFn;\n var changedFields = LocalCollection._makeChangedFields(\n projectionFn(doc), projectionFn(old_doc));\n\n if (!query.ordered) {\n if (!_.isEmpty(changedFields)) {\n query.changed(doc._id, changedFields);\n query.results.set(doc._id, doc);\n }\n return;\n }\n\n var orig_idx = LocalCollection._findInOrderedResults(query, doc);\n\n if (!_.isEmpty(changedFields))\n query.changed(doc._id, changedFields);\n if (!query.sorter)\n return;\n\n // just take it out and put it back in again, and see if the index\n // changes\n query.results.splice(orig_idx, 1);\n var new_idx = LocalCollection._insertInSortedList(\n query.sorter.getComparator({distances: query.distances}),\n query.results, doc);\n if (orig_idx !== new_idx) {\n var next = query.results[new_idx+1];\n if (next)\n next = next._id;\n else\n next = null;\n query.movedBefore && query.movedBefore(doc._id, next);\n }\n};\n\n// Recomputes the results of a query and runs observe callbacks for the\n// difference between the previous results and the current results (unless\n// paused). Used for skip/limit queries.\n//\n// When this is used by insert or remove, it can just use query.results for the\n// old results (and there's no need to pass in oldResults), because these\n// operations don't mutate the documents in the collection. Update needs to pass\n// in an oldResults which was deep-copied before the modifier was applied.\n//\n// oldResults is guaranteed to be ignored if the query is not paused.\nLocalCollection.prototype._recomputeResults = function (query, oldResults) {\n var self = this;\n if (! self.paused && ! oldResults)\n oldResults = query.results;\n if (query.distances)\n query.distances.clear();\n query.results = query.cursor._getRawObjects({\n ordered: query.ordered, distances: query.distances});\n\n if (! self.paused) {\n LocalCollection._diffQueryChanges(\n query.ordered, oldResults, query.results, query,\n { projectionFn: query.projectionFn });\n }\n};\n\n\nLocalCollection._findInOrderedResults = function (query, doc) {\n if (!query.ordered)\n throw new Error(\"Can't call _findInOrderedResults on unordered query\");\n for (var i = 0; i < query.results.length; i++)\n if (query.results[i] === doc)\n return i;\n throw Error(\"object missing from query\");\n};\n\n// This binary search puts a value between any equal values, and the first\n// lesser value.\nLocalCollection._binarySearch = function (cmp, array, value) {\n var first = 0, rangeLength = array.length;\n\n while (rangeLength > 0) {\n var halfRange = Math.floor(rangeLength/2);\n if (cmp(value, array[first + halfRange]) >= 0) {\n first += halfRange + 1;\n rangeLength -= halfRange + 1;\n } else {\n rangeLength = halfRange;\n }\n }\n return first;\n};\n\nLocalCollection._insertInSortedList = function (cmp, array, value) {\n if (array.length === 0) {\n array.push(value);\n return 0;\n }\n\n var idx = LocalCollection._binarySearch(cmp, array, value);\n array.splice(idx, 0, value);\n return idx;\n};\n\n// To track what documents are affected by a piece of code, call saveOriginals()\n// before it and retrieveOriginals() after it. retrieveOriginals returns an\n// object whose keys are the ids of the documents that were affected since the\n// call to saveOriginals(), and the values are equal to the document's contents\n// at the time of saveOriginals. (In the case of an inserted document, undefined\n// is the value.) You must alternate between calls to saveOriginals() and\n// retrieveOriginals().\nLocalCollection.prototype.saveOriginals = function () {\n var self = this;\n if (self._savedOriginals)\n throw new Error(\"Called saveOriginals twice without retrieveOriginals\");\n self._savedOriginals = new LocalCollection._IdMap;\n};\nLocalCollection.prototype.retrieveOriginals = function () {\n var self = this;\n if (!self._savedOriginals)\n throw new Error(\"Called retrieveOriginals without saveOriginals\");\n\n var originals = self._savedOriginals;\n self._savedOriginals = null;\n return originals;\n};\n\nLocalCollection.prototype._saveOriginal = function (id, doc) {\n var self = this;\n // Are we even trying to save originals?\n if (!self._savedOriginals)\n return;\n // Have we previously mutated the original (and so 'doc' is not actually\n // original)? (Note the 'has' check rather than truth: we store undefined\n // here for inserted docs!)\n if (self._savedOriginals.has(id))\n return;\n self._savedOriginals.set(id, EJSON.clone(doc));\n};\n\n// Pause the observers. No callbacks from observers will fire until\n// 'resumeObservers' is called.\nLocalCollection.prototype.pauseObservers = function () {\n // No-op if already paused.\n if (this.paused)\n return;\n\n // Set the 'paused' flag such that new observer messages don't fire.\n this.paused = true;\n\n // Take a snapshot of the query results for each query.\n for (var qid in this.queries) {\n var query = this.queries[qid];\n\n query.resultsSnapshot = EJSON.clone(query.results);\n }\n};\n\n// Resume the observers. Observers immediately receive change\n// notifications to bring them to the current state of the\n// database. Note that this is not just replaying all the changes that\n// happened during the pause, it is a smarter 'coalesced' diff.\nLocalCollection.prototype.resumeObservers = function () {\n var self = this;\n // No-op if not paused.\n if (!this.paused)\n return;\n\n // Unset the 'paused' flag. Make sure to do this first, otherwise\n // observer methods won't actually fire when we trigger them.\n this.paused = false;\n\n for (var qid in this.queries) {\n var query = self.queries[qid];\n // Diff the current results against the snapshot and send to observers.\n // pass the query object for its observer callbacks.\n LocalCollection._diffQueryChanges(\n query.ordered, query.resultsSnapshot, query.results, query,\n { projectionFn: query.projectionFn });\n query.resultsSnapshot = null;\n }\n self._observeQueue.drain();\n};\n\n\n// NB: used by livedata\nLocalCollection._idStringify = function (id) {\n if (id instanceof LocalCollection._ObjectID) {\n return id.valueOf();\n } else if (typeof id === 'string') {\n if (id === \"\") {\n return id;\n } else if (id.substr(0, 1) === \"-\" || // escape previously dashed strings\n id.substr(0, 1) === \"~\" || // escape escaped numbers, true, false\n LocalCollection._looksLikeObjectID(id) || // escape object-id-form strings\n id.substr(0, 1) === '{') { // escape object-form strings, for maybe implementing later\n return \"-\" + id;\n } else {\n return id; // other strings go through unchanged.\n }\n } else if (id === undefined) {\n return '-';\n } else if (typeof id === 'object' && id !== null) {\n throw new Error(\"Meteor does not currently support objects other than ObjectID as ids\");\n } else { // Numbers, true, false, null\n return \"~\" + JSON.stringify(id);\n }\n};\n\n\n// NB: used by livedata\nLocalCollection._idParse = function (id) {\n if (id === \"\") {\n return id;\n } else if (id === '-') {\n return undefined;\n } else if (id.substr(0, 1) === '-') {\n return id.substr(1);\n } else if (id.substr(0, 1) === '~') {\n return JSON.parse(id.substr(1));\n } else if (LocalCollection._looksLikeObjectID(id)) {\n return new LocalCollection._ObjectID(id);\n } else {\n return id;\n }\n};\n\nLocalCollection._makeChangedFields = function (newDoc, oldDoc) {\n var fields = {};\n LocalCollection._diffObjects(oldDoc, newDoc, {\n leftOnly: function (key, value) {\n fields[key] = undefined;\n },\n rightOnly: function (key, value) {\n fields[key] = value;\n },\n both: function (key, leftValue, rightValue) {\n if (!EJSON.equals(leftValue, rightValue))\n fields[key] = rightValue;\n }\n });\n return fields;\n};\n","// Wrap a transform function to return objects that have the _id field\n// of the untransformed document. This ensures that subsystems such as\n// the observe-sequence package that call `observe` can keep track of\n// the documents identities.\n//\n// - Require that it returns objects\n// - If the return value has an _id field, verify that it matches the\n// original _id field\n// - If the return value doesn't have an _id field, add it back.\nLocalCollection.wrapTransform = function (transform) {\n if (! transform)\n return null;\n\n // No need to doubly-wrap transforms.\n if (transform.__wrappedTransform__)\n return transform;\n\n var wrapped = function (doc) {\n if (!_.has(doc, '_id')) {\n // XXX do we ever have a transform on the oplog's collection? because that\n // collection has no _id.\n throw new Error(\"can only transform documents with _id\");\n }\n\n var id = doc._id;\n // XXX consider making tracker a weak dependency and checking Package.tracker here\n var transformed = Tracker.nonreactive(function () {\n return transform(doc);\n });\n\n if (!isPlainObject(transformed)) {\n throw new Error(\"transform must return object\");\n }\n\n if (_.has(transformed, '_id')) {\n if (!EJSON.equals(transformed._id, id)) {\n throw new Error(\"transformed document can't have different _id\");\n }\n } else {\n transformed._id = id;\n }\n return transformed;\n };\n wrapped.__wrappedTransform__ = true;\n return wrapped;\n};\n","// Like _.isArray, but doesn't regard polyfilled Uint8Arrays on old browsers as\n// arrays.\n// XXX maybe this should be EJSON.isArray\nisArray = function (x) {\n return _.isArray(x) && !EJSON.isBinary(x);\n};\n\n// XXX maybe this should be EJSON.isObject, though EJSON doesn't know about\n// RegExp\n// XXX note that _type(undefined) === 3!!!!\nisPlainObject = LocalCollection._isPlainObject = function (x) {\n return x && LocalCollection._f._type(x) === 3;\n};\n\nisIndexable = function (x) {\n return isArray(x) || isPlainObject(x);\n};\n\n// Returns true if this is an object with at least one key and all keys begin\n// with $. Unless inconsistentOK is set, throws if some keys begin with $ and\n// others don't.\nisOperatorObject = function (valueSelector, inconsistentOK) {\n if (!isPlainObject(valueSelector))\n return false;\n\n var theseAreOperators = undefined;\n _.each(valueSelector, function (value, selKey) {\n var thisIsOperator = selKey.substr(0, 1) === '$';\n if (theseAreOperators === undefined) {\n theseAreOperators = thisIsOperator;\n } else if (theseAreOperators !== thisIsOperator) {\n if (!inconsistentOK)\n throw new Error(\"Inconsistent operator: \" +\n JSON.stringify(valueSelector));\n theseAreOperators = false;\n }\n });\n return !!theseAreOperators; // {} has no operators\n};\n\n\n// string can be converted to integer\nisNumericKey = function (s) {\n return /^[0-9]+$/.test(s);\n};\n","// The minimongo selector compiler!\n\n// Terminology:\n// - a \"selector\" is the EJSON object representing a selector\n// - a \"matcher\" is its compiled form (whether a full Minimongo.Matcher\n// object or one of the component lambdas that matches parts of it)\n// - a \"result object\" is an object with a \"result\" field and maybe\n// distance and arrayIndices.\n// - a \"branched value\" is an object with a \"value\" field and maybe\n// \"dontIterate\" and \"arrayIndices\".\n// - a \"document\" is a top-level object that can be stored in a collection.\n// - a \"lookup function\" is a function that takes in a document and returns\n// an array of \"branched values\".\n// - a \"branched matcher\" maps from an array of branched values to a result\n// object.\n// - an \"element matcher\" maps from a single value to a bool.\n\n// Main entry point.\n// var matcher = new Minimongo.Matcher({a: {$gt: 5}});\n// if (matcher.documentMatches({a: 7})) ...\nMinimongo.Matcher = function (selector) {\n var self = this;\n // A set (object mapping string -> *) of all of the document paths looked\n // at by the selector. Also includes the empty string if it may look at any\n // path (eg, $where).\n self._paths = {};\n // Set to true if compilation finds a $near.\n self._hasGeoQuery = false;\n // Set to true if compilation finds a $where.\n self._hasWhere = false;\n // Set to false if compilation finds anything other than a simple equality or\n // one or more of '$gt', '$gte', '$lt', '$lte', '$ne', '$in', '$nin' used with\n // scalars as operands.\n self._isSimple = true;\n // Set to a dummy document which always matches this Matcher. Or set to null\n // if such document is too hard to find.\n self._matchingDocument = undefined;\n // A clone of the original selector. It may just be a function if the user\n // passed in a function; otherwise is definitely an object (eg, IDs are\n // translated into {_id: ID} first. Used by canBecomeTrueByModifier and\n // Sorter._useWithMatcher.\n self._selector = null;\n self._docMatcher = self._compileSelector(selector);\n};\n\n_.extend(Minimongo.Matcher.prototype, {\n documentMatches: function (doc) {\n if (!doc || typeof doc !== \"object\") {\n throw Error(\"documentMatches needs a document\");\n }\n return this._docMatcher(doc);\n },\n hasGeoQuery: function () {\n return this._hasGeoQuery;\n },\n hasWhere: function () {\n return this._hasWhere;\n },\n isSimple: function () {\n return this._isSimple;\n },\n\n // Given a selector, return a function that takes one argument, a\n // document. It returns a result object.\n _compileSelector: function (selector) {\n var self = this;\n // you can pass a literal function instead of a selector\n if (selector instanceof Function) {\n self._isSimple = false;\n self._selector = selector;\n self._recordPathUsed('');\n return function (doc) {\n return {result: !!selector.call(doc)};\n };\n }\n\n // shorthand -- scalars match _id\n if (LocalCollection._selectorIsId(selector)) {\n self._selector = {_id: selector};\n self._recordPathUsed('_id');\n return function (doc) {\n return {result: EJSON.equals(doc._id, selector)};\n };\n }\n\n // protect against dangerous selectors. falsey and {_id: falsey} are both\n // likely programmer error, and not what you want, particularly for\n // destructive operations.\n if (!selector || (('_id' in selector) && !selector._id)) {\n self._isSimple = false;\n return nothingMatcher;\n }\n\n // Top level can't be an array or true or binary.\n if (typeof(selector) === 'boolean' || isArray(selector) ||\n EJSON.isBinary(selector))\n throw new Error(\"Invalid selector: \" + selector);\n\n self._selector = EJSON.clone(selector);\n return compileDocumentSelector(selector, self, {isRoot: true});\n },\n _recordPathUsed: function (path) {\n this._paths[path] = true;\n },\n // Returns a list of key paths the given selector is looking for. It includes\n // the empty string if there is a $where.\n _getPaths: function () {\n return _.keys(this._paths);\n }\n});\n\n\n// Takes in a selector that could match a full document (eg, the original\n// selector). Returns a function mapping document->result object.\n//\n// matcher is the Matcher object we are compiling.\n//\n// If this is the root document selector (ie, not wrapped in $and or the like),\n// then isRoot is true. (This is used by $near.)\nvar compileDocumentSelector = function (docSelector, matcher, options) {\n options = options || {};\n var docMatchers = [];\n _.each(docSelector, function (subSelector, key) {\n if (key.substr(0, 1) === '$') {\n // Outer operators are either logical operators (they recurse back into\n // this function), or $where.\n if (!_.has(LOGICAL_OPERATORS, key))\n throw new Error(\"Unrecognized logical operator: \" + key);\n matcher._isSimple = false;\n docMatchers.push(LOGICAL_OPERATORS[key](subSelector, matcher,\n options.inElemMatch));\n } else {\n // Record this path, but only if we aren't in an elemMatcher, since in an\n // elemMatch this is a path inside an object in an array, not in the doc\n // root.\n if (!options.inElemMatch)\n matcher._recordPathUsed(key);\n var lookUpByIndex = makeLookupFunction(key);\n var valueMatcher =\n compileValueSelector(subSelector, matcher, options.isRoot);\n docMatchers.push(function (doc) {\n var branchValues = lookUpByIndex(doc);\n return valueMatcher(branchValues);\n });\n }\n });\n\n return andDocumentMatchers(docMatchers);\n};\n\n// Takes in a selector that could match a key-indexed value in a document; eg,\n// {$gt: 5, $lt: 9}, or a regular expression, or any non-expression object (to\n// indicate equality). Returns a branched matcher: a function mapping\n// [branched value]->result object.\nvar compileValueSelector = function (valueSelector, matcher, isRoot) {\n if (valueSelector instanceof RegExp) {\n matcher._isSimple = false;\n return convertElementMatcherToBranchedMatcher(\n regexpElementMatcher(valueSelector));\n } else if (isOperatorObject(valueSelector)) {\n return operatorBranchedMatcher(valueSelector, matcher, isRoot);\n } else {\n return convertElementMatcherToBranchedMatcher(\n equalityElementMatcher(valueSelector));\n }\n};\n\n// Given an element matcher (which evaluates a single value), returns a branched\n// value (which evaluates the element matcher on all the branches and returns a\n// more structured return value possibly including arrayIndices).\nvar convertElementMatcherToBranchedMatcher = function (\n elementMatcher, options) {\n options = options || {};\n return function (branches) {\n var expanded = branches;\n if (!options.dontExpandLeafArrays) {\n expanded = expandArraysInBranches(\n branches, options.dontIncludeLeafArrays);\n }\n var ret = {};\n ret.result = _.any(expanded, function (element) {\n var matched = elementMatcher(element.value);\n\n // Special case for $elemMatch: it means \"true, and use this as an array\n // index if I didn't already have one\".\n if (typeof matched === 'number') {\n // XXX This code dates from when we only stored a single array index\n // (for the outermost array). Should we be also including deeper array\n // indices from the $elemMatch match?\n if (!element.arrayIndices)\n element.arrayIndices = [matched];\n matched = true;\n }\n\n // If some element matched, and it's tagged with array indices, include\n // those indices in our result object.\n if (matched && element.arrayIndices)\n ret.arrayIndices = element.arrayIndices;\n\n return matched;\n });\n return ret;\n };\n};\n\n// Takes a RegExp object and returns an element matcher.\nregexpElementMatcher = function (regexp) {\n return function (value) {\n if (value instanceof RegExp) {\n // Comparing two regexps means seeing if the regexps are identical\n // (really!). Underscore knows how.\n return _.isEqual(value, regexp);\n }\n // Regexps only work against strings.\n if (typeof value !== 'string')\n return false;\n\n // Reset regexp's state to avoid inconsistent matching for objects with the\n // same value on consecutive calls of regexp.test. This happens only if the\n // regexp has the 'g' flag. Also note that ES6 introduces a new flag 'y' for\n // which we should *not* change the lastIndex but MongoDB doesn't support\n // either of these flags.\n regexp.lastIndex = 0;\n\n return regexp.test(value);\n };\n};\n\n// Takes something that is not an operator object and returns an element matcher\n// for equality with that thing.\nequalityElementMatcher = function (elementSelector) {\n if (isOperatorObject(elementSelector))\n throw Error(\"Can't create equalityValueSelector for operator object\");\n\n // Special-case: null and undefined are equal (if you got undefined in there\n // somewhere, or if you got it due to some branch being non-existent in the\n // weird special case), even though they aren't with EJSON.equals.\n if (elementSelector == null) { // undefined or null\n return function (value) {\n return value == null; // undefined or null\n };\n }\n\n return function (value) {\n return LocalCollection._f._equal(elementSelector, value);\n };\n};\n\n// Takes an operator object (an object with $ keys) and returns a branched\n// matcher for it.\nvar operatorBranchedMatcher = function (valueSelector, matcher, isRoot) {\n // Each valueSelector works separately on the various branches. So one\n // operator can match one branch and another can match another branch. This\n // is OK.\n\n var operatorMatchers = [];\n _.each(valueSelector, function (operand, operator) {\n // XXX we should actually implement $eq, which is new in 2.6\n var simpleRange = _.contains(['$lt', '$lte', '$gt', '$gte'], operator) &&\n _.isNumber(operand);\n var simpleInequality = operator === '$ne' && !_.isObject(operand);\n var simpleInclusion = _.contains(['$in', '$nin'], operator) &&\n _.isArray(operand) && !_.any(operand, _.isObject);\n\n if (! (operator === '$eq' || simpleRange ||\n simpleInclusion || simpleInequality)) {\n matcher._isSimple = false;\n }\n\n if (_.has(VALUE_OPERATORS, operator)) {\n operatorMatchers.push(\n VALUE_OPERATORS[operator](operand, valueSelector, matcher, isRoot));\n } else if (_.has(ELEMENT_OPERATORS, operator)) {\n var options = ELEMENT_OPERATORS[operator];\n operatorMatchers.push(\n convertElementMatcherToBranchedMatcher(\n options.compileElementSelector(\n operand, valueSelector, matcher),\n options));\n } else {\n throw new Error(\"Unrecognized operator: \" + operator);\n }\n });\n\n return andBranchedMatchers(operatorMatchers);\n};\n\nvar compileArrayOfDocumentSelectors = function (\n selectors, matcher, inElemMatch) {\n if (!isArray(selectors) || _.isEmpty(selectors))\n throw Error(\"$and/$or/$nor must be nonempty array\");\n return _.map(selectors, function (subSelector) {\n if (!isPlainObject(subSelector))\n throw Error(\"$or/$and/$nor entries need to be full objects\");\n return compileDocumentSelector(\n subSelector, matcher, {inElemMatch: inElemMatch});\n });\n};\n\n// Operators that appear at the top level of a document selector.\nvar LOGICAL_OPERATORS = {\n $and: function (subSelector, matcher, inElemMatch) {\n var matchers = compileArrayOfDocumentSelectors(\n subSelector, matcher, inElemMatch);\n return andDocumentMatchers(matchers);\n },\n\n $or: function (subSelector, matcher, inElemMatch) {\n var matchers = compileArrayOfDocumentSelectors(\n subSelector, matcher, inElemMatch);\n\n // Special case: if there is only one matcher, use it directly, *preserving*\n // any arrayIndices it returns.\n if (matchers.length === 1)\n return matchers[0];\n\n return function (doc) {\n var result = _.any(matchers, function (f) {\n return f(doc).result;\n });\n // $or does NOT set arrayIndices when it has multiple\n // sub-expressions. (Tested against MongoDB.)\n return {result: result};\n };\n },\n\n $nor: function (subSelector, matcher, inElemMatch) {\n var matchers = compileArrayOfDocumentSelectors(\n subSelector, matcher, inElemMatch);\n return function (doc) {\n var result = _.all(matchers, function (f) {\n return !f(doc).result;\n });\n // Never set arrayIndices, because we only match if nothing in particular\n // \"matched\" (and because this is consistent with MongoDB).\n return {result: result};\n };\n },\n\n $where: function (selectorValue, matcher) {\n // Record that *any* path may be used.\n matcher._recordPathUsed('');\n matcher._hasWhere = true;\n if (!(selectorValue instanceof Function)) {\n // XXX MongoDB seems to have more complex logic to decide where or or not\n // to add \"return\"; not sure exactly what it is.\n selectorValue = Function(\"obj\", \"return \" + selectorValue);\n }\n return function (doc) {\n // We make the document available as both `this` and `obj`.\n // XXX not sure what we should do if this throws\n return {result: selectorValue.call(doc, doc)};\n };\n },\n\n // This is just used as a comment in the query (in MongoDB, it also ends up in\n // query logs); it has no effect on the actual selection.\n $comment: function () {\n return function () {\n return {result: true};\n };\n }\n};\n\n// Returns a branched matcher that matches iff the given matcher does not.\n// Note that this implicitly \"deMorganizes\" the wrapped function. ie, it\n// means that ALL branch values need to fail to match innerBranchedMatcher.\nvar invertBranchedMatcher = function (branchedMatcher) {\n return function (branchValues) {\n var invertMe = branchedMatcher(branchValues);\n // We explicitly choose to strip arrayIndices here: it doesn't make sense to\n // say \"update the array element that does not match something\", at least\n // in mongo-land.\n return {result: !invertMe.result};\n };\n};\n\n// Operators that (unlike LOGICAL_OPERATORS) pertain to individual paths in a\n// document, but (unlike ELEMENT_OPERATORS) do not have a simple definition as\n// \"match each branched value independently and combine with\n// convertElementMatcherToBranchedMatcher\".\nvar VALUE_OPERATORS = {\n $not: function (operand, valueSelector, matcher) {\n return invertBranchedMatcher(compileValueSelector(operand, matcher));\n },\n $ne: function (operand) {\n return invertBranchedMatcher(convertElementMatcherToBranchedMatcher(\n equalityElementMatcher(operand)));\n },\n $nin: function (operand) {\n return invertBranchedMatcher(convertElementMatcherToBranchedMatcher(\n ELEMENT_OPERATORS.$in.compileElementSelector(operand)));\n },\n $exists: function (operand) {\n var exists = convertElementMatcherToBranchedMatcher(function (value) {\n return value !== undefined;\n });\n return operand ? exists : invertBranchedMatcher(exists);\n },\n // $options just provides options for $regex; its logic is inside $regex\n $options: function (operand, valueSelector) {\n if (!_.has(valueSelector, '$regex'))\n throw Error(\"$options needs a $regex\");\n return everythingMatcher;\n },\n // $maxDistance is basically an argument to $near\n $maxDistance: function (operand, valueSelector) {\n if (!valueSelector.$near)\n throw Error(\"$maxDistance needs a $near\");\n return everythingMatcher;\n },\n $all: function (operand, valueSelector, matcher) {\n if (!isArray(operand))\n throw Error(\"$all requires array\");\n // Not sure why, but this seems to be what MongoDB does.\n if (_.isEmpty(operand))\n return nothingMatcher;\n\n var branchedMatchers = [];\n _.each(operand, function (criterion) {\n // XXX handle $all/$elemMatch combination\n if (isOperatorObject(criterion))\n throw Error(\"no $ expressions in $all\");\n // This is always a regexp or equality selector.\n branchedMatchers.push(compileValueSelector(criterion, matcher));\n });\n // andBranchedMatchers does NOT require all selectors to return true on the\n // SAME branch.\n return andBranchedMatchers(branchedMatchers);\n },\n $near: function (operand, valueSelector, matcher, isRoot) {\n if (!isRoot)\n throw Error(\"$near can't be inside another $ operator\");\n matcher._hasGeoQuery = true;\n\n // There are two kinds of geodata in MongoDB: coordinate pairs and\n // GeoJSON. They use different distance metrics, too. GeoJSON queries are\n // marked with a $geometry property.\n\n var maxDistance, point, distance;\n if (isPlainObject(operand) && _.has(operand, '$geometry')) {\n // GeoJSON \"2dsphere\" mode.\n maxDistance = operand.$maxDistance;\n point = operand.$geometry;\n distance = function (value) {\n // XXX: for now, we don't calculate the actual distance between, say,\n // polygon and circle. If people care about this use-case it will get\n // a priority.\n if (!value || !value.type)\n return null;\n if (value.type === \"Point\") {\n return GeoJSON.pointDistance(point, value);\n } else {\n return GeoJSON.geometryWithinRadius(value, point, maxDistance)\n ? 0 : maxDistance + 1;\n }\n };\n } else {\n maxDistance = valueSelector.$maxDistance;\n if (!isArray(operand) && !isPlainObject(operand))\n throw Error(\"$near argument must be coordinate pair or GeoJSON\");\n point = pointToArray(operand);\n distance = function (value) {\n if (!isArray(value) && !isPlainObject(value))\n return null;\n return distanceCoordinatePairs(point, value);\n };\n }\n\n return function (branchedValues) {\n // There might be multiple points in the document that match the given\n // field. Only one of them needs to be within $maxDistance, but we need to\n // evaluate all of them and use the nearest one for the implicit sort\n // specifier. (That's why we can't just use ELEMENT_OPERATORS here.)\n //\n // Note: This differs from MongoDB's implementation, where a document will\n // actually show up *multiple times* in the result set, with one entry for\n // each within-$maxDistance branching point.\n branchedValues = expandArraysInBranches(branchedValues);\n var result = {result: false};\n _.each(branchedValues, function (branch) {\n var curDistance = distance(branch.value);\n // Skip branches that aren't real points or are too far away.\n if (curDistance === null || curDistance > maxDistance)\n return;\n // Skip anything that's a tie.\n if (result.distance !== undefined && result.distance <= curDistance)\n return;\n result.result = true;\n result.distance = curDistance;\n if (!branch.arrayIndices)\n delete result.arrayIndices;\n else\n result.arrayIndices = branch.arrayIndices;\n });\n return result;\n };\n }\n};\n\n// Helpers for $near.\nvar distanceCoordinatePairs = function (a, b) {\n a = pointToArray(a);\n b = pointToArray(b);\n var x = a[0] - b[0];\n var y = a[1] - b[1];\n if (_.isNaN(x) || _.isNaN(y))\n return null;\n return Math.sqrt(x * x + y * y);\n};\n// Makes sure we get 2 elements array and assume the first one to be x and\n// the second one to y no matter what user passes.\n// In case user passes { lon: x, lat: y } returns [x, y]\nvar pointToArray = function (point) {\n return _.map(point, _.identity);\n};\n\n// Helper for $lt/$gt/$lte/$gte.\nvar makeInequality = function (cmpValueComparator) {\n return {\n compileElementSelector: function (operand) {\n // Arrays never compare false with non-arrays for any inequality.\n // XXX This was behavior we observed in pre-release MongoDB 2.5, but\n // it seems to have been reverted.\n // See https://jira.mongodb.org/browse/SERVER-11444\n if (isArray(operand)) {\n return function () {\n return false;\n };\n }\n\n // Special case: consider undefined and null the same (so true with\n // $gte/$lte).\n if (operand === undefined)\n operand = null;\n\n var operandType = LocalCollection._f._type(operand);\n\n return function (value) {\n if (value === undefined)\n value = null;\n // Comparisons are never true among things of different type (except\n // null vs undefined).\n if (LocalCollection._f._type(value) !== operandType)\n return false;\n return cmpValueComparator(LocalCollection._f._cmp(value, operand));\n };\n }\n };\n};\n\n// Each element selector contains:\n// - compileElementSelector, a function with args:\n// - operand - the \"right hand side\" of the operator\n// - valueSelector - the \"context\" for the operator (so that $regex can find\n// $options)\n// - matcher - the Matcher this is going into (so that $elemMatch can compile\n// more things)\n// returning a function mapping a single value to bool.\n// - dontExpandLeafArrays, a bool which prevents expandArraysInBranches from\n// being called\n// - dontIncludeLeafArrays, a bool which causes an argument to be passed to\n// expandArraysInBranches if it is called\nELEMENT_OPERATORS = {\n $lt: makeInequality(function (cmpValue) {\n return cmpValue < 0;\n }),\n $gt: makeInequality(function (cmpValue) {\n return cmpValue > 0;\n }),\n $lte: makeInequality(function (cmpValue) {\n return cmpValue <= 0;\n }),\n $gte: makeInequality(function (cmpValue) {\n return cmpValue >= 0;\n }),\n $mod: {\n compileElementSelector: function (operand) {\n if (!(isArray(operand) && operand.length === 2\n && typeof(operand[0]) === 'number'\n && typeof(operand[1]) === 'number')) {\n throw Error(\"argument to $mod must be an array of two numbers\");\n }\n // XXX could require to be ints or round or something\n var divisor = operand[0];\n var remainder = operand[1];\n return function (value) {\n return typeof value === 'number' && value % divisor === remainder;\n };\n }\n },\n $in: {\n compileElementSelector: function (operand) {\n if (!isArray(operand))\n throw Error(\"$in needs an array\");\n\n var elementMatchers = [];\n _.each(operand, function (option) {\n if (option instanceof RegExp)\n elementMatchers.push(regexpElementMatcher(option));\n else if (isOperatorObject(option))\n throw Error(\"cannot nest $ under $in\");\n else\n elementMatchers.push(equalityElementMatcher(option));\n });\n\n return function (value) {\n // Allow {a: {$in: [null]}} to match when 'a' does not exist.\n if (value === undefined)\n value = null;\n return _.any(elementMatchers, function (e) {\n return e(value);\n });\n };\n }\n },\n $size: {\n // {a: [[5, 5]]} must match {a: {$size: 1}} but not {a: {$size: 2}}, so we\n // don't want to consider the element [5,5] in the leaf array [[5,5]] as a\n // possible value.\n dontExpandLeafArrays: true,\n compileElementSelector: function (operand) {\n if (typeof operand === 'string') {\n // Don't ask me why, but by experimentation, this seems to be what Mongo\n // does.\n operand = 0;\n } else if (typeof operand !== 'number') {\n throw Error(\"$size needs a number\");\n }\n return function (value) {\n return isArray(value) && value.length === operand;\n };\n }\n },\n $type: {\n // {a: [5]} must not match {a: {$type: 4}} (4 means array), but it should\n // match {a: {$type: 1}} (1 means number), and {a: [[5]]} must match {$a:\n // {$type: 4}}. Thus, when we see a leaf array, we *should* expand it but\n // should *not* include it itself.\n dontIncludeLeafArrays: true,\n compileElementSelector: function (operand) {\n if (typeof operand !== 'number')\n throw Error(\"$type needs a number\");\n return function (value) {\n return value !== undefined\n && LocalCollection._f._type(value) === operand;\n };\n }\n },\n $regex: {\n compileElementSelector: function (operand, valueSelector) {\n if (!(typeof operand === 'string' || operand instanceof RegExp))\n throw Error(\"$regex has to be a string or RegExp\");\n\n var regexp;\n if (valueSelector.$options !== undefined) {\n // Options passed in $options (even the empty string) always overrides\n // options in the RegExp object itself. (See also\n // Mongo.Collection._rewriteSelector.)\n\n // Be clear that we only support the JS-supported options, not extended\n // ones (eg, Mongo supports x and s). Ideally we would implement x and s\n // by transforming the regexp, but not today...\n if (/[^gim]/.test(valueSelector.$options))\n throw new Error(\"Only the i, m, and g regexp options are supported\");\n\n var regexSource = operand instanceof RegExp ? operand.source : operand;\n regexp = new RegExp(regexSource, valueSelector.$options);\n } else if (operand instanceof RegExp) {\n regexp = operand;\n } else {\n regexp = new RegExp(operand);\n }\n return regexpElementMatcher(regexp);\n }\n },\n $elemMatch: {\n dontExpandLeafArrays: true,\n compileElementSelector: function (operand, valueSelector, matcher) {\n if (!isPlainObject(operand))\n throw Error(\"$elemMatch need an object\");\n\n var subMatcher, isDocMatcher;\n if (isOperatorObject(operand, true)) {\n subMatcher = compileValueSelector(operand, matcher);\n isDocMatcher = false;\n } else {\n // This is NOT the same as compileValueSelector(operand), and not just\n // because of the slightly different calling convention.\n // {$elemMatch: {x: 3}} means \"an element has a field x:3\", not\n // \"consists only of a field x:3\". Also, regexps and sub-$ are allowed.\n subMatcher = compileDocumentSelector(operand, matcher,\n {inElemMatch: true});\n isDocMatcher = true;\n }\n\n return function (value) {\n if (!isArray(value))\n return false;\n for (var i = 0; i < value.length; ++i) {\n var arrayElement = value[i];\n var arg;\n if (isDocMatcher) {\n // We can only match {$elemMatch: {b: 3}} against objects.\n // (We can also match against arrays, if there's numeric indices,\n // eg {$elemMatch: {'0.b': 3}} or {$elemMatch: {0: 3}}.)\n if (!isPlainObject(arrayElement) && !isArray(arrayElement))\n return false;\n arg = arrayElement;\n } else {\n // dontIterate ensures that {a: {$elemMatch: {$gt: 5}}} matches\n // {a: [8]} but not {a: [[8]]}\n arg = [{value: arrayElement, dontIterate: true}];\n }\n // XXX support $near in $elemMatch by propagating $distance?\n if (subMatcher(arg).result)\n return i; // specially understood to mean \"use as arrayIndices\"\n }\n return false;\n };\n }\n }\n};\n\n// makeLookupFunction(key) returns a lookup function.\n//\n// A lookup function takes in a document and returns an array of matching\n// branches. If no arrays are found while looking up the key, this array will\n// have exactly one branches (possibly 'undefined', if some segment of the key\n// was not found).\n//\n// If arrays are found in the middle, this can have more than one element, since\n// we \"branch\". When we \"branch\", if there are more key segments to look up,\n// then we only pursue branches that are plain objects (not arrays or scalars).\n// This means we can actually end up with no branches!\n//\n// We do *NOT* branch on arrays that are found at the end (ie, at the last\n// dotted member of the key). We just return that array; if you want to\n// effectively \"branch\" over the array's values, post-process the lookup\n// function with expandArraysInBranches.\n//\n// Each branch is an object with keys:\n// - value: the value at the branch\n// - dontIterate: an optional bool; if true, it means that 'value' is an array\n// that expandArraysInBranches should NOT expand. This specifically happens\n// when there is a numeric index in the key, and ensures the\n// perhaps-surprising MongoDB behavior where {'a.0': 5} does NOT\n// match {a: [[5]]}.\n// - arrayIndices: if any array indexing was done during lookup (either due to\n// explicit numeric indices or implicit branching), this will be an array of\n// the array indices used, from outermost to innermost; it is falsey or\n// absent if no array index is used. If an explicit numeric index is used,\n// the index will be followed in arrayIndices by the string 'x'.\n//\n// Note: arrayIndices is used for two purposes. First, it is used to\n// implement the '$' modifier feature, which only ever looks at its first\n// element.\n//\n// Second, it is used for sort key generation, which needs to be able to tell\n// the difference between different paths. Moreover, it needs to\n// differentiate between explicit and implicit branching, which is why\n// there's the somewhat hacky 'x' entry: this means that explicit and\n// implicit array lookups will have different full arrayIndices paths. (That\n// code only requires that different paths have different arrayIndices; it\n// doesn't actually \"parse\" arrayIndices. As an alternative, arrayIndices\n// could contain objects with flags like \"implicit\", but I think that only\n// makes the code surrounding them more complex.)\n//\n// (By the way, this field ends up getting passed around a lot without\n// cloning, so never mutate any arrayIndices field/var in this package!)\n//\n//\n// At the top level, you may only pass in a plain object or array.\n//\n// See the test 'minimongo - lookup' for some examples of what lookup functions\n// return.\nmakeLookupFunction = function (key, options) {\n options = options || {};\n var parts = key.split('.');\n var firstPart = parts.length ? parts[0] : '';\n var firstPartIsNumeric = isNumericKey(firstPart);\n var nextPartIsNumeric = parts.length >= 2 && isNumericKey(parts[1]);\n var lookupRest;\n if (parts.length > 1) {\n lookupRest = makeLookupFunction(parts.slice(1).join('.'));\n }\n\n var omitUnnecessaryFields = function (retVal) {\n if (!retVal.dontIterate)\n delete retVal.dontIterate;\n if (retVal.arrayIndices && !retVal.arrayIndices.length)\n delete retVal.arrayIndices;\n return retVal;\n };\n\n // Doc will always be a plain object or an array.\n // apply an explicit numeric index, an array.\n return function (doc, arrayIndices) {\n if (!arrayIndices)\n arrayIndices = [];\n\n if (isArray(doc)) {\n // If we're being asked to do an invalid lookup into an array (non-integer\n // or out-of-bounds), return no results (which is different from returning\n // a single undefined result, in that `null` equality checks won't match).\n if (!(firstPartIsNumeric && firstPart < doc.length))\n return [];\n\n // Remember that we used this array index. Include an 'x' to indicate that\n // the previous index came from being considered as an explicit array\n // index (not branching).\n arrayIndices = arrayIndices.concat(+firstPart, 'x');\n }\n\n // Do our first lookup.\n var firstLevel = doc[firstPart];\n\n // If there is no deeper to dig, return what we found.\n //\n // If what we found is an array, most value selectors will choose to treat\n // the elements of the array as matchable values in their own right, but\n // that's done outside of the lookup function. (Exceptions to this are $size\n // and stuff relating to $elemMatch. eg, {a: {$size: 2}} does not match {a:\n // [[1, 2]]}.)\n //\n // That said, if we just did an *explicit* array lookup (on doc) to find\n // firstLevel, and firstLevel is an array too, we do NOT want value\n // selectors to iterate over it. eg, {'a.0': 5} does not match {a: [[5]]}.\n // So in that case, we mark the return value as \"don't iterate\".\n if (!lookupRest) {\n return [omitUnnecessaryFields({\n value: firstLevel,\n dontIterate: isArray(doc) && isArray(firstLevel),\n arrayIndices: arrayIndices})];\n }\n\n // We need to dig deeper. But if we can't, because what we've found is not\n // an array or plain object, we're done. If we just did a numeric index into\n // an array, we return nothing here (this is a change in Mongo 2.5 from\n // Mongo 2.4, where {'a.0.b': null} stopped matching {a: [5]}). Otherwise,\n // return a single `undefined` (which can, for example, match via equality\n // with `null`).\n if (!isIndexable(firstLevel)) {\n if (isArray(doc))\n return [];\n return [omitUnnecessaryFields({value: undefined,\n arrayIndices: arrayIndices})];\n }\n\n var result = [];\n var appendToResult = function (more) {\n Array.prototype.push.apply(result, more);\n };\n\n // Dig deeper: look up the rest of the parts on whatever we've found.\n // (lookupRest is smart enough to not try to do invalid lookups into\n // firstLevel if it's an array.)\n appendToResult(lookupRest(firstLevel, arrayIndices));\n\n // If we found an array, then in *addition* to potentially treating the next\n // part as a literal integer lookup, we should also \"branch\": try to look up\n // the rest of the parts on each array element in parallel.\n //\n // In this case, we *only* dig deeper into array elements that are plain\n // objects. (Recall that we only got this far if we have further to dig.)\n // This makes sense: we certainly don't dig deeper into non-indexable\n // objects. And it would be weird to dig into an array: it's simpler to have\n // a rule that explicit integer indexes only apply to an outer array, not to\n // an array you find after a branching search.\n //\n // In the special case of a numeric part in a *sort selector* (not a query\n // selector), we skip the branching: we ONLY allow the numeric part to mean\n // \"look up this index\" in that case, not \"also look up this index in all\n // the elements of the array\".\n if (isArray(firstLevel) && !(nextPartIsNumeric && options.forSort)) {\n _.each(firstLevel, function (branch, arrayIndex) {\n if (isPlainObject(branch)) {\n appendToResult(lookupRest(\n branch,\n arrayIndices.concat(arrayIndex)));\n }\n });\n }\n\n return result;\n };\n};\nMinimongoTest.makeLookupFunction = makeLookupFunction;\n\nexpandArraysInBranches = function (branches, skipTheArrays) {\n var branchesOut = [];\n _.each(branches, function (branch) {\n var thisIsArray = isArray(branch.value);\n // We include the branch itself, *UNLESS* we it's an array that we're going\n // to iterate and we're told to skip arrays. (That's right, we include some\n // arrays even skipTheArrays is true: these are arrays that were found via\n // explicit numerical indices.)\n if (!(skipTheArrays && thisIsArray && !branch.dontIterate)) {\n branchesOut.push({\n value: branch.value,\n arrayIndices: branch.arrayIndices\n });\n }\n if (thisIsArray && !branch.dontIterate) {\n _.each(branch.value, function (leaf, i) {\n branchesOut.push({\n value: leaf,\n arrayIndices: (branch.arrayIndices || []).concat(i)\n });\n });\n }\n });\n return branchesOut;\n};\n\nvar nothingMatcher = function (docOrBranchedValues) {\n return {result: false};\n};\n\nvar everythingMatcher = function (docOrBranchedValues) {\n return {result: true};\n};\n\n\n// NB: We are cheating and using this function to implement \"AND\" for both\n// \"document matchers\" and \"branched matchers\". They both return result objects\n// but the argument is different: for the former it's a whole doc, whereas for\n// the latter it's an array of \"branched values\".\nvar andSomeMatchers = function (subMatchers) {\n if (subMatchers.length === 0)\n return everythingMatcher;\n if (subMatchers.length === 1)\n return subMatchers[0];\n\n return function (docOrBranches) {\n var ret = {};\n ret.result = _.all(subMatchers, function (f) {\n var subResult = f(docOrBranches);\n // Copy a 'distance' number out of the first sub-matcher that has\n // one. Yes, this means that if there are multiple $near fields in a\n // query, something arbitrary happens; this appears to be consistent with\n // Mongo.\n if (subResult.result && subResult.distance !== undefined\n && ret.distance === undefined) {\n ret.distance = subResult.distance;\n }\n // Similarly, propagate arrayIndices from sub-matchers... but to match\n // MongoDB behavior, this time the *last* sub-matcher with arrayIndices\n // wins.\n if (subResult.result && subResult.arrayIndices) {\n ret.arrayIndices = subResult.arrayIndices;\n }\n return subResult.result;\n });\n\n // If we didn't actually match, forget any extra metadata we came up with.\n if (!ret.result) {\n delete ret.distance;\n delete ret.arrayIndices;\n }\n return ret;\n };\n};\n\nvar andDocumentMatchers = andSomeMatchers;\nvar andBranchedMatchers = andSomeMatchers;\n\n\n// helpers used by compiled selector code\nLocalCollection._f = {\n // XXX for _all and _in, consider building 'inquery' at compile time..\n\n _type: function (v) {\n if (typeof v === \"number\")\n return 1;\n if (typeof v === \"string\")\n return 2;\n if (typeof v === \"boolean\")\n return 8;\n if (isArray(v))\n return 4;\n if (v === null)\n return 10;\n if (v instanceof RegExp)\n // note that typeof(/x/) === \"object\"\n return 11;\n if (typeof v === \"function\")\n return 13;\n if (v instanceof Date)\n return 9;\n if (EJSON.isBinary(v))\n return 5;\n if (v instanceof LocalCollection._ObjectID)\n return 7;\n return 3; // object\n\n // XXX support some/all of these:\n // 14, symbol\n // 15, javascript code with scope\n // 16, 18: 32-bit/64-bit integer\n // 17, timestamp\n // 255, minkey\n // 127, maxkey\n },\n\n // deep equality test: use for literal document and array matches\n _equal: function (a, b) {\n return EJSON.equals(a, b, {keyOrderSensitive: true});\n },\n\n // maps a type code to a value that can be used to sort values of\n // different types\n _typeorder: function (t) {\n // http://www.mongodb.org/display/DOCS/What+is+the+Compare+Order+for+BSON+Types\n // XXX what is the correct sort position for Javascript code?\n // ('100' in the matrix below)\n // XXX minkey/maxkey\n return [-1, // (not a type)\n 1, // number\n 2, // string\n 3, // object\n 4, // array\n 5, // binary\n -1, // deprecated\n 6, // ObjectID\n 7, // bool\n 8, // Date\n 0, // null\n 9, // RegExp\n -1, // deprecated\n 100, // JS code\n 2, // deprecated (symbol)\n 100, // JS code\n 1, // 32-bit int\n 8, // Mongo timestamp\n 1 // 64-bit int\n ][t];\n },\n\n // compare two values of unknown type according to BSON ordering\n // semantics. (as an extension, consider 'undefined' to be less than\n // any other value.) return negative if a is less, positive if b is\n // less, or 0 if equal\n _cmp: function (a, b) {\n if (a === undefined)\n return b === undefined ? 0 : -1;\n if (b === undefined)\n return 1;\n var ta = LocalCollection._f._type(a);\n var tb = LocalCollection._f._type(b);\n var oa = LocalCollection._f._typeorder(ta);\n var ob = LocalCollection._f._typeorder(tb);\n if (oa !== ob)\n return oa < ob ? -1 : 1;\n if (ta !== tb)\n // XXX need to implement this if we implement Symbol or integers, or\n // Timestamp\n throw Error(\"Missing type coercion logic in _cmp\");\n if (ta === 7) { // ObjectID\n // Convert to string.\n ta = tb = 2;\n a = a.toHexString();\n b = b.toHexString();\n }\n if (ta === 9) { // Date\n // Convert to millis.\n ta = tb = 1;\n a = a.getTime();\n b = b.getTime();\n }\n\n if (ta === 1) // double\n return a - b;\n if (tb === 2) // string\n return a < b ? -1 : (a === b ? 0 : 1);\n if (ta === 3) { // Object\n // this could be much more efficient in the expected case ...\n var to_array = function (obj) {\n var ret = [];\n for (var key in obj) {\n ret.push(key);\n ret.push(obj[key]);\n }\n return ret;\n };\n return LocalCollection._f._cmp(to_array(a), to_array(b));\n }\n if (ta === 4) { // Array\n for (var i = 0; ; i++) {\n if (i === a.length)\n return (i === b.length) ? 0 : -1;\n if (i === b.length)\n return 1;\n var s = LocalCollection._f._cmp(a[i], b[i]);\n if (s !== 0)\n return s;\n }\n }\n if (ta === 5) { // binary\n // Surprisingly, a small binary blob is always less than a large one in\n // Mongo.\n if (a.length !== b.length)\n return a.length - b.length;\n for (i = 0; i < a.length; i++) {\n if (a[i] < b[i])\n return -1;\n if (a[i] > b[i])\n return 1;\n }\n return 0;\n }\n if (ta === 8) { // boolean\n if (a) return b ? 0 : 1;\n return b ? -1 : 0;\n }\n if (ta === 10) // null\n return 0;\n if (ta === 11) // regexp\n throw Error(\"Sorting not supported on regular expression\"); // XXX\n // 13: javascript code\n // 14: symbol\n // 15: javascript code with scope\n // 16: 32-bit integer\n // 17: timestamp\n // 18: 64-bit integer\n // 255: minkey\n // 127: maxkey\n if (ta === 13) // javascript code\n throw Error(\"Sorting not supported on Javascript code\"); // XXX\n throw Error(\"Unknown type to sort\");\n }\n};\n\n// Oddball function used by upsert.\nLocalCollection._removeDollarOperators = function (selector) {\n var selectorDoc = {};\n for (var k in selector)\n if (k.substr(0, 1) !== '$')\n selectorDoc[k] = selector[k];\n return selectorDoc;\n};\n","// Give a sort spec, which can be in any of these forms:\n// {\"key1\": 1, \"key2\": -1}\n// [[\"key1\", \"asc\"], [\"key2\", \"desc\"]]\n// [\"key1\", [\"key2\", \"desc\"]]\n//\n// (.. with the first form being dependent on the key enumeration\n// behavior of your javascript VM, which usually does what you mean in\n// this case if the key names don't look like integers ..)\n//\n// return a function that takes two objects, and returns -1 if the\n// first object comes first in order, 1 if the second object comes\n// first, or 0 if neither object comes before the other.\n\nMinimongo.Sorter = function (spec, options) {\n var self = this;\n options = options || {};\n\n self._sortSpecParts = [];\n\n var addSpecPart = function (path, ascending) {\n if (!path)\n throw Error(\"sort keys must be non-empty\");\n if (path.charAt(0) === '$')\n throw Error(\"unsupported sort key: \" + path);\n self._sortSpecParts.push({\n path: path,\n lookup: makeLookupFunction(path, {forSort: true}),\n ascending: ascending\n });\n };\n\n if (spec instanceof Array) {\n for (var i = 0; i < spec.length; i++) {\n if (typeof spec[i] === \"string\") {\n addSpecPart(spec[i], true);\n } else {\n addSpecPart(spec[i][0], spec[i][1] !== \"desc\");\n }\n }\n } else if (typeof spec === \"object\") {\n _.each(spec, function (value, key) {\n addSpecPart(key, value >= 0);\n });\n } else {\n throw Error(\"Bad sort specification: \" + JSON.stringify(spec));\n }\n\n // To implement affectedByModifier, we piggy-back on top of Matcher's\n // affectedByModifier code; we create a selector that is affected by the same\n // modifiers as this sort order. This is only implemented on the server.\n if (self.affectedByModifier) {\n var selector = {};\n _.each(self._sortSpecParts, function (spec) {\n selector[spec.path] = 1;\n });\n self._selectorForAffectedByModifier = new Minimongo.Matcher(selector);\n }\n\n self._keyComparator = composeComparators(\n _.map(self._sortSpecParts, function (spec, i) {\n return self._keyFieldComparator(i);\n }));\n\n // If you specify a matcher for this Sorter, _keyFilter may be set to a\n // function which selects whether or not a given \"sort key\" (tuple of values\n // for the different sort spec fields) is compatible with the selector.\n self._keyFilter = null;\n options.matcher && self._useWithMatcher(options.matcher);\n};\n\n// In addition to these methods, sorter_project.js defines combineIntoProjection\n// on the server only.\n_.extend(Minimongo.Sorter.prototype, {\n getComparator: function (options) {\n var self = this;\n\n // If we have no distances, just use the comparator from the source\n // specification (which defaults to \"everything is equal\".\n if (!options || !options.distances) {\n return self._getBaseComparator();\n }\n\n var distances = options.distances;\n\n // Return a comparator which first tries the sort specification, and if that\n // says \"it's equal\", breaks ties using $near distances.\n return composeComparators([self._getBaseComparator(), function (a, b) {\n if (!distances.has(a._id))\n throw Error(\"Missing distance for \" + a._id);\n if (!distances.has(b._id))\n throw Error(\"Missing distance for \" + b._id);\n return distances.get(a._id) - distances.get(b._id);\n }]);\n },\n\n _getPaths: function () {\n var self = this;\n return _.pluck(self._sortSpecParts, 'path');\n },\n\n // Finds the minimum key from the doc, according to the sort specs. (We say\n // \"minimum\" here but this is with respect to the sort spec, so \"descending\"\n // sort fields mean we're finding the max for that field.)\n //\n // Note that this is NOT \"find the minimum value of the first field, the\n // minimum value of the second field, etc\"... it's \"choose the\n // lexicographically minimum value of the key vector, allowing only keys which\n // you can find along the same paths\". ie, for a doc {a: [{x: 0, y: 5}, {x:\n // 1, y: 3}]} with sort spec {'a.x': 1, 'a.y': 1}, the only keys are [0,5] and\n // [1,3], and the minimum key is [0,5]; notably, [0,3] is NOT a key.\n _getMinKeyFromDoc: function (doc) {\n var self = this;\n var minKey = null;\n\n self._generateKeysFromDoc(doc, function (key) {\n if (!self._keyCompatibleWithSelector(key))\n return;\n\n if (minKey === null) {\n minKey = key;\n return;\n }\n if (self._compareKeys(key, minKey) < 0) {\n minKey = key;\n }\n });\n\n // This could happen if our key filter somehow filters out all the keys even\n // though somehow the selector matches.\n if (minKey === null)\n throw Error(\"sort selector found no keys in doc?\");\n return minKey;\n },\n\n _keyCompatibleWithSelector: function (key) {\n var self = this;\n return !self._keyFilter || self._keyFilter(key);\n },\n\n // Iterates over each possible \"key\" from doc (ie, over each branch), calling\n // 'cb' with the key.\n _generateKeysFromDoc: function (doc, cb) {\n var self = this;\n\n if (self._sortSpecParts.length === 0)\n throw new Error(\"can't generate keys without a spec\");\n\n // maps index -> ({'' -> value} or {path -> value})\n var valuesByIndexAndPath = [];\n\n var pathFromIndices = function (indices) {\n return indices.join(',') + ',';\n };\n\n var knownPaths = null;\n\n _.each(self._sortSpecParts, function (spec, whichField) {\n // Expand any leaf arrays that we find, and ignore those arrays\n // themselves. (We never sort based on an array itself.)\n var branches = expandArraysInBranches(spec.lookup(doc), true);\n\n // If there are no values for a key (eg, key goes to an empty array),\n // pretend we found one null value.\n if (!branches.length)\n branches = [{value: null}];\n\n var usedPaths = false;\n valuesByIndexAndPath[whichField] = {};\n _.each(branches, function (branch) {\n if (!branch.arrayIndices) {\n // If there are no array indices for a branch, then it must be the\n // only branch, because the only thing that produces multiple branches\n // is the use of arrays.\n if (branches.length > 1)\n throw Error(\"multiple branches but no array used?\");\n valuesByIndexAndPath[whichField][''] = branch.value;\n return;\n }\n\n usedPaths = true;\n var path = pathFromIndices(branch.arrayIndices);\n if (_.has(valuesByIndexAndPath[whichField], path))\n throw Error(\"duplicate path: \" + path);\n valuesByIndexAndPath[whichField][path] = branch.value;\n\n // If two sort fields both go into arrays, they have to go into the\n // exact same arrays and we have to find the same paths. This is\n // roughly the same condition that makes MongoDB throw this strange\n // error message. eg, the main thing is that if sort spec is {a: 1,\n // b:1} then a and b cannot both be arrays.\n //\n // (In MongoDB it seems to be OK to have {a: 1, 'a.x.y': 1} where 'a'\n // and 'a.x.y' are both arrays, but we don't allow this for now.\n // #NestedArraySort\n // XXX achieve full compatibility here\n if (knownPaths && !_.has(knownPaths, path)) {\n throw Error(\"cannot index parallel arrays\");\n }\n });\n\n if (knownPaths) {\n // Similarly to above, paths must match everywhere, unless this is a\n // non-array field.\n if (!_.has(valuesByIndexAndPath[whichField], '') &&\n _.size(knownPaths) !== _.size(valuesByIndexAndPath[whichField])) {\n throw Error(\"cannot index parallel arrays!\");\n }\n } else if (usedPaths) {\n knownPaths = {};\n _.each(valuesByIndexAndPath[whichField], function (x, path) {\n knownPaths[path] = true;\n });\n }\n });\n\n if (!knownPaths) {\n // Easy case: no use of arrays.\n var soleKey = _.map(valuesByIndexAndPath, function (values) {\n if (!_.has(values, ''))\n throw Error(\"no value in sole key case?\");\n return values[''];\n });\n cb(soleKey);\n return;\n }\n\n _.each(knownPaths, function (x, path) {\n var key = _.map(valuesByIndexAndPath, function (values) {\n if (_.has(values, ''))\n return values[''];\n if (!_.has(values, path))\n throw Error(\"missing path?\");\n return values[path];\n });\n cb(key);\n });\n },\n\n // Takes in two keys: arrays whose lengths match the number of spec\n // parts. Returns negative, 0, or positive based on using the sort spec to\n // compare fields.\n _compareKeys: function (key1, key2) {\n var self = this;\n if (key1.length !== self._sortSpecParts.length ||\n key2.length !== self._sortSpecParts.length) {\n throw Error(\"Key has wrong length\");\n }\n\n return self._keyComparator(key1, key2);\n },\n\n // Given an index 'i', returns a comparator that compares two key arrays based\n // on field 'i'.\n _keyFieldComparator: function (i) {\n var self = this;\n var invert = !self._sortSpecParts[i].ascending;\n return function (key1, key2) {\n var compare = LocalCollection._f._cmp(key1[i], key2[i]);\n if (invert)\n compare = -compare;\n return compare;\n };\n },\n\n // Returns a comparator that represents the sort specification (but not\n // including a possible geoquery distance tie-breaker).\n _getBaseComparator: function () {\n var self = this;\n\n // If we're only sorting on geoquery distance and no specs, just say\n // everything is equal.\n if (!self._sortSpecParts.length) {\n return function (doc1, doc2) {\n return 0;\n };\n }\n\n return function (doc1, doc2) {\n var key1 = self._getMinKeyFromDoc(doc1);\n var key2 = self._getMinKeyFromDoc(doc2);\n return self._compareKeys(key1, key2);\n };\n },\n\n // In MongoDB, if you have documents\n // {_id: 'x', a: [1, 10]} and\n // {_id: 'y', a: [5, 15]},\n // then C.find({}, {sort: {a: 1}}) puts x before y (1 comes before 5).\n // But C.find({a: {$gt: 3}}, {sort: {a: 1}}) puts y before x (1 does not\n // match the selector, and 5 comes before 10).\n //\n // The way this works is pretty subtle! For example, if the documents\n // are instead {_id: 'x', a: [{x: 1}, {x: 10}]}) and\n // {_id: 'y', a: [{x: 5}, {x: 15}]}),\n // then C.find({'a.x': {$gt: 3}}, {sort: {'a.x': 1}}) and\n // C.find({a: {$elemMatch: {x: {$gt: 3}}}}, {sort: {'a.x': 1}})\n // both follow this rule (y before x). (ie, you do have to apply this\n // through $elemMatch.)\n //\n // So if you pass a matcher to this sorter's constructor, we will attempt to\n // skip sort keys that don't match the selector. The logic here is pretty\n // subtle and undocumented; we've gotten as close as we can figure out based\n // on our understanding of Mongo's behavior.\n _useWithMatcher: function (matcher) {\n var self = this;\n\n if (self._keyFilter)\n throw Error(\"called _useWithMatcher twice?\");\n\n // If we are only sorting by distance, then we're not going to bother to\n // build a key filter.\n // XXX figure out how geoqueries interact with this stuff\n if (_.isEmpty(self._sortSpecParts))\n return;\n\n var selector = matcher._selector;\n\n // If the user just passed a literal function to find(), then we can't get a\n // key filter from it.\n if (selector instanceof Function)\n return;\n\n var constraintsByPath = {};\n _.each(self._sortSpecParts, function (spec, i) {\n constraintsByPath[spec.path] = [];\n });\n\n _.each(selector, function (subSelector, key) {\n // XXX support $and and $or\n\n var constraints = constraintsByPath[key];\n if (!constraints)\n return;\n\n // XXX it looks like the real MongoDB implementation isn't \"does the\n // regexp match\" but \"does the value fall into a range named by the\n // literal prefix of the regexp\", ie \"foo\" in /^foo(bar|baz)+/ But\n // \"does the regexp match\" is a good approximation.\n if (subSelector instanceof RegExp) {\n // As far as we can tell, using either of the options that both we and\n // MongoDB support ('i' and 'm') disables use of the key filter. This\n // makes sense: MongoDB mostly appears to be calculating ranges of an\n // index to use, which means it only cares about regexps that match\n // one range (with a literal prefix), and both 'i' and 'm' prevent the\n // literal prefix of the regexp from actually meaning one range.\n if (subSelector.ignoreCase || subSelector.multiline)\n return;\n constraints.push(regexpElementMatcher(subSelector));\n return;\n }\n\n if (isOperatorObject(subSelector)) {\n _.each(subSelector, function (operand, operator) {\n if (_.contains(['$lt', '$lte', '$gt', '$gte'], operator)) {\n // XXX this depends on us knowing that these operators don't use any\n // of the arguments to compileElementSelector other than operand.\n constraints.push(\n ELEMENT_OPERATORS[operator].compileElementSelector(operand));\n }\n\n // See comments in the RegExp block above.\n if (operator === '$regex' && !subSelector.$options) {\n constraints.push(\n ELEMENT_OPERATORS.$regex.compileElementSelector(\n operand, subSelector));\n }\n\n // XXX support {$exists: true}, $mod, $type, $in, $elemMatch\n });\n return;\n }\n\n // OK, it's an equality thing.\n constraints.push(equalityElementMatcher(subSelector));\n });\n\n // It appears that the first sort field is treated differently from the\n // others; we shouldn't create a key filter unless the first sort field is\n // restricted, though after that point we can restrict the other sort fields\n // or not as we wish.\n if (_.isEmpty(constraintsByPath[self._sortSpecParts[0].path]))\n return;\n\n self._keyFilter = function (key) {\n return _.all(self._sortSpecParts, function (specPart, index) {\n return _.all(constraintsByPath[specPart.path], function (f) {\n return f(key[index]);\n });\n });\n };\n }\n});\n\n// Given an array of comparators\n// (functions (a,b)->(negative or positive or zero)), returns a single\n// comparator which uses each comparator in order and returns the first\n// non-zero value.\nvar composeComparators = function (comparatorArray) {\n return function (a, b) {\n for (var i = 0; i < comparatorArray.length; ++i) {\n var compare = comparatorArray[i](a, b);\n if (compare !== 0)\n return compare;\n }\n return 0;\n };\n};\n","// Knows how to compile a fields projection to a predicate function.\n// @returns - Function: a closure that filters out an object according to the\n// fields projection rules:\n// @param obj - Object: MongoDB-styled document\n// @returns - Object: a document with the fields filtered out\n// according to projection rules. Doesn't retain subfields\n// of passed argument.\nLocalCollection._compileProjection = function (fields) {\n LocalCollection._checkSupportedProjection(fields);\n\n var _idProjection = _.isUndefined(fields._id) ? true : fields._id;\n var details = projectionDetails(fields);\n\n // returns transformed doc according to ruleTree\n var transform = function (doc, ruleTree) {\n // Special case for \"sets\"\n if (_.isArray(doc))\n return _.map(doc, function (subdoc) { return transform(subdoc, ruleTree); });\n\n var res = details.including ? {} : EJSON.clone(doc);\n _.each(ruleTree, function (rule, key) {\n if (!_.has(doc, key))\n return;\n if (_.isObject(rule)) {\n // For sub-objects/subsets we branch\n if (_.isObject(doc[key]))\n res[key] = transform(doc[key], rule);\n // Otherwise we don't even touch this subfield\n } else if (details.including)\n res[key] = EJSON.clone(doc[key]);\n else\n delete res[key];\n });\n\n return res;\n };\n\n return function (obj) {\n var res = transform(obj, details.tree);\n\n if (_idProjection && _.has(obj, '_id'))\n res._id = obj._id;\n if (!_idProjection && _.has(res, '_id'))\n delete res._id;\n return res;\n };\n};\n\n// Traverses the keys of passed projection and constructs a tree where all\n// leaves are either all True or all False\n// @returns Object:\n// - tree - Object - tree representation of keys involved in projection\n// (exception for '_id' as it is a special case handled separately)\n// - including - Boolean - \"take only certain fields\" type of projection\nprojectionDetails = function (fields) {\n // Find the non-_id keys (_id is handled specially because it is included unless\n // explicitly excluded). Sort the keys, so that our code to detect overlaps\n // like 'foo' and 'foo.bar' can assume that 'foo' comes first.\n var fieldsKeys = _.keys(fields).sort();\n\n // If there are other rules other than '_id', treat '_id' differently in a\n // separate case. If '_id' is the only rule, use it to understand if it is\n // including/excluding projection.\n if (fieldsKeys.length > 0 && !(fieldsKeys.length === 1 && fieldsKeys[0] === '_id'))\n fieldsKeys = _.reject(fieldsKeys, function (key) { return key === '_id'; });\n\n var including = null; // Unknown\n\n _.each(fieldsKeys, function (keyPath) {\n var rule = !!fields[keyPath];\n if (including === null)\n including = rule;\n if (including !== rule)\n // This error message is copies from MongoDB shell\n throw MinimongoError(\"You cannot currently mix including and excluding fields.\");\n });\n\n\n var projectionRulesTree = pathsToTree(\n fieldsKeys,\n function (path) { return including; },\n function (node, path, fullPath) {\n // Check passed projection fields' keys: If you have two rules such as\n // 'foo.bar' and 'foo.bar.baz', then the result becomes ambiguous. If\n // that happens, there is a probability you are doing something wrong,\n // framework should notify you about such mistake earlier on cursor\n // compilation step than later during runtime. Note, that real mongo\n // doesn't do anything about it and the later rule appears in projection\n // project, more priority it takes.\n //\n // Example, assume following in mongo shell:\n // > db.coll.insert({ a: { b: 23, c: 44 } })\n // > db.coll.find({}, { 'a': 1, 'a.b': 1 })\n // { \"_id\" : ObjectId(\"520bfe456024608e8ef24af3\"), \"a\" : { \"b\" : 23 } }\n // > db.coll.find({}, { 'a.b': 1, 'a': 1 })\n // { \"_id\" : ObjectId(\"520bfe456024608e8ef24af3\"), \"a\" : { \"b\" : 23, \"c\" : 44 } }\n //\n // Note, how second time the return set of keys is different.\n\n var currentPath = fullPath;\n var anotherPath = path;\n throw MinimongoError(\"both \" + currentPath + \" and \" + anotherPath +\n \" found in fields option, using both of them may trigger \" +\n \"unexpected behavior. Did you mean to use only one of them?\");\n });\n\n return {\n tree: projectionRulesTree,\n including: including\n };\n};\n\n// paths - Array: list of mongo style paths\n// newLeafFn - Function: of form function(path) should return a scalar value to\n// put into list created for that path\n// conflictFn - Function: of form function(node, path, fullPath) is called\n// when building a tree path for 'fullPath' node on\n// 'path' was already a leaf with a value. Must return a\n// conflict resolution.\n// initial tree - Optional Object: starting tree.\n// @returns - Object: tree represented as a set of nested objects\npathsToTree = function (paths, newLeafFn, conflictFn, tree) {\n tree = tree || {};\n _.each(paths, function (keyPath) {\n var treePos = tree;\n var pathArr = keyPath.split('.');\n\n // use _.all just for iteration with break\n var success = _.all(pathArr.slice(0, -1), function (key, idx) {\n if (!_.has(treePos, key))\n treePos[key] = {};\n else if (!_.isObject(treePos[key])) {\n treePos[key] = conflictFn(treePos[key],\n pathArr.slice(0, idx + 1).join('.'),\n keyPath);\n // break out of loop if we are failing for this path\n if (!_.isObject(treePos[key]))\n return false;\n }\n\n treePos = treePos[key];\n return true;\n });\n\n if (success) {\n var lastKey = _.last(pathArr);\n if (!_.has(treePos, lastKey))\n treePos[lastKey] = newLeafFn(keyPath);\n else\n treePos[lastKey] = conflictFn(treePos[lastKey], keyPath, keyPath);\n }\n });\n\n return tree;\n};\n\nLocalCollection._checkSupportedProjection = function (fields) {\n if (!_.isObject(fields) || _.isArray(fields))\n throw MinimongoError(\"fields option must be an object\");\n\n _.each(fields, function (val, keyPath) {\n if (_.contains(keyPath.split('.'), '$'))\n throw MinimongoError(\"Minimongo doesn't support $ operator in projections yet.\");\n if (_.indexOf([1, 0, true, false], val) === -1)\n throw MinimongoError(\"Projection values should be one of 1, 0, true, or false\");\n });\n};\n\n","// XXX need a strategy for passing the binding of $ into this\n// function, from the compiled selector\n//\n// maybe just {key.up.to.just.before.dollarsign: array_index}\n//\n// XXX atomicity: if one modification fails, do we roll back the whole\n// change?\n//\n// options:\n// - isInsert is set when _modify is being called to compute the document to\n// insert as part of an upsert operation. We use this primarily to figure\n// out when to set the fields in $setOnInsert, if present.\nLocalCollection._modify = function (doc, mod, options) {\n options = options || {};\n if (!isPlainObject(mod))\n throw MinimongoError(\"Modifier must be an object\");\n var isModifier = isOperatorObject(mod);\n\n var newDoc;\n\n if (!isModifier) {\n if (mod._id && !EJSON.equals(doc._id, mod._id))\n throw MinimongoError(\"Cannot change the _id of a document\");\n\n // replace the whole document\n for (var k in mod) {\n if (/\\./.test(k))\n throw MinimongoError(\n \"When replacing document, field name may not contain '.'\");\n }\n newDoc = mod;\n } else {\n // apply modifiers to the doc.\n newDoc = EJSON.clone(doc);\n\n _.each(mod, function (operand, op) {\n var modFunc = MODIFIERS[op];\n // Treat $setOnInsert as $set if this is an insert.\n if (options.isInsert && op === '$setOnInsert')\n modFunc = MODIFIERS['$set'];\n if (!modFunc)\n throw MinimongoError(\"Invalid modifier specified \" + op);\n _.each(operand, function (arg, keypath) {\n if (keypath === '') {\n throw MinimongoError(\"An empty update path is not valid.\");\n }\n\n if (keypath === '_id') {\n throw MinimongoError(\"Mod on _id not allowed\");\n }\n\n var keyparts = keypath.split('.');\n\n if (! _.all(keyparts, _.identity)) {\n throw MinimongoError(\n \"The update path '\" + keypath +\n \"' contains an empty field name, which is not allowed.\");\n }\n\n var noCreate = _.has(NO_CREATE_MODIFIERS, op);\n var forbidArray = (op === \"$rename\");\n var target = findModTarget(newDoc, keyparts, {\n noCreate: NO_CREATE_MODIFIERS[op],\n forbidArray: (op === \"$rename\"),\n arrayIndices: options.arrayIndices\n });\n var field = keyparts.pop();\n modFunc(target, field, arg, keypath, newDoc);\n });\n });\n }\n\n // move new document into place.\n _.each(_.keys(doc), function (k) {\n // Note: this used to be for (var k in doc) however, this does not\n // work right in Opera. Deleting from a doc while iterating over it\n // would sometimes cause opera to skip some keys.\n if (k !== '_id')\n delete doc[k];\n });\n _.each(newDoc, function (v, k) {\n doc[k] = v;\n });\n};\n\n// for a.b.c.2.d.e, keyparts should be ['a', 'b', 'c', '2', 'd', 'e'],\n// and then you would operate on the 'e' property of the returned\n// object.\n//\n// if options.noCreate is falsey, creates intermediate levels of\n// structure as necessary, like mkdir -p (and raises an exception if\n// that would mean giving a non-numeric property to an array.) if\n// options.noCreate is true, return undefined instead.\n//\n// may modify the last element of keyparts to signal to the caller that it needs\n// to use a different value to index into the returned object (for example,\n// ['a', '01'] -> ['a', 1]).\n//\n// if forbidArray is true, return null if the keypath goes through an array.\n//\n// if options.arrayIndices is set, use its first element for the (first) '$' in\n// the path.\nvar findModTarget = function (doc, keyparts, options) {\n options = options || {};\n var usedArrayIndex = false;\n for (var i = 0; i < keyparts.length; i++) {\n var last = (i === keyparts.length - 1);\n var keypart = keyparts[i];\n var indexable = isIndexable(doc);\n if (!indexable) {\n if (options.noCreate)\n return undefined;\n var e = MinimongoError(\n \"cannot use the part '\" + keypart + \"' to traverse \" + doc);\n e.setPropertyError = true;\n throw e;\n }\n if (doc instanceof Array) {\n if (options.forbidArray)\n return null;\n if (keypart === '$') {\n if (usedArrayIndex)\n throw MinimongoError(\"Too many positional (i.e. '$') elements\");\n if (!options.arrayIndices || !options.arrayIndices.length) {\n throw MinimongoError(\"The positional operator did not find the \" +\n \"match needed from the query\");\n }\n keypart = options.arrayIndices[0];\n usedArrayIndex = true;\n } else if (isNumericKey(keypart)) {\n keypart = parseInt(keypart);\n } else {\n if (options.noCreate)\n return undefined;\n throw MinimongoError(\n \"can't append to array using string field name [\"\n + keypart + \"]\");\n }\n if (last)\n // handle 'a.01'\n keyparts[i] = keypart;\n if (options.noCreate && keypart >= doc.length)\n return undefined;\n while (doc.length < keypart)\n doc.push(null);\n if (!last) {\n if (doc.length === keypart)\n doc.push({});\n else if (typeof doc[keypart] !== \"object\")\n throw MinimongoError(\"can't modify field '\" + keyparts[i + 1] +\n \"' of list value \" + JSON.stringify(doc[keypart]));\n }\n } else {\n if (keypart.length && keypart.substr(0, 1) === '$')\n throw MinimongoError(\"can't set field named \" + keypart);\n if (!(keypart in doc)) {\n if (options.noCreate)\n return undefined;\n if (!last)\n doc[keypart] = {};\n }\n }\n\n if (last)\n return doc;\n doc = doc[keypart];\n }\n\n // notreached\n};\n\nvar NO_CREATE_MODIFIERS = {\n $unset: true,\n $pop: true,\n $rename: true,\n $pull: true,\n $pullAll: true\n};\n\nvar MODIFIERS = {\n $inc: function (target, field, arg) {\n if (typeof arg !== \"number\")\n throw MinimongoError(\"Modifier $inc allowed for numbers only\");\n if (field in target) {\n if (typeof target[field] !== \"number\")\n throw MinimongoError(\"Cannot apply $inc modifier to non-number\");\n target[field] += arg;\n } else {\n target[field] = arg;\n }\n },\n $set: function (target, field, arg) {\n if (!_.isObject(target)) { // not an array or an object\n var e = MinimongoError(\"Cannot set property on non-object field\");\n e.setPropertyError = true;\n throw e;\n }\n if (target === null) {\n var e = MinimongoError(\"Cannot set property on null\");\n e.setPropertyError = true;\n throw e;\n }\n target[field] = EJSON.clone(arg);\n },\n $setOnInsert: function (target, field, arg) {\n // converted to `$set` in `_modify`\n },\n $unset: function (target, field, arg) {\n if (target !== undefined) {\n if (target instanceof Array) {\n if (field in target)\n target[field] = null;\n } else\n delete target[field];\n }\n },\n $push: function (target, field, arg) {\n if (target[field] === undefined)\n target[field] = [];\n if (!(target[field] instanceof Array))\n throw MinimongoError(\"Cannot apply $push modifier to non-array\");\n\n if (!(arg && arg.$each)) {\n // Simple mode: not $each\n target[field].push(EJSON.clone(arg));\n return;\n }\n\n // Fancy mode: $each (and maybe $slice and $sort)\n var toPush = arg.$each;\n if (!(toPush instanceof Array))\n throw MinimongoError(\"$each must be an array\");\n\n // Parse $slice.\n var slice = undefined;\n if ('$slice' in arg) {\n if (typeof arg.$slice !== \"number\")\n throw MinimongoError(\"$slice must be a numeric value\");\n // XXX should check to make sure integer\n if (arg.$slice > 0)\n throw MinimongoError(\"$slice in $push must be zero or negative\");\n slice = arg.$slice;\n }\n\n // Parse $sort.\n var sortFunction = undefined;\n if (arg.$sort) {\n if (slice === undefined)\n throw MinimongoError(\"$sort requires $slice to be present\");\n // XXX this allows us to use a $sort whose value is an array, but that's\n // actually an extension of the Node driver, so it won't work\n // server-side. Could be confusing!\n // XXX is it correct that we don't do geo-stuff here?\n sortFunction = new Minimongo.Sorter(arg.$sort).getComparator();\n for (var i = 0; i < toPush.length; i++) {\n if (LocalCollection._f._type(toPush[i]) !== 3) {\n throw MinimongoError(\"$push like modifiers using $sort \" +\n \"require all elements to be objects\");\n }\n }\n }\n\n // Actually push.\n for (var j = 0; j < toPush.length; j++)\n target[field].push(EJSON.clone(toPush[j]));\n\n // Actually sort.\n if (sortFunction)\n target[field].sort(sortFunction);\n\n // Actually slice.\n if (slice !== undefined) {\n if (slice === 0)\n target[field] = []; // differs from Array.slice!\n else\n target[field] = target[field].slice(slice);\n }\n },\n $pushAll: function (target, field, arg) {\n if (!(typeof arg === \"object\" && arg instanceof Array))\n throw MinimongoError(\"Modifier $pushAll/pullAll allowed for arrays only\");\n var x = target[field];\n if (x === undefined)\n target[field] = arg;\n else if (!(x instanceof Array))\n throw MinimongoError(\"Cannot apply $pushAll modifier to non-array\");\n else {\n for (var i = 0; i < arg.length; i++)\n x.push(arg[i]);\n }\n },\n $addToSet: function (target, field, arg) {\n var isEach = false;\n if (typeof arg === \"object\") {\n //check if first key is '$each'\n for (var k in arg) {\n if (k === \"$each\")\n isEach = true;\n break;\n }\n }\n var values = isEach ? arg[\"$each\"] : [arg];\n var x = target[field];\n if (x === undefined)\n target[field] = values;\n else if (!(x instanceof Array))\n throw MinimongoError(\"Cannot apply $addToSet modifier to non-array\");\n else {\n _.each(values, function (value) {\n for (var i = 0; i < x.length; i++)\n if (LocalCollection._f._equal(value, x[i]))\n return;\n x.push(EJSON.clone(value));\n });\n }\n },\n $pop: function (target, field, arg) {\n if (target === undefined)\n return;\n var x = target[field];\n if (x === undefined)\n return;\n else if (!(x instanceof Array))\n throw MinimongoError(\"Cannot apply $pop modifier to non-array\");\n else {\n if (typeof arg === 'number' && arg < 0)\n x.splice(0, 1);\n else\n x.pop();\n }\n },\n $pull: function (target, field, arg) {\n if (target === undefined)\n return;\n var x = target[field];\n if (x === undefined)\n return;\n else if (!(x instanceof Array))\n throw MinimongoError(\"Cannot apply $pull/pullAll modifier to non-array\");\n else {\n var out = [];\n if (typeof arg === \"object\" && !(arg instanceof Array)) {\n // XXX would be much nicer to compile this once, rather than\n // for each document we modify.. but usually we're not\n // modifying that many documents, so we'll let it slide for\n // now\n\n // XXX Minimongo.Matcher isn't up for the job, because we need\n // to permit stuff like {$pull: {a: {$gt: 4}}}.. something\n // like {$gt: 4} is not normally a complete selector.\n // same issue as $elemMatch possibly?\n var matcher = new Minimongo.Matcher(arg);\n for (var i = 0; i < x.length; i++)\n if (!matcher.documentMatches(x[i]).result)\n out.push(x[i]);\n } else {\n for (var i = 0; i < x.length; i++)\n if (!LocalCollection._f._equal(x[i], arg))\n out.push(x[i]);\n }\n target[field] = out;\n }\n },\n $pullAll: function (target, field, arg) {\n if (!(typeof arg === \"object\" && arg instanceof Array))\n throw MinimongoError(\"Modifier $pushAll/pullAll allowed for arrays only\");\n if (target === undefined)\n return;\n var x = target[field];\n if (x === undefined)\n return;\n else if (!(x instanceof Array))\n throw MinimongoError(\"Cannot apply $pull/pullAll modifier to non-array\");\n else {\n var out = [];\n for (var i = 0; i < x.length; i++) {\n var exclude = false;\n for (var j = 0; j < arg.length; j++) {\n if (LocalCollection._f._equal(x[i], arg[j])) {\n exclude = true;\n break;\n }\n }\n if (!exclude)\n out.push(x[i]);\n }\n target[field] = out;\n }\n },\n $rename: function (target, field, arg, keypath, doc) {\n if (keypath === arg)\n // no idea why mongo has this restriction..\n throw MinimongoError(\"$rename source must differ from target\");\n if (target === null)\n throw MinimongoError(\"$rename source field invalid\");\n if (typeof arg !== \"string\")\n throw MinimongoError(\"$rename target must be a string\");\n if (target === undefined)\n return;\n var v = target[field];\n delete target[field];\n\n var keyparts = arg.split('.');\n var target2 = findModTarget(doc, keyparts, {forbidArray: true});\n if (target2 === null)\n throw MinimongoError(\"$rename target field invalid\");\n var field2 = keyparts.pop();\n target2[field2] = v;\n },\n $bit: function (target, field, arg) {\n // XXX mongo only supports $bit on integers, and we only support\n // native javascript numbers (doubles) so far, so we can't support $bit\n throw MinimongoError(\"$bit is not supported\");\n }\n};\n","// ordered: bool.\n// old_results and new_results: collections of documents.\n// if ordered, they are arrays.\n// if unordered, they are IdMaps\nLocalCollection._diffQueryChanges = function (ordered, oldResults, newResults,\n observer, options) {\n if (ordered)\n LocalCollection._diffQueryOrderedChanges(\n oldResults, newResults, observer, options);\n else\n LocalCollection._diffQueryUnorderedChanges(\n oldResults, newResults, observer, options);\n};\n\nLocalCollection._diffQueryUnorderedChanges = function (oldResults, newResults,\n observer, options) {\n options = options || {};\n var projectionFn = options.projectionFn || EJSON.clone;\n\n if (observer.movedBefore) {\n throw new Error(\"_diffQueryUnordered called with a movedBefore observer!\");\n }\n\n newResults.forEach(function (newDoc, id) {\n var oldDoc = oldResults.get(id);\n if (oldDoc) {\n if (observer.changed && !EJSON.equals(oldDoc, newDoc)) {\n var projectedNew = projectionFn(newDoc);\n var projectedOld = projectionFn(oldDoc);\n var changedFields =\n LocalCollection._makeChangedFields(projectedNew, projectedOld);\n if (! _.isEmpty(changedFields)) {\n observer.changed(id, changedFields);\n }\n }\n } else if (observer.added) {\n var fields = projectionFn(newDoc);\n delete fields._id;\n observer.added(newDoc._id, fields);\n }\n });\n\n if (observer.removed) {\n oldResults.forEach(function (oldDoc, id) {\n if (!newResults.has(id))\n observer.removed(id);\n });\n }\n};\n\n\nLocalCollection._diffQueryOrderedChanges = function (old_results, new_results,\n observer, options) {\n options = options || {};\n var projectionFn = options.projectionFn || EJSON.clone;\n\n var new_presence_of_id = {};\n _.each(new_results, function (doc) {\n if (new_presence_of_id[doc._id])\n Meteor._debug(\"Duplicate _id in new_results\");\n new_presence_of_id[doc._id] = true;\n });\n\n var old_index_of_id = {};\n _.each(old_results, function (doc, i) {\n if (doc._id in old_index_of_id)\n Meteor._debug(\"Duplicate _id in old_results\");\n old_index_of_id[doc._id] = i;\n });\n\n // ALGORITHM:\n //\n // To determine which docs should be considered \"moved\" (and which\n // merely change position because of other docs moving) we run\n // a \"longest common subsequence\" (LCS) algorithm. The LCS of the\n // old doc IDs and the new doc IDs gives the docs that should NOT be\n // considered moved.\n\n // To actually call the appropriate callbacks to get from the old state to the\n // new state:\n\n // First, we call removed() on all the items that only appear in the old\n // state.\n\n // Then, once we have the items that should not move, we walk through the new\n // results array group-by-group, where a \"group\" is a set of items that have\n // moved, anchored on the end by an item that should not move. One by one, we\n // move each of those elements into place \"before\" the anchoring end-of-group\n // item, and fire changed events on them if necessary. Then we fire a changed\n // event on the anchor, and move on to the next group. There is always at\n // least one group; the last group is anchored by a virtual \"null\" id at the\n // end.\n\n // Asymptotically: O(N k) where k is number of ops, or potentially\n // O(N log N) if inner loop of LCS were made to be binary search.\n\n\n //////// LCS (longest common sequence, with respect to _id)\n // (see Wikipedia article on Longest Increasing Subsequence,\n // where the LIS is taken of the sequence of old indices of the\n // docs in new_results)\n //\n // unmoved: the output of the algorithm; members of the LCS,\n // in the form of indices into new_results\n var unmoved = [];\n // max_seq_len: length of LCS found so far\n var max_seq_len = 0;\n // seq_ends[i]: the index into new_results of the last doc in a\n // common subsequence of length of i+1 <= max_seq_len\n var N = new_results.length;\n var seq_ends = new Array(N);\n // ptrs: the common subsequence ending with new_results[n] extends\n // a common subsequence ending with new_results[ptr[n]], unless\n // ptr[n] is -1.\n var ptrs = new Array(N);\n // virtual sequence of old indices of new results\n var old_idx_seq = function(i_new) {\n return old_index_of_id[new_results[i_new]._id];\n };\n // for each item in new_results, use it to extend a common subsequence\n // of length j <= max_seq_len\n for(var i=0; i 0) {\n if (old_idx_seq(seq_ends[j-1]) < old_idx_seq(i))\n break;\n j--;\n }\n\n ptrs[i] = (j === 0 ? -1 : seq_ends[j-1]);\n seq_ends[j] = i;\n if (j+1 > max_seq_len)\n max_seq_len = j+1;\n }\n }\n\n // pull out the LCS/LIS into unmoved\n var idx = (max_seq_len === 0 ? -1 : seq_ends[max_seq_len-1]);\n while (idx >= 0) {\n unmoved.push(idx);\n idx = ptrs[idx];\n }\n // the unmoved item list is built backwards, so fix that\n unmoved.reverse();\n\n // the last group is always anchored by the end of the result list, which is\n // an id of \"null\"\n unmoved.push(new_results.length);\n\n _.each(old_results, function (doc) {\n if (!new_presence_of_id[doc._id])\n observer.removed && observer.removed(doc._id);\n });\n // for each group of things in the new_results that is anchored by an unmoved\n // element, iterate through the things before it.\n var startOfGroup = 0;\n _.each(unmoved, function (endOfGroup) {\n var groupId = new_results[endOfGroup] ? new_results[endOfGroup]._id : null;\n var oldDoc, newDoc, fields, projectedNew, projectedOld;\n for (var i = startOfGroup; i < endOfGroup; i++) {\n newDoc = new_results[i];\n if (!_.has(old_index_of_id, newDoc._id)) {\n fields = projectionFn(newDoc);\n delete fields._id;\n observer.addedBefore && observer.addedBefore(newDoc._id, fields, groupId);\n observer.added && observer.added(newDoc._id, fields);\n } else {\n // moved\n oldDoc = old_results[old_index_of_id[newDoc._id]];\n projectedNew = projectionFn(newDoc);\n projectedOld = projectionFn(oldDoc);\n fields = LocalCollection._makeChangedFields(projectedNew, projectedOld);\n if (!_.isEmpty(fields)) {\n observer.changed && observer.changed(newDoc._id, fields);\n }\n observer.movedBefore && observer.movedBefore(newDoc._id, groupId);\n }\n }\n if (groupId) {\n newDoc = new_results[endOfGroup];\n oldDoc = old_results[old_index_of_id[newDoc._id]];\n projectedNew = projectionFn(newDoc);\n projectedOld = projectionFn(oldDoc);\n fields = LocalCollection._makeChangedFields(projectedNew, projectedOld);\n if (!_.isEmpty(fields)) {\n observer.changed && observer.changed(newDoc._id, fields);\n }\n }\n startOfGroup = endOfGroup+1;\n });\n\n\n};\n\n\n// General helper for diff-ing two objects.\n// callbacks is an object like so:\n// { leftOnly: function (key, leftValue) {...},\n// rightOnly: function (key, rightValue) {...},\n// both: function (key, leftValue, rightValue) {...},\n// }\nLocalCollection._diffObjects = function (left, right, callbacks) {\n _.each(left, function (leftValue, key) {\n if (_.has(right, key))\n callbacks.both && callbacks.both(key, leftValue, right[key]);\n else\n callbacks.leftOnly && callbacks.leftOnly(key, leftValue);\n });\n if (callbacks.rightOnly) {\n _.each(right, function(rightValue, key) {\n if (!_.has(left, key))\n callbacks.rightOnly(key, rightValue);\n });\n }\n};\n","LocalCollection._IdMap = function () {\n var self = this;\n IdMap.call(self, LocalCollection._idStringify, LocalCollection._idParse);\n};\n\nMeteor._inherits(LocalCollection._IdMap, IdMap);\n\n","// XXX maybe move these into another ObserveHelpers package or something\n\n// _CachingChangeObserver is an object which receives observeChanges callbacks\n// and keeps a cache of the current cursor state up to date in self.docs. Users\n// of this class should read the docs field but not modify it. You should pass\n// the \"applyChange\" field as the callbacks to the underlying observeChanges\n// call. Optionally, you can specify your own observeChanges callbacks which are\n// invoked immediately before the docs field is updated; this object is made\n// available as `this` to those callbacks.\nLocalCollection._CachingChangeObserver = function (options) {\n var self = this;\n options = options || {};\n\n var orderedFromCallbacks = options.callbacks &&\n LocalCollection._observeChangesCallbacksAreOrdered(options.callbacks);\n if (_.has(options, 'ordered')) {\n self.ordered = options.ordered;\n if (options.callbacks && options.ordered !== orderedFromCallbacks)\n throw Error(\"ordered option doesn't match callbacks\");\n } else if (options.callbacks) {\n self.ordered = orderedFromCallbacks;\n } else {\n throw Error(\"must provide ordered or callbacks\");\n }\n var callbacks = options.callbacks || {};\n\n if (self.ordered) {\n self.docs = new OrderedDict(LocalCollection._idStringify);\n self.applyChange = {\n addedBefore: function (id, fields, before) {\n var doc = EJSON.clone(fields);\n doc._id = id;\n callbacks.addedBefore && callbacks.addedBefore.call(\n self, id, fields, before);\n // This line triggers if we provide added with movedBefore.\n callbacks.added && callbacks.added.call(self, id, fields);\n // XXX could `before` be a falsy ID? Technically\n // idStringify seems to allow for them -- though\n // OrderedDict won't call stringify on a falsy arg.\n self.docs.putBefore(id, doc, before || null);\n },\n movedBefore: function (id, before) {\n var doc = self.docs.get(id);\n callbacks.movedBefore && callbacks.movedBefore.call(self, id, before);\n self.docs.moveBefore(id, before || null);\n }\n };\n } else {\n self.docs = new LocalCollection._IdMap;\n self.applyChange = {\n added: function (id, fields) {\n var doc = EJSON.clone(fields);\n callbacks.added && callbacks.added.call(self, id, fields);\n doc._id = id;\n self.docs.set(id, doc);\n }\n };\n }\n\n // The methods in _IdMap and OrderedDict used by these callbacks are\n // identical.\n self.applyChange.changed = function (id, fields) {\n var doc = self.docs.get(id);\n if (!doc)\n throw new Error(\"Unknown id for changed: \" + id);\n callbacks.changed && callbacks.changed.call(\n self, id, EJSON.clone(fields));\n LocalCollection._applyChanges(doc, fields);\n };\n self.applyChange.removed = function (id) {\n callbacks.removed && callbacks.removed.call(self, id);\n self.docs.remove(id);\n };\n};\n\nLocalCollection._observeFromObserveChanges = function (cursor, observeCallbacks) {\n var transform = cursor.getTransform() || function (doc) {return doc;};\n var suppressed = !!observeCallbacks._suppress_initial;\n\n var observeChangesCallbacks;\n if (LocalCollection._observeCallbacksAreOrdered(observeCallbacks)) {\n // The \"_no_indices\" option sets all index arguments to -1 and skips the\n // linear scans required to generate them. This lets observers that don't\n // need absolute indices benefit from the other features of this API --\n // relative order, transforms, and applyChanges -- without the speed hit.\n var indices = !observeCallbacks._no_indices;\n observeChangesCallbacks = {\n addedBefore: function (id, fields, before) {\n var self = this;\n if (suppressed || !(observeCallbacks.addedAt || observeCallbacks.added))\n return;\n var doc = transform(_.extend(fields, {_id: id}));\n if (observeCallbacks.addedAt) {\n var index = indices\n ? (before ? self.docs.indexOf(before) : self.docs.size()) : -1;\n observeCallbacks.addedAt(doc, index, before);\n } else {\n observeCallbacks.added(doc);\n }\n },\n changed: function (id, fields) {\n var self = this;\n if (!(observeCallbacks.changedAt || observeCallbacks.changed))\n return;\n var doc = EJSON.clone(self.docs.get(id));\n if (!doc)\n throw new Error(\"Unknown id for changed: \" + id);\n var oldDoc = transform(EJSON.clone(doc));\n LocalCollection._applyChanges(doc, fields);\n doc = transform(doc);\n if (observeCallbacks.changedAt) {\n var index = indices ? self.docs.indexOf(id) : -1;\n observeCallbacks.changedAt(doc, oldDoc, index);\n } else {\n observeCallbacks.changed(doc, oldDoc);\n }\n },\n movedBefore: function (id, before) {\n var self = this;\n if (!observeCallbacks.movedTo)\n return;\n var from = indices ? self.docs.indexOf(id) : -1;\n\n var to = indices\n ? (before ? self.docs.indexOf(before) : self.docs.size()) : -1;\n // When not moving backwards, adjust for the fact that removing the\n // document slides everything back one slot.\n if (to > from)\n --to;\n observeCallbacks.movedTo(transform(EJSON.clone(self.docs.get(id))),\n from, to, before || null);\n },\n removed: function (id) {\n var self = this;\n if (!(observeCallbacks.removedAt || observeCallbacks.removed))\n return;\n // technically maybe there should be an EJSON.clone here, but it's about\n // to be removed from self.docs!\n var doc = transform(self.docs.get(id));\n if (observeCallbacks.removedAt) {\n var index = indices ? self.docs.indexOf(id) : -1;\n observeCallbacks.removedAt(doc, index);\n } else {\n observeCallbacks.removed(doc);\n }\n }\n };\n } else {\n observeChangesCallbacks = {\n added: function (id, fields) {\n if (!suppressed && observeCallbacks.added) {\n var doc = _.extend(fields, {_id: id});\n observeCallbacks.added(transform(doc));\n }\n },\n changed: function (id, fields) {\n var self = this;\n if (observeCallbacks.changed) {\n var oldDoc = self.docs.get(id);\n var doc = EJSON.clone(oldDoc);\n LocalCollection._applyChanges(doc, fields);\n observeCallbacks.changed(transform(doc),\n transform(EJSON.clone(oldDoc)));\n }\n },\n removed: function (id) {\n var self = this;\n if (observeCallbacks.removed) {\n observeCallbacks.removed(transform(self.docs.get(id)));\n }\n }\n };\n }\n\n var changeObserver = new LocalCollection._CachingChangeObserver(\n {callbacks: observeChangesCallbacks});\n var handle = cursor.observeChanges(changeObserver.applyChange);\n suppressed = false;\n\n return handle;\n};\n","LocalCollection._looksLikeObjectID = function (str) {\n return str.length === 24 && str.match(/^[0-9a-f]*$/);\n};\n\nLocalCollection._ObjectID = function (hexString) {\n //random-based impl of Mongo ObjectID\n var self = this;\n if (hexString) {\n hexString = hexString.toLowerCase();\n if (!LocalCollection._looksLikeObjectID(hexString)) {\n throw new Error(\"Invalid hexadecimal string for creating an ObjectID\");\n }\n // meant to work with _.isEqual(), which relies on structural equality\n self._str = hexString;\n } else {\n self._str = Random.hexString(24);\n }\n};\n\nLocalCollection._ObjectID.prototype.toString = function () {\n var self = this;\n return \"ObjectID(\\\"\" + self._str + \"\\\")\";\n};\n\nLocalCollection._ObjectID.prototype.equals = function (other) {\n var self = this;\n return other instanceof LocalCollection._ObjectID &&\n self.valueOf() === other.valueOf();\n};\n\nLocalCollection._ObjectID.prototype.clone = function () {\n var self = this;\n return new LocalCollection._ObjectID(self._str);\n};\n\nLocalCollection._ObjectID.prototype.typeName = function() {\n return \"oid\";\n};\n\nLocalCollection._ObjectID.prototype.getTimestamp = function() {\n var self = this;\n return parseInt(self._str.substr(0, 8), 16);\n};\n\nLocalCollection._ObjectID.prototype.valueOf =\n LocalCollection._ObjectID.prototype.toJSONValue =\n LocalCollection._ObjectID.prototype.toHexString =\n function () { return this._str; };\n\n// Is this selector just shorthand for lookup by _id?\nLocalCollection._selectorIsId = function (selector) {\n return (typeof selector === \"string\") ||\n (typeof selector === \"number\") ||\n selector instanceof LocalCollection._ObjectID;\n};\n\n// Is the selector just lookup by _id (shorthand or not)?\nLocalCollection._selectorIsIdPerhapsAsObject = function (selector) {\n return LocalCollection._selectorIsId(selector) ||\n (selector && typeof selector === \"object\" &&\n selector._id && LocalCollection._selectorIsId(selector._id) &&\n _.size(selector) === 1);\n};\n\n// If this is a selector which explicitly constrains the match by ID to a finite\n// number of documents, returns a list of their IDs. Otherwise returns\n// null. Note that the selector may have other restrictions so it may not even\n// match those document! We care about $in and $and since those are generated\n// access-controlled update and remove.\nLocalCollection._idsMatchedBySelector = function (selector) {\n // Is the selector just an ID?\n if (LocalCollection._selectorIsId(selector))\n return [selector];\n if (!selector)\n return null;\n\n // Do we have an _id clause?\n if (_.has(selector, '_id')) {\n // Is the _id clause just an ID?\n if (LocalCollection._selectorIsId(selector._id))\n return [selector._id];\n // Is the _id clause {_id: {$in: [\"x\", \"y\", \"z\"]}}?\n if (selector._id && selector._id.$in\n && _.isArray(selector._id.$in)\n && !_.isEmpty(selector._id.$in)\n && _.all(selector._id.$in, LocalCollection._selectorIsId)) {\n return selector._id.$in;\n }\n return null;\n }\n\n // If this is a top-level $and, and any of the clauses constrain their\n // documents, then the whole selector is constrained by any one clause's\n // constraint. (Well, by their intersection, but that seems unlikely.)\n if (selector.$and && _.isArray(selector.$and)) {\n for (var i = 0; i < selector.$and.length; ++i) {\n var subIds = LocalCollection._idsMatchedBySelector(selector.$and[i]);\n if (subIds)\n return subIds;\n }\n }\n\n return null;\n};\n\nEJSON.addType(\"oid\", function (str) {\n return new LocalCollection._ObjectID(str);\n});\n"]}