(function () {

/* Imports */
var Meteor = Package.meteor.Meteor;
var _ = Package.underscore._;
var EJSON = Package.ejson.EJSON;
var IdMap = Package['id-map'].IdMap;
var OrderedDict = Package['ordered-dict'].OrderedDict;
var Tracker = Package.tracker.Tracker;
var Deps = Package.tracker.Deps;
var Random = Package.random.Random;
var GeoJSON = Package['geojson-utils'].GeoJSON;

/* Package-scope variables */
var LocalCollection, Minimongo, MinimongoTest, MinimongoError, isArray, isPlainObject, isIndexable, isOperatorObject, isNumericKey, regexpElementMatcher, equalityElementMatcher, ELEMENT_OPERATORS, makeLookupFunction, expandArraysInBranches, projectionDetails, pathsToTree, combineImportantPathsIntoProjection;

(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/minimongo.js                                                                           //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
// XXX type checking on selectors (graceful error if malformed)                                              // 1
                                                                                                             // 2
// LocalCollection: a set of documents that supports queries and modifiers.                                  // 3
                                                                                                             // 4
// Cursor: a specification for a particular subset of documents, w/                                          // 5
// a defined order, limit, and offset.  creating a Cursor with LocalCollection.find(),                       // 6
                                                                                                             // 7
// ObserveHandle: the return value of a live query.                                                          // 8
                                                                                                             // 9
LocalCollection = function (name) {                                                                          // 10
  var self = this;                                                                                           // 11
  self.name = name;                                                                                          // 12
  // _id -> document (also containing id)                                                                    // 13
  self._docs = new LocalCollection._IdMap;                                                                   // 14
                                                                                                             // 15
  self._observeQueue = new Meteor._SynchronousQueue();                                                       // 16
                                                                                                             // 17
  self.next_qid = 1; // live query id generator                                                              // 18
                                                                                                             // 19
  // qid -> live query object. keys:                                                                         // 20
  //  ordered: bool. ordered queries have addedBefore/movedBefore callbacks.                                 // 21
  //  results: array (ordered) or object (unordered) of current results                                      // 22
  //    (aliased with self._docs!)                                                                           // 23
  //  resultsSnapshot: snapshot of results. null if not paused.                                              // 24
  //  cursor: Cursor object for the query.                                                                   // 25
  //  selector, sorter, (callbacks): functions                                                               // 26
  self.queries = {};                                                                                         // 27
                                                                                                             // 28
  // null if not saving originals; an IdMap from id to original document value if                            // 29
  // saving originals. See comments before saveOriginals().                                                  // 30
  self._savedOriginals = null;                                                                               // 31
                                                                                                             // 32
  // True when observers are paused and we should not send callbacks.                                        // 33
  self.paused = false;                                                                                       // 34
};                                                                                                           // 35
                                                                                                             // 36
Minimongo = {};                                                                                              // 37
                                                                                                             // 38
// Object exported only for unit testing.                                                                    // 39
// Use it to export private functions to test in Tinytest.                                                   // 40
MinimongoTest = {};                                                                                          // 41
                                                                                                             // 42
LocalCollection._applyChanges = function (doc, changeFields) {                                               // 43
  _.each(changeFields, function (value, key) {                                                               // 44
    if (value === undefined)                                                                                 // 45
      delete doc[key];                                                                                       // 46
    else                                                                                                     // 47
      doc[key] = value;                                                                                      // 48
  });                                                                                                        // 49
};                                                                                                           // 50
                                                                                                             // 51
MinimongoError = function (message) {                                                                        // 52
  var e = new Error(message);                                                                                // 53
  e.name = "MinimongoError";                                                                                 // 54
  return e;                                                                                                  // 55
};                                                                                                           // 56
                                                                                                             // 57
                                                                                                             // 58
// options may include sort, skip, limit, reactive                                                           // 59
// sort may be any of these forms:                                                                           // 60
//     {a: 1, b: -1}                                                                                         // 61
//     [["a", "asc"], ["b", "desc"]]                                                                         // 62
//     ["a", ["b", "desc"]]                                                                                  // 63
//   (in the first form you're beholden to key enumeration order in                                          // 64
//   your javascript VM)                                                                                     // 65
//                                                                                                           // 66
// reactive: if given, and false, don't register with Tracker (default                                       // 67
// is true)                                                                                                  // 68
//                                                                                                           // 69
// XXX possibly should support retrieving a subset of fields? and                                            // 70
// have it be a hint (ignored on the client, when not copying the                                            // 71
// doc?)                                                                                                     // 72
//                                                                                                           // 73
// XXX sort does not yet support subkeys ('a.b') .. fix that!                                                // 74
// XXX add one more sort form: "key"                                                                         // 75
// XXX tests                                                                                                 // 76
LocalCollection.prototype.find = function (selector, options) {                                              // 77
  // default syntax for everything is to omit the selector argument.                                         // 78
  // but if selector is explicitly passed in as false or undefined, we                                       // 79
  // want a selector that matches nothing.                                                                   // 80
  if (arguments.length === 0)                                                                                // 81
    selector = {};                                                                                           // 82
                                                                                                             // 83
  return new LocalCollection.Cursor(this, selector, options);                                                // 84
};                                                                                                           // 85
                                                                                                             // 86
// don't call this ctor directly.  use LocalCollection.find().                                               // 87
                                                                                                             // 88
LocalCollection.Cursor = function (collection, selector, options) {                                          // 89
  var self = this;                                                                                           // 90
  if (!options) options = {};                                                                                // 91
                                                                                                             // 92
  self.collection = collection;                                                                              // 93
  self.sorter = null;                                                                                        // 94
                                                                                                             // 95
  if (LocalCollection._selectorIsId(selector)) {                                                             // 96
    // stash for fast path                                                                                   // 97
    self._selectorId = selector;                                                                             // 98
    self.matcher = new Minimongo.Matcher(selector);                                                          // 99
  } else {                                                                                                   // 100
    self._selectorId = undefined;                                                                            // 101
    self.matcher = new Minimongo.Matcher(selector);                                                          // 102
    if (self.matcher.hasGeoQuery() || options.sort) {                                                        // 103
      self.sorter = new Minimongo.Sorter(options.sort || [],                                                 // 104
                                         { matcher: self.matcher });                                         // 105
    }                                                                                                        // 106
  }                                                                                                          // 107
  self.skip = options.skip;                                                                                  // 108
  self.limit = options.limit;                                                                                // 109
  self.fields = options.fields;                                                                              // 110
                                                                                                             // 111
  self._projectionFn = LocalCollection._compileProjection(self.fields || {});                                // 112
                                                                                                             // 113
  self._transform = LocalCollection.wrapTransform(options.transform);                                        // 114
                                                                                                             // 115
  // by default, queries register w/ Tracker when it is available.                                           // 116
  if (typeof Tracker !== "undefined")                                                                        // 117
    self.reactive = (options.reactive === undefined) ? true : options.reactive;                              // 118
};                                                                                                           // 119
                                                                                                             // 120
// Since we don't actually have a "nextObject" interface, there's really no                                  // 121
// reason to have a "rewind" interface.  All it did was make multiple calls                                  // 122
// to fetch/map/forEach return nothing the second time.                                                      // 123
// XXX COMPAT WITH 0.8.1                                                                                     // 124
LocalCollection.Cursor.prototype.rewind = function () {                                                      // 125
};                                                                                                           // 126
                                                                                                             // 127
LocalCollection.prototype.findOne = function (selector, options) {                                           // 128
  if (arguments.length === 0)                                                                                // 129
    selector = {};                                                                                           // 130
                                                                                                             // 131
  // NOTE: by setting limit 1 here, we end up using very inefficient                                         // 132
  // code that recomputes the whole query on each update. The upside is                                      // 133
  // that when you reactively depend on a findOne you only get                                               // 134
  // invalidated when the found object changes, not any object in the                                        // 135
  // collection. Most findOne will be by id, which has a fast path, so                                       // 136
  // this might not be a big deal. In most cases, invalidation causes                                        // 137
  // the called to re-query anyway, so this should be a net performance                                      // 138
  // improvement.                                                                                            // 139
  options = options || {};                                                                                   // 140
  options.limit = 1;                                                                                         // 141
                                                                                                             // 142
  return this.find(selector, options).fetch()[0];                                                            // 143
};                                                                                                           // 144
                                                                                                             // 145
/**                                                                                                          // 146
 * @callback IterationCallback                                                                               // 147
 * @param {Object} doc                                                                                       // 148
 * @param {Number} index                                                                                     // 149
 */                                                                                                          // 150
/**                                                                                                          // 151
 * @summary Call `callback` once for each matching document, sequentially and synchronously.                 // 152
 * @locus Anywhere                                                                                           // 153
 * @method  forEach                                                                                          // 154
 * @instance                                                                                                 // 155
 * @memberOf Mongo.Cursor                                                                                    // 156
 * @param {IterationCallback} callback Function to call. It will be called with three arguments: the document, a 0-based index, and <em>cursor</em> itself.
 * @param {Any} [thisArg] An object which will be the value of `this` inside `callback`.                     // 158
 */                                                                                                          // 159
LocalCollection.Cursor.prototype.forEach = function (callback, thisArg) {                                    // 160
  var self = this;                                                                                           // 161
                                                                                                             // 162
  var objects = self._getRawObjects({ordered: true});                                                        // 163
                                                                                                             // 164
  if (self.reactive) {                                                                                       // 165
    self._depend({                                                                                           // 166
      addedBefore: true,                                                                                     // 167
      removed: true,                                                                                         // 168
      changed: true,                                                                                         // 169
      movedBefore: true});                                                                                   // 170
  }                                                                                                          // 171
                                                                                                             // 172
  _.each(objects, function (elt, i) {                                                                        // 173
    // This doubles as a clone operation.                                                                    // 174
    elt = self._projectionFn(elt);                                                                           // 175
                                                                                                             // 176
    if (self._transform)                                                                                     // 177
      elt = self._transform(elt);                                                                            // 178
    callback.call(thisArg, elt, i, self);                                                                    // 179
  });                                                                                                        // 180
};                                                                                                           // 181
                                                                                                             // 182
LocalCollection.Cursor.prototype.getTransform = function () {                                                // 183
  return this._transform;                                                                                    // 184
};                                                                                                           // 185
                                                                                                             // 186
/**                                                                                                          // 187
 * @summary Map callback over all matching documents.  Returns an Array.                                     // 188
 * @locus Anywhere                                                                                           // 189
 * @method map                                                                                               // 190
 * @instance                                                                                                 // 191
 * @memberOf Mongo.Cursor                                                                                    // 192
 * @param {IterationCallback} callback Function to call. It will be called with three arguments: the document, a 0-based index, and <em>cursor</em> itself.
 * @param {Any} [thisArg] An object which will be the value of `this` inside `callback`.                     // 194
 */                                                                                                          // 195
LocalCollection.Cursor.prototype.map = function (callback, thisArg) {                                        // 196
  var self = this;                                                                                           // 197
  var res = [];                                                                                              // 198
  self.forEach(function (doc, index) {                                                                       // 199
    res.push(callback.call(thisArg, doc, index, self));                                                      // 200
  });                                                                                                        // 201
  return res;                                                                                                // 202
};                                                                                                           // 203
                                                                                                             // 204
/**                                                                                                          // 205
 * @summary Return all matching documents as an Array.                                                       // 206
 * @memberOf Mongo.Cursor                                                                                    // 207
 * @method  fetch                                                                                            // 208
 * @instance                                                                                                 // 209
 * @locus Anywhere                                                                                           // 210
 * @returns {Object[]}                                                                                       // 211
 */                                                                                                          // 212
LocalCollection.Cursor.prototype.fetch = function () {                                                       // 213
  var self = this;                                                                                           // 214
  var res = [];                                                                                              // 215
  self.forEach(function (doc) {                                                                              // 216
    res.push(doc);                                                                                           // 217
  });                                                                                                        // 218
  return res;                                                                                                // 219
};                                                                                                           // 220
                                                                                                             // 221
/**                                                                                                          // 222
 * @summary Returns the number of documents that match a query.                                              // 223
 * @memberOf Mongo.Cursor                                                                                    // 224
 * @method  count                                                                                            // 225
 * @instance                                                                                                 // 226
 * @locus Anywhere                                                                                           // 227
 * @returns {Number}                                                                                         // 228
 */                                                                                                          // 229
LocalCollection.Cursor.prototype.count = function () {                                                       // 230
  var self = this;                                                                                           // 231
                                                                                                             // 232
  if (self.reactive)                                                                                         // 233
    self._depend({added: true, removed: true},                                                               // 234
                 true /* allow the observe to be unordered */);                                              // 235
                                                                                                             // 236
  return self._getRawObjects({ordered: true}).length;                                                        // 237
};                                                                                                           // 238
                                                                                                             // 239
LocalCollection.Cursor.prototype._publishCursor = function (sub) {                                           // 240
  var self = this;                                                                                           // 241
  if (! self.collection.name)                                                                                // 242
    throw new Error("Can't publish a cursor from a collection without a name.");                             // 243
  var collection = self.collection.name;                                                                     // 244
                                                                                                             // 245
  // XXX minimongo should not depend on mongo-livedata!                                                      // 246
  return Mongo.Collection._publishCursor(self, sub, collection);                                             // 247
};                                                                                                           // 248
                                                                                                             // 249
LocalCollection.Cursor.prototype._getCollectionName = function () {                                          // 250
  var self = this;                                                                                           // 251
  return self.collection.name;                                                                               // 252
};                                                                                                           // 253
                                                                                                             // 254
LocalCollection._observeChangesCallbacksAreOrdered = function (callbacks) {                                  // 255
  if (callbacks.added && callbacks.addedBefore)                                                              // 256
    throw new Error("Please specify only one of added() and addedBefore()");                                 // 257
  return !!(callbacks.addedBefore || callbacks.movedBefore);                                                 // 258
};                                                                                                           // 259
                                                                                                             // 260
LocalCollection._observeCallbacksAreOrdered = function (callbacks) {                                         // 261
  if (callbacks.addedAt && callbacks.added)                                                                  // 262
    throw new Error("Please specify only one of added() and addedAt()");                                     // 263
  if (callbacks.changedAt && callbacks.changed)                                                              // 264
    throw new Error("Please specify only one of changed() and changedAt()");                                 // 265
  if (callbacks.removed && callbacks.removedAt)                                                              // 266
    throw new Error("Please specify only one of removed() and removedAt()");                                 // 267
                                                                                                             // 268
  return !!(callbacks.addedAt || callbacks.movedTo || callbacks.changedAt                                    // 269
            || callbacks.removedAt);                                                                         // 270
};                                                                                                           // 271
                                                                                                             // 272
// the handle that comes back from observe.                                                                  // 273
LocalCollection.ObserveHandle = function () {};                                                              // 274
                                                                                                             // 275
// options to contain:                                                                                       // 276
//  * callbacks for observe():                                                                               // 277
//    - addedAt (document, atIndex)                                                                          // 278
//    - added (document)                                                                                     // 279
//    - changedAt (newDocument, oldDocument, atIndex)                                                        // 280
//    - changed (newDocument, oldDocument)                                                                   // 281
//    - removedAt (document, atIndex)                                                                        // 282
//    - removed (document)                                                                                   // 283
//    - movedTo (document, oldIndex, newIndex)                                                               // 284
//                                                                                                           // 285
// attributes available on returned query handle:                                                            // 286
//  * stop(): end updates                                                                                    // 287
//  * collection: the collection this query is querying                                                      // 288
//                                                                                                           // 289
// iff x is a returned query handle, (x instanceof                                                           // 290
// LocalCollection.ObserveHandle) is true                                                                    // 291
//                                                                                                           // 292
// initial results delivered through added callback                                                          // 293
// XXX maybe callbacks should take a list of objects, to expose transactions?                                // 294
// XXX maybe support field limiting (to limit what you're notified on)                                       // 295
                                                                                                             // 296
_.extend(LocalCollection.Cursor.prototype, {                                                                 // 297
  /**                                                                                                        // 298
   * @summary Watch a query.  Receive callbacks as the result set changes.                                   // 299
   * @locus Anywhere                                                                                         // 300
   * @memberOf Mongo.Cursor                                                                                  // 301
   * @instance                                                                                               // 302
   * @param {Object} callbacks Functions to call to deliver the result set as it changes                     // 303
   */                                                                                                        // 304
  observe: function (options) {                                                                              // 305
    var self = this;                                                                                         // 306
    return LocalCollection._observeFromObserveChanges(self, options);                                        // 307
  },                                                                                                         // 308
                                                                                                             // 309
  /**                                                                                                        // 310
   * @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.
   * @locus Anywhere                                                                                         // 312
   * @memberOf Mongo.Cursor                                                                                  // 313
   * @instance                                                                                               // 314
   * @param {Object} callbacks Functions to call to deliver the result set as it changes                     // 315
   */                                                                                                        // 316
  observeChanges: function (options) {                                                                       // 317
    var self = this;                                                                                         // 318
                                                                                                             // 319
    var ordered = LocalCollection._observeChangesCallbacksAreOrdered(options);                               // 320
                                                                                                             // 321
    // there are several places that assume you aren't combining skip/limit with                             // 322
    // unordered observe.  eg, update's EJSON.clone, and the "there are several"                             // 323
    // comment in _modifyAndNotify                                                                           // 324
    // XXX allow skip/limit with unordered observe                                                           // 325
    if (!options._allow_unordered && !ordered && (self.skip || self.limit))                                  // 326
      throw new Error("must use ordered observe (ie, 'addedBefore' instead of 'added') with skip or limit"); // 327
                                                                                                             // 328
    if (self.fields && (self.fields._id === 0 || self.fields._id === false))                                 // 329
      throw Error("You may not observe a cursor with {fields: {_id: 0}}");                                   // 330
                                                                                                             // 331
    var query = {                                                                                            // 332
      matcher: self.matcher, // not fast pathed                                                              // 333
      sorter: ordered && self.sorter,                                                                        // 334
      distances: (                                                                                           // 335
        self.matcher.hasGeoQuery() && ordered && new LocalCollection._IdMap),                                // 336
      resultsSnapshot: null,                                                                                 // 337
      ordered: ordered,                                                                                      // 338
      cursor: self,                                                                                          // 339
      projectionFn: self._projectionFn                                                                       // 340
    };                                                                                                       // 341
    var qid;                                                                                                 // 342
                                                                                                             // 343
    // Non-reactive queries call added[Before] and then never call anything                                  // 344
    // else.                                                                                                 // 345
    if (self.reactive) {                                                                                     // 346
      qid = self.collection.next_qid++;                                                                      // 347
      self.collection.queries[qid] = query;                                                                  // 348
    }                                                                                                        // 349
    query.results = self._getRawObjects({                                                                    // 350
      ordered: ordered, distances: query.distances});                                                        // 351
    if (self.collection.paused)                                                                              // 352
      query.resultsSnapshot = (ordered ? [] : new LocalCollection._IdMap);                                   // 353
                                                                                                             // 354
    // wrap callbacks we were passed. callbacks only fire when not paused and                                // 355
    // are never undefined                                                                                   // 356
    // Filters out blacklisted fields according to cursor's projection.                                      // 357
    // XXX wrong place for this?                                                                             // 358
                                                                                                             // 359
    // furthermore, callbacks enqueue until the operation we're working on is                                // 360
    // done.                                                                                                 // 361
    var wrapCallback = function (f) {                                                                        // 362
      if (!f)                                                                                                // 363
        return function () {};                                                                               // 364
      return function (/*args*/) {                                                                           // 365
        var context = this;                                                                                  // 366
        var args = arguments;                                                                                // 367
                                                                                                             // 368
        if (self.collection.paused)                                                                          // 369
          return;                                                                                            // 370
                                                                                                             // 371
        self.collection._observeQueue.queueTask(function () {                                                // 372
          f.apply(context, args);                                                                            // 373
        });                                                                                                  // 374
      };                                                                                                     // 375
    };                                                                                                       // 376
    query.added = wrapCallback(options.added);                                                               // 377
    query.changed = wrapCallback(options.changed);                                                           // 378
    query.removed = wrapCallback(options.removed);                                                           // 379
    if (ordered) {                                                                                           // 380
      query.addedBefore = wrapCallback(options.addedBefore);                                                 // 381
      query.movedBefore = wrapCallback(options.movedBefore);                                                 // 382
    }                                                                                                        // 383
                                                                                                             // 384
    if (!options._suppress_initial && !self.collection.paused) {                                             // 385
      // XXX unify ordered and unordered interface                                                           // 386
      var each = ordered                                                                                     // 387
            ? _.bind(_.each, null, query.results)                                                            // 388
            : _.bind(query.results.forEach, query.results);                                                  // 389
      each(function (doc) {                                                                                  // 390
        var fields = EJSON.clone(doc);                                                                       // 391
                                                                                                             // 392
        delete fields._id;                                                                                   // 393
        if (ordered)                                                                                         // 394
          query.addedBefore(doc._id, self._projectionFn(fields), null);                                      // 395
        query.added(doc._id, self._projectionFn(fields));                                                    // 396
      });                                                                                                    // 397
    }                                                                                                        // 398
                                                                                                             // 399
    var handle = new LocalCollection.ObserveHandle;                                                          // 400
    _.extend(handle, {                                                                                       // 401
      collection: self.collection,                                                                           // 402
      stop: function () {                                                                                    // 403
        if (self.reactive)                                                                                   // 404
          delete self.collection.queries[qid];                                                               // 405
      }                                                                                                      // 406
    });                                                                                                      // 407
                                                                                                             // 408
    if (self.reactive && Tracker.active) {                                                                   // 409
      // XXX in many cases, the same observe will be recreated when                                          // 410
      // the current autorun is rerun.  we could save work by                                                // 411
      // letting it linger across rerun and potentially get                                                  // 412
      // repurposed if the same observe is performed, using logic                                            // 413
      // similar to that of Meteor.subscribe.                                                                // 414
      Tracker.onInvalidate(function () {                                                                     // 415
        handle.stop();                                                                                       // 416
      });                                                                                                    // 417
    }                                                                                                        // 418
    // run the observe callbacks resulting from the initial contents                                         // 419
    // before we leave the observe.                                                                          // 420
    self.collection._observeQueue.drain();                                                                   // 421
                                                                                                             // 422
    return handle;                                                                                           // 423
  }                                                                                                          // 424
});                                                                                                          // 425
                                                                                                             // 426
// Returns a collection of matching objects, but doesn't deep copy them.                                     // 427
//                                                                                                           // 428
// If ordered is set, returns a sorted array, respecting sorter, skip, and limit                             // 429
// properties of the query.  if sorter is falsey, no sort -- you get the natural                             // 430
// order.                                                                                                    // 431
//                                                                                                           // 432
// If ordered is not set, returns an object mapping from ID to doc (sorter, skip                             // 433
// and limit should not be set).                                                                             // 434
//                                                                                                           // 435
// If ordered is set and this cursor is a $near geoquery, then this function                                 // 436
// will use an _IdMap to track each distance from the $near argument point in                                // 437
// order to use it as a sort key. If an _IdMap is passed in the 'distances'                                  // 438
// argument, this function will clear it and use it for this purpose (otherwise                              // 439
// it will just create its own _IdMap). The observeChanges implementation uses                               // 440
// this to remember the distances after this function returns.                                               // 441
LocalCollection.Cursor.prototype._getRawObjects = function (options) {                                       // 442
  var self = this;                                                                                           // 443
  options = options || {};                                                                                   // 444
                                                                                                             // 445
  // XXX use OrderedDict instead of array, and make IdMap and OrderedDict                                    // 446
  // compatible                                                                                              // 447
  var results = options.ordered ? [] : new LocalCollection._IdMap;                                           // 448
                                                                                                             // 449
  // fast path for single ID value                                                                           // 450
  if (self._selectorId !== undefined) {                                                                      // 451
    // If you have non-zero skip and ask for a single id, you get                                            // 452
    // nothing. This is so it matches the behavior of the '{_id: foo}'                                       // 453
    // path.                                                                                                 // 454
    if (self.skip)                                                                                           // 455
      return results;                                                                                        // 456
                                                                                                             // 457
    var selectedDoc = self.collection._docs.get(self._selectorId);                                           // 458
    if (selectedDoc) {                                                                                       // 459
      if (options.ordered)                                                                                   // 460
        results.push(selectedDoc);                                                                           // 461
      else                                                                                                   // 462
        results.set(self._selectorId, selectedDoc);                                                          // 463
    }                                                                                                        // 464
    return results;                                                                                          // 465
  }                                                                                                          // 466
                                                                                                             // 467
  // slow path for arbitrary selector, sort, skip, limit                                                     // 468
                                                                                                             // 469
  // in the observeChanges case, distances is actually part of the "query" (ie,                              // 470
  // live results set) object.  in other cases, distances is only used inside                                // 471
  // this function.                                                                                          // 472
  var distances;                                                                                             // 473
  if (self.matcher.hasGeoQuery() && options.ordered) {                                                       // 474
    if (options.distances) {                                                                                 // 475
      distances = options.distances;                                                                         // 476
      distances.clear();                                                                                     // 477
    } else {                                                                                                 // 478
      distances = new LocalCollection._IdMap();                                                              // 479
    }                                                                                                        // 480
  }                                                                                                          // 481
                                                                                                             // 482
  self.collection._docs.forEach(function (doc, id) {                                                         // 483
    var matchResult = self.matcher.documentMatches(doc);                                                     // 484
    if (matchResult.result) {                                                                                // 485
      if (options.ordered) {                                                                                 // 486
        results.push(doc);                                                                                   // 487
        if (distances && matchResult.distance !== undefined)                                                 // 488
          distances.set(id, matchResult.distance);                                                           // 489
      } else {                                                                                               // 490
        results.set(id, doc);                                                                                // 491
      }                                                                                                      // 492
    }                                                                                                        // 493
    // Fast path for limited unsorted queries.                                                               // 494
    // XXX 'length' check here seems wrong for ordered                                                       // 495
    if (self.limit && !self.skip && !self.sorter &&                                                          // 496
        results.length === self.limit)                                                                       // 497
      return false;  // break                                                                                // 498
    return true;  // continue                                                                                // 499
  });                                                                                                        // 500
                                                                                                             // 501
  if (!options.ordered)                                                                                      // 502
    return results;                                                                                          // 503
                                                                                                             // 504
  if (self.sorter) {                                                                                         // 505
    var comparator = self.sorter.getComparator({distances: distances});                                      // 506
    results.sort(comparator);                                                                                // 507
  }                                                                                                          // 508
                                                                                                             // 509
  var idx_start = self.skip || 0;                                                                            // 510
  var idx_end = self.limit ? (self.limit + idx_start) : results.length;                                      // 511
  return results.slice(idx_start, idx_end);                                                                  // 512
};                                                                                                           // 513
                                                                                                             // 514
// XXX Maybe we need a version of observe that just calls a callback if                                      // 515
// anything changed.                                                                                         // 516
LocalCollection.Cursor.prototype._depend = function (changers, _allow_unordered) {                           // 517
  var self = this;                                                                                           // 518
                                                                                                             // 519
  if (Tracker.active) {                                                                                      // 520
    var v = new Tracker.Dependency;                                                                          // 521
    v.depend();                                                                                              // 522
    var notifyChange = _.bind(v.changed, v);                                                                 // 523
                                                                                                             // 524
    var options = {                                                                                          // 525
      _suppress_initial: true,                                                                               // 526
      _allow_unordered: _allow_unordered                                                                     // 527
    };                                                                                                       // 528
    _.each(['added', 'changed', 'removed', 'addedBefore', 'movedBefore'],                                    // 529
           function (fnName) {                                                                               // 530
             if (changers[fnName])                                                                           // 531
               options[fnName] = notifyChange;                                                               // 532
           });                                                                                               // 533
                                                                                                             // 534
    // observeChanges will stop() when this computation is invalidated                                       // 535
    self.observeChanges(options);                                                                            // 536
  }                                                                                                          // 537
};                                                                                                           // 538
                                                                                                             // 539
// XXX enforce rule that field names can't start with '$' or contain '.'                                     // 540
// (real mongodb does in fact enforce this)                                                                  // 541
// XXX possibly enforce that 'undefined' does not appear (we assume                                          // 542
// this in our handling of null and $exists)                                                                 // 543
LocalCollection.prototype.insert = function (doc, callback) {                                                // 544
  var self = this;                                                                                           // 545
  doc = EJSON.clone(doc);                                                                                    // 546
                                                                                                             // 547
  if (!_.has(doc, '_id')) {                                                                                  // 548
    // if you really want to use ObjectIDs, set this global.                                                 // 549
    // Mongo.Collection specifies its own ids and does not use this code.                                    // 550
    doc._id = LocalCollection._useOID ? new LocalCollection._ObjectID()                                      // 551
                                      : Random.id();                                                         // 552
  }                                                                                                          // 553
  var id = doc._id;                                                                                          // 554
                                                                                                             // 555
  if (self._docs.has(id))                                                                                    // 556
    throw MinimongoError("Duplicate _id '" + id + "'");                                                      // 557
                                                                                                             // 558
  self._saveOriginal(id, undefined);                                                                         // 559
  self._docs.set(id, doc);                                                                                   // 560
                                                                                                             // 561
  var queriesToRecompute = [];                                                                               // 562
  // trigger live queries that match                                                                         // 563
  for (var qid in self.queries) {                                                                            // 564
    var query = self.queries[qid];                                                                           // 565
    var matchResult = query.matcher.documentMatches(doc);                                                    // 566
    if (matchResult.result) {                                                                                // 567
      if (query.distances && matchResult.distance !== undefined)                                             // 568
        query.distances.set(id, matchResult.distance);                                                       // 569
      if (query.cursor.skip || query.cursor.limit)                                                           // 570
        queriesToRecompute.push(qid);                                                                        // 571
      else                                                                                                   // 572
        LocalCollection._insertInResults(query, doc);                                                        // 573
    }                                                                                                        // 574
  }                                                                                                          // 575
                                                                                                             // 576
  _.each(queriesToRecompute, function (qid) {                                                                // 577
    if (self.queries[qid])                                                                                   // 578
      self._recomputeResults(self.queries[qid]);                                                             // 579
  });                                                                                                        // 580
  self._observeQueue.drain();                                                                                // 581
                                                                                                             // 582
  // Defer because the caller likely doesn't expect the callback to be run                                   // 583
  // immediately.                                                                                            // 584
  if (callback)                                                                                              // 585
    Meteor.defer(function () {                                                                               // 586
      callback(null, id);                                                                                    // 587
    });                                                                                                      // 588
  return id;                                                                                                 // 589
};                                                                                                           // 590
                                                                                                             // 591
// Iterates over a subset of documents that could match selector; calls                                      // 592
// f(doc, id) on each of them.  Specifically, if selector specifies                                          // 593
// specific _id's, it only looks at those.  doc is *not* cloned: it is the                                   // 594
// same object that is in _docs.                                                                             // 595
LocalCollection.prototype._eachPossiblyMatchingDoc = function (selector, f) {                                // 596
  var self = this;                                                                                           // 597
  var specificIds = LocalCollection._idsMatchedBySelector(selector);                                         // 598
  if (specificIds) {                                                                                         // 599
    for (var i = 0; i < specificIds.length; ++i) {                                                           // 600
      var id = specificIds[i];                                                                               // 601
      var doc = self._docs.get(id);                                                                          // 602
      if (doc) {                                                                                             // 603
        var breakIfFalse = f(doc, id);                                                                       // 604
        if (breakIfFalse === false)                                                                          // 605
          break;                                                                                             // 606
      }                                                                                                      // 607
    }                                                                                                        // 608
  } else {                                                                                                   // 609
    self._docs.forEach(f);                                                                                   // 610
  }                                                                                                          // 611
};                                                                                                           // 612
                                                                                                             // 613
LocalCollection.prototype.remove = function (selector, callback) {                                           // 614
  var self = this;                                                                                           // 615
                                                                                                             // 616
  // Easy special case: if we're not calling observeChanges callbacks and we're                              // 617
  // not saving originals and we got asked to remove everything, then just empty                             // 618
  // everything directly.                                                                                    // 619
  if (self.paused && !self._savedOriginals && EJSON.equals(selector, {})) {                                  // 620
    var result = self._docs.size();                                                                          // 621
    self._docs.clear();                                                                                      // 622
    _.each(self.queries, function (query) {                                                                  // 623
      if (query.ordered) {                                                                                   // 624
        query.results = [];                                                                                  // 625
      } else {                                                                                               // 626
        query.results.clear();                                                                               // 627
      }                                                                                                      // 628
    });                                                                                                      // 629
    if (callback) {                                                                                          // 630
      Meteor.defer(function () {                                                                             // 631
        callback(null, result);                                                                              // 632
      });                                                                                                    // 633
    }                                                                                                        // 634
    return result;                                                                                           // 635
  }                                                                                                          // 636
                                                                                                             // 637
  var matcher = new Minimongo.Matcher(selector);                                                             // 638
  var remove = [];                                                                                           // 639
  self._eachPossiblyMatchingDoc(selector, function (doc, id) {                                               // 640
    if (matcher.documentMatches(doc).result)                                                                 // 641
      remove.push(id);                                                                                       // 642
  });                                                                                                        // 643
                                                                                                             // 644
  var queriesToRecompute = [];                                                                               // 645
  var queryRemove = [];                                                                                      // 646
  for (var i = 0; i < remove.length; i++) {                                                                  // 647
    var removeId = remove[i];                                                                                // 648
    var removeDoc = self._docs.get(removeId);                                                                // 649
    _.each(self.queries, function (query, qid) {                                                             // 650
      if (query.matcher.documentMatches(removeDoc).result) {                                                 // 651
        if (query.cursor.skip || query.cursor.limit)                                                         // 652
          queriesToRecompute.push(qid);                                                                      // 653
        else                                                                                                 // 654
          queryRemove.push({qid: qid, doc: removeDoc});                                                      // 655
      }                                                                                                      // 656
    });                                                                                                      // 657
    self._saveOriginal(removeId, removeDoc);                                                                 // 658
    self._docs.remove(removeId);                                                                             // 659
  }                                                                                                          // 660
                                                                                                             // 661
  // run live query callbacks _after_ we've removed the documents.                                           // 662
  _.each(queryRemove, function (remove) {                                                                    // 663
    var query = self.queries[remove.qid];                                                                    // 664
    if (query) {                                                                                             // 665
      query.distances && query.distances.remove(remove.doc._id);                                             // 666
      LocalCollection._removeFromResults(query, remove.doc);                                                 // 667
    }                                                                                                        // 668
  });                                                                                                        // 669
  _.each(queriesToRecompute, function (qid) {                                                                // 670
    var query = self.queries[qid];                                                                           // 671
    if (query)                                                                                               // 672
      self._recomputeResults(query);                                                                         // 673
  });                                                                                                        // 674
  self._observeQueue.drain();                                                                                // 675
  result = remove.length;                                                                                    // 676
  if (callback)                                                                                              // 677
    Meteor.defer(function () {                                                                               // 678
      callback(null, result);                                                                                // 679
    });                                                                                                      // 680
  return result;                                                                                             // 681
};                                                                                                           // 682
                                                                                                             // 683
// XXX atomicity: if multi is true, and one modification fails, do                                           // 684
// we rollback the whole operation, or what?                                                                 // 685
LocalCollection.prototype.update = function (selector, mod, options, callback) {                             // 686
  var self = this;                                                                                           // 687
  if (! callback && options instanceof Function) {                                                           // 688
    callback = options;                                                                                      // 689
    options = null;                                                                                          // 690
  }                                                                                                          // 691
  if (!options) options = {};                                                                                // 692
                                                                                                             // 693
  var matcher = new Minimongo.Matcher(selector);                                                             // 694
                                                                                                             // 695
  // Save the original results of any query that we might need to                                            // 696
  // _recomputeResults on, because _modifyAndNotify will mutate the objects in                               // 697
  // it. (We don't need to save the original results of paused queries because                               // 698
  // they already have a resultsSnapshot and we won't be diffing in                                          // 699
  // _recomputeResults.)                                                                                     // 700
  var qidToOriginalResults = {};                                                                             // 701
  _.each(self.queries, function (query, qid) {                                                               // 702
    // XXX for now, skip/limit implies ordered observe, so query.results is                                  // 703
    // always an array                                                                                       // 704
    if ((query.cursor.skip || query.cursor.limit) && ! self.paused)                                          // 705
      qidToOriginalResults[qid] = EJSON.clone(query.results);                                                // 706
  });                                                                                                        // 707
  var recomputeQids = {};                                                                                    // 708
                                                                                                             // 709
  var updateCount = 0;                                                                                       // 710
                                                                                                             // 711
  self._eachPossiblyMatchingDoc(selector, function (doc, id) {                                               // 712
    var queryResult = matcher.documentMatches(doc);                                                          // 713
    if (queryResult.result) {                                                                                // 714
      // XXX Should we save the original even if mod ends up being a no-op?                                  // 715
      self._saveOriginal(id, doc);                                                                           // 716
      self._modifyAndNotify(doc, mod, recomputeQids, queryResult.arrayIndices);                              // 717
      ++updateCount;                                                                                         // 718
      if (!options.multi)                                                                                    // 719
        return false;  // break                                                                              // 720
    }                                                                                                        // 721
    return true;                                                                                             // 722
  });                                                                                                        // 723
                                                                                                             // 724
  _.each(recomputeQids, function (dummy, qid) {                                                              // 725
    var query = self.queries[qid];                                                                           // 726
    if (query)                                                                                               // 727
      self._recomputeResults(query, qidToOriginalResults[qid]);                                              // 728
  });                                                                                                        // 729
  self._observeQueue.drain();                                                                                // 730
                                                                                                             // 731
  // If we are doing an upsert, and we didn't modify any documents yet, then                                 // 732
  // it's time to do an insert. Figure out what document we are inserting, and                               // 733
  // generate an id for it.                                                                                  // 734
  var insertedId;                                                                                            // 735
  if (updateCount === 0 && options.upsert) {                                                                 // 736
    var newDoc = LocalCollection._removeDollarOperators(selector);                                           // 737
    LocalCollection._modify(newDoc, mod, {isInsert: true});                                                  // 738
    if (! newDoc._id && options.insertedId)                                                                  // 739
      newDoc._id = options.insertedId;                                                                       // 740
    insertedId = self.insert(newDoc);                                                                        // 741
    updateCount = 1;                                                                                         // 742
  }                                                                                                          // 743
                                                                                                             // 744
  // Return the number of affected documents, or in the upsert case, an object                               // 745
  // containing the number of affected docs and the id of the doc that was                                   // 746
  // inserted, if any.                                                                                       // 747
  var result;                                                                                                // 748
  if (options._returnObject) {                                                                               // 749
    result = {                                                                                               // 750
      numberAffected: updateCount                                                                            // 751
    };                                                                                                       // 752
    if (insertedId !== undefined)                                                                            // 753
      result.insertedId = insertedId;                                                                        // 754
  } else {                                                                                                   // 755
    result = updateCount;                                                                                    // 756
  }                                                                                                          // 757
                                                                                                             // 758
  if (callback)                                                                                              // 759
    Meteor.defer(function () {                                                                               // 760
      callback(null, result);                                                                                // 761
    });                                                                                                      // 762
  return result;                                                                                             // 763
};                                                                                                           // 764
                                                                                                             // 765
// A convenience wrapper on update. LocalCollection.upsert(sel, mod) is                                      // 766
// equivalent to LocalCollection.update(sel, mod, { upsert: true, _returnObject:                             // 767
// true }).                                                                                                  // 768
LocalCollection.prototype.upsert = function (selector, mod, options, callback) {                             // 769
  var self = this;                                                                                           // 770
  if (! callback && typeof options === "function") {                                                         // 771
    callback = options;                                                                                      // 772
    options = {};                                                                                            // 773
  }                                                                                                          // 774
  return self.update(selector, mod, _.extend({}, options, {                                                  // 775
    upsert: true,                                                                                            // 776
    _returnObject: true                                                                                      // 777
  }), callback);                                                                                             // 778
};                                                                                                           // 779
                                                                                                             // 780
LocalCollection.prototype._modifyAndNotify = function (                                                      // 781
    doc, mod, recomputeQids, arrayIndices) {                                                                 // 782
  var self = this;                                                                                           // 783
                                                                                                             // 784
  var matched_before = {};                                                                                   // 785
  for (var qid in self.queries) {                                                                            // 786
    var query = self.queries[qid];                                                                           // 787
    if (query.ordered) {                                                                                     // 788
      matched_before[qid] = query.matcher.documentMatches(doc).result;                                       // 789
    } else {                                                                                                 // 790
      // Because we don't support skip or limit (yet) in unordered queries, we                               // 791
      // can just do a direct lookup.                                                                        // 792
      matched_before[qid] = query.results.has(doc._id);                                                      // 793
    }                                                                                                        // 794
  }                                                                                                          // 795
                                                                                                             // 796
  var old_doc = EJSON.clone(doc);                                                                            // 797
                                                                                                             // 798
  LocalCollection._modify(doc, mod, {arrayIndices: arrayIndices});                                           // 799
                                                                                                             // 800
  for (qid in self.queries) {                                                                                // 801
    query = self.queries[qid];                                                                               // 802
    var before = matched_before[qid];                                                                        // 803
    var afterMatch = query.matcher.documentMatches(doc);                                                     // 804
    var after = afterMatch.result;                                                                           // 805
    if (after && query.distances && afterMatch.distance !== undefined)                                       // 806
      query.distances.set(doc._id, afterMatch.distance);                                                     // 807
                                                                                                             // 808
    if (query.cursor.skip || query.cursor.limit) {                                                           // 809
      // We need to recompute any query where the doc may have been in the                                   // 810
      // cursor's window either before or after the update. (Note that if skip                               // 811
      // or limit is set, "before" and "after" being true do not necessarily                                 // 812
      // mean that the document is in the cursor's output after skip/limit is                                // 813
      // applied... but if they are false, then the document definitely is NOT                               // 814
      // in the output. So it's safe to skip recompute if neither before or                                  // 815
      // after are true.)                                                                                    // 816
      if (before || after)                                                                                   // 817
        recomputeQids[qid] = true;                                                                           // 818
    } else if (before && !after) {                                                                           // 819
      LocalCollection._removeFromResults(query, doc);                                                        // 820
    } else if (!before && after) {                                                                           // 821
      LocalCollection._insertInResults(query, doc);                                                          // 822
    } else if (before && after) {                                                                            // 823
      LocalCollection._updateInResults(query, doc, old_doc);                                                 // 824
    }                                                                                                        // 825
  }                                                                                                          // 826
};                                                                                                           // 827
                                                                                                             // 828
// XXX the sorted-query logic below is laughably inefficient. we'll                                          // 829
// need to come up with a better datastructure for this.                                                     // 830
//                                                                                                           // 831
// XXX the logic for observing with a skip or a limit is even more                                           // 832
// laughably inefficient. we recompute the whole results every time!                                         // 833
                                                                                                             // 834
LocalCollection._insertInResults = function (query, doc) {                                                   // 835
  var fields = EJSON.clone(doc);                                                                             // 836
  delete fields._id;                                                                                         // 837
  if (query.ordered) {                                                                                       // 838
    if (!query.sorter) {                                                                                     // 839
      query.addedBefore(doc._id, query.projectionFn(fields), null);                                          // 840
      query.results.push(doc);                                                                               // 841
    } else {                                                                                                 // 842
      var i = LocalCollection._insertInSortedList(                                                           // 843
        query.sorter.getComparator({distances: query.distances}),                                            // 844
        query.results, doc);                                                                                 // 845
      var next = query.results[i+1];                                                                         // 846
      if (next)                                                                                              // 847
        next = next._id;                                                                                     // 848
      else                                                                                                   // 849
        next = null;                                                                                         // 850
      query.addedBefore(doc._id, query.projectionFn(fields), next);                                          // 851
    }                                                                                                        // 852
    query.added(doc._id, query.projectionFn(fields));                                                        // 853
  } else {                                                                                                   // 854
    query.added(doc._id, query.projectionFn(fields));                                                        // 855
    query.results.set(doc._id, doc);                                                                         // 856
  }                                                                                                          // 857
};                                                                                                           // 858
                                                                                                             // 859
LocalCollection._removeFromResults = function (query, doc) {                                                 // 860
  if (query.ordered) {                                                                                       // 861
    var i = LocalCollection._findInOrderedResults(query, doc);                                               // 862
    query.removed(doc._id);                                                                                  // 863
    query.results.splice(i, 1);                                                                              // 864
  } else {                                                                                                   // 865
    var id = doc._id;  // in case callback mutates doc                                                       // 866
    query.removed(doc._id);                                                                                  // 867
    query.results.remove(id);                                                                                // 868
  }                                                                                                          // 869
};                                                                                                           // 870
                                                                                                             // 871
LocalCollection._updateInResults = function (query, doc, old_doc) {                                          // 872
  if (!EJSON.equals(doc._id, old_doc._id))                                                                   // 873
    throw new Error("Can't change a doc's _id while updating");                                              // 874
  var projectionFn = query.projectionFn;                                                                     // 875
  var changedFields = LocalCollection._makeChangedFields(                                                    // 876
    projectionFn(doc), projectionFn(old_doc));                                                               // 877
                                                                                                             // 878
  if (!query.ordered) {                                                                                      // 879
    if (!_.isEmpty(changedFields)) {                                                                         // 880
      query.changed(doc._id, changedFields);                                                                 // 881
      query.results.set(doc._id, doc);                                                                       // 882
    }                                                                                                        // 883
    return;                                                                                                  // 884
  }                                                                                                          // 885
                                                                                                             // 886
  var orig_idx = LocalCollection._findInOrderedResults(query, doc);                                          // 887
                                                                                                             // 888
  if (!_.isEmpty(changedFields))                                                                             // 889
    query.changed(doc._id, changedFields);                                                                   // 890
  if (!query.sorter)                                                                                         // 891
    return;                                                                                                  // 892
                                                                                                             // 893
  // just take it out and put it back in again, and see if the index                                         // 894
  // changes                                                                                                 // 895
  query.results.splice(orig_idx, 1);                                                                         // 896
  var new_idx = LocalCollection._insertInSortedList(                                                         // 897
    query.sorter.getComparator({distances: query.distances}),                                                // 898
    query.results, doc);                                                                                     // 899
  if (orig_idx !== new_idx) {                                                                                // 900
    var next = query.results[new_idx+1];                                                                     // 901
    if (next)                                                                                                // 902
      next = next._id;                                                                                       // 903
    else                                                                                                     // 904
      next = null;                                                                                           // 905
    query.movedBefore && query.movedBefore(doc._id, next);                                                   // 906
  }                                                                                                          // 907
};                                                                                                           // 908
                                                                                                             // 909
// Recomputes the results of a query and runs observe callbacks for the                                      // 910
// difference between the previous results and the current results (unless                                   // 911
// paused). Used for skip/limit queries.                                                                     // 912
//                                                                                                           // 913
// When this is used by insert or remove, it can just use query.results for the                              // 914
// old results (and there's no need to pass in oldResults), because these                                    // 915
// operations don't mutate the documents in the collection. Update needs to pass                             // 916
// in an oldResults which was deep-copied before the modifier was applied.                                   // 917
//                                                                                                           // 918
// oldResults is guaranteed to be ignored if the query is not paused.                                        // 919
LocalCollection.prototype._recomputeResults = function (query, oldResults) {                                 // 920
  var self = this;                                                                                           // 921
  if (! self.paused && ! oldResults)                                                                         // 922
    oldResults = query.results;                                                                              // 923
  if (query.distances)                                                                                       // 924
    query.distances.clear();                                                                                 // 925
  query.results = query.cursor._getRawObjects({                                                              // 926
    ordered: query.ordered, distances: query.distances});                                                    // 927
                                                                                                             // 928
  if (! self.paused) {                                                                                       // 929
    LocalCollection._diffQueryChanges(                                                                       // 930
      query.ordered, oldResults, query.results, query,                                                       // 931
      { projectionFn: query.projectionFn });                                                                 // 932
  }                                                                                                          // 933
};                                                                                                           // 934
                                                                                                             // 935
                                                                                                             // 936
LocalCollection._findInOrderedResults = function (query, doc) {                                              // 937
  if (!query.ordered)                                                                                        // 938
    throw new Error("Can't call _findInOrderedResults on unordered query");                                  // 939
  for (var i = 0; i < query.results.length; i++)                                                             // 940
    if (query.results[i] === doc)                                                                            // 941
      return i;                                                                                              // 942
  throw Error("object missing from query");                                                                  // 943
};                                                                                                           // 944
                                                                                                             // 945
// This binary search puts a value between any equal values, and the first                                   // 946
// lesser value.                                                                                             // 947
LocalCollection._binarySearch = function (cmp, array, value) {                                               // 948
  var first = 0, rangeLength = array.length;                                                                 // 949
                                                                                                             // 950
  while (rangeLength > 0) {                                                                                  // 951
    var halfRange = Math.floor(rangeLength/2);                                                               // 952
    if (cmp(value, array[first + halfRange]) >= 0) {                                                         // 953
      first += halfRange + 1;                                                                                // 954
      rangeLength -= halfRange + 1;                                                                          // 955
    } else {                                                                                                 // 956
      rangeLength = halfRange;                                                                               // 957
    }                                                                                                        // 958
  }                                                                                                          // 959
  return first;                                                                                              // 960
};                                                                                                           // 961
                                                                                                             // 962
LocalCollection._insertInSortedList = function (cmp, array, value) {                                         // 963
  if (array.length === 0) {                                                                                  // 964
    array.push(value);                                                                                       // 965
    return 0;                                                                                                // 966
  }                                                                                                          // 967
                                                                                                             // 968
  var idx = LocalCollection._binarySearch(cmp, array, value);                                                // 969
  array.splice(idx, 0, value);                                                                               // 970
  return idx;                                                                                                // 971
};                                                                                                           // 972
                                                                                                             // 973
// To track what documents are affected by a piece of code, call saveOriginals()                             // 974
// before it and retrieveOriginals() after it. retrieveOriginals returns an                                  // 975
// object whose keys are the ids of the documents that were affected since the                               // 976
// call to saveOriginals(), and the values are equal to the document's contents                              // 977
// at the time of saveOriginals. (In the case of an inserted document, undefined                             // 978
// is the value.) You must alternate between calls to saveOriginals() and                                    // 979
// retrieveOriginals().                                                                                      // 980
LocalCollection.prototype.saveOriginals = function () {                                                      // 981
  var self = this;                                                                                           // 982
  if (self._savedOriginals)                                                                                  // 983
    throw new Error("Called saveOriginals twice without retrieveOriginals");                                 // 984
  self._savedOriginals = new LocalCollection._IdMap;                                                         // 985
};                                                                                                           // 986
LocalCollection.prototype.retrieveOriginals = function () {                                                  // 987
  var self = this;                                                                                           // 988
  if (!self._savedOriginals)                                                                                 // 989
    throw new Error("Called retrieveOriginals without saveOriginals");                                       // 990
                                                                                                             // 991
  var originals = self._savedOriginals;                                                                      // 992
  self._savedOriginals = null;                                                                               // 993
  return originals;                                                                                          // 994
};                                                                                                           // 995
                                                                                                             // 996
LocalCollection.prototype._saveOriginal = function (id, doc) {                                               // 997
  var self = this;                                                                                           // 998
  // Are we even trying to save originals?                                                                   // 999
  if (!self._savedOriginals)                                                                                 // 1000
    return;                                                                                                  // 1001
  // Have we previously mutated the original (and so 'doc' is not actually                                   // 1002
  // original)?  (Note the 'has' check rather than truth: we store undefined                                 // 1003
  // here for inserted docs!)                                                                                // 1004
  if (self._savedOriginals.has(id))                                                                          // 1005
    return;                                                                                                  // 1006
  self._savedOriginals.set(id, EJSON.clone(doc));                                                            // 1007
};                                                                                                           // 1008
                                                                                                             // 1009
// Pause the observers. No callbacks from observers will fire until                                          // 1010
// 'resumeObservers' is called.                                                                              // 1011
LocalCollection.prototype.pauseObservers = function () {                                                     // 1012
  // No-op if already paused.                                                                                // 1013
  if (this.paused)                                                                                           // 1014
    return;                                                                                                  // 1015
                                                                                                             // 1016
  // Set the 'paused' flag such that new observer messages don't fire.                                       // 1017
  this.paused = true;                                                                                        // 1018
                                                                                                             // 1019
  // Take a snapshot of the query results for each query.                                                    // 1020
  for (var qid in this.queries) {                                                                            // 1021
    var query = this.queries[qid];                                                                           // 1022
                                                                                                             // 1023
    query.resultsSnapshot = EJSON.clone(query.results);                                                      // 1024
  }                                                                                                          // 1025
};                                                                                                           // 1026
                                                                                                             // 1027
// Resume the observers. Observers immediately receive change                                                // 1028
// notifications to bring them to the current state of the                                                   // 1029
// database. Note that this is not just replaying all the changes that                                       // 1030
// happened during the pause, it is a smarter 'coalesced' diff.                                              // 1031
LocalCollection.prototype.resumeObservers = function () {                                                    // 1032
  var self = this;                                                                                           // 1033
  // No-op if not paused.                                                                                    // 1034
  if (!this.paused)                                                                                          // 1035
    return;                                                                                                  // 1036
                                                                                                             // 1037
  // Unset the 'paused' flag. Make sure to do this first, otherwise                                          // 1038
  // observer methods won't actually fire when we trigger them.                                              // 1039
  this.paused = false;                                                                                       // 1040
                                                                                                             // 1041
  for (var qid in this.queries) {                                                                            // 1042
    var query = self.queries[qid];                                                                           // 1043
    // Diff the current results against the snapshot and send to observers.                                  // 1044
    // pass the query object for its observer callbacks.                                                     // 1045
    LocalCollection._diffQueryChanges(                                                                       // 1046
      query.ordered, query.resultsSnapshot, query.results, query,                                            // 1047
      { projectionFn: query.projectionFn });                                                                 // 1048
    query.resultsSnapshot = null;                                                                            // 1049
  }                                                                                                          // 1050
  self._observeQueue.drain();                                                                                // 1051
};                                                                                                           // 1052
                                                                                                             // 1053
                                                                                                             // 1054
// NB: used by livedata                                                                                      // 1055
LocalCollection._idStringify = function (id) {                                                               // 1056
  if (id instanceof LocalCollection._ObjectID) {                                                             // 1057
    return id.valueOf();                                                                                     // 1058
  } else if (typeof id === 'string') {                                                                       // 1059
    if (id === "") {                                                                                         // 1060
      return id;                                                                                             // 1061
    } else if (id.substr(0, 1) === "-" || // escape previously dashed strings                                // 1062
               id.substr(0, 1) === "~" || // escape escaped numbers, true, false                             // 1063
               LocalCollection._looksLikeObjectID(id) || // escape object-id-form strings                    // 1064
               id.substr(0, 1) === '{') { // escape object-form strings, for maybe implementing later        // 1065
      return "-" + id;                                                                                       // 1066
    } else {                                                                                                 // 1067
      return id; // other strings go through unchanged.                                                      // 1068
    }                                                                                                        // 1069
  } else if (id === undefined) {                                                                             // 1070
    return '-';                                                                                              // 1071
  } else if (typeof id === 'object' && id !== null) {                                                        // 1072
    throw new Error("Meteor does not currently support objects other than ObjectID as ids");                 // 1073
  } else { // Numbers, true, false, null                                                                     // 1074
    return "~" + JSON.stringify(id);                                                                         // 1075
  }                                                                                                          // 1076
};                                                                                                           // 1077
                                                                                                             // 1078
                                                                                                             // 1079
// NB: used by livedata                                                                                      // 1080
LocalCollection._idParse = function (id) {                                                                   // 1081
  if (id === "") {                                                                                           // 1082
    return id;                                                                                               // 1083
  } else if (id === '-') {                                                                                   // 1084
    return undefined;                                                                                        // 1085
  } else if (id.substr(0, 1) === '-') {                                                                      // 1086
    return id.substr(1);                                                                                     // 1087
  } else if (id.substr(0, 1) === '~') {                                                                      // 1088
    return JSON.parse(id.substr(1));                                                                         // 1089
  } else if (LocalCollection._looksLikeObjectID(id)) {                                                       // 1090
    return new LocalCollection._ObjectID(id);                                                                // 1091
  } else {                                                                                                   // 1092
    return id;                                                                                               // 1093
  }                                                                                                          // 1094
};                                                                                                           // 1095
                                                                                                             // 1096
LocalCollection._makeChangedFields = function (newDoc, oldDoc) {                                             // 1097
  var fields = {};                                                                                           // 1098
  LocalCollection._diffObjects(oldDoc, newDoc, {                                                             // 1099
    leftOnly: function (key, value) {                                                                        // 1100
      fields[key] = undefined;                                                                               // 1101
    },                                                                                                       // 1102
    rightOnly: function (key, value) {                                                                       // 1103
      fields[key] = value;                                                                                   // 1104
    },                                                                                                       // 1105
    both: function (key, leftValue, rightValue) {                                                            // 1106
      if (!EJSON.equals(leftValue, rightValue))                                                              // 1107
        fields[key] = rightValue;                                                                            // 1108
    }                                                                                                        // 1109
  });                                                                                                        // 1110
  return fields;                                                                                             // 1111
};                                                                                                           // 1112
                                                                                                             // 1113
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/wrap_transform.js                                                                      //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
// Wrap a transform function to return objects that have the _id field                                       // 1
// of the untransformed document. This ensures that subsystems such as                                       // 2
// the observe-sequence package that call `observe` can keep track of                                        // 3
// the documents identities.                                                                                 // 4
//                                                                                                           // 5
// - Require that it returns objects                                                                         // 6
// - If the return value has an _id field, verify that it matches the                                        // 7
//   original _id field                                                                                      // 8
// - If the return value doesn't have an _id field, add it back.                                             // 9
LocalCollection.wrapTransform = function (transform) {                                                       // 10
  if (! transform)                                                                                           // 11
    return null;                                                                                             // 12
                                                                                                             // 13
  // No need to doubly-wrap transforms.                                                                      // 14
  if (transform.__wrappedTransform__)                                                                        // 15
    return transform;                                                                                        // 16
                                                                                                             // 17
  var wrapped = function (doc) {                                                                             // 18
    if (!_.has(doc, '_id')) {                                                                                // 19
      // XXX do we ever have a transform on the oplog's collection? because that                             // 20
      // collection has no _id.                                                                              // 21
      throw new Error("can only transform documents with _id");                                              // 22
    }                                                                                                        // 23
                                                                                                             // 24
    var id = doc._id;                                                                                        // 25
    // XXX consider making tracker a weak dependency and checking Package.tracker here                       // 26
    var transformed = Tracker.nonreactive(function () {                                                      // 27
      return transform(doc);                                                                                 // 28
    });                                                                                                      // 29
                                                                                                             // 30
    if (!isPlainObject(transformed)) {                                                                       // 31
      throw new Error("transform must return object");                                                       // 32
    }                                                                                                        // 33
                                                                                                             // 34
    if (_.has(transformed, '_id')) {                                                                         // 35
      if (!EJSON.equals(transformed._id, id)) {                                                              // 36
        throw new Error("transformed document can't have different _id");                                    // 37
      }                                                                                                      // 38
    } else {                                                                                                 // 39
      transformed._id = id;                                                                                  // 40
    }                                                                                                        // 41
    return transformed;                                                                                      // 42
  };                                                                                                         // 43
  wrapped.__wrappedTransform__ = true;                                                                       // 44
  return wrapped;                                                                                            // 45
};                                                                                                           // 46
                                                                                                             // 47
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/helpers.js                                                                             //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
// Like _.isArray, but doesn't regard polyfilled Uint8Arrays on old browsers as                              // 1
// arrays.                                                                                                   // 2
// XXX maybe this should be EJSON.isArray                                                                    // 3
isArray = function (x) {                                                                                     // 4
  return _.isArray(x) && !EJSON.isBinary(x);                                                                 // 5
};                                                                                                           // 6
                                                                                                             // 7
// XXX maybe this should be EJSON.isObject, though EJSON doesn't know about                                  // 8
// RegExp                                                                                                    // 9
// XXX note that _type(undefined) === 3!!!!                                                                  // 10
isPlainObject = LocalCollection._isPlainObject = function (x) {                                              // 11
  return x && LocalCollection._f._type(x) === 3;                                                             // 12
};                                                                                                           // 13
                                                                                                             // 14
isIndexable = function (x) {                                                                                 // 15
  return isArray(x) || isPlainObject(x);                                                                     // 16
};                                                                                                           // 17
                                                                                                             // 18
// Returns true if this is an object with at least one key and all keys begin                                // 19
// with $.  Unless inconsistentOK is set, throws if some keys begin with $ and                               // 20
// others don't.                                                                                             // 21
isOperatorObject = function (valueSelector, inconsistentOK) {                                                // 22
  if (!isPlainObject(valueSelector))                                                                         // 23
    return false;                                                                                            // 24
                                                                                                             // 25
  var theseAreOperators = undefined;                                                                         // 26
  _.each(valueSelector, function (value, selKey) {                                                           // 27
    var thisIsOperator = selKey.substr(0, 1) === '$';                                                        // 28
    if (theseAreOperators === undefined) {                                                                   // 29
      theseAreOperators = thisIsOperator;                                                                    // 30
    } else if (theseAreOperators !== thisIsOperator) {                                                       // 31
      if (!inconsistentOK)                                                                                   // 32
        throw new Error("Inconsistent operator: " +                                                          // 33
                        JSON.stringify(valueSelector));                                                      // 34
      theseAreOperators = false;                                                                             // 35
    }                                                                                                        // 36
  });                                                                                                        // 37
  return !!theseAreOperators;  // {} has no operators                                                        // 38
};                                                                                                           // 39
                                                                                                             // 40
                                                                                                             // 41
// string can be converted to integer                                                                        // 42
isNumericKey = function (s) {                                                                                // 43
  return /^[0-9]+$/.test(s);                                                                                 // 44
};                                                                                                           // 45
                                                                                                             // 46
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/selector.js                                                                            //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
// The minimongo selector compiler!                                                                          // 1
                                                                                                             // 2
// Terminology:                                                                                              // 3
//  - a "selector" is the EJSON object representing a selector                                               // 4
//  - a "matcher" is its compiled form (whether a full Minimongo.Matcher                                     // 5
//    object or one of the component lambdas that matches parts of it)                                       // 6
//  - a "result object" is an object with a "result" field and maybe                                         // 7
//    distance and arrayIndices.                                                                             // 8
//  - a "branched value" is an object with a "value" field and maybe                                         // 9
//    "dontIterate" and "arrayIndices".                                                                      // 10
//  - a "document" is a top-level object that can be stored in a collection.                                 // 11
//  - a "lookup function" is a function that takes in a document and returns                                 // 12
//    an array of "branched values".                                                                         // 13
//  - a "branched matcher" maps from an array of branched values to a result                                 // 14
//    object.                                                                                                // 15
//  - an "element matcher" maps from a single value to a bool.                                               // 16
                                                                                                             // 17
// Main entry point.                                                                                         // 18
//   var matcher = new Minimongo.Matcher({a: {$gt: 5}});                                                     // 19
//   if (matcher.documentMatches({a: 7})) ...                                                                // 20
Minimongo.Matcher = function (selector) {                                                                    // 21
  var self = this;                                                                                           // 22
  // A set (object mapping string -> *) of all of the document paths looked                                  // 23
  // at by the selector. Also includes the empty string if it may look at any                                // 24
  // path (eg, $where).                                                                                      // 25
  self._paths = {};                                                                                          // 26
  // Set to true if compilation finds a $near.                                                               // 27
  self._hasGeoQuery = false;                                                                                 // 28
  // Set to true if compilation finds a $where.                                                              // 29
  self._hasWhere = false;                                                                                    // 30
  // Set to false if compilation finds anything other than a simple equality or                              // 31
  // one or more of '$gt', '$gte', '$lt', '$lte', '$ne', '$in', '$nin' used with                             // 32
  // scalars as operands.                                                                                    // 33
  self._isSimple = true;                                                                                     // 34
  // Set to a dummy document which always matches this Matcher. Or set to null                               // 35
  // if such document is too hard to find.                                                                   // 36
  self._matchingDocument = undefined;                                                                        // 37
  // A clone of the original selector. It may just be a function if the user                                 // 38
  // passed in a function; otherwise is definitely an object (eg, IDs are                                    // 39
  // translated into {_id: ID} first. Used by canBecomeTrueByModifier and                                    // 40
  // Sorter._useWithMatcher.                                                                                 // 41
  self._selector = null;                                                                                     // 42
  self._docMatcher = self._compileSelector(selector);                                                        // 43
};                                                                                                           // 44
                                                                                                             // 45
_.extend(Minimongo.Matcher.prototype, {                                                                      // 46
  documentMatches: function (doc) {                                                                          // 47
    if (!doc || typeof doc !== "object") {                                                                   // 48
      throw Error("documentMatches needs a document");                                                       // 49
    }                                                                                                        // 50
    return this._docMatcher(doc);                                                                            // 51
  },                                                                                                         // 52
  hasGeoQuery: function () {                                                                                 // 53
    return this._hasGeoQuery;                                                                                // 54
  },                                                                                                         // 55
  hasWhere: function () {                                                                                    // 56
    return this._hasWhere;                                                                                   // 57
  },                                                                                                         // 58
  isSimple: function () {                                                                                    // 59
    return this._isSimple;                                                                                   // 60
  },                                                                                                         // 61
                                                                                                             // 62
  // Given a selector, return a function that takes one argument, a                                          // 63
  // document. It returns a result object.                                                                   // 64
  _compileSelector: function (selector) {                                                                    // 65
    var self = this;                                                                                         // 66
    // you can pass a literal function instead of a selector                                                 // 67
    if (selector instanceof Function) {                                                                      // 68
      self._isSimple = false;                                                                                // 69
      self._selector = selector;                                                                             // 70
      self._recordPathUsed('');                                                                              // 71
      return function (doc) {                                                                                // 72
        return {result: !!selector.call(doc)};                                                               // 73
      };                                                                                                     // 74
    }                                                                                                        // 75
                                                                                                             // 76
    // shorthand -- scalars match _id                                                                        // 77
    if (LocalCollection._selectorIsId(selector)) {                                                           // 78
      self._selector = {_id: selector};                                                                      // 79
      self._recordPathUsed('_id');                                                                           // 80
      return function (doc) {                                                                                // 81
        return {result: EJSON.equals(doc._id, selector)};                                                    // 82
      };                                                                                                     // 83
    }                                                                                                        // 84
                                                                                                             // 85
    // protect against dangerous selectors.  falsey and {_id: falsey} are both                               // 86
    // likely programmer error, and not what you want, particularly for                                      // 87
    // destructive operations.                                                                               // 88
    if (!selector || (('_id' in selector) && !selector._id)) {                                               // 89
      self._isSimple = false;                                                                                // 90
      return nothingMatcher;                                                                                 // 91
    }                                                                                                        // 92
                                                                                                             // 93
    // Top level can't be an array or true or binary.                                                        // 94
    if (typeof(selector) === 'boolean' || isArray(selector) ||                                               // 95
        EJSON.isBinary(selector))                                                                            // 96
      throw new Error("Invalid selector: " + selector);                                                      // 97
                                                                                                             // 98
    self._selector = EJSON.clone(selector);                                                                  // 99
    return compileDocumentSelector(selector, self, {isRoot: true});                                          // 100
  },                                                                                                         // 101
  _recordPathUsed: function (path) {                                                                         // 102
    this._paths[path] = true;                                                                                // 103
  },                                                                                                         // 104
  // Returns a list of key paths the given selector is looking for. It includes                              // 105
  // the empty string if there is a $where.                                                                  // 106
  _getPaths: function () {                                                                                   // 107
    return _.keys(this._paths);                                                                              // 108
  }                                                                                                          // 109
});                                                                                                          // 110
                                                                                                             // 111
                                                                                                             // 112
// Takes in a selector that could match a full document (eg, the original                                    // 113
// selector). Returns a function mapping document->result object.                                            // 114
//                                                                                                           // 115
// matcher is the Matcher object we are compiling.                                                           // 116
//                                                                                                           // 117
// If this is the root document selector (ie, not wrapped in $and or the like),                              // 118
// then isRoot is true. (This is used by $near.)                                                             // 119
var compileDocumentSelector = function (docSelector, matcher, options) {                                     // 120
  options = options || {};                                                                                   // 121
  var docMatchers = [];                                                                                      // 122
  _.each(docSelector, function (subSelector, key) {                                                          // 123
    if (key.substr(0, 1) === '$') {                                                                          // 124
      // Outer operators are either logical operators (they recurse back into                                // 125
      // this function), or $where.                                                                          // 126
      if (!_.has(LOGICAL_OPERATORS, key))                                                                    // 127
        throw new Error("Unrecognized logical operator: " + key);                                            // 128
      matcher._isSimple = false;                                                                             // 129
      docMatchers.push(LOGICAL_OPERATORS[key](subSelector, matcher,                                          // 130
                                              options.inElemMatch));                                         // 131
    } else {                                                                                                 // 132
      // Record this path, but only if we aren't in an elemMatcher, since in an                              // 133
      // elemMatch this is a path inside an object in an array, not in the doc                               // 134
      // root.                                                                                               // 135
      if (!options.inElemMatch)                                                                              // 136
        matcher._recordPathUsed(key);                                                                        // 137
      var lookUpByIndex = makeLookupFunction(key);                                                           // 138
      var valueMatcher =                                                                                     // 139
        compileValueSelector(subSelector, matcher, options.isRoot);                                          // 140
      docMatchers.push(function (doc) {                                                                      // 141
        var branchValues = lookUpByIndex(doc);                                                               // 142
        return valueMatcher(branchValues);                                                                   // 143
      });                                                                                                    // 144
    }                                                                                                        // 145
  });                                                                                                        // 146
                                                                                                             // 147
  return andDocumentMatchers(docMatchers);                                                                   // 148
};                                                                                                           // 149
                                                                                                             // 150
// Takes in a selector that could match a key-indexed value in a document; eg,                               // 151
// {$gt: 5, $lt: 9}, or a regular expression, or any non-expression object (to                               // 152
// indicate equality).  Returns a branched matcher: a function mapping                                       // 153
// [branched value]->result object.                                                                          // 154
var compileValueSelector = function (valueSelector, matcher, isRoot) {                                       // 155
  if (valueSelector instanceof RegExp) {                                                                     // 156
    matcher._isSimple = false;                                                                               // 157
    return convertElementMatcherToBranchedMatcher(                                                           // 158
      regexpElementMatcher(valueSelector));                                                                  // 159
  } else if (isOperatorObject(valueSelector)) {                                                              // 160
    return operatorBranchedMatcher(valueSelector, matcher, isRoot);                                          // 161
  } else {                                                                                                   // 162
    return convertElementMatcherToBranchedMatcher(                                                           // 163
      equalityElementMatcher(valueSelector));                                                                // 164
  }                                                                                                          // 165
};                                                                                                           // 166
                                                                                                             // 167
// Given an element matcher (which evaluates a single value), returns a branched                             // 168
// value (which evaluates the element matcher on all the branches and returns a                              // 169
// more structured return value possibly including arrayIndices).                                            // 170
var convertElementMatcherToBranchedMatcher = function (                                                      // 171
    elementMatcher, options) {                                                                               // 172
  options = options || {};                                                                                   // 173
  return function (branches) {                                                                               // 174
    var expanded = branches;                                                                                 // 175
    if (!options.dontExpandLeafArrays) {                                                                     // 176
      expanded = expandArraysInBranches(                                                                     // 177
        branches, options.dontIncludeLeafArrays);                                                            // 178
    }                                                                                                        // 179
    var ret = {};                                                                                            // 180
    ret.result = _.any(expanded, function (element) {                                                        // 181
      var matched = elementMatcher(element.value);                                                           // 182
                                                                                                             // 183
      // Special case for $elemMatch: it means "true, and use this as an array                               // 184
      // index if I didn't already have one".                                                                // 185
      if (typeof matched === 'number') {                                                                     // 186
        // XXX This code dates from when we only stored a single array index                                 // 187
        // (for the outermost array). Should we be also including deeper array                               // 188
        // indices from the $elemMatch match?                                                                // 189
        if (!element.arrayIndices)                                                                           // 190
          element.arrayIndices = [matched];                                                                  // 191
        matched = true;                                                                                      // 192
      }                                                                                                      // 193
                                                                                                             // 194
      // If some element matched, and it's tagged with array indices, include                                // 195
      // those indices in our result object.                                                                 // 196
      if (matched && element.arrayIndices)                                                                   // 197
        ret.arrayIndices = element.arrayIndices;                                                             // 198
                                                                                                             // 199
      return matched;                                                                                        // 200
    });                                                                                                      // 201
    return ret;                                                                                              // 202
  };                                                                                                         // 203
};                                                                                                           // 204
                                                                                                             // 205
// Takes a RegExp object and returns an element matcher.                                                     // 206
regexpElementMatcher = function (regexp) {                                                                   // 207
  return function (value) {                                                                                  // 208
    if (value instanceof RegExp) {                                                                           // 209
      // Comparing two regexps means seeing if the regexps are identical                                     // 210
      // (really!). Underscore knows how.                                                                    // 211
      return _.isEqual(value, regexp);                                                                       // 212
    }                                                                                                        // 213
    // Regexps only work against strings.                                                                    // 214
    if (typeof value !== 'string')                                                                           // 215
      return false;                                                                                          // 216
                                                                                                             // 217
    // Reset regexp's state to avoid inconsistent matching for objects with the                              // 218
    // same value on consecutive calls of regexp.test. This happens only if the                              // 219
    // regexp has the 'g' flag. Also note that ES6 introduces a new flag 'y' for                             // 220
    // which we should *not* change the lastIndex but MongoDB doesn't support                                // 221
    // either of these flags.                                                                                // 222
    regexp.lastIndex = 0;                                                                                    // 223
                                                                                                             // 224
    return regexp.test(value);                                                                               // 225
  };                                                                                                         // 226
};                                                                                                           // 227
                                                                                                             // 228
// Takes something that is not an operator object and returns an element matcher                             // 229
// for equality with that thing.                                                                             // 230
equalityElementMatcher = function (elementSelector) {                                                        // 231
  if (isOperatorObject(elementSelector))                                                                     // 232
    throw Error("Can't create equalityValueSelector for operator object");                                   // 233
                                                                                                             // 234
  // Special-case: null and undefined are equal (if you got undefined in there                               // 235
  // somewhere, or if you got it due to some branch being non-existent in the                                // 236
  // weird special case), even though they aren't with EJSON.equals.                                         // 237
  if (elementSelector == null) {  // undefined or null                                                       // 238
    return function (value) {                                                                                // 239
      return value == null;  // undefined or null                                                            // 240
    };                                                                                                       // 241
  }                                                                                                          // 242
                                                                                                             // 243
  return function (value) {                                                                                  // 244
    return LocalCollection._f._equal(elementSelector, value);                                                // 245
  };                                                                                                         // 246
};                                                                                                           // 247
                                                                                                             // 248
// Takes an operator object (an object with $ keys) and returns a branched                                   // 249
// matcher for it.                                                                                           // 250
var operatorBranchedMatcher = function (valueSelector, matcher, isRoot) {                                    // 251
  // Each valueSelector works separately on the various branches.  So one                                    // 252
  // operator can match one branch and another can match another branch.  This                               // 253
  // is OK.                                                                                                  // 254
                                                                                                             // 255
  var operatorMatchers = [];                                                                                 // 256
  _.each(valueSelector, function (operand, operator) {                                                       // 257
    // XXX we should actually implement $eq, which is new in 2.6                                             // 258
    var simpleRange = _.contains(['$lt', '$lte', '$gt', '$gte'], operator) &&                                // 259
      _.isNumber(operand);                                                                                   // 260
    var simpleInequality = operator === '$ne' && !_.isObject(operand);                                       // 261
    var simpleInclusion = _.contains(['$in', '$nin'], operator) &&                                           // 262
      _.isArray(operand) && !_.any(operand, _.isObject);                                                     // 263
                                                                                                             // 264
    if (! (operator === '$eq' || simpleRange ||                                                              // 265
           simpleInclusion || simpleInequality)) {                                                           // 266
      matcher._isSimple = false;                                                                             // 267
    }                                                                                                        // 268
                                                                                                             // 269
    if (_.has(VALUE_OPERATORS, operator)) {                                                                  // 270
      operatorMatchers.push(                                                                                 // 271
        VALUE_OPERATORS[operator](operand, valueSelector, matcher, isRoot));                                 // 272
    } else if (_.has(ELEMENT_OPERATORS, operator)) {                                                         // 273
      var options = ELEMENT_OPERATORS[operator];                                                             // 274
      operatorMatchers.push(                                                                                 // 275
        convertElementMatcherToBranchedMatcher(                                                              // 276
          options.compileElementSelector(                                                                    // 277
            operand, valueSelector, matcher),                                                                // 278
          options));                                                                                         // 279
    } else {                                                                                                 // 280
      throw new Error("Unrecognized operator: " + operator);                                                 // 281
    }                                                                                                        // 282
  });                                                                                                        // 283
                                                                                                             // 284
  return andBranchedMatchers(operatorMatchers);                                                              // 285
};                                                                                                           // 286
                                                                                                             // 287
var compileArrayOfDocumentSelectors = function (                                                             // 288
    selectors, matcher, inElemMatch) {                                                                       // 289
  if (!isArray(selectors) || _.isEmpty(selectors))                                                           // 290
    throw Error("$and/$or/$nor must be nonempty array");                                                     // 291
  return _.map(selectors, function (subSelector) {                                                           // 292
    if (!isPlainObject(subSelector))                                                                         // 293
      throw Error("$or/$and/$nor entries need to be full objects");                                          // 294
    return compileDocumentSelector(                                                                          // 295
      subSelector, matcher, {inElemMatch: inElemMatch});                                                     // 296
  });                                                                                                        // 297
};                                                                                                           // 298
                                                                                                             // 299
// Operators that appear at the top level of a document selector.                                            // 300
var LOGICAL_OPERATORS = {                                                                                    // 301
  $and: function (subSelector, matcher, inElemMatch) {                                                       // 302
    var matchers = compileArrayOfDocumentSelectors(                                                          // 303
      subSelector, matcher, inElemMatch);                                                                    // 304
    return andDocumentMatchers(matchers);                                                                    // 305
  },                                                                                                         // 306
                                                                                                             // 307
  $or: function (subSelector, matcher, inElemMatch) {                                                        // 308
    var matchers = compileArrayOfDocumentSelectors(                                                          // 309
      subSelector, matcher, inElemMatch);                                                                    // 310
                                                                                                             // 311
    // Special case: if there is only one matcher, use it directly, *preserving*                             // 312
    // any arrayIndices it returns.                                                                          // 313
    if (matchers.length === 1)                                                                               // 314
      return matchers[0];                                                                                    // 315
                                                                                                             // 316
    return function (doc) {                                                                                  // 317
      var result = _.any(matchers, function (f) {                                                            // 318
        return f(doc).result;                                                                                // 319
      });                                                                                                    // 320
      // $or does NOT set arrayIndices when it has multiple                                                  // 321
      // sub-expressions. (Tested against MongoDB.)                                                          // 322
      return {result: result};                                                                               // 323
    };                                                                                                       // 324
  },                                                                                                         // 325
                                                                                                             // 326
  $nor: function (subSelector, matcher, inElemMatch) {                                                       // 327
    var matchers = compileArrayOfDocumentSelectors(                                                          // 328
      subSelector, matcher, inElemMatch);                                                                    // 329
    return function (doc) {                                                                                  // 330
      var result = _.all(matchers, function (f) {                                                            // 331
        return !f(doc).result;                                                                               // 332
      });                                                                                                    // 333
      // Never set arrayIndices, because we only match if nothing in particular                              // 334
      // "matched" (and because this is consistent with MongoDB).                                            // 335
      return {result: result};                                                                               // 336
    };                                                                                                       // 337
  },                                                                                                         // 338
                                                                                                             // 339
  $where: function (selectorValue, matcher) {                                                                // 340
    // Record that *any* path may be used.                                                                   // 341
    matcher._recordPathUsed('');                                                                             // 342
    matcher._hasWhere = true;                                                                                // 343
    if (!(selectorValue instanceof Function)) {                                                              // 344
      // XXX MongoDB seems to have more complex logic to decide where or or not                              // 345
      // to add "return"; not sure exactly what it is.                                                       // 346
      selectorValue = Function("obj", "return " + selectorValue);                                            // 347
    }                                                                                                        // 348
    return function (doc) {                                                                                  // 349
      // We make the document available as both `this` and `obj`.                                            // 350
      // XXX not sure what we should do if this throws                                                       // 351
      return {result: selectorValue.call(doc, doc)};                                                         // 352
    };                                                                                                       // 353
  },                                                                                                         // 354
                                                                                                             // 355
  // This is just used as a comment in the query (in MongoDB, it also ends up in                             // 356
  // query logs); it has no effect on the actual selection.                                                  // 357
  $comment: function () {                                                                                    // 358
    return function () {                                                                                     // 359
      return {result: true};                                                                                 // 360
    };                                                                                                       // 361
  }                                                                                                          // 362
};                                                                                                           // 363
                                                                                                             // 364
// Returns a branched matcher that matches iff the given matcher does not.                                   // 365
// Note that this implicitly "deMorganizes" the wrapped function.  ie, it                                    // 366
// means that ALL branch values need to fail to match innerBranchedMatcher.                                  // 367
var invertBranchedMatcher = function (branchedMatcher) {                                                     // 368
  return function (branchValues) {                                                                           // 369
    var invertMe = branchedMatcher(branchValues);                                                            // 370
    // We explicitly choose to strip arrayIndices here: it doesn't make sense to                             // 371
    // say "update the array element that does not match something", at least                                // 372
    // in mongo-land.                                                                                        // 373
    return {result: !invertMe.result};                                                                       // 374
  };                                                                                                         // 375
};                                                                                                           // 376
                                                                                                             // 377
// Operators that (unlike LOGICAL_OPERATORS) pertain to individual paths in a                                // 378
// document, but (unlike ELEMENT_OPERATORS) do not have a simple definition as                               // 379
// "match each branched value independently and combine with                                                 // 380
// convertElementMatcherToBranchedMatcher".                                                                  // 381
var VALUE_OPERATORS = {                                                                                      // 382
  $not: function (operand, valueSelector, matcher) {                                                         // 383
    return invertBranchedMatcher(compileValueSelector(operand, matcher));                                    // 384
  },                                                                                                         // 385
  $ne: function (operand) {                                                                                  // 386
    return invertBranchedMatcher(convertElementMatcherToBranchedMatcher(                                     // 387
      equalityElementMatcher(operand)));                                                                     // 388
  },                                                                                                         // 389
  $nin: function (operand) {                                                                                 // 390
    return invertBranchedMatcher(convertElementMatcherToBranchedMatcher(                                     // 391
      ELEMENT_OPERATORS.$in.compileElementSelector(operand)));                                               // 392
  },                                                                                                         // 393
  $exists: function (operand) {                                                                              // 394
    var exists = convertElementMatcherToBranchedMatcher(function (value) {                                   // 395
      return value !== undefined;                                                                            // 396
    });                                                                                                      // 397
    return operand ? exists : invertBranchedMatcher(exists);                                                 // 398
  },                                                                                                         // 399
  // $options just provides options for $regex; its logic is inside $regex                                   // 400
  $options: function (operand, valueSelector) {                                                              // 401
    if (!_.has(valueSelector, '$regex'))                                                                     // 402
      throw Error("$options needs a $regex");                                                                // 403
    return everythingMatcher;                                                                                // 404
  },                                                                                                         // 405
  // $maxDistance is basically an argument to $near                                                          // 406
  $maxDistance: function (operand, valueSelector) {                                                          // 407
    if (!valueSelector.$near)                                                                                // 408
      throw Error("$maxDistance needs a $near");                                                             // 409
    return everythingMatcher;                                                                                // 410
  },                                                                                                         // 411
  $all: function (operand, valueSelector, matcher) {                                                         // 412
    if (!isArray(operand))                                                                                   // 413
      throw Error("$all requires array");                                                                    // 414
    // Not sure why, but this seems to be what MongoDB does.                                                 // 415
    if (_.isEmpty(operand))                                                                                  // 416
      return nothingMatcher;                                                                                 // 417
                                                                                                             // 418
    var branchedMatchers = [];                                                                               // 419
    _.each(operand, function (criterion) {                                                                   // 420
      // XXX handle $all/$elemMatch combination                                                              // 421
      if (isOperatorObject(criterion))                                                                       // 422
        throw Error("no $ expressions in $all");                                                             // 423
      // This is always a regexp or equality selector.                                                       // 424
      branchedMatchers.push(compileValueSelector(criterion, matcher));                                       // 425
    });                                                                                                      // 426
    // andBranchedMatchers does NOT require all selectors to return true on the                              // 427
    // SAME branch.                                                                                          // 428
    return andBranchedMatchers(branchedMatchers);                                                            // 429
  },                                                                                                         // 430
  $near: function (operand, valueSelector, matcher, isRoot) {                                                // 431
    if (!isRoot)                                                                                             // 432
      throw Error("$near can't be inside another $ operator");                                               // 433
    matcher._hasGeoQuery = true;                                                                             // 434
                                                                                                             // 435
    // There are two kinds of geodata in MongoDB: coordinate pairs and                                       // 436
    // GeoJSON. They use different distance metrics, too. GeoJSON queries are                                // 437
    // marked with a $geometry property.                                                                     // 438
                                                                                                             // 439
    var maxDistance, point, distance;                                                                        // 440
    if (isPlainObject(operand) && _.has(operand, '$geometry')) {                                             // 441
      // GeoJSON "2dsphere" mode.                                                                            // 442
      maxDistance = operand.$maxDistance;                                                                    // 443
      point = operand.$geometry;                                                                             // 444
      distance = function (value) {                                                                          // 445
        // XXX: for now, we don't calculate the actual distance between, say,                                // 446
        // polygon and circle. If people care about this use-case it will get                                // 447
        // a priority.                                                                                       // 448
        if (!value || !value.type)                                                                           // 449
          return null;                                                                                       // 450
        if (value.type === "Point") {                                                                        // 451
          return GeoJSON.pointDistance(point, value);                                                        // 452
        } else {                                                                                             // 453
          return GeoJSON.geometryWithinRadius(value, point, maxDistance)                                     // 454
            ? 0 : maxDistance + 1;                                                                           // 455
        }                                                                                                    // 456
      };                                                                                                     // 457
    } else {                                                                                                 // 458
      maxDistance = valueSelector.$maxDistance;                                                              // 459
      if (!isArray(operand) && !isPlainObject(operand))                                                      // 460
        throw Error("$near argument must be coordinate pair or GeoJSON");                                    // 461
      point = pointToArray(operand);                                                                         // 462
      distance = function (value) {                                                                          // 463
        if (!isArray(value) && !isPlainObject(value))                                                        // 464
          return null;                                                                                       // 465
        return distanceCoordinatePairs(point, value);                                                        // 466
      };                                                                                                     // 467
    }                                                                                                        // 468
                                                                                                             // 469
    return function (branchedValues) {                                                                       // 470
      // There might be multiple points in the document that match the given                                 // 471
      // field. Only one of them needs to be within $maxDistance, but we need to                             // 472
      // evaluate all of them and use the nearest one for the implicit sort                                  // 473
      // specifier. (That's why we can't just use ELEMENT_OPERATORS here.)                                   // 474
      //                                                                                                     // 475
      // Note: This differs from MongoDB's implementation, where a document will                             // 476
      // actually show up *multiple times* in the result set, with one entry for                             // 477
      // each within-$maxDistance branching point.                                                           // 478
      branchedValues = expandArraysInBranches(branchedValues);                                               // 479
      var result = {result: false};                                                                          // 480
      _.each(branchedValues, function (branch) {                                                             // 481
        var curDistance = distance(branch.value);                                                            // 482
        // Skip branches that aren't real points or are too far away.                                        // 483
        if (curDistance === null || curDistance > maxDistance)                                               // 484
          return;                                                                                            // 485
        // Skip anything that's a tie.                                                                       // 486
        if (result.distance !== undefined && result.distance <= curDistance)                                 // 487
          return;                                                                                            // 488
        result.result = true;                                                                                // 489
        result.distance = curDistance;                                                                       // 490
        if (!branch.arrayIndices)                                                                            // 491
          delete result.arrayIndices;                                                                        // 492
        else                                                                                                 // 493
          result.arrayIndices = branch.arrayIndices;                                                         // 494
      });                                                                                                    // 495
      return result;                                                                                         // 496
    };                                                                                                       // 497
  }                                                                                                          // 498
};                                                                                                           // 499
                                                                                                             // 500
// Helpers for $near.                                                                                        // 501
var distanceCoordinatePairs = function (a, b) {                                                              // 502
  a = pointToArray(a);                                                                                       // 503
  b = pointToArray(b);                                                                                       // 504
  var x = a[0] - b[0];                                                                                       // 505
  var y = a[1] - b[1];                                                                                       // 506
  if (_.isNaN(x) || _.isNaN(y))                                                                              // 507
    return null;                                                                                             // 508
  return Math.sqrt(x * x + y * y);                                                                           // 509
};                                                                                                           // 510
// Makes sure we get 2 elements array and assume the first one to be x and                                   // 511
// the second one to y no matter what user passes.                                                           // 512
// In case user passes { lon: x, lat: y } returns [x, y]                                                     // 513
var pointToArray = function (point) {                                                                        // 514
  return _.map(point, _.identity);                                                                           // 515
};                                                                                                           // 516
                                                                                                             // 517
// Helper for $lt/$gt/$lte/$gte.                                                                             // 518
var makeInequality = function (cmpValueComparator) {                                                         // 519
  return {                                                                                                   // 520
    compileElementSelector: function (operand) {                                                             // 521
      // Arrays never compare false with non-arrays for any inequality.                                      // 522
      // XXX This was behavior we observed in pre-release MongoDB 2.5, but                                   // 523
      //     it seems to have been reverted.                                                                 // 524
      //     See https://jira.mongodb.org/browse/SERVER-11444                                                // 525
      if (isArray(operand)) {                                                                                // 526
        return function () {                                                                                 // 527
          return false;                                                                                      // 528
        };                                                                                                   // 529
      }                                                                                                      // 530
                                                                                                             // 531
      // Special case: consider undefined and null the same (so true with                                    // 532
      // $gte/$lte).                                                                                         // 533
      if (operand === undefined)                                                                             // 534
        operand = null;                                                                                      // 535
                                                                                                             // 536
      var operandType = LocalCollection._f._type(operand);                                                   // 537
                                                                                                             // 538
      return function (value) {                                                                              // 539
        if (value === undefined)                                                                             // 540
          value = null;                                                                                      // 541
        // Comparisons are never true among things of different type (except                                 // 542
        // null vs undefined).                                                                               // 543
        if (LocalCollection._f._type(value) !== operandType)                                                 // 544
          return false;                                                                                      // 545
        return cmpValueComparator(LocalCollection._f._cmp(value, operand));                                  // 546
      };                                                                                                     // 547
    }                                                                                                        // 548
  };                                                                                                         // 549
};                                                                                                           // 550
                                                                                                             // 551
// Each element selector contains:                                                                           // 552
//  - compileElementSelector, a function with args:                                                          // 553
//    - operand - the "right hand side" of the operator                                                      // 554
//    - valueSelector - the "context" for the operator (so that $regex can find                              // 555
//      $options)                                                                                            // 556
//    - matcher - the Matcher this is going into (so that $elemMatch can compile                             // 557
//      more things)                                                                                         // 558
//    returning a function mapping a single value to bool.                                                   // 559
//  - dontExpandLeafArrays, a bool which prevents expandArraysInBranches from                                // 560
//    being called                                                                                           // 561
//  - dontIncludeLeafArrays, a bool which causes an argument to be passed to                                 // 562
//    expandArraysInBranches if it is called                                                                 // 563
ELEMENT_OPERATORS = {                                                                                        // 564
  $lt: makeInequality(function (cmpValue) {                                                                  // 565
    return cmpValue < 0;                                                                                     // 566
  }),                                                                                                        // 567
  $gt: makeInequality(function (cmpValue) {                                                                  // 568
    return cmpValue > 0;                                                                                     // 569
  }),                                                                                                        // 570
  $lte: makeInequality(function (cmpValue) {                                                                 // 571
    return cmpValue <= 0;                                                                                    // 572
  }),                                                                                                        // 573
  $gte: makeInequality(function (cmpValue) {                                                                 // 574
    return cmpValue >= 0;                                                                                    // 575
  }),                                                                                                        // 576
  $mod: {                                                                                                    // 577
    compileElementSelector: function (operand) {                                                             // 578
      if (!(isArray(operand) && operand.length === 2                                                         // 579
            && typeof(operand[0]) === 'number'                                                               // 580
            && typeof(operand[1]) === 'number')) {                                                           // 581
        throw Error("argument to $mod must be an array of two numbers");                                     // 582
      }                                                                                                      // 583
      // XXX could require to be ints or round or something                                                  // 584
      var divisor = operand[0];                                                                              // 585
      var remainder = operand[1];                                                                            // 586
      return function (value) {                                                                              // 587
        return typeof value === 'number' && value % divisor === remainder;                                   // 588
      };                                                                                                     // 589
    }                                                                                                        // 590
  },                                                                                                         // 591
  $in: {                                                                                                     // 592
    compileElementSelector: function (operand) {                                                             // 593
      if (!isArray(operand))                                                                                 // 594
        throw Error("$in needs an array");                                                                   // 595
                                                                                                             // 596
      var elementMatchers = [];                                                                              // 597
      _.each(operand, function (option) {                                                                    // 598
        if (option instanceof RegExp)                                                                        // 599
          elementMatchers.push(regexpElementMatcher(option));                                                // 600
        else if (isOperatorObject(option))                                                                   // 601
          throw Error("cannot nest $ under $in");                                                            // 602
        else                                                                                                 // 603
          elementMatchers.push(equalityElementMatcher(option));                                              // 604
      });                                                                                                    // 605
                                                                                                             // 606
      return function (value) {                                                                              // 607
        // Allow {a: {$in: [null]}} to match when 'a' does not exist.                                        // 608
        if (value === undefined)                                                                             // 609
          value = null;                                                                                      // 610
        return _.any(elementMatchers, function (e) {                                                         // 611
          return e(value);                                                                                   // 612
        });                                                                                                  // 613
      };                                                                                                     // 614
    }                                                                                                        // 615
  },                                                                                                         // 616
  $size: {                                                                                                   // 617
    // {a: [[5, 5]]} must match {a: {$size: 1}} but not {a: {$size: 2}}, so we                               // 618
    // don't want to consider the element [5,5] in the leaf array [[5,5]] as a                               // 619
    // possible value.                                                                                       // 620
    dontExpandLeafArrays: true,                                                                              // 621
    compileElementSelector: function (operand) {                                                             // 622
      if (typeof operand === 'string') {                                                                     // 623
        // Don't ask me why, but by experimentation, this seems to be what Mongo                             // 624
        // does.                                                                                             // 625
        operand = 0;                                                                                         // 626
      } else if (typeof operand !== 'number') {                                                              // 627
        throw Error("$size needs a number");                                                                 // 628
      }                                                                                                      // 629
      return function (value) {                                                                              // 630
        return isArray(value) && value.length === operand;                                                   // 631
      };                                                                                                     // 632
    }                                                                                                        // 633
  },                                                                                                         // 634
  $type: {                                                                                                   // 635
    // {a: [5]} must not match {a: {$type: 4}} (4 means array), but it should                                // 636
    // match {a: {$type: 1}} (1 means number), and {a: [[5]]} must match {$a:                                // 637
    // {$type: 4}}. Thus, when we see a leaf array, we *should* expand it but                                // 638
    // should *not* include it itself.                                                                       // 639
    dontIncludeLeafArrays: true,                                                                             // 640
    compileElementSelector: function (operand) {                                                             // 641
      if (typeof operand !== 'number')                                                                       // 642
        throw Error("$type needs a number");                                                                 // 643
      return function (value) {                                                                              // 644
        return value !== undefined                                                                           // 645
          && LocalCollection._f._type(value) === operand;                                                    // 646
      };                                                                                                     // 647
    }                                                                                                        // 648
  },                                                                                                         // 649
  $regex: {                                                                                                  // 650
    compileElementSelector: function (operand, valueSelector) {                                              // 651
      if (!(typeof operand === 'string' || operand instanceof RegExp))                                       // 652
        throw Error("$regex has to be a string or RegExp");                                                  // 653
                                                                                                             // 654
      var regexp;                                                                                            // 655
      if (valueSelector.$options !== undefined) {                                                            // 656
        // Options passed in $options (even the empty string) always overrides                               // 657
        // options in the RegExp object itself. (See also                                                    // 658
        // Mongo.Collection._rewriteSelector.)                                                               // 659
                                                                                                             // 660
        // Be clear that we only support the JS-supported options, not extended                              // 661
        // ones (eg, Mongo supports x and s). Ideally we would implement x and s                             // 662
        // by transforming the regexp, but not today...                                                      // 663
        if (/[^gim]/.test(valueSelector.$options))                                                           // 664
          throw new Error("Only the i, m, and g regexp options are supported");                              // 665
                                                                                                             // 666
        var regexSource = operand instanceof RegExp ? operand.source : operand;                              // 667
        regexp = new RegExp(regexSource, valueSelector.$options);                                            // 668
      } else if (operand instanceof RegExp) {                                                                // 669
        regexp = operand;                                                                                    // 670
      } else {                                                                                               // 671
        regexp = new RegExp(operand);                                                                        // 672
      }                                                                                                      // 673
      return regexpElementMatcher(regexp);                                                                   // 674
    }                                                                                                        // 675
  },                                                                                                         // 676
  $elemMatch: {                                                                                              // 677
    dontExpandLeafArrays: true,                                                                              // 678
    compileElementSelector: function (operand, valueSelector, matcher) {                                     // 679
      if (!isPlainObject(operand))                                                                           // 680
        throw Error("$elemMatch need an object");                                                            // 681
                                                                                                             // 682
      var subMatcher, isDocMatcher;                                                                          // 683
      if (isOperatorObject(operand, true)) {                                                                 // 684
        subMatcher = compileValueSelector(operand, matcher);                                                 // 685
        isDocMatcher = false;                                                                                // 686
      } else {                                                                                               // 687
        // This is NOT the same as compileValueSelector(operand), and not just                               // 688
        // because of the slightly different calling convention.                                             // 689
        // {$elemMatch: {x: 3}} means "an element has a field x:3", not                                      // 690
        // "consists only of a field x:3". Also, regexps and sub-$ are allowed.                              // 691
        subMatcher = compileDocumentSelector(operand, matcher,                                               // 692
                                             {inElemMatch: true});                                           // 693
        isDocMatcher = true;                                                                                 // 694
      }                                                                                                      // 695
                                                                                                             // 696
      return function (value) {                                                                              // 697
        if (!isArray(value))                                                                                 // 698
          return false;                                                                                      // 699
        for (var i = 0; i < value.length; ++i) {                                                             // 700
          var arrayElement = value[i];                                                                       // 701
          var arg;                                                                                           // 702
          if (isDocMatcher) {                                                                                // 703
            // We can only match {$elemMatch: {b: 3}} against objects.                                       // 704
            // (We can also match against arrays, if there's numeric indices,                                // 705
            // eg {$elemMatch: {'0.b': 3}} or {$elemMatch: {0: 3}}.)                                         // 706
            if (!isPlainObject(arrayElement) && !isArray(arrayElement))                                      // 707
              return false;                                                                                  // 708
            arg = arrayElement;                                                                              // 709
          } else {                                                                                           // 710
            // dontIterate ensures that {a: {$elemMatch: {$gt: 5}}} matches                                  // 711
            // {a: [8]} but not {a: [[8]]}                                                                   // 712
            arg = [{value: arrayElement, dontIterate: true}];                                                // 713
          }                                                                                                  // 714
          // XXX support $near in $elemMatch by propagating $distance?                                       // 715
          if (subMatcher(arg).result)                                                                        // 716
            return i;   // specially understood to mean "use as arrayIndices"                                // 717
        }                                                                                                    // 718
        return false;                                                                                        // 719
      };                                                                                                     // 720
    }                                                                                                        // 721
  }                                                                                                          // 722
};                                                                                                           // 723
                                                                                                             // 724
// makeLookupFunction(key) returns a lookup function.                                                        // 725
//                                                                                                           // 726
// A lookup function takes in a document and returns an array of matching                                    // 727
// branches.  If no arrays are found while looking up the key, this array will                               // 728
// have exactly one branches (possibly 'undefined', if some segment of the key                               // 729
// was not found).                                                                                           // 730
//                                                                                                           // 731
// If arrays are found in the middle, this can have more than one element, since                             // 732
// we "branch". When we "branch", if there are more key segments to look up,                                 // 733
// then we only pursue branches that are plain objects (not arrays or scalars).                              // 734
// This means we can actually end up with no branches!                                                       // 735
//                                                                                                           // 736
// We do *NOT* branch on arrays that are found at the end (ie, at the last                                   // 737
// dotted member of the key). We just return that array; if you want to                                      // 738
// effectively "branch" over the array's values, post-process the lookup                                     // 739
// function with expandArraysInBranches.                                                                     // 740
//                                                                                                           // 741
// Each branch is an object with keys:                                                                       // 742
//  - value: the value at the branch                                                                         // 743
//  - dontIterate: an optional bool; if true, it means that 'value' is an array                              // 744
//    that expandArraysInBranches should NOT expand. This specifically happens                               // 745
//    when there is a numeric index in the key, and ensures the                                              // 746
//    perhaps-surprising MongoDB behavior where {'a.0': 5} does NOT                                          // 747
//    match {a: [[5]]}.                                                                                      // 748
//  - arrayIndices: if any array indexing was done during lookup (either due to                              // 749
//    explicit numeric indices or implicit branching), this will be an array of                              // 750
//    the array indices used, from outermost to innermost; it is falsey or                                   // 751
//    absent if no array index is used. If an explicit numeric index is used,                                // 752
//    the index will be followed in arrayIndices by the string 'x'.                                          // 753
//                                                                                                           // 754
//    Note: arrayIndices is used for two purposes. First, it is used to                                      // 755
//    implement the '$' modifier feature, which only ever looks at its first                                 // 756
//    element.                                                                                               // 757
//                                                                                                           // 758
//    Second, it is used for sort key generation, which needs to be able to tell                             // 759
//    the difference between different paths. Moreover, it needs to                                          // 760
//    differentiate between explicit and implicit branching, which is why                                    // 761
//    there's the somewhat hacky 'x' entry: this means that explicit and                                     // 762
//    implicit array lookups will have different full arrayIndices paths. (That                              // 763
//    code only requires that different paths have different arrayIndices; it                                // 764
//    doesn't actually "parse" arrayIndices. As an alternative, arrayIndices                                 // 765
//    could contain objects with flags like "implicit", but I think that only                                // 766
//    makes the code surrounding them more complex.)                                                         // 767
//                                                                                                           // 768
//    (By the way, this field ends up getting passed around a lot without                                    // 769
//    cloning, so never mutate any arrayIndices field/var in this package!)                                  // 770
//                                                                                                           // 771
//                                                                                                           // 772
// At the top level, you may only pass in a plain object or array.                                           // 773
//                                                                                                           // 774
// See the test 'minimongo - lookup' for some examples of what lookup functions                              // 775
// return.                                                                                                   // 776
makeLookupFunction = function (key, options) {                                                               // 777
  options = options || {};                                                                                   // 778
  var parts = key.split('.');                                                                                // 779
  var firstPart = parts.length ? parts[0] : '';                                                              // 780
  var firstPartIsNumeric = isNumericKey(firstPart);                                                          // 781
  var nextPartIsNumeric = parts.length >= 2 && isNumericKey(parts[1]);                                       // 782
  var lookupRest;                                                                                            // 783
  if (parts.length > 1) {                                                                                    // 784
    lookupRest = makeLookupFunction(parts.slice(1).join('.'));                                               // 785
  }                                                                                                          // 786
                                                                                                             // 787
  var omitUnnecessaryFields = function (retVal) {                                                            // 788
    if (!retVal.dontIterate)                                                                                 // 789
      delete retVal.dontIterate;                                                                             // 790
    if (retVal.arrayIndices && !retVal.arrayIndices.length)                                                  // 791
      delete retVal.arrayIndices;                                                                            // 792
    return retVal;                                                                                           // 793
  };                                                                                                         // 794
                                                                                                             // 795
  // Doc will always be a plain object or an array.                                                          // 796
  // apply an explicit numeric index, an array.                                                              // 797
  return function (doc, arrayIndices) {                                                                      // 798
    if (!arrayIndices)                                                                                       // 799
      arrayIndices = [];                                                                                     // 800
                                                                                                             // 801
    if (isArray(doc)) {                                                                                      // 802
      // If we're being asked to do an invalid lookup into an array (non-integer                             // 803
      // or out-of-bounds), return no results (which is different from returning                             // 804
      // a single undefined result, in that `null` equality checks won't match).                             // 805
      if (!(firstPartIsNumeric && firstPart < doc.length))                                                   // 806
        return [];                                                                                           // 807
                                                                                                             // 808
      // Remember that we used this array index. Include an 'x' to indicate that                             // 809
      // the previous index came from being considered as an explicit array                                  // 810
      // index (not branching).                                                                              // 811
      arrayIndices = arrayIndices.concat(+firstPart, 'x');                                                   // 812
    }                                                                                                        // 813
                                                                                                             // 814
    // Do our first lookup.                                                                                  // 815
    var firstLevel = doc[firstPart];                                                                         // 816
                                                                                                             // 817
    // If there is no deeper to dig, return what we found.                                                   // 818
    //                                                                                                       // 819
    // If what we found is an array, most value selectors will choose to treat                               // 820
    // the elements of the array as matchable values in their own right, but                                 // 821
    // that's done outside of the lookup function. (Exceptions to this are $size                             // 822
    // and stuff relating to $elemMatch.  eg, {a: {$size: 2}} does not match {a:                             // 823
    // [[1, 2]]}.)                                                                                           // 824
    //                                                                                                       // 825
    // That said, if we just did an *explicit* array lookup (on doc) to find                                 // 826
    // firstLevel, and firstLevel is an array too, we do NOT want value                                      // 827
    // selectors to iterate over it.  eg, {'a.0': 5} does not match {a: [[5]]}.                              // 828
    // So in that case, we mark the return value as "don't iterate".                                         // 829
    if (!lookupRest) {                                                                                       // 830
      return [omitUnnecessaryFields({                                                                        // 831
        value: firstLevel,                                                                                   // 832
        dontIterate: isArray(doc) && isArray(firstLevel),                                                    // 833
        arrayIndices: arrayIndices})];                                                                       // 834
    }                                                                                                        // 835
                                                                                                             // 836
    // We need to dig deeper.  But if we can't, because what we've found is not                              // 837
    // an array or plain object, we're done. If we just did a numeric index into                             // 838
    // an array, we return nothing here (this is a change in Mongo 2.5 from                                  // 839
    // Mongo 2.4, where {'a.0.b': null} stopped matching {a: [5]}). Otherwise,                               // 840
    // return a single `undefined` (which can, for example, match via equality                               // 841
    // with `null`).                                                                                         // 842
    if (!isIndexable(firstLevel)) {                                                                          // 843
      if (isArray(doc))                                                                                      // 844
        return [];                                                                                           // 845
      return [omitUnnecessaryFields({value: undefined,                                                       // 846
                                      arrayIndices: arrayIndices})];                                         // 847
    }                                                                                                        // 848
                                                                                                             // 849
    var result = [];                                                                                         // 850
    var appendToResult = function (more) {                                                                   // 851
      Array.prototype.push.apply(result, more);                                                              // 852
    };                                                                                                       // 853
                                                                                                             // 854
    // Dig deeper: look up the rest of the parts on whatever we've found.                                    // 855
    // (lookupRest is smart enough to not try to do invalid lookups into                                     // 856
    // firstLevel if it's an array.)                                                                         // 857
    appendToResult(lookupRest(firstLevel, arrayIndices));                                                    // 858
                                                                                                             // 859
    // If we found an array, then in *addition* to potentially treating the next                             // 860
    // part as a literal integer lookup, we should also "branch": try to look up                             // 861
    // the rest of the parts on each array element in parallel.                                              // 862
    //                                                                                                       // 863
    // In this case, we *only* dig deeper into array elements that are plain                                 // 864
    // objects. (Recall that we only got this far if we have further to dig.)                                // 865
    // This makes sense: we certainly don't dig deeper into non-indexable                                    // 866
    // objects. And it would be weird to dig into an array: it's simpler to have                             // 867
    // a rule that explicit integer indexes only apply to an outer array, not to                             // 868
    // an array you find after a branching search.                                                           // 869
    //                                                                                                       // 870
    // In the special case of a numeric part in a *sort selector* (not a query                               // 871
    // selector), we skip the branching: we ONLY allow the numeric part to mean                              // 872
    // "look up this index" in that case, not "also look up this index in all                                // 873
    // the elements of the array".                                                                           // 874
    if (isArray(firstLevel) && !(nextPartIsNumeric && options.forSort)) {                                    // 875
      _.each(firstLevel, function (branch, arrayIndex) {                                                     // 876
        if (isPlainObject(branch)) {                                                                         // 877
          appendToResult(lookupRest(                                                                         // 878
            branch,                                                                                          // 879
            arrayIndices.concat(arrayIndex)));                                                               // 880
        }                                                                                                    // 881
      });                                                                                                    // 882
    }                                                                                                        // 883
                                                                                                             // 884
    return result;                                                                                           // 885
  };                                                                                                         // 886
};                                                                                                           // 887
MinimongoTest.makeLookupFunction = makeLookupFunction;                                                       // 888
                                                                                                             // 889
expandArraysInBranches = function (branches, skipTheArrays) {                                                // 890
  var branchesOut = [];                                                                                      // 891
  _.each(branches, function (branch) {                                                                       // 892
    var thisIsArray = isArray(branch.value);                                                                 // 893
    // We include the branch itself, *UNLESS* we it's an array that we're going                              // 894
    // to iterate and we're told to skip arrays.  (That's right, we include some                             // 895
    // arrays even skipTheArrays is true: these are arrays that were found via                               // 896
    // explicit numerical indices.)                                                                          // 897
    if (!(skipTheArrays && thisIsArray && !branch.dontIterate)) {                                            // 898
      branchesOut.push({                                                                                     // 899
        value: branch.value,                                                                                 // 900
        arrayIndices: branch.arrayIndices                                                                    // 901
      });                                                                                                    // 902
    }                                                                                                        // 903
    if (thisIsArray && !branch.dontIterate) {                                                                // 904
      _.each(branch.value, function (leaf, i) {                                                              // 905
        branchesOut.push({                                                                                   // 906
          value: leaf,                                                                                       // 907
          arrayIndices: (branch.arrayIndices || []).concat(i)                                                // 908
        });                                                                                                  // 909
      });                                                                                                    // 910
    }                                                                                                        // 911
  });                                                                                                        // 912
  return branchesOut;                                                                                        // 913
};                                                                                                           // 914
                                                                                                             // 915
var nothingMatcher = function (docOrBranchedValues) {                                                        // 916
  return {result: false};                                                                                    // 917
};                                                                                                           // 918
                                                                                                             // 919
var everythingMatcher = function (docOrBranchedValues) {                                                     // 920
  return {result: true};                                                                                     // 921
};                                                                                                           // 922
                                                                                                             // 923
                                                                                                             // 924
// NB: We are cheating and using this function to implement "AND" for both                                   // 925
// "document matchers" and "branched matchers". They both return result objects                              // 926
// but the argument is different: for the former it's a whole doc, whereas for                               // 927
// the latter it's an array of "branched values".                                                            // 928
var andSomeMatchers = function (subMatchers) {                                                               // 929
  if (subMatchers.length === 0)                                                                              // 930
    return everythingMatcher;                                                                                // 931
  if (subMatchers.length === 1)                                                                              // 932
    return subMatchers[0];                                                                                   // 933
                                                                                                             // 934
  return function (docOrBranches) {                                                                          // 935
    var ret = {};                                                                                            // 936
    ret.result = _.all(subMatchers, function (f) {                                                           // 937
      var subResult = f(docOrBranches);                                                                      // 938
      // Copy a 'distance' number out of the first sub-matcher that has                                      // 939
      // one. Yes, this means that if there are multiple $near fields in a                                   // 940
      // query, something arbitrary happens; this appears to be consistent with                              // 941
      // Mongo.                                                                                              // 942
      if (subResult.result && subResult.distance !== undefined                                               // 943
          && ret.distance === undefined) {                                                                   // 944
        ret.distance = subResult.distance;                                                                   // 945
      }                                                                                                      // 946
      // Similarly, propagate arrayIndices from sub-matchers... but to match                                 // 947
      // MongoDB behavior, this time the *last* sub-matcher with arrayIndices                                // 948
      // wins.                                                                                               // 949
      if (subResult.result && subResult.arrayIndices) {                                                      // 950
        ret.arrayIndices = subResult.arrayIndices;                                                           // 951
      }                                                                                                      // 952
      return subResult.result;                                                                               // 953
    });                                                                                                      // 954
                                                                                                             // 955
    // If we didn't actually match, forget any extra metadata we came up with.                               // 956
    if (!ret.result) {                                                                                       // 957
      delete ret.distance;                                                                                   // 958
      delete ret.arrayIndices;                                                                               // 959
    }                                                                                                        // 960
    return ret;                                                                                              // 961
  };                                                                                                         // 962
};                                                                                                           // 963
                                                                                                             // 964
var andDocumentMatchers = andSomeMatchers;                                                                   // 965
var andBranchedMatchers = andSomeMatchers;                                                                   // 966
                                                                                                             // 967
                                                                                                             // 968
// helpers used by compiled selector code                                                                    // 969
LocalCollection._f = {                                                                                       // 970
  // XXX for _all and _in, consider building 'inquery' at compile time..                                     // 971
                                                                                                             // 972
  _type: function (v) {                                                                                      // 973
    if (typeof v === "number")                                                                               // 974
      return 1;                                                                                              // 975
    if (typeof v === "string")                                                                               // 976
      return 2;                                                                                              // 977
    if (typeof v === "boolean")                                                                              // 978
      return 8;                                                                                              // 979
    if (isArray(v))                                                                                          // 980
      return 4;                                                                                              // 981
    if (v === null)                                                                                          // 982
      return 10;                                                                                             // 983
    if (v instanceof RegExp)                                                                                 // 984
      // note that typeof(/x/) === "object"                                                                  // 985
      return 11;                                                                                             // 986
    if (typeof v === "function")                                                                             // 987
      return 13;                                                                                             // 988
    if (v instanceof Date)                                                                                   // 989
      return 9;                                                                                              // 990
    if (EJSON.isBinary(v))                                                                                   // 991
      return 5;                                                                                              // 992
    if (v instanceof LocalCollection._ObjectID)                                                              // 993
      return 7;                                                                                              // 994
    return 3; // object                                                                                      // 995
                                                                                                             // 996
    // XXX support some/all of these:                                                                        // 997
    // 14, symbol                                                                                            // 998
    // 15, javascript code with scope                                                                        // 999
    // 16, 18: 32-bit/64-bit integer                                                                         // 1000
    // 17, timestamp                                                                                         // 1001
    // 255, minkey                                                                                           // 1002
    // 127, maxkey                                                                                           // 1003
  },                                                                                                         // 1004
                                                                                                             // 1005
  // deep equality test: use for literal document and array matches                                          // 1006
  _equal: function (a, b) {                                                                                  // 1007
    return EJSON.equals(a, b, {keyOrderSensitive: true});                                                    // 1008
  },                                                                                                         // 1009
                                                                                                             // 1010
  // maps a type code to a value that can be used to sort values of                                          // 1011
  // different types                                                                                         // 1012
  _typeorder: function (t) {                                                                                 // 1013
    // http://www.mongodb.org/display/DOCS/What+is+the+Compare+Order+for+BSON+Types                          // 1014
    // XXX what is the correct sort position for Javascript code?                                            // 1015
    // ('100' in the matrix below)                                                                           // 1016
    // XXX minkey/maxkey                                                                                     // 1017
    return [-1,  // (not a type)                                                                             // 1018
            1,   // number                                                                                   // 1019
            2,   // string                                                                                   // 1020
            3,   // object                                                                                   // 1021
            4,   // array                                                                                    // 1022
            5,   // binary                                                                                   // 1023
            -1,  // deprecated                                                                               // 1024
            6,   // ObjectID                                                                                 // 1025
            7,   // bool                                                                                     // 1026
            8,   // Date                                                                                     // 1027
            0,   // null                                                                                     // 1028
            9,   // RegExp                                                                                   // 1029
            -1,  // deprecated                                                                               // 1030
            100, // JS code                                                                                  // 1031
            2,   // deprecated (symbol)                                                                      // 1032
            100, // JS code                                                                                  // 1033
            1,   // 32-bit int                                                                               // 1034
            8,   // Mongo timestamp                                                                          // 1035
            1    // 64-bit int                                                                               // 1036
           ][t];                                                                                             // 1037
  },                                                                                                         // 1038
                                                                                                             // 1039
  // compare two values of unknown type according to BSON ordering                                           // 1040
  // semantics. (as an extension, consider 'undefined' to be less than                                       // 1041
  // any other value.) return negative if a is less, positive if b is                                        // 1042
  // less, or 0 if equal                                                                                     // 1043
  _cmp: function (a, b) {                                                                                    // 1044
    if (a === undefined)                                                                                     // 1045
      return b === undefined ? 0 : -1;                                                                       // 1046
    if (b === undefined)                                                                                     // 1047
      return 1;                                                                                              // 1048
    var ta = LocalCollection._f._type(a);                                                                    // 1049
    var tb = LocalCollection._f._type(b);                                                                    // 1050
    var oa = LocalCollection._f._typeorder(ta);                                                              // 1051
    var ob = LocalCollection._f._typeorder(tb);                                                              // 1052
    if (oa !== ob)                                                                                           // 1053
      return oa < ob ? -1 : 1;                                                                               // 1054
    if (ta !== tb)                                                                                           // 1055
      // XXX need to implement this if we implement Symbol or integers, or                                   // 1056
      // Timestamp                                                                                           // 1057
      throw Error("Missing type coercion logic in _cmp");                                                    // 1058
    if (ta === 7) { // ObjectID                                                                              // 1059
      // Convert to string.                                                                                  // 1060
      ta = tb = 2;                                                                                           // 1061
      a = a.toHexString();                                                                                   // 1062
      b = b.toHexString();                                                                                   // 1063
    }                                                                                                        // 1064
    if (ta === 9) { // Date                                                                                  // 1065
      // Convert to millis.                                                                                  // 1066
      ta = tb = 1;                                                                                           // 1067
      a = a.getTime();                                                                                       // 1068
      b = b.getTime();                                                                                       // 1069
    }                                                                                                        // 1070
                                                                                                             // 1071
    if (ta === 1) // double                                                                                  // 1072
      return a - b;                                                                                          // 1073
    if (tb === 2) // string                                                                                  // 1074
      return a < b ? -1 : (a === b ? 0 : 1);                                                                 // 1075
    if (ta === 3) { // Object                                                                                // 1076
      // this could be much more efficient in the expected case ...                                          // 1077
      var to_array = function (obj) {                                                                        // 1078
        var ret = [];                                                                                        // 1079
        for (var key in obj) {                                                                               // 1080
          ret.push(key);                                                                                     // 1081
          ret.push(obj[key]);                                                                                // 1082
        }                                                                                                    // 1083
        return ret;                                                                                          // 1084
      };                                                                                                     // 1085
      return LocalCollection._f._cmp(to_array(a), to_array(b));                                              // 1086
    }                                                                                                        // 1087
    if (ta === 4) { // Array                                                                                 // 1088
      for (var i = 0; ; i++) {                                                                               // 1089
        if (i === a.length)                                                                                  // 1090
          return (i === b.length) ? 0 : -1;                                                                  // 1091
        if (i === b.length)                                                                                  // 1092
          return 1;                                                                                          // 1093
        var s = LocalCollection._f._cmp(a[i], b[i]);                                                         // 1094
        if (s !== 0)                                                                                         // 1095
          return s;                                                                                          // 1096
      }                                                                                                      // 1097
    }                                                                                                        // 1098
    if (ta === 5) { // binary                                                                                // 1099
      // Surprisingly, a small binary blob is always less than a large one in                                // 1100
      // Mongo.                                                                                              // 1101
      if (a.length !== b.length)                                                                             // 1102
        return a.length - b.length;                                                                          // 1103
      for (i = 0; i < a.length; i++) {                                                                       // 1104
        if (a[i] < b[i])                                                                                     // 1105
          return -1;                                                                                         // 1106
        if (a[i] > b[i])                                                                                     // 1107
          return 1;                                                                                          // 1108
      }                                                                                                      // 1109
      return 0;                                                                                              // 1110
    }                                                                                                        // 1111
    if (ta === 8) { // boolean                                                                               // 1112
      if (a) return b ? 0 : 1;                                                                               // 1113
      return b ? -1 : 0;                                                                                     // 1114
    }                                                                                                        // 1115
    if (ta === 10) // null                                                                                   // 1116
      return 0;                                                                                              // 1117
    if (ta === 11) // regexp                                                                                 // 1118
      throw Error("Sorting not supported on regular expression"); // XXX                                     // 1119
    // 13: javascript code                                                                                   // 1120
    // 14: symbol                                                                                            // 1121
    // 15: javascript code with scope                                                                        // 1122
    // 16: 32-bit integer                                                                                    // 1123
    // 17: timestamp                                                                                         // 1124
    // 18: 64-bit integer                                                                                    // 1125
    // 255: minkey                                                                                           // 1126
    // 127: maxkey                                                                                           // 1127
    if (ta === 13) // javascript code                                                                        // 1128
      throw Error("Sorting not supported on Javascript code"); // XXX                                        // 1129
    throw Error("Unknown type to sort");                                                                     // 1130
  }                                                                                                          // 1131
};                                                                                                           // 1132
                                                                                                             // 1133
// Oddball function used by upsert.                                                                          // 1134
LocalCollection._removeDollarOperators = function (selector) {                                               // 1135
  var selectorDoc = {};                                                                                      // 1136
  for (var k in selector)                                                                                    // 1137
    if (k.substr(0, 1) !== '$')                                                                              // 1138
      selectorDoc[k] = selector[k];                                                                          // 1139
  return selectorDoc;                                                                                        // 1140
};                                                                                                           // 1141
                                                                                                             // 1142
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/sort.js                                                                                //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
// Give a sort spec, which can be in any of these forms:                                                     // 1
//   {"key1": 1, "key2": -1}                                                                                 // 2
//   [["key1", "asc"], ["key2", "desc"]]                                                                     // 3
//   ["key1", ["key2", "desc"]]                                                                              // 4
//                                                                                                           // 5
// (.. with the first form being dependent on the key enumeration                                            // 6
// behavior of your javascript VM, which usually does what you mean in                                       // 7
// this case if the key names don't look like integers ..)                                                   // 8
//                                                                                                           // 9
// return a function that takes two objects, and returns -1 if the                                           // 10
// first object comes first in order, 1 if the second object comes                                           // 11
// first, or 0 if neither object comes before the other.                                                     // 12
                                                                                                             // 13
Minimongo.Sorter = function (spec, options) {                                                                // 14
  var self = this;                                                                                           // 15
  options = options || {};                                                                                   // 16
                                                                                                             // 17
  self._sortSpecParts = [];                                                                                  // 18
                                                                                                             // 19
  var addSpecPart = function (path, ascending) {                                                             // 20
    if (!path)                                                                                               // 21
      throw Error("sort keys must be non-empty");                                                            // 22
    if (path.charAt(0) === '$')                                                                              // 23
      throw Error("unsupported sort key: " + path);                                                          // 24
    self._sortSpecParts.push({                                                                               // 25
      path: path,                                                                                            // 26
      lookup: makeLookupFunction(path, {forSort: true}),                                                     // 27
      ascending: ascending                                                                                   // 28
    });                                                                                                      // 29
  };                                                                                                         // 30
                                                                                                             // 31
  if (spec instanceof Array) {                                                                               // 32
    for (var i = 0; i < spec.length; i++) {                                                                  // 33
      if (typeof spec[i] === "string") {                                                                     // 34
        addSpecPart(spec[i], true);                                                                          // 35
      } else {                                                                                               // 36
        addSpecPart(spec[i][0], spec[i][1] !== "desc");                                                      // 37
      }                                                                                                      // 38
    }                                                                                                        // 39
  } else if (typeof spec === "object") {                                                                     // 40
    _.each(spec, function (value, key) {                                                                     // 41
      addSpecPart(key, value >= 0);                                                                          // 42
    });                                                                                                      // 43
  } else {                                                                                                   // 44
    throw Error("Bad sort specification: " + JSON.stringify(spec));                                          // 45
  }                                                                                                          // 46
                                                                                                             // 47
  // To implement affectedByModifier, we piggy-back on top of Matcher's                                      // 48
  // affectedByModifier code; we create a selector that is affected by the same                              // 49
  // modifiers as this sort order. This is only implemented on the server.                                   // 50
  if (self.affectedByModifier) {                                                                             // 51
    var selector = {};                                                                                       // 52
    _.each(self._sortSpecParts, function (spec) {                                                            // 53
      selector[spec.path] = 1;                                                                               // 54
    });                                                                                                      // 55
    self._selectorForAffectedByModifier = new Minimongo.Matcher(selector);                                   // 56
  }                                                                                                          // 57
                                                                                                             // 58
  self._keyComparator = composeComparators(                                                                  // 59
    _.map(self._sortSpecParts, function (spec, i) {                                                          // 60
      return self._keyFieldComparator(i);                                                                    // 61
    }));                                                                                                     // 62
                                                                                                             // 63
  // If you specify a matcher for this Sorter, _keyFilter may be set to a                                    // 64
  // function which selects whether or not a given "sort key" (tuple of values                               // 65
  // for the different sort spec fields) is compatible with the selector.                                    // 66
  self._keyFilter = null;                                                                                    // 67
  options.matcher && self._useWithMatcher(options.matcher);                                                  // 68
};                                                                                                           // 69
                                                                                                             // 70
// In addition to these methods, sorter_project.js defines combineIntoProjection                             // 71
// on the server only.                                                                                       // 72
_.extend(Minimongo.Sorter.prototype, {                                                                       // 73
  getComparator: function (options) {                                                                        // 74
    var self = this;                                                                                         // 75
                                                                                                             // 76
    // If we have no distances, just use the comparator from the source                                      // 77
    // specification (which defaults to "everything is equal".                                               // 78
    if (!options || !options.distances) {                                                                    // 79
      return self._getBaseComparator();                                                                      // 80
    }                                                                                                        // 81
                                                                                                             // 82
    var distances = options.distances;                                                                       // 83
                                                                                                             // 84
    // Return a comparator which first tries the sort specification, and if that                             // 85
    // says "it's equal", breaks ties using $near distances.                                                 // 86
    return composeComparators([self._getBaseComparator(), function (a, b) {                                  // 87
      if (!distances.has(a._id))                                                                             // 88
        throw Error("Missing distance for " + a._id);                                                        // 89
      if (!distances.has(b._id))                                                                             // 90
        throw Error("Missing distance for " + b._id);                                                        // 91
      return distances.get(a._id) - distances.get(b._id);                                                    // 92
    }]);                                                                                                     // 93
  },                                                                                                         // 94
                                                                                                             // 95
  _getPaths: function () {                                                                                   // 96
    var self = this;                                                                                         // 97
    return _.pluck(self._sortSpecParts, 'path');                                                             // 98
  },                                                                                                         // 99
                                                                                                             // 100
  // Finds the minimum key from the doc, according to the sort specs.  (We say                               // 101
  // "minimum" here but this is with respect to the sort spec, so "descending"                               // 102
  // sort fields mean we're finding the max for that field.)                                                 // 103
  //                                                                                                         // 104
  // Note that this is NOT "find the minimum value of the first field, the                                   // 105
  // minimum value of the second field, etc"... it's "choose the                                             // 106
  // lexicographically minimum value of the key vector, allowing only keys which                             // 107
  // you can find along the same paths".  ie, for a doc {a: [{x: 0, y: 5}, {x:                               // 108
  // 1, y: 3}]} with sort spec {'a.x': 1, 'a.y': 1}, the only keys are [0,5] and                             // 109
  // [1,3], and the minimum key is [0,5]; notably, [0,3] is NOT a key.                                       // 110
  _getMinKeyFromDoc: function (doc) {                                                                        // 111
    var self = this;                                                                                         // 112
    var minKey = null;                                                                                       // 113
                                                                                                             // 114
    self._generateKeysFromDoc(doc, function (key) {                                                          // 115
      if (!self._keyCompatibleWithSelector(key))                                                             // 116
        return;                                                                                              // 117
                                                                                                             // 118
      if (minKey === null) {                                                                                 // 119
        minKey = key;                                                                                        // 120
        return;                                                                                              // 121
      }                                                                                                      // 122
      if (self._compareKeys(key, minKey) < 0) {                                                              // 123
        minKey = key;                                                                                        // 124
      }                                                                                                      // 125
    });                                                                                                      // 126
                                                                                                             // 127
    // This could happen if our key filter somehow filters out all the keys even                             // 128
    // though somehow the selector matches.                                                                  // 129
    if (minKey === null)                                                                                     // 130
      throw Error("sort selector found no keys in doc?");                                                    // 131
    return minKey;                                                                                           // 132
  },                                                                                                         // 133
                                                                                                             // 134
  _keyCompatibleWithSelector: function (key) {                                                               // 135
    var self = this;                                                                                         // 136
    return !self._keyFilter || self._keyFilter(key);                                                         // 137
  },                                                                                                         // 138
                                                                                                             // 139
  // Iterates over each possible "key" from doc (ie, over each branch), calling                              // 140
  // 'cb' with the key.                                                                                      // 141
  _generateKeysFromDoc: function (doc, cb) {                                                                 // 142
    var self = this;                                                                                         // 143
                                                                                                             // 144
    if (self._sortSpecParts.length === 0)                                                                    // 145
      throw new Error("can't generate keys without a spec");                                                 // 146
                                                                                                             // 147
    // maps index -> ({'' -> value} or {path -> value})                                                      // 148
    var valuesByIndexAndPath = [];                                                                           // 149
                                                                                                             // 150
    var pathFromIndices = function (indices) {                                                               // 151
      return indices.join(',') + ',';                                                                        // 152
    };                                                                                                       // 153
                                                                                                             // 154
    var knownPaths = null;                                                                                   // 155
                                                                                                             // 156
    _.each(self._sortSpecParts, function (spec, whichField) {                                                // 157
      // Expand any leaf arrays that we find, and ignore those arrays                                        // 158
      // themselves.  (We never sort based on an array itself.)                                              // 159
      var branches = expandArraysInBranches(spec.lookup(doc), true);                                         // 160
                                                                                                             // 161
      // If there are no values for a key (eg, key goes to an empty array),                                  // 162
      // pretend we found one null value.                                                                    // 163
      if (!branches.length)                                                                                  // 164
        branches = [{value: null}];                                                                          // 165
                                                                                                             // 166
      var usedPaths = false;                                                                                 // 167
      valuesByIndexAndPath[whichField] = {};                                                                 // 168
      _.each(branches, function (branch) {                                                                   // 169
        if (!branch.arrayIndices) {                                                                          // 170
          // If there are no array indices for a branch, then it must be the                                 // 171
          // only branch, because the only thing that produces multiple branches                             // 172
          // is the use of arrays.                                                                           // 173
          if (branches.length > 1)                                                                           // 174
            throw Error("multiple branches but no array used?");                                             // 175
          valuesByIndexAndPath[whichField][''] = branch.value;                                               // 176
          return;                                                                                            // 177
        }                                                                                                    // 178
                                                                                                             // 179
        usedPaths = true;                                                                                    // 180
        var path = pathFromIndices(branch.arrayIndices);                                                     // 181
        if (_.has(valuesByIndexAndPath[whichField], path))                                                   // 182
          throw Error("duplicate path: " + path);                                                            // 183
        valuesByIndexAndPath[whichField][path] = branch.value;                                               // 184
                                                                                                             // 185
        // If two sort fields both go into arrays, they have to go into the                                  // 186
        // exact same arrays and we have to find the same paths.  This is                                    // 187
        // roughly the same condition that makes MongoDB throw this strange                                  // 188
        // error message.  eg, the main thing is that if sort spec is {a: 1,                                 // 189
        // b:1} then a and b cannot both be arrays.                                                          // 190
        //                                                                                                   // 191
        // (In MongoDB it seems to be OK to have {a: 1, 'a.x.y': 1} where 'a'                                // 192
        // and 'a.x.y' are both arrays, but we don't allow this for now.                                     // 193
        // #NestedArraySort                                                                                  // 194
        // XXX achieve full compatibility here                                                               // 195
        if (knownPaths && !_.has(knownPaths, path)) {                                                        // 196
          throw Error("cannot index parallel arrays");                                                       // 197
        }                                                                                                    // 198
      });                                                                                                    // 199
                                                                                                             // 200
      if (knownPaths) {                                                                                      // 201
        // Similarly to above, paths must match everywhere, unless this is a                                 // 202
        // non-array field.                                                                                  // 203
        if (!_.has(valuesByIndexAndPath[whichField], '') &&                                                  // 204
            _.size(knownPaths) !== _.size(valuesByIndexAndPath[whichField])) {                               // 205
          throw Error("cannot index parallel arrays!");                                                      // 206
        }                                                                                                    // 207
      } else if (usedPaths) {                                                                                // 208
        knownPaths = {};                                                                                     // 209
        _.each(valuesByIndexAndPath[whichField], function (x, path) {                                        // 210
          knownPaths[path] = true;                                                                           // 211
        });                                                                                                  // 212
      }                                                                                                      // 213
    });                                                                                                      // 214
                                                                                                             // 215
    if (!knownPaths) {                                                                                       // 216
      // Easy case: no use of arrays.                                                                        // 217
      var soleKey = _.map(valuesByIndexAndPath, function (values) {                                          // 218
        if (!_.has(values, ''))                                                                              // 219
          throw Error("no value in sole key case?");                                                         // 220
        return values[''];                                                                                   // 221
      });                                                                                                    // 222
      cb(soleKey);                                                                                           // 223
      return;                                                                                                // 224
    }                                                                                                        // 225
                                                                                                             // 226
    _.each(knownPaths, function (x, path) {                                                                  // 227
      var key = _.map(valuesByIndexAndPath, function (values) {                                              // 228
        if (_.has(values, ''))                                                                               // 229
          return values[''];                                                                                 // 230
        if (!_.has(values, path))                                                                            // 231
          throw Error("missing path?");                                                                      // 232
        return values[path];                                                                                 // 233
      });                                                                                                    // 234
      cb(key);                                                                                               // 235
    });                                                                                                      // 236
  },                                                                                                         // 237
                                                                                                             // 238
  // Takes in two keys: arrays whose lengths match the number of spec                                        // 239
  // parts. Returns negative, 0, or positive based on using the sort spec to                                 // 240
  // compare fields.                                                                                         // 241
  _compareKeys: function (key1, key2) {                                                                      // 242
    var self = this;                                                                                         // 243
    if (key1.length !== self._sortSpecParts.length ||                                                        // 244
        key2.length !== self._sortSpecParts.length) {                                                        // 245
      throw Error("Key has wrong length");                                                                   // 246
    }                                                                                                        // 247
                                                                                                             // 248
    return self._keyComparator(key1, key2);                                                                  // 249
  },                                                                                                         // 250
                                                                                                             // 251
  // Given an index 'i', returns a comparator that compares two key arrays based                             // 252
  // on field 'i'.                                                                                           // 253
  _keyFieldComparator: function (i) {                                                                        // 254
    var self = this;                                                                                         // 255
    var invert = !self._sortSpecParts[i].ascending;                                                          // 256
    return function (key1, key2) {                                                                           // 257
      var compare = LocalCollection._f._cmp(key1[i], key2[i]);                                               // 258
      if (invert)                                                                                            // 259
        compare = -compare;                                                                                  // 260
      return compare;                                                                                        // 261
    };                                                                                                       // 262
  },                                                                                                         // 263
                                                                                                             // 264
  // Returns a comparator that represents the sort specification (but not                                    // 265
  // including a possible geoquery distance tie-breaker).                                                    // 266
  _getBaseComparator: function () {                                                                          // 267
    var self = this;                                                                                         // 268
                                                                                                             // 269
    // If we're only sorting on geoquery distance and no specs, just say                                     // 270
    // everything is equal.                                                                                  // 271
    if (!self._sortSpecParts.length) {                                                                       // 272
      return function (doc1, doc2) {                                                                         // 273
        return 0;                                                                                            // 274
      };                                                                                                     // 275
    }                                                                                                        // 276
                                                                                                             // 277
    return function (doc1, doc2) {                                                                           // 278
      var key1 = self._getMinKeyFromDoc(doc1);                                                               // 279
      var key2 = self._getMinKeyFromDoc(doc2);                                                               // 280
      return self._compareKeys(key1, key2);                                                                  // 281
    };                                                                                                       // 282
  },                                                                                                         // 283
                                                                                                             // 284
  // In MongoDB, if you have documents                                                                       // 285
  //    {_id: 'x', a: [1, 10]} and                                                                           // 286
  //    {_id: 'y', a: [5, 15]},                                                                              // 287
  // then C.find({}, {sort: {a: 1}}) puts x before y (1 comes before 5).                                     // 288
  // But  C.find({a: {$gt: 3}}, {sort: {a: 1}}) puts y before x (1 does not                                  // 289
  // match the selector, and 5 comes before 10).                                                             // 290
  //                                                                                                         // 291
  // The way this works is pretty subtle!  For example, if the documents                                     // 292
  // are instead {_id: 'x', a: [{x: 1}, {x: 10}]}) and                                                       // 293
  //             {_id: 'y', a: [{x: 5}, {x: 15}]}),                                                          // 294
  // then C.find({'a.x': {$gt: 3}}, {sort: {'a.x': 1}}) and                                                  // 295
  //      C.find({a: {$elemMatch: {x: {$gt: 3}}}}, {sort: {'a.x': 1}})                                       // 296
  // both follow this rule (y before x).  (ie, you do have to apply this                                     // 297
  // through $elemMatch.)                                                                                    // 298
  //                                                                                                         // 299
  // So if you pass a matcher to this sorter's constructor, we will attempt to                               // 300
  // skip sort keys that don't match the selector. The logic here is pretty                                  // 301
  // subtle and undocumented; we've gotten as close as we can figure out based                               // 302
  // on our understanding of Mongo's behavior.                                                               // 303
  _useWithMatcher: function (matcher) {                                                                      // 304
    var self = this;                                                                                         // 305
                                                                                                             // 306
    if (self._keyFilter)                                                                                     // 307
      throw Error("called _useWithMatcher twice?");                                                          // 308
                                                                                                             // 309
    // If we are only sorting by distance, then we're not going to bother to                                 // 310
    // build a key filter.                                                                                   // 311
    // XXX figure out how geoqueries interact with this stuff                                                // 312
    if (_.isEmpty(self._sortSpecParts))                                                                      // 313
      return;                                                                                                // 314
                                                                                                             // 315
    var selector = matcher._selector;                                                                        // 316
                                                                                                             // 317
    // If the user just passed a literal function to find(), then we can't get a                             // 318
    // key filter from it.                                                                                   // 319
    if (selector instanceof Function)                                                                        // 320
      return;                                                                                                // 321
                                                                                                             // 322
    var constraintsByPath = {};                                                                              // 323
    _.each(self._sortSpecParts, function (spec, i) {                                                         // 324
      constraintsByPath[spec.path] = [];                                                                     // 325
    });                                                                                                      // 326
                                                                                                             // 327
    _.each(selector, function (subSelector, key) {                                                           // 328
      // XXX support $and and $or                                                                            // 329
                                                                                                             // 330
      var constraints = constraintsByPath[key];                                                              // 331
      if (!constraints)                                                                                      // 332
        return;                                                                                              // 333
                                                                                                             // 334
      // XXX it looks like the real MongoDB implementation isn't "does the                                   // 335
      // regexp match" but "does the value fall into a range named by the                                    // 336
      // literal prefix of the regexp", ie "foo" in /^foo(bar|baz)+/  But                                    // 337
      // "does the regexp match" is a good approximation.                                                    // 338
      if (subSelector instanceof RegExp) {                                                                   // 339
        // As far as we can tell, using either of the options that both we and                               // 340
        // MongoDB support ('i' and 'm') disables use of the key filter. This                                // 341
        // makes sense: MongoDB mostly appears to be calculating ranges of an                                // 342
        // index to use, which means it only cares about regexps that match                                  // 343
        // one range (with a literal prefix), and both 'i' and 'm' prevent the                               // 344
        // literal prefix of the regexp from actually meaning one range.                                     // 345
        if (subSelector.ignoreCase || subSelector.multiline)                                                 // 346
          return;                                                                                            // 347
        constraints.push(regexpElementMatcher(subSelector));                                                 // 348
        return;                                                                                              // 349
      }                                                                                                      // 350
                                                                                                             // 351
      if (isOperatorObject(subSelector)) {                                                                   // 352
        _.each(subSelector, function (operand, operator) {                                                   // 353
          if (_.contains(['$lt', '$lte', '$gt', '$gte'], operator)) {                                        // 354
            // XXX this depends on us knowing that these operators don't use any                             // 355
            // of the arguments to compileElementSelector other than operand.                                // 356
            constraints.push(                                                                                // 357
              ELEMENT_OPERATORS[operator].compileElementSelector(operand));                                  // 358
          }                                                                                                  // 359
                                                                                                             // 360
          // See comments in the RegExp block above.                                                         // 361
          if (operator === '$regex' && !subSelector.$options) {                                              // 362
            constraints.push(                                                                                // 363
              ELEMENT_OPERATORS.$regex.compileElementSelector(                                               // 364
                operand, subSelector));                                                                      // 365
          }                                                                                                  // 366
                                                                                                             // 367
          // XXX support {$exists: true}, $mod, $type, $in, $elemMatch                                       // 368
        });                                                                                                  // 369
        return;                                                                                              // 370
      }                                                                                                      // 371
                                                                                                             // 372
      // OK, it's an equality thing.                                                                         // 373
      constraints.push(equalityElementMatcher(subSelector));                                                 // 374
    });                                                                                                      // 375
                                                                                                             // 376
    // It appears that the first sort field is treated differently from the                                  // 377
    // others; we shouldn't create a key filter unless the first sort field is                               // 378
    // restricted, though after that point we can restrict the other sort fields                             // 379
    // or not as we wish.                                                                                    // 380
    if (_.isEmpty(constraintsByPath[self._sortSpecParts[0].path]))                                           // 381
      return;                                                                                                // 382
                                                                                                             // 383
    self._keyFilter = function (key) {                                                                       // 384
      return _.all(self._sortSpecParts, function (specPart, index) {                                         // 385
        return _.all(constraintsByPath[specPart.path], function (f) {                                        // 386
          return f(key[index]);                                                                              // 387
        });                                                                                                  // 388
      });                                                                                                    // 389
    };                                                                                                       // 390
  }                                                                                                          // 391
});                                                                                                          // 392
                                                                                                             // 393
// Given an array of comparators                                                                             // 394
// (functions (a,b)->(negative or positive or zero)), returns a single                                       // 395
// comparator which uses each comparator in order and returns the first                                      // 396
// non-zero value.                                                                                           // 397
var composeComparators = function (comparatorArray) {                                                        // 398
  return function (a, b) {                                                                                   // 399
    for (var i = 0; i < comparatorArray.length; ++i) {                                                       // 400
      var compare = comparatorArray[i](a, b);                                                                // 401
      if (compare !== 0)                                                                                     // 402
        return compare;                                                                                      // 403
    }                                                                                                        // 404
    return 0;                                                                                                // 405
  };                                                                                                         // 406
};                                                                                                           // 407
                                                                                                             // 408
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/projection.js                                                                          //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
// Knows how to compile a fields projection to a predicate function.                                         // 1
// @returns - Function: a closure that filters out an object according to the                                // 2
//            fields projection rules:                                                                       // 3
//            @param obj - Object: MongoDB-styled document                                                   // 4
//            @returns - Object: a document with the fields filtered out                                     // 5
//                       according to projection rules. Doesn't retain subfields                             // 6
//                       of passed argument.                                                                 // 7
LocalCollection._compileProjection = function (fields) {                                                     // 8
  LocalCollection._checkSupportedProjection(fields);                                                         // 9
                                                                                                             // 10
  var _idProjection = _.isUndefined(fields._id) ? true : fields._id;                                         // 11
  var details = projectionDetails(fields);                                                                   // 12
                                                                                                             // 13
  // returns transformed doc according to ruleTree                                                           // 14
  var transform = function (doc, ruleTree) {                                                                 // 15
    // Special case for "sets"                                                                               // 16
    if (_.isArray(doc))                                                                                      // 17
      return _.map(doc, function (subdoc) { return transform(subdoc, ruleTree); });                          // 18
                                                                                                             // 19
    var res = details.including ? {} : EJSON.clone(doc);                                                     // 20
    _.each(ruleTree, function (rule, key) {                                                                  // 21
      if (!_.has(doc, key))                                                                                  // 22
        return;                                                                                              // 23
      if (_.isObject(rule)) {                                                                                // 24
        // For sub-objects/subsets we branch                                                                 // 25
        if (_.isObject(doc[key]))                                                                            // 26
          res[key] = transform(doc[key], rule);                                                              // 27
        // Otherwise we don't even touch this subfield                                                       // 28
      } else if (details.including)                                                                          // 29
        res[key] = EJSON.clone(doc[key]);                                                                    // 30
      else                                                                                                   // 31
        delete res[key];                                                                                     // 32
    });                                                                                                      // 33
                                                                                                             // 34
    return res;                                                                                              // 35
  };                                                                                                         // 36
                                                                                                             // 37
  return function (obj) {                                                                                    // 38
    var res = transform(obj, details.tree);                                                                  // 39
                                                                                                             // 40
    if (_idProjection && _.has(obj, '_id'))                                                                  // 41
      res._id = obj._id;                                                                                     // 42
    if (!_idProjection && _.has(res, '_id'))                                                                 // 43
      delete res._id;                                                                                        // 44
    return res;                                                                                              // 45
  };                                                                                                         // 46
};                                                                                                           // 47
                                                                                                             // 48
// Traverses the keys of passed projection and constructs a tree where all                                   // 49
// leaves are either all True or all False                                                                   // 50
// @returns Object:                                                                                          // 51
//  - tree - Object - tree representation of keys involved in projection                                     // 52
//  (exception for '_id' as it is a special case handled separately)                                         // 53
//  - including - Boolean - "take only certain fields" type of projection                                    // 54
projectionDetails = function (fields) {                                                                      // 55
  // Find the non-_id keys (_id is handled specially because it is included unless                           // 56
  // explicitly excluded). Sort the keys, so that our code to detect overlaps                                // 57
  // like 'foo' and 'foo.bar' can assume that 'foo' comes first.                                             // 58
  var fieldsKeys = _.keys(fields).sort();                                                                    // 59
                                                                                                             // 60
  // If there are other rules other than '_id', treat '_id' differently in a                                 // 61
  // separate case. If '_id' is the only rule, use it to understand if it is                                 // 62
  // including/excluding projection.                                                                         // 63
  if (fieldsKeys.length > 0 && !(fieldsKeys.length === 1 && fieldsKeys[0] === '_id'))                        // 64
    fieldsKeys = _.reject(fieldsKeys, function (key) { return key === '_id'; });                             // 65
                                                                                                             // 66
  var including = null; // Unknown                                                                           // 67
                                                                                                             // 68
  _.each(fieldsKeys, function (keyPath) {                                                                    // 69
    var rule = !!fields[keyPath];                                                                            // 70
    if (including === null)                                                                                  // 71
      including = rule;                                                                                      // 72
    if (including !== rule)                                                                                  // 73
      // This error message is copies from MongoDB shell                                                     // 74
      throw MinimongoError("You cannot currently mix including and excluding fields.");                      // 75
  });                                                                                                        // 76
                                                                                                             // 77
                                                                                                             // 78
  var projectionRulesTree = pathsToTree(                                                                     // 79
    fieldsKeys,                                                                                              // 80
    function (path) { return including; },                                                                   // 81
    function (node, path, fullPath) {                                                                        // 82
      // Check passed projection fields' keys: If you have two rules such as                                 // 83
      // 'foo.bar' and 'foo.bar.baz', then the result becomes ambiguous. If                                  // 84
      // that happens, there is a probability you are doing something wrong,                                 // 85
      // framework should notify you about such mistake earlier on cursor                                    // 86
      // compilation step than later during runtime.  Note, that real mongo                                  // 87
      // doesn't do anything about it and the later rule appears in projection                               // 88
      // project, more priority it takes.                                                                    // 89
      //                                                                                                     // 90
      // Example, assume following in mongo shell:                                                           // 91
      // > db.coll.insert({ a: { b: 23, c: 44 } })                                                           // 92
      // > db.coll.find({}, { 'a': 1, 'a.b': 1 })                                                            // 93
      // { "_id" : ObjectId("520bfe456024608e8ef24af3"), "a" : { "b" : 23 } }                                // 94
      // > db.coll.find({}, { 'a.b': 1, 'a': 1 })                                                            // 95
      // { "_id" : ObjectId("520bfe456024608e8ef24af3"), "a" : { "b" : 23, "c" : 44 } }                      // 96
      //                                                                                                     // 97
      // Note, how second time the return set of keys is different.                                          // 98
                                                                                                             // 99
      var currentPath = fullPath;                                                                            // 100
      var anotherPath = path;                                                                                // 101
      throw MinimongoError("both " + currentPath + " and " + anotherPath +                                   // 102
                           " found in fields option, using both of them may trigger " +                      // 103
                           "unexpected behavior. Did you mean to use only one of them?");                    // 104
    });                                                                                                      // 105
                                                                                                             // 106
  return {                                                                                                   // 107
    tree: projectionRulesTree,                                                                               // 108
    including: including                                                                                     // 109
  };                                                                                                         // 110
};                                                                                                           // 111
                                                                                                             // 112
// paths - Array: list of mongo style paths                                                                  // 113
// newLeafFn - Function: of form function(path) should return a scalar value to                              // 114
//                       put into list created for that path                                                 // 115
// conflictFn - Function: of form function(node, path, fullPath) is called                                   // 116
//                        when building a tree path for 'fullPath' node on                                   // 117
//                        'path' was already a leaf with a value. Must return a                              // 118
//                        conflict resolution.                                                               // 119
// initial tree - Optional Object: starting tree.                                                            // 120
// @returns - Object: tree represented as a set of nested objects                                            // 121
pathsToTree = function (paths, newLeafFn, conflictFn, tree) {                                                // 122
  tree = tree || {};                                                                                         // 123
  _.each(paths, function (keyPath) {                                                                         // 124
    var treePos = tree;                                                                                      // 125
    var pathArr = keyPath.split('.');                                                                        // 126
                                                                                                             // 127
    // use _.all just for iteration with break                                                               // 128
    var success = _.all(pathArr.slice(0, -1), function (key, idx) {                                          // 129
      if (!_.has(treePos, key))                                                                              // 130
        treePos[key] = {};                                                                                   // 131
      else if (!_.isObject(treePos[key])) {                                                                  // 132
        treePos[key] = conflictFn(treePos[key],                                                              // 133
                                  pathArr.slice(0, idx + 1).join('.'),                                       // 134
                                  keyPath);                                                                  // 135
        // break out of loop if we are failing for this path                                                 // 136
        if (!_.isObject(treePos[key]))                                                                       // 137
          return false;                                                                                      // 138
      }                                                                                                      // 139
                                                                                                             // 140
      treePos = treePos[key];                                                                                // 141
      return true;                                                                                           // 142
    });                                                                                                      // 143
                                                                                                             // 144
    if (success) {                                                                                           // 145
      var lastKey = _.last(pathArr);                                                                         // 146
      if (!_.has(treePos, lastKey))                                                                          // 147
        treePos[lastKey] = newLeafFn(keyPath);                                                               // 148
      else                                                                                                   // 149
        treePos[lastKey] = conflictFn(treePos[lastKey], keyPath, keyPath);                                   // 150
    }                                                                                                        // 151
  });                                                                                                        // 152
                                                                                                             // 153
  return tree;                                                                                               // 154
};                                                                                                           // 155
                                                                                                             // 156
LocalCollection._checkSupportedProjection = function (fields) {                                              // 157
  if (!_.isObject(fields) || _.isArray(fields))                                                              // 158
    throw MinimongoError("fields option must be an object");                                                 // 159
                                                                                                             // 160
  _.each(fields, function (val, keyPath) {                                                                   // 161
    if (_.contains(keyPath.split('.'), '$'))                                                                 // 162
      throw MinimongoError("Minimongo doesn't support $ operator in projections yet.");                      // 163
    if (_.indexOf([1, 0, true, false], val) === -1)                                                          // 164
      throw MinimongoError("Projection values should be one of 1, 0, true, or false");                       // 165
  });                                                                                                        // 166
};                                                                                                           // 167
                                                                                                             // 168
                                                                                                             // 169
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/modify.js                                                                              //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
// XXX need a strategy for passing the binding of $ into this                                                // 1
// function, from the compiled selector                                                                      // 2
//                                                                                                           // 3
// maybe just {key.up.to.just.before.dollarsign: array_index}                                                // 4
//                                                                                                           // 5
// XXX atomicity: if one modification fails, do we roll back the whole                                       // 6
// change?                                                                                                   // 7
//                                                                                                           // 8
// options:                                                                                                  // 9
//   - isInsert is set when _modify is being called to compute the document to                               // 10
//     insert as part of an upsert operation. We use this primarily to figure                                // 11
//     out when to set the fields in $setOnInsert, if present.                                               // 12
LocalCollection._modify = function (doc, mod, options) {                                                     // 13
  options = options || {};                                                                                   // 14
  if (!isPlainObject(mod))                                                                                   // 15
    throw MinimongoError("Modifier must be an object");                                                      // 16
  var isModifier = isOperatorObject(mod);                                                                    // 17
                                                                                                             // 18
  var newDoc;                                                                                                // 19
                                                                                                             // 20
  if (!isModifier) {                                                                                         // 21
    if (mod._id && !EJSON.equals(doc._id, mod._id))                                                          // 22
      throw MinimongoError("Cannot change the _id of a document");                                           // 23
                                                                                                             // 24
    // replace the whole document                                                                            // 25
    for (var k in mod) {                                                                                     // 26
      if (/\./.test(k))                                                                                      // 27
        throw MinimongoError(                                                                                // 28
          "When replacing document, field name may not contain '.'");                                        // 29
    }                                                                                                        // 30
    newDoc = mod;                                                                                            // 31
  } else {                                                                                                   // 32
    // apply modifiers to the doc.                                                                           // 33
    newDoc = EJSON.clone(doc);                                                                               // 34
                                                                                                             // 35
    _.each(mod, function (operand, op) {                                                                     // 36
      var modFunc = MODIFIERS[op];                                                                           // 37
      // Treat $setOnInsert as $set if this is an insert.                                                    // 38
      if (options.isInsert && op === '$setOnInsert')                                                         // 39
        modFunc = MODIFIERS['$set'];                                                                         // 40
      if (!modFunc)                                                                                          // 41
        throw MinimongoError("Invalid modifier specified " + op);                                            // 42
      _.each(operand, function (arg, keypath) {                                                              // 43
        if (keypath === '') {                                                                                // 44
          throw MinimongoError("An empty update path is not valid.");                                        // 45
        }                                                                                                    // 46
                                                                                                             // 47
        if (keypath === '_id') {                                                                             // 48
          throw MinimongoError("Mod on _id not allowed");                                                    // 49
        }                                                                                                    // 50
                                                                                                             // 51
        var keyparts = keypath.split('.');                                                                   // 52
                                                                                                             // 53
        if (! _.all(keyparts, _.identity)) {                                                                 // 54
          throw MinimongoError(                                                                              // 55
            "The update path '" + keypath +                                                                  // 56
              "' contains an empty field name, which is not allowed.");                                      // 57
        }                                                                                                    // 58
                                                                                                             // 59
        var noCreate = _.has(NO_CREATE_MODIFIERS, op);                                                       // 60
        var forbidArray = (op === "$rename");                                                                // 61
        var target = findModTarget(newDoc, keyparts, {                                                       // 62
          noCreate: NO_CREATE_MODIFIERS[op],                                                                 // 63
          forbidArray: (op === "$rename"),                                                                   // 64
          arrayIndices: options.arrayIndices                                                                 // 65
        });                                                                                                  // 66
        var field = keyparts.pop();                                                                          // 67
        modFunc(target, field, arg, keypath, newDoc);                                                        // 68
      });                                                                                                    // 69
    });                                                                                                      // 70
  }                                                                                                          // 71
                                                                                                             // 72
  // move new document into place.                                                                           // 73
  _.each(_.keys(doc), function (k) {                                                                         // 74
    // Note: this used to be for (var k in doc) however, this does not                                       // 75
    // work right in Opera. Deleting from a doc while iterating over it                                      // 76
    // would sometimes cause opera to skip some keys.                                                        // 77
    if (k !== '_id')                                                                                         // 78
      delete doc[k];                                                                                         // 79
  });                                                                                                        // 80
  _.each(newDoc, function (v, k) {                                                                           // 81
    doc[k] = v;                                                                                              // 82
  });                                                                                                        // 83
};                                                                                                           // 84
                                                                                                             // 85
// for a.b.c.2.d.e, keyparts should be ['a', 'b', 'c', '2', 'd', 'e'],                                       // 86
// and then you would operate on the 'e' property of the returned                                            // 87
// object.                                                                                                   // 88
//                                                                                                           // 89
// if options.noCreate is falsey, creates intermediate levels of                                             // 90
// structure as necessary, like mkdir -p (and raises an exception if                                         // 91
// that would mean giving a non-numeric property to an array.) if                                            // 92
// options.noCreate is true, return undefined instead.                                                       // 93
//                                                                                                           // 94
// may modify the last element of keyparts to signal to the caller that it needs                             // 95
// to use a different value to index into the returned object (for example,                                  // 96
// ['a', '01'] -> ['a', 1]).                                                                                 // 97
//                                                                                                           // 98
// if forbidArray is true, return null if the keypath goes through an array.                                 // 99
//                                                                                                           // 100
// if options.arrayIndices is set, use its first element for the (first) '$' in                              // 101
// the path.                                                                                                 // 102
var findModTarget = function (doc, keyparts, options) {                                                      // 103
  options = options || {};                                                                                   // 104
  var usedArrayIndex = false;                                                                                // 105
  for (var i = 0; i < keyparts.length; i++) {                                                                // 106
    var last = (i === keyparts.length - 1);                                                                  // 107
    var keypart = keyparts[i];                                                                               // 108
    var indexable = isIndexable(doc);                                                                        // 109
    if (!indexable) {                                                                                        // 110
      if (options.noCreate)                                                                                  // 111
        return undefined;                                                                                    // 112
      var e = MinimongoError(                                                                                // 113
        "cannot use the part '" + keypart + "' to traverse " + doc);                                         // 114
      e.setPropertyError = true;                                                                             // 115
      throw e;                                                                                               // 116
    }                                                                                                        // 117
    if (doc instanceof Array) {                                                                              // 118
      if (options.forbidArray)                                                                               // 119
        return null;                                                                                         // 120
      if (keypart === '$') {                                                                                 // 121
        if (usedArrayIndex)                                                                                  // 122
          throw MinimongoError("Too many positional (i.e. '$') elements");                                   // 123
        if (!options.arrayIndices || !options.arrayIndices.length) {                                         // 124
          throw MinimongoError("The positional operator did not find the " +                                 // 125
                               "match needed from the query");                                               // 126
        }                                                                                                    // 127
        keypart = options.arrayIndices[0];                                                                   // 128
        usedArrayIndex = true;                                                                               // 129
      } else if (isNumericKey(keypart)) {                                                                    // 130
        keypart = parseInt(keypart);                                                                         // 131
      } else {                                                                                               // 132
        if (options.noCreate)                                                                                // 133
          return undefined;                                                                                  // 134
        throw MinimongoError(                                                                                // 135
          "can't append to array using string field name ["                                                  // 136
                    + keypart + "]");                                                                        // 137
      }                                                                                                      // 138
      if (last)                                                                                              // 139
        // handle 'a.01'                                                                                     // 140
        keyparts[i] = keypart;                                                                               // 141
      if (options.noCreate && keypart >= doc.length)                                                         // 142
        return undefined;                                                                                    // 143
      while (doc.length < keypart)                                                                           // 144
        doc.push(null);                                                                                      // 145
      if (!last) {                                                                                           // 146
        if (doc.length === keypart)                                                                          // 147
          doc.push({});                                                                                      // 148
        else if (typeof doc[keypart] !== "object")                                                           // 149
          throw MinimongoError("can't modify field '" + keyparts[i + 1] +                                    // 150
                      "' of list value " + JSON.stringify(doc[keypart]));                                    // 151
      }                                                                                                      // 152
    } else {                                                                                                 // 153
      if (keypart.length && keypart.substr(0, 1) === '$')                                                    // 154
        throw MinimongoError("can't set field named " + keypart);                                            // 155
      if (!(keypart in doc)) {                                                                               // 156
        if (options.noCreate)                                                                                // 157
          return undefined;                                                                                  // 158
        if (!last)                                                                                           // 159
          doc[keypart] = {};                                                                                 // 160
      }                                                                                                      // 161
    }                                                                                                        // 162
                                                                                                             // 163
    if (last)                                                                                                // 164
      return doc;                                                                                            // 165
    doc = doc[keypart];                                                                                      // 166
  }                                                                                                          // 167
                                                                                                             // 168
  // notreached                                                                                              // 169
};                                                                                                           // 170
                                                                                                             // 171
var NO_CREATE_MODIFIERS = {                                                                                  // 172
  $unset: true,                                                                                              // 173
  $pop: true,                                                                                                // 174
  $rename: true,                                                                                             // 175
  $pull: true,                                                                                               // 176
  $pullAll: true                                                                                             // 177
};                                                                                                           // 178
                                                                                                             // 179
var MODIFIERS = {                                                                                            // 180
  $inc: function (target, field, arg) {                                                                      // 181
    if (typeof arg !== "number")                                                                             // 182
      throw MinimongoError("Modifier $inc allowed for numbers only");                                        // 183
    if (field in target) {                                                                                   // 184
      if (typeof target[field] !== "number")                                                                 // 185
        throw MinimongoError("Cannot apply $inc modifier to non-number");                                    // 186
      target[field] += arg;                                                                                  // 187
    } else {                                                                                                 // 188
      target[field] = arg;                                                                                   // 189
    }                                                                                                        // 190
  },                                                                                                         // 191
  $set: function (target, field, arg) {                                                                      // 192
    if (!_.isObject(target)) { // not an array or an object                                                  // 193
      var e = MinimongoError("Cannot set property on non-object field");                                     // 194
      e.setPropertyError = true;                                                                             // 195
      throw e;                                                                                               // 196
    }                                                                                                        // 197
    if (target === null) {                                                                                   // 198
      var e = MinimongoError("Cannot set property on null");                                                 // 199
      e.setPropertyError = true;                                                                             // 200
      throw e;                                                                                               // 201
    }                                                                                                        // 202
    target[field] = EJSON.clone(arg);                                                                        // 203
  },                                                                                                         // 204
  $setOnInsert: function (target, field, arg) {                                                              // 205
    // converted to `$set` in `_modify`                                                                      // 206
  },                                                                                                         // 207
  $unset: function (target, field, arg) {                                                                    // 208
    if (target !== undefined) {                                                                              // 209
      if (target instanceof Array) {                                                                         // 210
        if (field in target)                                                                                 // 211
          target[field] = null;                                                                              // 212
      } else                                                                                                 // 213
        delete target[field];                                                                                // 214
    }                                                                                                        // 215
  },                                                                                                         // 216
  $push: function (target, field, arg) {                                                                     // 217
    if (target[field] === undefined)                                                                         // 218
      target[field] = [];                                                                                    // 219
    if (!(target[field] instanceof Array))                                                                   // 220
      throw MinimongoError("Cannot apply $push modifier to non-array");                                      // 221
                                                                                                             // 222
    if (!(arg && arg.$each)) {                                                                               // 223
      // Simple mode: not $each                                                                              // 224
      target[field].push(EJSON.clone(arg));                                                                  // 225
      return;                                                                                                // 226
    }                                                                                                        // 227
                                                                                                             // 228
    // Fancy mode: $each (and maybe $slice and $sort)                                                        // 229
    var toPush = arg.$each;                                                                                  // 230
    if (!(toPush instanceof Array))                                                                          // 231
      throw MinimongoError("$each must be an array");                                                        // 232
                                                                                                             // 233
    // Parse $slice.                                                                                         // 234
    var slice = undefined;                                                                                   // 235
    if ('$slice' in arg) {                                                                                   // 236
      if (typeof arg.$slice !== "number")                                                                    // 237
        throw MinimongoError("$slice must be a numeric value");                                              // 238
      // XXX should check to make sure integer                                                               // 239
      if (arg.$slice > 0)                                                                                    // 240
        throw MinimongoError("$slice in $push must be zero or negative");                                    // 241
      slice = arg.$slice;                                                                                    // 242
    }                                                                                                        // 243
                                                                                                             // 244
    // Parse $sort.                                                                                          // 245
    var sortFunction = undefined;                                                                            // 246
    if (arg.$sort) {                                                                                         // 247
      if (slice === undefined)                                                                               // 248
        throw MinimongoError("$sort requires $slice to be present");                                         // 249
      // XXX this allows us to use a $sort whose value is an array, but that's                               // 250
      // actually an extension of the Node driver, so it won't work                                          // 251
      // server-side. Could be confusing!                                                                    // 252
      // XXX is it correct that we don't do geo-stuff here?                                                  // 253
      sortFunction = new Minimongo.Sorter(arg.$sort).getComparator();                                        // 254
      for (var i = 0; i < toPush.length; i++) {                                                              // 255
        if (LocalCollection._f._type(toPush[i]) !== 3) {                                                     // 256
          throw MinimongoError("$push like modifiers using $sort " +                                         // 257
                      "require all elements to be objects");                                                 // 258
        }                                                                                                    // 259
      }                                                                                                      // 260
    }                                                                                                        // 261
                                                                                                             // 262
    // Actually push.                                                                                        // 263
    for (var j = 0; j < toPush.length; j++)                                                                  // 264
      target[field].push(EJSON.clone(toPush[j]));                                                            // 265
                                                                                                             // 266
    // Actually sort.                                                                                        // 267
    if (sortFunction)                                                                                        // 268
      target[field].sort(sortFunction);                                                                      // 269
                                                                                                             // 270
    // Actually slice.                                                                                       // 271
    if (slice !== undefined) {                                                                               // 272
      if (slice === 0)                                                                                       // 273
        target[field] = [];  // differs from Array.slice!                                                    // 274
      else                                                                                                   // 275
        target[field] = target[field].slice(slice);                                                          // 276
    }                                                                                                        // 277
  },                                                                                                         // 278
  $pushAll: function (target, field, arg) {                                                                  // 279
    if (!(typeof arg === "object" && arg instanceof Array))                                                  // 280
      throw MinimongoError("Modifier $pushAll/pullAll allowed for arrays only");                             // 281
    var x = target[field];                                                                                   // 282
    if (x === undefined)                                                                                     // 283
      target[field] = arg;                                                                                   // 284
    else if (!(x instanceof Array))                                                                          // 285
      throw MinimongoError("Cannot apply $pushAll modifier to non-array");                                   // 286
    else {                                                                                                   // 287
      for (var i = 0; i < arg.length; i++)                                                                   // 288
        x.push(arg[i]);                                                                                      // 289
    }                                                                                                        // 290
  },                                                                                                         // 291
  $addToSet: function (target, field, arg) {                                                                 // 292
    var isEach = false;                                                                                      // 293
    if (typeof arg === "object") {                                                                           // 294
      //check if first key is '$each'                                                                        // 295
      for (var k in arg) {                                                                                   // 296
        if (k === "$each")                                                                                   // 297
          isEach = true;                                                                                     // 298
        break;                                                                                               // 299
      }                                                                                                      // 300
    }                                                                                                        // 301
    var values = isEach ? arg["$each"] : [arg];                                                              // 302
    var x = target[field];                                                                                   // 303
    if (x === undefined)                                                                                     // 304
      target[field] = values;                                                                                // 305
    else if (!(x instanceof Array))                                                                          // 306
      throw MinimongoError("Cannot apply $addToSet modifier to non-array");                                  // 307
    else {                                                                                                   // 308
      _.each(values, function (value) {                                                                      // 309
        for (var i = 0; i < x.length; i++)                                                                   // 310
          if (LocalCollection._f._equal(value, x[i]))                                                        // 311
            return;                                                                                          // 312
        x.push(EJSON.clone(value));                                                                          // 313
      });                                                                                                    // 314
    }                                                                                                        // 315
  },                                                                                                         // 316
  $pop: function (target, field, arg) {                                                                      // 317
    if (target === undefined)                                                                                // 318
      return;                                                                                                // 319
    var x = target[field];                                                                                   // 320
    if (x === undefined)                                                                                     // 321
      return;                                                                                                // 322
    else if (!(x instanceof Array))                                                                          // 323
      throw MinimongoError("Cannot apply $pop modifier to non-array");                                       // 324
    else {                                                                                                   // 325
      if (typeof arg === 'number' && arg < 0)                                                                // 326
        x.splice(0, 1);                                                                                      // 327
      else                                                                                                   // 328
        x.pop();                                                                                             // 329
    }                                                                                                        // 330
  },                                                                                                         // 331
  $pull: function (target, field, arg) {                                                                     // 332
    if (target === undefined)                                                                                // 333
      return;                                                                                                // 334
    var x = target[field];                                                                                   // 335
    if (x === undefined)                                                                                     // 336
      return;                                                                                                // 337
    else if (!(x instanceof Array))                                                                          // 338
      throw MinimongoError("Cannot apply $pull/pullAll modifier to non-array");                              // 339
    else {                                                                                                   // 340
      var out = [];                                                                                          // 341
      if (typeof arg === "object" && !(arg instanceof Array)) {                                              // 342
        // XXX would be much nicer to compile this once, rather than                                         // 343
        // for each document we modify.. but usually we're not                                               // 344
        // modifying that many documents, so we'll let it slide for                                          // 345
        // now                                                                                               // 346
                                                                                                             // 347
        // XXX Minimongo.Matcher isn't up for the job, because we need                                       // 348
        // to permit stuff like {$pull: {a: {$gt: 4}}}.. something                                           // 349
        // like {$gt: 4} is not normally a complete selector.                                                // 350
        // same issue as $elemMatch possibly?                                                                // 351
        var matcher = new Minimongo.Matcher(arg);                                                            // 352
        for (var i = 0; i < x.length; i++)                                                                   // 353
          if (!matcher.documentMatches(x[i]).result)                                                         // 354
            out.push(x[i]);                                                                                  // 355
      } else {                                                                                               // 356
        for (var i = 0; i < x.length; i++)                                                                   // 357
          if (!LocalCollection._f._equal(x[i], arg))                                                         // 358
            out.push(x[i]);                                                                                  // 359
      }                                                                                                      // 360
      target[field] = out;                                                                                   // 361
    }                                                                                                        // 362
  },                                                                                                         // 363
  $pullAll: function (target, field, arg) {                                                                  // 364
    if (!(typeof arg === "object" && arg instanceof Array))                                                  // 365
      throw MinimongoError("Modifier $pushAll/pullAll allowed for arrays only");                             // 366
    if (target === undefined)                                                                                // 367
      return;                                                                                                // 368
    var x = target[field];                                                                                   // 369
    if (x === undefined)                                                                                     // 370
      return;                                                                                                // 371
    else if (!(x instanceof Array))                                                                          // 372
      throw MinimongoError("Cannot apply $pull/pullAll modifier to non-array");                              // 373
    else {                                                                                                   // 374
      var out = [];                                                                                          // 375
      for (var i = 0; i < x.length; i++) {                                                                   // 376
        var exclude = false;                                                                                 // 377
        for (var j = 0; j < arg.length; j++) {                                                               // 378
          if (LocalCollection._f._equal(x[i], arg[j])) {                                                     // 379
            exclude = true;                                                                                  // 380
            break;                                                                                           // 381
          }                                                                                                  // 382
        }                                                                                                    // 383
        if (!exclude)                                                                                        // 384
          out.push(x[i]);                                                                                    // 385
      }                                                                                                      // 386
      target[field] = out;                                                                                   // 387
    }                                                                                                        // 388
  },                                                                                                         // 389
  $rename: function (target, field, arg, keypath, doc) {                                                     // 390
    if (keypath === arg)                                                                                     // 391
      // no idea why mongo has this restriction..                                                            // 392
      throw MinimongoError("$rename source must differ from target");                                        // 393
    if (target === null)                                                                                     // 394
      throw MinimongoError("$rename source field invalid");                                                  // 395
    if (typeof arg !== "string")                                                                             // 396
      throw MinimongoError("$rename target must be a string");                                               // 397
    if (target === undefined)                                                                                // 398
      return;                                                                                                // 399
    var v = target[field];                                                                                   // 400
    delete target[field];                                                                                    // 401
                                                                                                             // 402
    var keyparts = arg.split('.');                                                                           // 403
    var target2 = findModTarget(doc, keyparts, {forbidArray: true});                                         // 404
    if (target2 === null)                                                                                    // 405
      throw MinimongoError("$rename target field invalid");                                                  // 406
    var field2 = keyparts.pop();                                                                             // 407
    target2[field2] = v;                                                                                     // 408
  },                                                                                                         // 409
  $bit: function (target, field, arg) {                                                                      // 410
    // XXX mongo only supports $bit on integers, and we only support                                         // 411
    // native javascript numbers (doubles) so far, so we can't support $bit                                  // 412
    throw MinimongoError("$bit is not supported");                                                           // 413
  }                                                                                                          // 414
};                                                                                                           // 415
                                                                                                             // 416
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/diff.js                                                                                //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
// ordered: bool.                                                                                            // 1
// old_results and new_results: collections of documents.                                                    // 2
//    if ordered, they are arrays.                                                                           // 3
//    if unordered, they are IdMaps                                                                          // 4
LocalCollection._diffQueryChanges = function (ordered, oldResults, newResults,                               // 5
                                              observer, options) {                                           // 6
  if (ordered)                                                                                               // 7
    LocalCollection._diffQueryOrderedChanges(                                                                // 8
      oldResults, newResults, observer, options);                                                            // 9
  else                                                                                                       // 10
    LocalCollection._diffQueryUnorderedChanges(                                                              // 11
      oldResults, newResults, observer, options);                                                            // 12
};                                                                                                           // 13
                                                                                                             // 14
LocalCollection._diffQueryUnorderedChanges = function (oldResults, newResults,                               // 15
                                                       observer, options) {                                  // 16
  options = options || {};                                                                                   // 17
  var projectionFn = options.projectionFn || EJSON.clone;                                                    // 18
                                                                                                             // 19
  if (observer.movedBefore) {                                                                                // 20
    throw new Error("_diffQueryUnordered called with a movedBefore observer!");                              // 21
  }                                                                                                          // 22
                                                                                                             // 23
  newResults.forEach(function (newDoc, id) {                                                                 // 24
    var oldDoc = oldResults.get(id);                                                                         // 25
    if (oldDoc) {                                                                                            // 26
      if (observer.changed && !EJSON.equals(oldDoc, newDoc)) {                                               // 27
        var projectedNew = projectionFn(newDoc);                                                             // 28
        var projectedOld = projectionFn(oldDoc);                                                             // 29
        var changedFields =                                                                                  // 30
              LocalCollection._makeChangedFields(projectedNew, projectedOld);                                // 31
        if (! _.isEmpty(changedFields)) {                                                                    // 32
          observer.changed(id, changedFields);                                                               // 33
        }                                                                                                    // 34
      }                                                                                                      // 35
    } else if (observer.added) {                                                                             // 36
      var fields = projectionFn(newDoc);                                                                     // 37
      delete fields._id;                                                                                     // 38
      observer.added(newDoc._id, fields);                                                                    // 39
    }                                                                                                        // 40
  });                                                                                                        // 41
                                                                                                             // 42
  if (observer.removed) {                                                                                    // 43
    oldResults.forEach(function (oldDoc, id) {                                                               // 44
      if (!newResults.has(id))                                                                               // 45
        observer.removed(id);                                                                                // 46
    });                                                                                                      // 47
  }                                                                                                          // 48
};                                                                                                           // 49
                                                                                                             // 50
                                                                                                             // 51
LocalCollection._diffQueryOrderedChanges = function (old_results, new_results,                               // 52
                                                     observer, options) {                                    // 53
  options = options || {};                                                                                   // 54
  var projectionFn = options.projectionFn || EJSON.clone;                                                    // 55
                                                                                                             // 56
  var new_presence_of_id = {};                                                                               // 57
  _.each(new_results, function (doc) {                                                                       // 58
    if (new_presence_of_id[doc._id])                                                                         // 59
      Meteor._debug("Duplicate _id in new_results");                                                         // 60
    new_presence_of_id[doc._id] = true;                                                                      // 61
  });                                                                                                        // 62
                                                                                                             // 63
  var old_index_of_id = {};                                                                                  // 64
  _.each(old_results, function (doc, i) {                                                                    // 65
    if (doc._id in old_index_of_id)                                                                          // 66
      Meteor._debug("Duplicate _id in old_results");                                                         // 67
    old_index_of_id[doc._id] = i;                                                                            // 68
  });                                                                                                        // 69
                                                                                                             // 70
  // ALGORITHM:                                                                                              // 71
  //                                                                                                         // 72
  // To determine which docs should be considered "moved" (and which                                         // 73
  // merely change position because of other docs moving) we run                                             // 74
  // a "longest common subsequence" (LCS) algorithm.  The LCS of the                                         // 75
  // old doc IDs and the new doc IDs gives the docs that should NOT be                                       // 76
  // considered moved.                                                                                       // 77
                                                                                                             // 78
  // To actually call the appropriate callbacks to get from the old state to the                             // 79
  // new state:                                                                                              // 80
                                                                                                             // 81
  // First, we call removed() on all the items that only appear in the old                                   // 82
  // state.                                                                                                  // 83
                                                                                                             // 84
  // Then, once we have the items that should not move, we walk through the new                              // 85
  // results array group-by-group, where a "group" is a set of items that have                               // 86
  // moved, anchored on the end by an item that should not move.  One by one, we                             // 87
  // move each of those elements into place "before" the anchoring end-of-group                              // 88
  // item, and fire changed events on them if necessary.  Then we fire a changed                             // 89
  // event on the anchor, and move on to the next group.  There is always at                                 // 90
  // least one group; the last group is anchored by a virtual "null" id at the                               // 91
  // end.                                                                                                    // 92
                                                                                                             // 93
  // Asymptotically: O(N k) where k is number of ops, or potentially                                         // 94
  // O(N log N) if inner loop of LCS were made to be binary search.                                          // 95
                                                                                                             // 96
                                                                                                             // 97
  //////// LCS (longest common sequence, with respect to _id)                                                // 98
  // (see Wikipedia article on Longest Increasing Subsequence,                                               // 99
  // where the LIS is taken of the sequence of old indices of the                                            // 100
  // docs in new_results)                                                                                    // 101
  //                                                                                                         // 102
  // unmoved: the output of the algorithm; members of the LCS,                                               // 103
  // in the form of indices into new_results                                                                 // 104
  var unmoved = [];                                                                                          // 105
  // max_seq_len: length of LCS found so far                                                                 // 106
  var max_seq_len = 0;                                                                                       // 107
  // seq_ends[i]: the index into new_results of the last doc in a                                            // 108
  // common subsequence of length of i+1 <= max_seq_len                                                      // 109
  var N = new_results.length;                                                                                // 110
  var seq_ends = new Array(N);                                                                               // 111
  // ptrs:  the common subsequence ending with new_results[n] extends                                        // 112
  // a common subsequence ending with new_results[ptr[n]], unless                                            // 113
  // ptr[n] is -1.                                                                                           // 114
  var ptrs = new Array(N);                                                                                   // 115
  // virtual sequence of old indices of new results                                                          // 116
  var old_idx_seq = function(i_new) {                                                                        // 117
    return old_index_of_id[new_results[i_new]._id];                                                          // 118
  };                                                                                                         // 119
  // for each item in new_results, use it to extend a common subsequence                                     // 120
  // of length j <= max_seq_len                                                                              // 121
  for(var i=0; i<N; i++) {                                                                                   // 122
    if (old_index_of_id[new_results[i]._id] !== undefined) {                                                 // 123
      var j = max_seq_len;                                                                                   // 124
      // this inner loop would traditionally be a binary search,                                             // 125
      // but scanning backwards we will likely find a subseq to extend                                       // 126
      // pretty soon, bounded for example by the total number of ops.                                        // 127
      // If this were to be changed to a binary search, we'd still want                                      // 128
      // to scan backwards a bit as an optimization.                                                         // 129
      while (j > 0) {                                                                                        // 130
        if (old_idx_seq(seq_ends[j-1]) < old_idx_seq(i))                                                     // 131
          break;                                                                                             // 132
        j--;                                                                                                 // 133
      }                                                                                                      // 134
                                                                                                             // 135
      ptrs[i] = (j === 0 ? -1 : seq_ends[j-1]);                                                              // 136
      seq_ends[j] = i;                                                                                       // 137
      if (j+1 > max_seq_len)                                                                                 // 138
        max_seq_len = j+1;                                                                                   // 139
    }                                                                                                        // 140
  }                                                                                                          // 141
                                                                                                             // 142
  // pull out the LCS/LIS into unmoved                                                                       // 143
  var idx = (max_seq_len === 0 ? -1 : seq_ends[max_seq_len-1]);                                              // 144
  while (idx >= 0) {                                                                                         // 145
    unmoved.push(idx);                                                                                       // 146
    idx = ptrs[idx];                                                                                         // 147
  }                                                                                                          // 148
  // the unmoved item list is built backwards, so fix that                                                   // 149
  unmoved.reverse();                                                                                         // 150
                                                                                                             // 151
  // the last group is always anchored by the end of the result list, which is                               // 152
  // an id of "null"                                                                                         // 153
  unmoved.push(new_results.length);                                                                          // 154
                                                                                                             // 155
  _.each(old_results, function (doc) {                                                                       // 156
    if (!new_presence_of_id[doc._id])                                                                        // 157
      observer.removed && observer.removed(doc._id);                                                         // 158
  });                                                                                                        // 159
  // for each group of things in the new_results that is anchored by an unmoved                              // 160
  // element, iterate through the things before it.                                                          // 161
  var startOfGroup = 0;                                                                                      // 162
  _.each(unmoved, function (endOfGroup) {                                                                    // 163
    var groupId = new_results[endOfGroup] ? new_results[endOfGroup]._id : null;                              // 164
    var oldDoc, newDoc, fields, projectedNew, projectedOld;                                                  // 165
    for (var i = startOfGroup; i < endOfGroup; i++) {                                                        // 166
      newDoc = new_results[i];                                                                               // 167
      if (!_.has(old_index_of_id, newDoc._id)) {                                                             // 168
        fields = projectionFn(newDoc);                                                                       // 169
        delete fields._id;                                                                                   // 170
        observer.addedBefore && observer.addedBefore(newDoc._id, fields, groupId);                           // 171
        observer.added && observer.added(newDoc._id, fields);                                                // 172
      } else {                                                                                               // 173
        // moved                                                                                             // 174
        oldDoc = old_results[old_index_of_id[newDoc._id]];                                                   // 175
        projectedNew = projectionFn(newDoc);                                                                 // 176
        projectedOld = projectionFn(oldDoc);                                                                 // 177
        fields = LocalCollection._makeChangedFields(projectedNew, projectedOld);                             // 178
        if (!_.isEmpty(fields)) {                                                                            // 179
          observer.changed && observer.changed(newDoc._id, fields);                                          // 180
        }                                                                                                    // 181
        observer.movedBefore && observer.movedBefore(newDoc._id, groupId);                                   // 182
      }                                                                                                      // 183
    }                                                                                                        // 184
    if (groupId) {                                                                                           // 185
      newDoc = new_results[endOfGroup];                                                                      // 186
      oldDoc = old_results[old_index_of_id[newDoc._id]];                                                     // 187
      projectedNew = projectionFn(newDoc);                                                                   // 188
      projectedOld = projectionFn(oldDoc);                                                                   // 189
      fields = LocalCollection._makeChangedFields(projectedNew, projectedOld);                               // 190
      if (!_.isEmpty(fields)) {                                                                              // 191
        observer.changed && observer.changed(newDoc._id, fields);                                            // 192
      }                                                                                                      // 193
    }                                                                                                        // 194
    startOfGroup = endOfGroup+1;                                                                             // 195
  });                                                                                                        // 196
                                                                                                             // 197
                                                                                                             // 198
};                                                                                                           // 199
                                                                                                             // 200
                                                                                                             // 201
// General helper for diff-ing two objects.                                                                  // 202
// callbacks is an object like so:                                                                           // 203
// { leftOnly: function (key, leftValue) {...},                                                              // 204
//   rightOnly: function (key, rightValue) {...},                                                            // 205
//   both: function (key, leftValue, rightValue) {...},                                                      // 206
// }                                                                                                         // 207
LocalCollection._diffObjects = function (left, right, callbacks) {                                           // 208
  _.each(left, function (leftValue, key) {                                                                   // 209
    if (_.has(right, key))                                                                                   // 210
      callbacks.both && callbacks.both(key, leftValue, right[key]);                                          // 211
    else                                                                                                     // 212
      callbacks.leftOnly && callbacks.leftOnly(key, leftValue);                                              // 213
  });                                                                                                        // 214
  if (callbacks.rightOnly) {                                                                                 // 215
    _.each(right, function(rightValue, key) {                                                                // 216
      if (!_.has(left, key))                                                                                 // 217
        callbacks.rightOnly(key, rightValue);                                                                // 218
    });                                                                                                      // 219
  }                                                                                                          // 220
};                                                                                                           // 221
                                                                                                             // 222
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/id_map.js                                                                              //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
LocalCollection._IdMap = function () {                                                                       // 1
  var self = this;                                                                                           // 2
  IdMap.call(self, LocalCollection._idStringify, LocalCollection._idParse);                                  // 3
};                                                                                                           // 4
                                                                                                             // 5
Meteor._inherits(LocalCollection._IdMap, IdMap);                                                             // 6
                                                                                                             // 7
                                                                                                             // 8
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/observe.js                                                                             //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
// XXX maybe move these into another ObserveHelpers package or something                                     // 1
                                                                                                             // 2
// _CachingChangeObserver is an object which receives observeChanges callbacks                               // 3
// and keeps a cache of the current cursor state up to date in self.docs. Users                              // 4
// of this class should read the docs field but not modify it. You should pass                               // 5
// the "applyChange" field as the callbacks to the underlying observeChanges                                 // 6
// call. Optionally, you can specify your own observeChanges callbacks which are                             // 7
// invoked immediately before the docs field is updated; this object is made                                 // 8
// available as `this` to those callbacks.                                                                   // 9
LocalCollection._CachingChangeObserver = function (options) {                                                // 10
  var self = this;                                                                                           // 11
  options = options || {};                                                                                   // 12
                                                                                                             // 13
  var orderedFromCallbacks = options.callbacks &&                                                            // 14
        LocalCollection._observeChangesCallbacksAreOrdered(options.callbacks);                               // 15
  if (_.has(options, 'ordered')) {                                                                           // 16
    self.ordered = options.ordered;                                                                          // 17
    if (options.callbacks && options.ordered !== orderedFromCallbacks)                                       // 18
      throw Error("ordered option doesn't match callbacks");                                                 // 19
  } else if (options.callbacks) {                                                                            // 20
    self.ordered = orderedFromCallbacks;                                                                     // 21
  } else {                                                                                                   // 22
    throw Error("must provide ordered or callbacks");                                                        // 23
  }                                                                                                          // 24
  var callbacks = options.callbacks || {};                                                                   // 25
                                                                                                             // 26
  if (self.ordered) {                                                                                        // 27
    self.docs = new OrderedDict(LocalCollection._idStringify);                                               // 28
    self.applyChange = {                                                                                     // 29
      addedBefore: function (id, fields, before) {                                                           // 30
        var doc = EJSON.clone(fields);                                                                       // 31
        doc._id = id;                                                                                        // 32
        callbacks.addedBefore && callbacks.addedBefore.call(                                                 // 33
          self, id, fields, before);                                                                         // 34
        // This line triggers if we provide added with movedBefore.                                          // 35
        callbacks.added && callbacks.added.call(self, id, fields);                                           // 36
        // XXX could `before` be a falsy ID?  Technically                                                    // 37
        // idStringify seems to allow for them -- though                                                     // 38
        // OrderedDict won't call stringify on a falsy arg.                                                  // 39
        self.docs.putBefore(id, doc, before || null);                                                        // 40
      },                                                                                                     // 41
      movedBefore: function (id, before) {                                                                   // 42
        var doc = self.docs.get(id);                                                                         // 43
        callbacks.movedBefore && callbacks.movedBefore.call(self, id, before);                               // 44
        self.docs.moveBefore(id, before || null);                                                            // 45
      }                                                                                                      // 46
    };                                                                                                       // 47
  } else {                                                                                                   // 48
    self.docs = new LocalCollection._IdMap;                                                                  // 49
    self.applyChange = {                                                                                     // 50
      added: function (id, fields) {                                                                         // 51
        var doc = EJSON.clone(fields);                                                                       // 52
        callbacks.added && callbacks.added.call(self, id, fields);                                           // 53
        doc._id = id;                                                                                        // 54
        self.docs.set(id,  doc);                                                                             // 55
      }                                                                                                      // 56
    };                                                                                                       // 57
  }                                                                                                          // 58
                                                                                                             // 59
  // The methods in _IdMap and OrderedDict used by these callbacks are                                       // 60
  // identical.                                                                                              // 61
  self.applyChange.changed = function (id, fields) {                                                         // 62
    var doc = self.docs.get(id);                                                                             // 63
    if (!doc)                                                                                                // 64
      throw new Error("Unknown id for changed: " + id);                                                      // 65
    callbacks.changed && callbacks.changed.call(                                                             // 66
      self, id, EJSON.clone(fields));                                                                        // 67
    LocalCollection._applyChanges(doc, fields);                                                              // 68
  };                                                                                                         // 69
  self.applyChange.removed = function (id) {                                                                 // 70
    callbacks.removed && callbacks.removed.call(self, id);                                                   // 71
    self.docs.remove(id);                                                                                    // 72
  };                                                                                                         // 73
};                                                                                                           // 74
                                                                                                             // 75
LocalCollection._observeFromObserveChanges = function (cursor, observeCallbacks) {                           // 76
  var transform = cursor.getTransform() || function (doc) {return doc;};                                     // 77
  var suppressed = !!observeCallbacks._suppress_initial;                                                     // 78
                                                                                                             // 79
  var observeChangesCallbacks;                                                                               // 80
  if (LocalCollection._observeCallbacksAreOrdered(observeCallbacks)) {                                       // 81
    // The "_no_indices" option sets all index arguments to -1 and skips the                                 // 82
    // linear scans required to generate them.  This lets observers that don't                               // 83
    // need absolute indices benefit from the other features of this API --                                  // 84
    // relative order, transforms, and applyChanges -- without the speed hit.                                // 85
    var indices = !observeCallbacks._no_indices;                                                             // 86
    observeChangesCallbacks = {                                                                              // 87
      addedBefore: function (id, fields, before) {                                                           // 88
        var self = this;                                                                                     // 89
        if (suppressed || !(observeCallbacks.addedAt || observeCallbacks.added))                             // 90
          return;                                                                                            // 91
        var doc = transform(_.extend(fields, {_id: id}));                                                    // 92
        if (observeCallbacks.addedAt) {                                                                      // 93
          var index = indices                                                                                // 94
                ? (before ? self.docs.indexOf(before) : self.docs.size()) : -1;                              // 95
          observeCallbacks.addedAt(doc, index, before);                                                      // 96
        } else {                                                                                             // 97
          observeCallbacks.added(doc);                                                                       // 98
        }                                                                                                    // 99
      },                                                                                                     // 100
      changed: function (id, fields) {                                                                       // 101
        var self = this;                                                                                     // 102
        if (!(observeCallbacks.changedAt || observeCallbacks.changed))                                       // 103
          return;                                                                                            // 104
        var doc = EJSON.clone(self.docs.get(id));                                                            // 105
        if (!doc)                                                                                            // 106
          throw new Error("Unknown id for changed: " + id);                                                  // 107
        var oldDoc = transform(EJSON.clone(doc));                                                            // 108
        LocalCollection._applyChanges(doc, fields);                                                          // 109
        doc = transform(doc);                                                                                // 110
        if (observeCallbacks.changedAt) {                                                                    // 111
          var index = indices ? self.docs.indexOf(id) : -1;                                                  // 112
          observeCallbacks.changedAt(doc, oldDoc, index);                                                    // 113
        } else {                                                                                             // 114
          observeCallbacks.changed(doc, oldDoc);                                                             // 115
        }                                                                                                    // 116
      },                                                                                                     // 117
      movedBefore: function (id, before) {                                                                   // 118
        var self = this;                                                                                     // 119
        if (!observeCallbacks.movedTo)                                                                       // 120
          return;                                                                                            // 121
        var from = indices ? self.docs.indexOf(id) : -1;                                                     // 122
                                                                                                             // 123
        var to = indices                                                                                     // 124
              ? (before ? self.docs.indexOf(before) : self.docs.size()) : -1;                                // 125
        // When not moving backwards, adjust for the fact that removing the                                  // 126
        // document slides everything back one slot.                                                         // 127
        if (to > from)                                                                                       // 128
          --to;                                                                                              // 129
        observeCallbacks.movedTo(transform(EJSON.clone(self.docs.get(id))),                                  // 130
                                 from, to, before || null);                                                  // 131
      },                                                                                                     // 132
      removed: function (id) {                                                                               // 133
        var self = this;                                                                                     // 134
        if (!(observeCallbacks.removedAt || observeCallbacks.removed))                                       // 135
          return;                                                                                            // 136
        // technically maybe there should be an EJSON.clone here, but it's about                             // 137
        // to be removed from self.docs!                                                                     // 138
        var doc = transform(self.docs.get(id));                                                              // 139
        if (observeCallbacks.removedAt) {                                                                    // 140
          var index = indices ? self.docs.indexOf(id) : -1;                                                  // 141
          observeCallbacks.removedAt(doc, index);                                                            // 142
        } else {                                                                                             // 143
          observeCallbacks.removed(doc);                                                                     // 144
        }                                                                                                    // 145
      }                                                                                                      // 146
    };                                                                                                       // 147
  } else {                                                                                                   // 148
    observeChangesCallbacks = {                                                                              // 149
      added: function (id, fields) {                                                                         // 150
        if (!suppressed && observeCallbacks.added) {                                                         // 151
          var doc = _.extend(fields, {_id:  id});                                                            // 152
          observeCallbacks.added(transform(doc));                                                            // 153
        }                                                                                                    // 154
      },                                                                                                     // 155
      changed: function (id, fields) {                                                                       // 156
        var self = this;                                                                                     // 157
        if (observeCallbacks.changed) {                                                                      // 158
          var oldDoc = self.docs.get(id);                                                                    // 159
          var doc = EJSON.clone(oldDoc);                                                                     // 160
          LocalCollection._applyChanges(doc, fields);                                                        // 161
          observeCallbacks.changed(transform(doc),                                                           // 162
                                   transform(EJSON.clone(oldDoc)));                                          // 163
        }                                                                                                    // 164
      },                                                                                                     // 165
      removed: function (id) {                                                                               // 166
        var self = this;                                                                                     // 167
        if (observeCallbacks.removed) {                                                                      // 168
          observeCallbacks.removed(transform(self.docs.get(id)));                                            // 169
        }                                                                                                    // 170
      }                                                                                                      // 171
    };                                                                                                       // 172
  }                                                                                                          // 173
                                                                                                             // 174
  var changeObserver = new LocalCollection._CachingChangeObserver(                                           // 175
    {callbacks: observeChangesCallbacks});                                                                   // 176
  var handle = cursor.observeChanges(changeObserver.applyChange);                                            // 177
  suppressed = false;                                                                                        // 178
                                                                                                             // 179
  return handle;                                                                                             // 180
};                                                                                                           // 181
                                                                                                             // 182
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/objectid.js                                                                            //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
LocalCollection._looksLikeObjectID = function (str) {                                                        // 1
  return str.length === 24 && str.match(/^[0-9a-f]*$/);                                                      // 2
};                                                                                                           // 3
                                                                                                             // 4
LocalCollection._ObjectID = function (hexString) {                                                           // 5
  //random-based impl of Mongo ObjectID                                                                      // 6
  var self = this;                                                                                           // 7
  if (hexString) {                                                                                           // 8
    hexString = hexString.toLowerCase();                                                                     // 9
    if (!LocalCollection._looksLikeObjectID(hexString)) {                                                    // 10
      throw new Error("Invalid hexadecimal string for creating an ObjectID");                                // 11
    }                                                                                                        // 12
    // meant to work with _.isEqual(), which relies on structural equality                                   // 13
    self._str = hexString;                                                                                   // 14
  } else {                                                                                                   // 15
    self._str = Random.hexString(24);                                                                        // 16
  }                                                                                                          // 17
};                                                                                                           // 18
                                                                                                             // 19
LocalCollection._ObjectID.prototype.toString = function () {                                                 // 20
  var self = this;                                                                                           // 21
  return "ObjectID(\"" + self._str + "\")";                                                                  // 22
};                                                                                                           // 23
                                                                                                             // 24
LocalCollection._ObjectID.prototype.equals = function (other) {                                              // 25
  var self = this;                                                                                           // 26
  return other instanceof LocalCollection._ObjectID &&                                                       // 27
    self.valueOf() === other.valueOf();                                                                      // 28
};                                                                                                           // 29
                                                                                                             // 30
LocalCollection._ObjectID.prototype.clone = function () {                                                    // 31
  var self = this;                                                                                           // 32
  return new LocalCollection._ObjectID(self._str);                                                           // 33
};                                                                                                           // 34
                                                                                                             // 35
LocalCollection._ObjectID.prototype.typeName = function() {                                                  // 36
  return "oid";                                                                                              // 37
};                                                                                                           // 38
                                                                                                             // 39
LocalCollection._ObjectID.prototype.getTimestamp = function() {                                              // 40
  var self = this;                                                                                           // 41
  return parseInt(self._str.substr(0, 8), 16);                                                               // 42
};                                                                                                           // 43
                                                                                                             // 44
LocalCollection._ObjectID.prototype.valueOf =                                                                // 45
    LocalCollection._ObjectID.prototype.toJSONValue =                                                        // 46
    LocalCollection._ObjectID.prototype.toHexString =                                                        // 47
    function () { return this._str; };                                                                       // 48
                                                                                                             // 49
// Is this selector just shorthand for lookup by _id?                                                        // 50
LocalCollection._selectorIsId = function (selector) {                                                        // 51
  return (typeof selector === "string") ||                                                                   // 52
    (typeof selector === "number") ||                                                                        // 53
    selector instanceof LocalCollection._ObjectID;                                                           // 54
};                                                                                                           // 55
                                                                                                             // 56
// Is the selector just lookup by _id (shorthand or not)?                                                    // 57
LocalCollection._selectorIsIdPerhapsAsObject = function (selector) {                                         // 58
  return LocalCollection._selectorIsId(selector) ||                                                          // 59
    (selector && typeof selector === "object" &&                                                             // 60
     selector._id && LocalCollection._selectorIsId(selector._id) &&                                          // 61
     _.size(selector) === 1);                                                                                // 62
};                                                                                                           // 63
                                                                                                             // 64
// If this is a selector which explicitly constrains the match by ID to a finite                             // 65
// number of documents, returns a list of their IDs.  Otherwise returns                                      // 66
// null. Note that the selector may have other restrictions so it may not even                               // 67
// match those document!  We care about $in and $and since those are generated                               // 68
// access-controlled update and remove.                                                                      // 69
LocalCollection._idsMatchedBySelector = function (selector) {                                                // 70
  // Is the selector just an ID?                                                                             // 71
  if (LocalCollection._selectorIsId(selector))                                                               // 72
    return [selector];                                                                                       // 73
  if (!selector)                                                                                             // 74
    return null;                                                                                             // 75
                                                                                                             // 76
  // Do we have an _id clause?                                                                               // 77
  if (_.has(selector, '_id')) {                                                                              // 78
    // Is the _id clause just an ID?                                                                         // 79
    if (LocalCollection._selectorIsId(selector._id))                                                         // 80
      return [selector._id];                                                                                 // 81
    // Is the _id clause {_id: {$in: ["x", "y", "z"]}}?                                                      // 82
    if (selector._id && selector._id.$in                                                                     // 83
        && _.isArray(selector._id.$in)                                                                       // 84
        && !_.isEmpty(selector._id.$in)                                                                      // 85
        && _.all(selector._id.$in, LocalCollection._selectorIsId)) {                                         // 86
      return selector._id.$in;                                                                               // 87
    }                                                                                                        // 88
    return null;                                                                                             // 89
  }                                                                                                          // 90
                                                                                                             // 91
  // If this is a top-level $and, and any of the clauses constrain their                                     // 92
  // documents, then the whole selector is constrained by any one clause's                                   // 93
  // constraint. (Well, by their intersection, but that seems unlikely.)                                     // 94
  if (selector.$and && _.isArray(selector.$and)) {                                                           // 95
    for (var i = 0; i < selector.$and.length; ++i) {                                                         // 96
      var subIds = LocalCollection._idsMatchedBySelector(selector.$and[i]);                                  // 97
      if (subIds)                                                                                            // 98
        return subIds;                                                                                       // 99
    }                                                                                                        // 100
  }                                                                                                          // 101
                                                                                                             // 102
  return null;                                                                                               // 103
};                                                                                                           // 104
                                                                                                             // 105
EJSON.addType("oid",  function (str) {                                                                       // 106
  return new LocalCollection._ObjectID(str);                                                                 // 107
});                                                                                                          // 108
                                                                                                             // 109
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/selector_projection.js                                                                 //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
// Knows how to combine a mongo selector and a fields projection to a new fields                             // 1
// projection taking into account active fields from the passed selector.                                    // 2
// @returns Object - projection object (same as fields option of mongo cursor)                               // 3
Minimongo.Matcher.prototype.combineIntoProjection = function (projection) {                                  // 4
  var self = this;                                                                                           // 5
  var selectorPaths = Minimongo._pathsElidingNumericKeys(self._getPaths());                                  // 6
                                                                                                             // 7
  // Special case for $where operator in the selector - projection should depend                             // 8
  // on all fields of the document. getSelectorPaths returns a list of paths                                 // 9
  // selector depends on. If one of the paths is '' (empty string) representing                              // 10
  // the root or the whole document, complete projection should be returned.                                 // 11
  if (_.contains(selectorPaths, ''))                                                                         // 12
    return {};                                                                                               // 13
                                                                                                             // 14
  return combineImportantPathsIntoProjection(selectorPaths, projection);                                     // 15
};                                                                                                           // 16
                                                                                                             // 17
Minimongo._pathsElidingNumericKeys = function (paths) {                                                      // 18
  var self = this;                                                                                           // 19
  return _.map(paths, function (path) {                                                                      // 20
    return _.reject(path.split('.'), isNumericKey).join('.');                                                // 21
  });                                                                                                        // 22
};                                                                                                           // 23
                                                                                                             // 24
combineImportantPathsIntoProjection = function (paths, projection) {                                         // 25
  var prjDetails = projectionDetails(projection);                                                            // 26
  var tree = prjDetails.tree;                                                                                // 27
  var mergedProjection = {};                                                                                 // 28
                                                                                                             // 29
  // merge the paths to include                                                                              // 30
  tree = pathsToTree(paths,                                                                                  // 31
                     function (path) { return true; },                                                       // 32
                     function (node, path, fullPath) { return true; },                                       // 33
                     tree);                                                                                  // 34
  mergedProjection = treeToPaths(tree);                                                                      // 35
  if (prjDetails.including) {                                                                                // 36
    // both selector and projection are pointing on fields to include                                        // 37
    // so we can just return the merged tree                                                                 // 38
    return mergedProjection;                                                                                 // 39
  } else {                                                                                                   // 40
    // selector is pointing at fields to include                                                             // 41
    // projection is pointing at fields to exclude                                                           // 42
    // make sure we don't exclude important paths                                                            // 43
    var mergedExclProjection = {};                                                                           // 44
    _.each(mergedProjection, function (incl, path) {                                                         // 45
      if (!incl)                                                                                             // 46
        mergedExclProjection[path] = false;                                                                  // 47
    });                                                                                                      // 48
                                                                                                             // 49
    return mergedExclProjection;                                                                             // 50
  }                                                                                                          // 51
};                                                                                                           // 52
                                                                                                             // 53
// Returns a set of key paths similar to                                                                     // 54
// { 'foo.bar': 1, 'a.b.c': 1 }                                                                              // 55
var treeToPaths = function (tree, prefix) {                                                                  // 56
  prefix = prefix || '';                                                                                     // 57
  var result = {};                                                                                           // 58
                                                                                                             // 59
  _.each(tree, function (val, key) {                                                                         // 60
    if (_.isObject(val))                                                                                     // 61
      _.extend(result, treeToPaths(val, prefix + key + '.'));                                                // 62
    else                                                                                                     // 63
      result[prefix + key] = val;                                                                            // 64
  });                                                                                                        // 65
                                                                                                             // 66
  return result;                                                                                             // 67
};                                                                                                           // 68
                                                                                                             // 69
                                                                                                             // 70
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/selector_modifier.js                                                                   //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
// Returns true if the modifier applied to some document may change the result                               // 1
// of matching the document by selector                                                                      // 2
// The modifier is always in a form of Object:                                                               // 3
//  - $set                                                                                                   // 4
//    - 'a.b.22.z': value                                                                                    // 5
//    - 'foo.bar': 42                                                                                        // 6
//  - $unset                                                                                                 // 7
//    - 'abc.d': 1                                                                                           // 8
Minimongo.Matcher.prototype.affectedByModifier = function (modifier) {                                       // 9
  var self = this;                                                                                           // 10
  // safe check for $set/$unset being objects                                                                // 11
  modifier = _.extend({ $set: {}, $unset: {} }, modifier);                                                   // 12
  var modifiedPaths = _.keys(modifier.$set).concat(_.keys(modifier.$unset));                                 // 13
  var meaningfulPaths = self._getPaths();                                                                    // 14
                                                                                                             // 15
  return _.any(modifiedPaths, function (path) {                                                              // 16
    var mod = path.split('.');                                                                               // 17
    return _.any(meaningfulPaths, function (meaningfulPath) {                                                // 18
      var sel = meaningfulPath.split('.');                                                                   // 19
      var i = 0, j = 0;                                                                                      // 20
                                                                                                             // 21
      while (i < sel.length && j < mod.length) {                                                             // 22
        if (isNumericKey(sel[i]) && isNumericKey(mod[j])) {                                                  // 23
          // foo.4.bar selector affected by foo.4 modifier                                                   // 24
          // foo.3.bar selector unaffected by foo.4 modifier                                                 // 25
          if (sel[i] === mod[j])                                                                             // 26
            i++, j++;                                                                                        // 27
          else                                                                                               // 28
            return false;                                                                                    // 29
        } else if (isNumericKey(sel[i])) {                                                                   // 30
          // foo.4.bar selector unaffected by foo.bar modifier                                               // 31
          return false;                                                                                      // 32
        } else if (isNumericKey(mod[j])) {                                                                   // 33
          j++;                                                                                               // 34
        } else if (sel[i] === mod[j])                                                                        // 35
          i++, j++;                                                                                          // 36
        else                                                                                                 // 37
          return false;                                                                                      // 38
      }                                                                                                      // 39
                                                                                                             // 40
      // One is a prefix of another, taking numeric fields into account                                      // 41
      return true;                                                                                           // 42
    });                                                                                                      // 43
  });                                                                                                        // 44
};                                                                                                           // 45
                                                                                                             // 46
// Minimongo.Sorter gets a similar method, which delegates to a Matcher it made                              // 47
// for this exact purpose.                                                                                   // 48
Minimongo.Sorter.prototype.affectedByModifier = function (modifier) {                                        // 49
  var self = this;                                                                                           // 50
  return self._selectorForAffectedByModifier.affectedByModifier(modifier);                                   // 51
};                                                                                                           // 52
                                                                                                             // 53
// @param modifier - Object: MongoDB-styled modifier with `$set`s and `$unsets`                              // 54
//                           only. (assumed to come from oplog)                                              // 55
// @returns - Boolean: if after applying the modifier, selector can start                                    // 56
//                     accepting the modified value.                                                         // 57
// NOTE: assumes that document affected by modifier didn't match this Matcher                                // 58
// before, so if modifier can't convince selector in a positive change it would                              // 59
// stay 'false'.                                                                                             // 60
// Currently doesn't support $-operators and numeric indices precisely.                                      // 61
Minimongo.Matcher.prototype.canBecomeTrueByModifier = function (modifier) {                                  // 62
  var self = this;                                                                                           // 63
  if (!this.affectedByModifier(modifier))                                                                    // 64
    return false;                                                                                            // 65
                                                                                                             // 66
  modifier = _.extend({$set:{}, $unset:{}}, modifier);                                                       // 67
  var modifierPaths = _.keys(modifier.$set).concat(_.keys(modifier.$unset));                                 // 68
                                                                                                             // 69
  if (!self.isSimple())                                                                                      // 70
    return true;                                                                                             // 71
                                                                                                             // 72
  if (_.any(self._getPaths(), pathHasNumericKeys) ||                                                         // 73
      _.any(modifierPaths, pathHasNumericKeys))                                                              // 74
    return true;                                                                                             // 75
                                                                                                             // 76
  // check if there is a $set or $unset that indicates something is an                                       // 77
  // object rather than a scalar in the actual object where we saw $-operator                                // 78
  // NOTE: it is correct since we allow only scalars in $-operators                                          // 79
  // Example: for selector {'a.b': {$gt: 5}} the modifier {'a.b.c':7} would                                  // 80
  // definitely set the result to false as 'a.b' appears to be an object.                                    // 81
  var expectedScalarIsObject = _.any(self._selector, function (sel, path) {                                  // 82
    if (! isOperatorObject(sel))                                                                             // 83
      return false;                                                                                          // 84
    return _.any(modifierPaths, function (modifierPath) {                                                    // 85
      return startsWith(modifierPath, path + '.');                                                           // 86
    });                                                                                                      // 87
  });                                                                                                        // 88
                                                                                                             // 89
  if (expectedScalarIsObject)                                                                                // 90
    return false;                                                                                            // 91
                                                                                                             // 92
  // See if we can apply the modifier on the ideally matching object. If it                                  // 93
  // still matches the selector, then the modifier could have turned the real                                // 94
  // object in the database into something matching.                                                         // 95
  var matchingDocument = EJSON.clone(self.matchingDocument());                                               // 96
                                                                                                             // 97
  // The selector is too complex, anything can happen.                                                       // 98
  if (matchingDocument === null)                                                                             // 99
    return true;                                                                                             // 100
                                                                                                             // 101
  try {                                                                                                      // 102
    LocalCollection._modify(matchingDocument, modifier);                                                     // 103
  } catch (e) {                                                                                              // 104
    // Couldn't set a property on a field which is a scalar or null in the                                   // 105
    // selector.                                                                                             // 106
    // Example:                                                                                              // 107
    // real document: { 'a.b': 3 }                                                                           // 108
    // selector: { 'a': 12 }                                                                                 // 109
    // converted selector (ideal document): { 'a': 12 }                                                      // 110
    // modifier: { $set: { 'a.b': 4 } }                                                                      // 111
    // We don't know what real document was like but from the error raised by                                // 112
    // $set on a scalar field we can reason that the structure of real document                              // 113
    // is completely different.                                                                              // 114
    if (e.name === "MinimongoError" && e.setPropertyError)                                                   // 115
      return false;                                                                                          // 116
    throw e;                                                                                                 // 117
  }                                                                                                          // 118
                                                                                                             // 119
  return self.documentMatches(matchingDocument).result;                                                      // 120
};                                                                                                           // 121
                                                                                                             // 122
// Returns an object that would match the selector if possible or null if the                                // 123
// selector is too complex for us to analyze                                                                 // 124
// { 'a.b': { ans: 42 }, 'foo.bar': null, 'foo.baz': "something" }                                           // 125
// => { a: { b: { ans: 42 } }, foo: { bar: null, baz: "something" } }                                        // 126
Minimongo.Matcher.prototype.matchingDocument = function () {                                                 // 127
  var self = this;                                                                                           // 128
                                                                                                             // 129
  // check if it was computed before                                                                         // 130
  if (self._matchingDocument !== undefined)                                                                  // 131
    return self._matchingDocument;                                                                           // 132
                                                                                                             // 133
  // If the analysis of this selector is too hard for our implementation                                     // 134
  // fallback to "YES"                                                                                       // 135
  var fallback = false;                                                                                      // 136
  self._matchingDocument = pathsToTree(self._getPaths(),                                                     // 137
    function (path) {                                                                                        // 138
      var valueSelector = self._selector[path];                                                              // 139
      if (isOperatorObject(valueSelector)) {                                                                 // 140
        // if there is a strict equality, there is a good                                                    // 141
        // chance we can use one of those as "matching"                                                      // 142
        // dummy value                                                                                       // 143
        if (valueSelector.$in) {                                                                             // 144
          var matcher = new Minimongo.Matcher({ placeholder: valueSelector });                               // 145
                                                                                                             // 146
          // Return anything from $in that matches the whole selector for this                               // 147
          // path. If nothing matches, returns `undefined` as nothing can make                               // 148
          // this selector into `true`.                                                                      // 149
          return _.find(valueSelector.$in, function (x) {                                                    // 150
            return matcher.documentMatches({ placeholder: x }).result;                                       // 151
          });                                                                                                // 152
        } else if (onlyContainsKeys(valueSelector, ['$gt', '$gte', '$lt', '$lte'])) {                        // 153
          var lowerBound = -Infinity, upperBound = Infinity;                                                 // 154
          _.each(['$lte', '$lt'], function (op) {                                                            // 155
            if (_.has(valueSelector, op) && valueSelector[op] < upperBound)                                  // 156
              upperBound = valueSelector[op];                                                                // 157
          });                                                                                                // 158
          _.each(['$gte', '$gt'], function (op) {                                                            // 159
            if (_.has(valueSelector, op) && valueSelector[op] > lowerBound)                                  // 160
              lowerBound = valueSelector[op];                                                                // 161
          });                                                                                                // 162
                                                                                                             // 163
          var middle = (lowerBound + upperBound) / 2;                                                        // 164
          var matcher = new Minimongo.Matcher({ placeholder: valueSelector });                               // 165
          if (!matcher.documentMatches({ placeholder: middle }).result &&                                    // 166
              (middle === lowerBound || middle === upperBound))                                              // 167
            fallback = true;                                                                                 // 168
                                                                                                             // 169
          return middle;                                                                                     // 170
        } else if (onlyContainsKeys(valueSelector, ['$nin',' $ne'])) {                                       // 171
          // Since self._isSimple makes sure $nin and $ne are not combined with                              // 172
          // objects or arrays, we can confidently return an empty object as it                              // 173
          // never matches any scalar.                                                                       // 174
          return {};                                                                                         // 175
        } else {                                                                                             // 176
          fallback = true;                                                                                   // 177
        }                                                                                                    // 178
      }                                                                                                      // 179
      return self._selector[path];                                                                           // 180
    },                                                                                                       // 181
    _.identity /*conflict resolution is no resolution*/);                                                    // 182
                                                                                                             // 183
  if (fallback)                                                                                              // 184
    self._matchingDocument = null;                                                                           // 185
                                                                                                             // 186
  return self._matchingDocument;                                                                             // 187
};                                                                                                           // 188
                                                                                                             // 189
var getPaths = function (sel) {                                                                              // 190
  return _.keys(new Minimongo.Matcher(sel)._paths);                                                          // 191
  return _.chain(sel).map(function (v, k) {                                                                  // 192
    // we don't know how to handle $where because it can be anything                                         // 193
    if (k === "$where")                                                                                      // 194
      return ''; // matches everything                                                                       // 195
    // we branch from $or/$and/$nor operator                                                                 // 196
    if (_.contains(['$or', '$and', '$nor'], k))                                                              // 197
      return _.map(v, getPaths);                                                                             // 198
    // the value is a literal or some comparison operator                                                    // 199
    return k;                                                                                                // 200
  }).flatten().uniq().value();                                                                               // 201
};                                                                                                           // 202
                                                                                                             // 203
// A helper to ensure object has only certain keys                                                           // 204
var onlyContainsKeys = function (obj, keys) {                                                                // 205
  return _.all(obj, function (v, k) {                                                                        // 206
    return _.contains(keys, k);                                                                              // 207
  });                                                                                                        // 208
};                                                                                                           // 209
                                                                                                             // 210
var pathHasNumericKeys = function (path) {                                                                   // 211
  return _.any(path.split('.'), isNumericKey);                                                               // 212
}                                                                                                            // 213
                                                                                                             // 214
// XXX from Underscore.String (http://epeli.github.com/underscore.string/)                                   // 215
var startsWith = function(str, starts) {                                                                     // 216
  return str.length >= starts.length &&                                                                      // 217
    str.substring(0, starts.length) === starts;                                                              // 218
};                                                                                                           // 219
                                                                                                             // 220
                                                                                                             // 221
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                           //
// packages/minimongo/sorter_projection.js                                                                   //
//                                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                             //
Minimongo.Sorter.prototype.combineIntoProjection = function (projection) {                                   // 1
  var self = this;                                                                                           // 2
  var specPaths = Minimongo._pathsElidingNumericKeys(self._getPaths());                                      // 3
  return combineImportantPathsIntoProjection(specPaths, projection);                                         // 4
};                                                                                                           // 5
                                                                                                             // 6
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);


/* Exports */
if (typeof Package === 'undefined') Package = {};
Package.minimongo = {
  LocalCollection: LocalCollection,
  Minimongo: Minimongo,
  MinimongoTest: MinimongoTest
};

})();

//# sourceMappingURL=minimongo.js.map