//////////////////////////////////////////////////////////////////////////
//                                                                      //
// This is a generated file. You can view the original                  //
// source in your browser if your browser supports source maps.         //
//                                                                      //
// If you are using Chrome, open the Developer Tools and click the gear //
// icon in its lower right corner. In the General Settings panel, turn  //
// on 'Enable source maps'.                                             //
//                                                                      //
// If you are using Firefox 23, go to `about:config` and set the        //
// `devtools.debugger.source-maps-enabled` preference to true.          //
// (The preference should be on by default in Firefox 24; versions      //
// older than 23 do not support source maps.)                           //
//                                                                      //
//////////////////////////////////////////////////////////////////////////


(function () {

/* Imports */
var Meteor = Package.meteor.Meteor;
var Random = Package.random.Random;
var EJSON = Package.ejson.EJSON;
var JSON = Package.json.JSON;
var _ = Package.underscore._;
var LocalCollection = Package.minimongo.LocalCollection;
var Minimongo = Package.minimongo.Minimongo;
var Log = Package.logging.Log;
var DDP = Package.ddp.DDP;
var Tracker = Package.tracker.Tracker;
var Deps = Package.tracker.Deps;
var check = Package.check.check;
var Match = Package.check.Match;

/* Package-scope variables */
var Mongo, LocalCollectionDriver;

(function () {

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                    //
// packages/mongo/local_collection_driver.js                                                                          //
//                                                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                                      //
LocalCollectionDriver = function () {                                                                                 // 1
  var self = this;                                                                                                    // 2
  self.noConnCollections = {};                                                                                        // 3
};                                                                                                                    // 4
                                                                                                                      // 5
var ensureCollection = function (name, collections) {                                                                 // 6
  if (!(name in collections))                                                                                         // 7
    collections[name] = new LocalCollection(name);                                                                    // 8
  return collections[name];                                                                                           // 9
};                                                                                                                    // 10
                                                                                                                      // 11
_.extend(LocalCollectionDriver.prototype, {                                                                           // 12
  open: function (name, conn) {                                                                                       // 13
    var self = this;                                                                                                  // 14
    if (!name)                                                                                                        // 15
      return new LocalCollection;                                                                                     // 16
    if (! conn) {                                                                                                     // 17
      return ensureCollection(name, self.noConnCollections);                                                          // 18
    }                                                                                                                 // 19
    if (! conn._mongo_livedata_collections)                                                                           // 20
      conn._mongo_livedata_collections = {};                                                                          // 21
    // XXX is there a way to keep track of a connection's collections without                                         // 22
    // dangling it off the connection object?                                                                         // 23
    return ensureCollection(name, conn._mongo_livedata_collections);                                                  // 24
  }                                                                                                                   // 25
});                                                                                                                   // 26
                                                                                                                      // 27
// singleton                                                                                                          // 28
LocalCollectionDriver = new LocalCollectionDriver;                                                                    // 29
                                                                                                                      // 30
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function () {

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                    //
// packages/mongo/collection.js                                                                                       //
//                                                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                                      //
// options.connection, if given, is a LivedataClient or LivedataServer                                                // 1
// XXX presently there is no way to destroy/clean up a Collection                                                     // 2
                                                                                                                      // 3
/**                                                                                                                   // 4
 * @summary Namespace for MongoDB-related items                                                                       // 5
 * @namespace                                                                                                         // 6
 */                                                                                                                   // 7
Mongo = {};                                                                                                           // 8
                                                                                                                      // 9
/**                                                                                                                   // 10
 * @summary Constructor for a Collection                                                                              // 11
 * @locus Anywhere                                                                                                    // 12
 * @instancename collection                                                                                           // 13
 * @class                                                                                                             // 14
 * @param {String} name The name of the collection.  If null, creates an unmanaged (unsynchronized) local collection. // 15
 * @param {Object} [options]                                                                                          // 16
 * @param {Object} options.connection The server connection that will manage this collection. Uses the default connection if not specified.  Pass the return value of calling [`DDP.connect`](#ddp_connect) to specify a different server. Pass `null` to specify no connection. Unmanaged (`name` is null) collections cannot specify a connection.
 * @param {String} options.idGeneration The method of generating the `_id` fields of new documents in this collection.  Possible values:
                                                                                                                      // 19
 - **`'STRING'`**: random strings                                                                                     // 20
 - **`'MONGO'`**:  random [`Mongo.ObjectID`](#mongo_object_id) values                                                 // 21
                                                                                                                      // 22
The default id generation technique is `'STRING'`.                                                                    // 23
 * @param {Function} options.transform An optional transformation function. Documents will be passed through this function before being returned from `fetch` or `findOne`, and before being passed to callbacks of `observe`, `map`, `forEach`, `allow`, and `deny`. Transforms are *not* applied for the callbacks of `observeChanges` or to cursors returned from publish functions.
 */                                                                                                                   // 25
Mongo.Collection = function (name, options) {                                                                         // 26
  var self = this;                                                                                                    // 27
  if (! (self instanceof Mongo.Collection))                                                                           // 28
    throw new Error('use "new" to construct a Mongo.Collection');                                                     // 29
                                                                                                                      // 30
  if (!name && (name !== null)) {                                                                                     // 31
    Meteor._debug("Warning: creating anonymous collection. It will not be " +                                         // 32
                  "saved or synchronized over the network. (Pass null for " +                                         // 33
                  "the collection name to turn off this warning.)");                                                  // 34
    name = null;                                                                                                      // 35
  }                                                                                                                   // 36
                                                                                                                      // 37
  if (name !== null && typeof name !== "string") {                                                                    // 38
    throw new Error(                                                                                                  // 39
      "First argument to new Mongo.Collection must be a string or null");                                             // 40
  }                                                                                                                   // 41
                                                                                                                      // 42
  if (options && options.methods) {                                                                                   // 43
    // Backwards compatibility hack with original signature (which passed                                             // 44
    // "connection" directly instead of in options. (Connections must have a "methods"                                // 45
    // method.)                                                                                                       // 46
    // XXX remove before 1.0                                                                                          // 47
    options = {connection: options};                                                                                  // 48
  }                                                                                                                   // 49
  // Backwards compatibility: "connection" used to be called "manager".                                               // 50
  if (options && options.manager && !options.connection) {                                                            // 51
    options.connection = options.manager;                                                                             // 52
  }                                                                                                                   // 53
  options = _.extend({                                                                                                // 54
    connection: undefined,                                                                                            // 55
    idGeneration: 'STRING',                                                                                           // 56
    transform: null,                                                                                                  // 57
    _driver: undefined,                                                                                               // 58
    _preventAutopublish: false                                                                                        // 59
  }, options);                                                                                                        // 60
                                                                                                                      // 61
  switch (options.idGeneration) {                                                                                     // 62
  case 'MONGO':                                                                                                       // 63
    self._makeNewID = function () {                                                                                   // 64
      var src = name ? DDP.randomStream('/collection/' + name) : Random;                                              // 65
      return new Mongo.ObjectID(src.hexString(24));                                                                   // 66
    };                                                                                                                // 67
    break;                                                                                                            // 68
  case 'STRING':                                                                                                      // 69
  default:                                                                                                            // 70
    self._makeNewID = function () {                                                                                   // 71
      var src = name ? DDP.randomStream('/collection/' + name) : Random;                                              // 72
      return src.id();                                                                                                // 73
    };                                                                                                                // 74
    break;                                                                                                            // 75
  }                                                                                                                   // 76
                                                                                                                      // 77
  self._transform = LocalCollection.wrapTransform(options.transform);                                                 // 78
                                                                                                                      // 79
  if (! name || options.connection === null)                                                                          // 80
    // note: nameless collections never have a connection                                                             // 81
    self._connection = null;                                                                                          // 82
  else if (options.connection)                                                                                        // 83
    self._connection = options.connection;                                                                            // 84
  else if (Meteor.isClient)                                                                                           // 85
    self._connection = Meteor.connection;                                                                             // 86
  else                                                                                                                // 87
    self._connection = Meteor.server;                                                                                 // 88
                                                                                                                      // 89
  if (!options._driver) {                                                                                             // 90
    // XXX This check assumes that webapp is loaded so that Meteor.server !==                                         // 91
    // null. We should fully support the case of "want to use a Mongo-backed                                          // 92
    // collection from Node code without webapp", but we don't yet.                                                   // 93
    // #MeteorServerNull                                                                                              // 94
    if (name && self._connection === Meteor.server &&                                                                 // 95
        typeof MongoInternals !== "undefined" &&                                                                      // 96
        MongoInternals.defaultRemoteCollectionDriver) {                                                               // 97
      options._driver = MongoInternals.defaultRemoteCollectionDriver();                                               // 98
    } else {                                                                                                          // 99
      options._driver = LocalCollectionDriver;                                                                        // 100
    }                                                                                                                 // 101
  }                                                                                                                   // 102
                                                                                                                      // 103
  self._collection = options._driver.open(name, self._connection);                                                    // 104
  self._name = name;                                                                                                  // 105
  self._driver = options._driver;                                                                                     // 106
                                                                                                                      // 107
  if (self._connection && self._connection.registerStore) {                                                           // 108
    // OK, we're going to be a slave, replicating some remote                                                         // 109
    // database, except possibly with some temporary divergence while                                                 // 110
    // we have unacknowledged RPC's.                                                                                  // 111
    var ok = self._connection.registerStore(name, {                                                                   // 112
      // Called at the beginning of a batch of updates. batchSize is the number                                       // 113
      // of update calls to expect.                                                                                   // 114
      //                                                                                                              // 115
      // XXX This interface is pretty janky. reset probably ought to go back to                                       // 116
      // being its own function, and callers shouldn't have to calculate                                              // 117
      // batchSize. The optimization of not calling pause/remove should be                                            // 118
      // delayed until later: the first call to update() should buffer its                                            // 119
      // message, and then we can either directly apply it at endUpdate time if                                       // 120
      // it was the only update, or do pauseObservers/apply/apply at the next                                         // 121
      // update() if there's another one.                                                                             // 122
      beginUpdate: function (batchSize, reset) {                                                                      // 123
        // pause observers so users don't see flicker when updating several                                           // 124
        // objects at once (including the post-reconnect reset-and-reapply                                            // 125
        // stage), and so that a re-sorting of a query can take advantage of the                                      // 126
        // full _diffQuery moved calculation instead of applying change one at a                                      // 127
        // time.                                                                                                      // 128
        if (batchSize > 1 || reset)                                                                                   // 129
          self._collection.pauseObservers();                                                                          // 130
                                                                                                                      // 131
        if (reset)                                                                                                    // 132
          self._collection.remove({});                                                                                // 133
      },                                                                                                              // 134
                                                                                                                      // 135
      // Apply an update.                                                                                             // 136
      // XXX better specify this interface (not in terms of a wire message)?                                          // 137
      update: function (msg) {                                                                                        // 138
        var mongoId = LocalCollection._idParse(msg.id);                                                               // 139
        var doc = self._collection.findOne(mongoId);                                                                  // 140
                                                                                                                      // 141
        // Is this a "replace the whole doc" message coming from the quiescence                                       // 142
        // of method writes to an object? (Note that 'undefined' is a valid                                           // 143
        // value meaning "remove it".)                                                                                // 144
        if (msg.msg === 'replace') {                                                                                  // 145
          var replace = msg.replace;                                                                                  // 146
          if (!replace) {                                                                                             // 147
            if (doc)                                                                                                  // 148
              self._collection.remove(mongoId);                                                                       // 149
          } else if (!doc) {                                                                                          // 150
            self._collection.insert(replace);                                                                         // 151
          } else {                                                                                                    // 152
            // XXX check that replace has no $ ops                                                                    // 153
            self._collection.update(mongoId, replace);                                                                // 154
          }                                                                                                           // 155
          return;                                                                                                     // 156
        } else if (msg.msg === 'added') {                                                                             // 157
          if (doc) {                                                                                                  // 158
            throw new Error("Expected not to find a document already present for an add");                            // 159
          }                                                                                                           // 160
          self._collection.insert(_.extend({_id: mongoId}, msg.fields));                                              // 161
        } else if (msg.msg === 'removed') {                                                                           // 162
          if (!doc)                                                                                                   // 163
            throw new Error("Expected to find a document already present for removed");                               // 164
          self._collection.remove(mongoId);                                                                           // 165
        } else if (msg.msg === 'changed') {                                                                           // 166
          if (!doc)                                                                                                   // 167
            throw new Error("Expected to find a document to change");                                                 // 168
          if (!_.isEmpty(msg.fields)) {                                                                               // 169
            var modifier = {};                                                                                        // 170
            _.each(msg.fields, function (value, key) {                                                                // 171
              if (value === undefined) {                                                                              // 172
                if (!modifier.$unset)                                                                                 // 173
                  modifier.$unset = {};                                                                               // 174
                modifier.$unset[key] = 1;                                                                             // 175
              } else {                                                                                                // 176
                if (!modifier.$set)                                                                                   // 177
                  modifier.$set = {};                                                                                 // 178
                modifier.$set[key] = value;                                                                           // 179
              }                                                                                                       // 180
            });                                                                                                       // 181
            self._collection.update(mongoId, modifier);                                                               // 182
          }                                                                                                           // 183
        } else {                                                                                                      // 184
          throw new Error("I don't know how to deal with this message");                                              // 185
        }                                                                                                             // 186
                                                                                                                      // 187
      },                                                                                                              // 188
                                                                                                                      // 189
      // Called at the end of a batch of updates.                                                                     // 190
      endUpdate: function () {                                                                                        // 191
        self._collection.resumeObservers();                                                                           // 192
      },                                                                                                              // 193
                                                                                                                      // 194
      // Called around method stub invocations to capture the original versions                                       // 195
      // of modified documents.                                                                                       // 196
      saveOriginals: function () {                                                                                    // 197
        self._collection.saveOriginals();                                                                             // 198
      },                                                                                                              // 199
      retrieveOriginals: function () {                                                                                // 200
        return self._collection.retrieveOriginals();                                                                  // 201
      }                                                                                                               // 202
    });                                                                                                               // 203
                                                                                                                      // 204
    if (!ok)                                                                                                          // 205
      throw new Error("There is already a collection named '" + name + "'");                                          // 206
  }                                                                                                                   // 207
                                                                                                                      // 208
  self._defineMutationMethods();                                                                                      // 209
                                                                                                                      // 210
  // autopublish                                                                                                      // 211
  if (Package.autopublish && !options._preventAutopublish && self._connection                                         // 212
      && self._connection.publish) {                                                                                  // 213
    self._connection.publish(null, function () {                                                                      // 214
      return self.find();                                                                                             // 215
    }, {is_auto: true});                                                                                              // 216
  }                                                                                                                   // 217
};                                                                                                                    // 218
                                                                                                                      // 219
///                                                                                                                   // 220
/// Main collection API                                                                                               // 221
///                                                                                                                   // 222
                                                                                                                      // 223
                                                                                                                      // 224
_.extend(Mongo.Collection.prototype, {                                                                                // 225
                                                                                                                      // 226
  _getFindSelector: function (args) {                                                                                 // 227
    if (args.length == 0)                                                                                             // 228
      return {};                                                                                                      // 229
    else                                                                                                              // 230
      return args[0];                                                                                                 // 231
  },                                                                                                                  // 232
                                                                                                                      // 233
  _getFindOptions: function (args) {                                                                                  // 234
    var self = this;                                                                                                  // 235
    if (args.length < 2) {                                                                                            // 236
      return { transform: self._transform };                                                                          // 237
    } else {                                                                                                          // 238
      check(args[1], Match.Optional(Match.ObjectIncluding({                                                           // 239
        fields: Match.Optional(Match.OneOf(Object, undefined)),                                                       // 240
        sort: Match.Optional(Match.OneOf(Object, Array, undefined)),                                                  // 241
        limit: Match.Optional(Match.OneOf(Number, undefined)),                                                        // 242
        skip: Match.Optional(Match.OneOf(Number, undefined))                                                          // 243
     })));                                                                                                            // 244
                                                                                                                      // 245
      return _.extend({                                                                                               // 246
        transform: self._transform                                                                                    // 247
      }, args[1]);                                                                                                    // 248
    }                                                                                                                 // 249
  },                                                                                                                  // 250
                                                                                                                      // 251
  /**                                                                                                                 // 252
   * @summary Find the documents in a collection that match the selector.                                             // 253
   * @locus Anywhere                                                                                                  // 254
   * @method find                                                                                                     // 255
   * @memberOf Mongo.Collection                                                                                       // 256
   * @instance                                                                                                        // 257
   * @param {MongoSelector} [selector] A query describing the documents to find                                       // 258
   * @param {Object} [options]                                                                                        // 259
   * @param {MongoSortSpecifier} options.sort Sort order (default: natural order)                                     // 260
   * @param {Number} options.skip Number of results to skip at the beginning                                          // 261
   * @param {Number} options.limit Maximum number of results to return                                                // 262
   * @param {MongoFieldSpecifier} options.fields Dictionary of fields to return or exclude.                           // 263
   * @param {Boolean} options.reactive (Client only) Default `true`; pass `false` to disable reactivity               // 264
   * @param {Function} options.transform Overrides `transform` on the  [`Collection`](#collections) for this cursor.  Pass `null` to disable transformation.
   * @returns {Mongo.Cursor}                                                                                          // 266
   */                                                                                                                 // 267
  find: function (/* selector, options */) {                                                                          // 268
    // Collection.find() (return all docs) behaves differently                                                        // 269
    // from Collection.find(undefined) (return 0 docs).  so be                                                        // 270
    // careful about the length of arguments.                                                                         // 271
    var self = this;                                                                                                  // 272
    var argArray = _.toArray(arguments);                                                                              // 273
    return self._collection.find(self._getFindSelector(argArray),                                                     // 274
                                 self._getFindOptions(argArray));                                                     // 275
  },                                                                                                                  // 276
                                                                                                                      // 277
  /**                                                                                                                 // 278
   * @summary Finds the first document that matches the selector, as ordered by sort and skip options.                // 279
   * @locus Anywhere                                                                                                  // 280
   * @method findOne                                                                                                  // 281
   * @memberOf Mongo.Collection                                                                                       // 282
   * @instance                                                                                                        // 283
   * @param {MongoSelector} [selector] A query describing the documents to find                                       // 284
   * @param {Object} [options]                                                                                        // 285
   * @param {MongoSortSpecifier} options.sort Sort order (default: natural order)                                     // 286
   * @param {Number} options.skip Number of results to skip at the beginning                                          // 287
   * @param {MongoFieldSpecifier} options.fields Dictionary of fields to return or exclude.                           // 288
   * @param {Boolean} options.reactive (Client only) Default true; pass false to disable reactivity                   // 289
   * @param {Function} options.transform Overrides `transform` on the [`Collection`](#collections) for this cursor.  Pass `null` to disable transformation.
   * @returns {Object}                                                                                                // 291
   */                                                                                                                 // 292
  findOne: function (/* selector, options */) {                                                                       // 293
    var self = this;                                                                                                  // 294
    var argArray = _.toArray(arguments);                                                                              // 295
    return self._collection.findOne(self._getFindSelector(argArray),                                                  // 296
                                    self._getFindOptions(argArray));                                                  // 297
  }                                                                                                                   // 298
                                                                                                                      // 299
});                                                                                                                   // 300
                                                                                                                      // 301
Mongo.Collection._publishCursor = function (cursor, sub, collection) {                                                // 302
  var observeHandle = cursor.observeChanges({                                                                         // 303
    added: function (id, fields) {                                                                                    // 304
      sub.added(collection, id, fields);                                                                              // 305
    },                                                                                                                // 306
    changed: function (id, fields) {                                                                                  // 307
      sub.changed(collection, id, fields);                                                                            // 308
    },                                                                                                                // 309
    removed: function (id) {                                                                                          // 310
      sub.removed(collection, id);                                                                                    // 311
    }                                                                                                                 // 312
  });                                                                                                                 // 313
                                                                                                                      // 314
  // We don't call sub.ready() here: it gets called in livedata_server, after                                         // 315
  // possibly calling _publishCursor on multiple returned cursors.                                                    // 316
                                                                                                                      // 317
  // register stop callback (expects lambda w/ no args).                                                              // 318
  sub.onStop(function () {observeHandle.stop();});                                                                    // 319
};                                                                                                                    // 320
                                                                                                                      // 321
// protect against dangerous selectors.  falsey and {_id: falsey} are both                                            // 322
// likely programmer error, and not what you want, particularly for destructive                                       // 323
// operations.  JS regexps don't serialize over DDP but can be trivially                                              // 324
// replaced by $regex.                                                                                                // 325
Mongo.Collection._rewriteSelector = function (selector) {                                                             // 326
  // shorthand -- scalars match _id                                                                                   // 327
  if (LocalCollection._selectorIsId(selector))                                                                        // 328
    selector = {_id: selector};                                                                                       // 329
                                                                                                                      // 330
  if (!selector || (('_id' in selector) && !selector._id))                                                            // 331
    // can't match anything                                                                                           // 332
    return {_id: Random.id()};                                                                                        // 333
                                                                                                                      // 334
  var ret = {};                                                                                                       // 335
  _.each(selector, function (value, key) {                                                                            // 336
    // Mongo supports both {field: /foo/} and {field: {$regex: /foo/}}                                                // 337
    if (value instanceof RegExp) {                                                                                    // 338
      ret[key] = convertRegexpToMongoSelector(value);                                                                 // 339
    } else if (value && value.$regex instanceof RegExp) {                                                             // 340
      ret[key] = convertRegexpToMongoSelector(value.$regex);                                                          // 341
      // if value is {$regex: /foo/, $options: ...} then $options                                                     // 342
      // override the ones set on $regex.                                                                             // 343
      if (value.$options !== undefined)                                                                               // 344
        ret[key].$options = value.$options;                                                                           // 345
    }                                                                                                                 // 346
    else if (_.contains(['$or','$and','$nor'], key)) {                                                                // 347
      // Translate lower levels of $and/$or/$nor                                                                      // 348
      ret[key] = _.map(value, function (v) {                                                                          // 349
        return Mongo.Collection._rewriteSelector(v);                                                                  // 350
      });                                                                                                             // 351
    } else {                                                                                                          // 352
      ret[key] = value;                                                                                               // 353
    }                                                                                                                 // 354
  });                                                                                                                 // 355
  return ret;                                                                                                         // 356
};                                                                                                                    // 357
                                                                                                                      // 358
// convert a JS RegExp object to a Mongo {$regex: ..., $options: ...}                                                 // 359
// selector                                                                                                           // 360
var convertRegexpToMongoSelector = function (regexp) {                                                                // 361
  check(regexp, RegExp); // safety belt                                                                               // 362
                                                                                                                      // 363
  var selector = {$regex: regexp.source};                                                                             // 364
  var regexOptions = '';                                                                                              // 365
  // JS RegExp objects support 'i', 'm', and 'g'. Mongo regex $options                                                // 366
  // support 'i', 'm', 'x', and 's'. So we support 'i' and 'm' here.                                                  // 367
  if (regexp.ignoreCase)                                                                                              // 368
    regexOptions += 'i';                                                                                              // 369
  if (regexp.multiline)                                                                                               // 370
    regexOptions += 'm';                                                                                              // 371
  if (regexOptions)                                                                                                   // 372
    selector.$options = regexOptions;                                                                                 // 373
                                                                                                                      // 374
  return selector;                                                                                                    // 375
};                                                                                                                    // 376
                                                                                                                      // 377
var throwIfSelectorIsNotId = function (selector, methodName) {                                                        // 378
  if (!LocalCollection._selectorIsIdPerhapsAsObject(selector)) {                                                      // 379
    throw new Meteor.Error(                                                                                           // 380
      403, "Not permitted. Untrusted code may only " + methodName +                                                   // 381
        " documents by ID.");                                                                                         // 382
  }                                                                                                                   // 383
};                                                                                                                    // 384
                                                                                                                      // 385
// 'insert' immediately returns the inserted document's new _id.                                                      // 386
// The others return values immediately if you are in a stub, an in-memory                                            // 387
// unmanaged collection, or a mongo-backed collection and you don't pass a                                            // 388
// callback. 'update' and 'remove' return the number of affected                                                      // 389
// documents. 'upsert' returns an object with keys 'numberAffected' and, if an                                        // 390
// insert happened, 'insertedId'.                                                                                     // 391
//                                                                                                                    // 392
// Otherwise, the semantics are exactly like other methods: they take                                                 // 393
// a callback as an optional last argument; if no callback is                                                         // 394
// provided, they block until the operation is complete, and throw an                                                 // 395
// exception if it fails; if a callback is provided, then they don't                                                  // 396
// necessarily block, and they call the callback when they finish with error and                                      // 397
// result arguments.  (The insert method provides the document ID as its result;                                      // 398
// update and remove provide the number of affected docs as the result; upsert                                        // 399
// provides an object with numberAffected and maybe insertedId.)                                                      // 400
//                                                                                                                    // 401
// On the client, blocking is impossible, so if a callback                                                            // 402
// isn't provided, they just return immediately and any error                                                         // 403
// information is lost.                                                                                               // 404
//                                                                                                                    // 405
// There's one more tweak. On the client, if you don't provide a                                                      // 406
// callback, then if there is an error, a message will be logged with                                                 // 407
// Meteor._debug.                                                                                                     // 408
//                                                                                                                    // 409
// The intent (though this is actually determined by the underlying                                                   // 410
// drivers) is that the operations should be done synchronously, not                                                  // 411
// generating their result until the database has acknowledged                                                        // 412
// them. In the future maybe we should provide a flag to turn this                                                    // 413
// off.                                                                                                               // 414
                                                                                                                      // 415
/**                                                                                                                   // 416
 * @summary Insert a document in the collection.  Returns its unique _id.                                             // 417
 * @locus Anywhere                                                                                                    // 418
 * @method  insert                                                                                                    // 419
 * @memberOf Mongo.Collection                                                                                         // 420
 * @instance                                                                                                          // 421
 * @param {Object} doc The document to insert. May not yet have an _id attribute, in which case Meteor will generate one for you.
 * @param {Function} [callback] Optional.  If present, called with an error object as the first argument and, if no error, the _id as the second.
 */                                                                                                                   // 424
                                                                                                                      // 425
/**                                                                                                                   // 426
 * @summary Modify one or more documents in the collection. Returns the number of affected documents.                 // 427
 * @locus Anywhere                                                                                                    // 428
 * @method update                                                                                                     // 429
 * @memberOf Mongo.Collection                                                                                         // 430
 * @instance                                                                                                          // 431
 * @param {MongoSelector} selector Specifies which documents to modify                                                // 432
 * @param {MongoModifier} modifier Specifies how to modify the documents                                              // 433
 * @param {Object} [options]                                                                                          // 434
 * @param {Boolean} options.multi True to modify all matching documents; false to only modify one of the matching documents (the default).
 * @param {Boolean} options.upsert True to insert a document if no matching documents are found.                      // 436
 * @param {Function} [callback] Optional.  If present, called with an error object as the first argument and, if no error, the number of affected documents as the second.
 */                                                                                                                   // 438
                                                                                                                      // 439
/**                                                                                                                   // 440
 * @summary Remove documents from the collection                                                                      // 441
 * @locus Anywhere                                                                                                    // 442
 * @method remove                                                                                                     // 443
 * @memberOf Mongo.Collection                                                                                         // 444
 * @instance                                                                                                          // 445
 * @param {MongoSelector} selector Specifies which documents to remove                                                // 446
 * @param {Function} [callback] Optional.  If present, called with an error object as its argument.                   // 447
 */                                                                                                                   // 448
                                                                                                                      // 449
_.each(["insert", "update", "remove"], function (name) {                                                              // 450
  Mongo.Collection.prototype[name] = function (/* arguments */) {                                                     // 451
    var self = this;                                                                                                  // 452
    var args = _.toArray(arguments);                                                                                  // 453
    var callback;                                                                                                     // 454
    var insertId;                                                                                                     // 455
    var ret;                                                                                                          // 456
                                                                                                                      // 457
    // Pull off any callback (or perhaps a 'callback' variable that was passed                                        // 458
    // in undefined, like how 'upsert' does it).                                                                      // 459
    if (args.length &&                                                                                                // 460
        (args[args.length - 1] === undefined ||                                                                       // 461
         args[args.length - 1] instanceof Function)) {                                                                // 462
      callback = args.pop();                                                                                          // 463
    }                                                                                                                 // 464
                                                                                                                      // 465
    if (name === "insert") {                                                                                          // 466
      if (!args.length)                                                                                               // 467
        throw new Error("insert requires an argument");                                                               // 468
      // shallow-copy the document and generate an ID                                                                 // 469
      args[0] = _.extend({}, args[0]);                                                                                // 470
      if ('_id' in args[0]) {                                                                                         // 471
        insertId = args[0]._id;                                                                                       // 472
        if (!insertId || !(typeof insertId === 'string'                                                               // 473
              || insertId instanceof Mongo.ObjectID))                                                                 // 474
          throw new Error("Meteor requires document _id fields to be non-empty strings or ObjectIDs");                // 475
      } else {                                                                                                        // 476
        var generateId = true;                                                                                        // 477
        // Don't generate the id if we're the client and the 'outermost' call                                         // 478
        // This optimization saves us passing both the randomSeed and the id                                          // 479
        // Passing both is redundant.                                                                                 // 480
        if (self._connection && self._connection !== Meteor.server) {                                                 // 481
          var enclosing = DDP._CurrentInvocation.get();                                                               // 482
          if (!enclosing) {                                                                                           // 483
            generateId = false;                                                                                       // 484
          }                                                                                                           // 485
        }                                                                                                             // 486
        if (generateId) {                                                                                             // 487
          insertId = args[0]._id = self._makeNewID();                                                                 // 488
        }                                                                                                             // 489
      }                                                                                                               // 490
    } else {                                                                                                          // 491
      args[0] = Mongo.Collection._rewriteSelector(args[0]);                                                           // 492
                                                                                                                      // 493
      if (name === "update") {                                                                                        // 494
        // Mutate args but copy the original options object. We need to add                                           // 495
        // insertedId to options, but don't want to mutate the caller's options                                       // 496
        // object. We need to mutate `args` because we pass `args` into the                                           // 497
        // driver below.                                                                                              // 498
        var options = args[2] = _.clone(args[2]) || {};                                                               // 499
        if (options && typeof options !== "function" && options.upsert) {                                             // 500
          // set `insertedId` if absent.  `insertedId` is a Meteor extension.                                         // 501
          if (options.insertedId) {                                                                                   // 502
            if (!(typeof options.insertedId === 'string'                                                              // 503
                  || options.insertedId instanceof Mongo.ObjectID))                                                   // 504
              throw new Error("insertedId must be string or ObjectID");                                               // 505
          } else if (! args[0]._id) {                                                                                 // 506
            options.insertedId = self._makeNewID();                                                                   // 507
          }                                                                                                           // 508
        }                                                                                                             // 509
      }                                                                                                               // 510
    }                                                                                                                 // 511
                                                                                                                      // 512
    // On inserts, always return the id that we generated; on all other                                               // 513
    // operations, just return the result from the collection.                                                        // 514
    var chooseReturnValueFromCollectionResult = function (result) {                                                   // 515
      if (name === "insert") {                                                                                        // 516
        if (!insertId && result) {                                                                                    // 517
          insertId = result;                                                                                          // 518
        }                                                                                                             // 519
        return insertId;                                                                                              // 520
      } else {                                                                                                        // 521
        return result;                                                                                                // 522
      }                                                                                                               // 523
    };                                                                                                                // 524
                                                                                                                      // 525
    var wrappedCallback;                                                                                              // 526
    if (callback) {                                                                                                   // 527
      wrappedCallback = function (error, result) {                                                                    // 528
        callback(error, ! error && chooseReturnValueFromCollectionResult(result));                                    // 529
      };                                                                                                              // 530
    }                                                                                                                 // 531
                                                                                                                      // 532
    // XXX see #MeteorServerNull                                                                                      // 533
    if (self._connection && self._connection !== Meteor.server) {                                                     // 534
      // just remote to another endpoint, propagate return value or                                                   // 535
      // exception.                                                                                                   // 536
                                                                                                                      // 537
      var enclosing = DDP._CurrentInvocation.get();                                                                   // 538
      var alreadyInSimulation = enclosing && enclosing.isSimulation;                                                  // 539
                                                                                                                      // 540
      if (Meteor.isClient && !wrappedCallback && ! alreadyInSimulation) {                                             // 541
        // Client can't block, so it can't report errors by exception,                                                // 542
        // only by callback. If they forget the callback, give them a                                                 // 543
        // default one that logs the error, so they aren't totally                                                    // 544
        // baffled if their writes don't work because their database is                                               // 545
        // down.                                                                                                      // 546
        // Don't give a default callback in simulation, because inside stubs we                                       // 547
        // want to return the results from the local collection immediately and                                       // 548
        // not force a callback.                                                                                      // 549
        wrappedCallback = function (err) {                                                                            // 550
          if (err)                                                                                                    // 551
            Meteor._debug(name + " failed: " + (err.reason || err.stack));                                            // 552
        };                                                                                                            // 553
      }                                                                                                               // 554
                                                                                                                      // 555
      if (!alreadyInSimulation && name !== "insert") {                                                                // 556
        // If we're about to actually send an RPC, we should throw an error if                                        // 557
        // this is a non-ID selector, because the mutation methods only allow                                         // 558
        // single-ID selectors. (If we don't throw here, we'll see flicker.)                                          // 559
        throwIfSelectorIsNotId(args[0], name);                                                                        // 560
      }                                                                                                               // 561
                                                                                                                      // 562
      ret = chooseReturnValueFromCollectionResult(                                                                    // 563
        self._connection.apply(self._prefix + name, args, {returnStubValue: true}, wrappedCallback)                   // 564
      );                                                                                                              // 565
                                                                                                                      // 566
    } else {                                                                                                          // 567
      // it's my collection.  descend into the collection object                                                      // 568
      // and propagate any exception.                                                                                 // 569
      args.push(wrappedCallback);                                                                                     // 570
      try {                                                                                                           // 571
        // If the user provided a callback and the collection implements this                                         // 572
        // operation asynchronously, then queryRet will be undefined, and the                                         // 573
        // result will be returned through the callback instead.                                                      // 574
        var queryRet = self._collection[name].apply(self._collection, args);                                          // 575
        ret = chooseReturnValueFromCollectionResult(queryRet);                                                        // 576
      } catch (e) {                                                                                                   // 577
        if (callback) {                                                                                               // 578
          callback(e);                                                                                                // 579
          return null;                                                                                                // 580
        }                                                                                                             // 581
        throw e;                                                                                                      // 582
      }                                                                                                               // 583
    }                                                                                                                 // 584
                                                                                                                      // 585
    // both sync and async, unless we threw an exception, return ret                                                  // 586
    // (new document ID for insert, num affected for update/remove, object with                                       // 587
    // numberAffected and maybe insertedId for upsert).                                                               // 588
    return ret;                                                                                                       // 589
  };                                                                                                                  // 590
});                                                                                                                   // 591
                                                                                                                      // 592
/**                                                                                                                   // 593
 * @summary Modify one or more documents in the collection, or insert one if no matching documents were found. Returns an object with keys `numberAffected` (the number of documents modified)  and `insertedId` (the unique _id of the document that was inserted, if any).
 * @locus Anywhere                                                                                                    // 595
 * @param {MongoSelector} selector Specifies which documents to modify                                                // 596
 * @param {MongoModifier} modifier Specifies how to modify the documents                                              // 597
 * @param {Object} [options]                                                                                          // 598
 * @param {Boolean} options.multi True to modify all matching documents; false to only modify one of the matching documents (the default).
 * @param {Function} [callback] Optional.  If present, called with an error object as the first argument and, if no error, the number of affected documents as the second.
 */                                                                                                                   // 601
Mongo.Collection.prototype.upsert = function (selector, modifier,                                                     // 602
                                               options, callback) {                                                   // 603
  var self = this;                                                                                                    // 604
  if (! callback && typeof options === "function") {                                                                  // 605
    callback = options;                                                                                               // 606
    options = {};                                                                                                     // 607
  }                                                                                                                   // 608
  return self.update(selector, modifier,                                                                              // 609
              _.extend({}, options, { _returnObject: true, upsert: true }),                                           // 610
              callback);                                                                                              // 611
};                                                                                                                    // 612
                                                                                                                      // 613
// We'll actually design an index API later. For now, we just pass through to                                         // 614
// Mongo's, but make it synchronous.                                                                                  // 615
Mongo.Collection.prototype._ensureIndex = function (index, options) {                                                 // 616
  var self = this;                                                                                                    // 617
  if (!self._collection._ensureIndex)                                                                                 // 618
    throw new Error("Can only call _ensureIndex on server collections");                                              // 619
  self._collection._ensureIndex(index, options);                                                                      // 620
};                                                                                                                    // 621
Mongo.Collection.prototype._dropIndex = function (index) {                                                            // 622
  var self = this;                                                                                                    // 623
  if (!self._collection._dropIndex)                                                                                   // 624
    throw new Error("Can only call _dropIndex on server collections");                                                // 625
  self._collection._dropIndex(index);                                                                                 // 626
};                                                                                                                    // 627
Mongo.Collection.prototype._dropCollection = function () {                                                            // 628
  var self = this;                                                                                                    // 629
  if (!self._collection.dropCollection)                                                                               // 630
    throw new Error("Can only call _dropCollection on server collections");                                           // 631
  self._collection.dropCollection();                                                                                  // 632
};                                                                                                                    // 633
Mongo.Collection.prototype._createCappedCollection = function (byteSize, maxDocuments) {                              // 634
  var self = this;                                                                                                    // 635
  if (!self._collection._createCappedCollection)                                                                      // 636
    throw new Error("Can only call _createCappedCollection on server collections");                                   // 637
  self._collection._createCappedCollection(byteSize, maxDocuments);                                                   // 638
};                                                                                                                    // 639
                                                                                                                      // 640
Mongo.Collection.prototype.rawCollection = function () {                                                              // 641
  var self = this;                                                                                                    // 642
  if (! self._collection.rawCollection) {                                                                             // 643
    throw new Error("Can only call rawCollection on server collections");                                             // 644
  }                                                                                                                   // 645
  return self._collection.rawCollection();                                                                            // 646
};                                                                                                                    // 647
                                                                                                                      // 648
Mongo.Collection.prototype.rawDatabase = function () {                                                                // 649
  var self = this;                                                                                                    // 650
  if (! (self._driver.mongo && self._driver.mongo.db)) {                                                              // 651
    throw new Error("Can only call rawDatabase on server collections");                                               // 652
  }                                                                                                                   // 653
  return self._driver.mongo.db;                                                                                       // 654
};                                                                                                                    // 655
                                                                                                                      // 656
                                                                                                                      // 657
/**                                                                                                                   // 658
 * @summary Create a Mongo-style `ObjectID`.  If you don't specify a `hexString`, the `ObjectID` will generated randomly (not using MongoDB's ID construction rules).
 * @locus Anywhere                                                                                                    // 660
 * @class                                                                                                             // 661
 * @param {String} hexString Optional.  The 24-character hexadecimal contents of the ObjectID to create               // 662
 */                                                                                                                   // 663
Mongo.ObjectID = LocalCollection._ObjectID;                                                                           // 664
                                                                                                                      // 665
/**                                                                                                                   // 666
 * @summary To create a cursor, use find. To access the documents in a cursor, use forEach, map, or fetch.            // 667
 * @class                                                                                                             // 668
 * @instanceName cursor                                                                                               // 669
 */                                                                                                                   // 670
Mongo.Cursor = LocalCollection.Cursor;                                                                                // 671
                                                                                                                      // 672
/**                                                                                                                   // 673
 * @deprecated in 0.9.1                                                                                               // 674
 */                                                                                                                   // 675
Mongo.Collection.Cursor = Mongo.Cursor;                                                                               // 676
                                                                                                                      // 677
/**                                                                                                                   // 678
 * @deprecated in 0.9.1                                                                                               // 679
 */                                                                                                                   // 680
Mongo.Collection.ObjectID = Mongo.ObjectID;                                                                           // 681
                                                                                                                      // 682
///                                                                                                                   // 683
/// Remote methods and access control.                                                                                // 684
///                                                                                                                   // 685
                                                                                                                      // 686
// Restrict default mutators on collection. allow() and deny() take the                                               // 687
// same options:                                                                                                      // 688
//                                                                                                                    // 689
// options.insert {Function(userId, doc)}                                                                             // 690
//   return true to allow/deny adding this document                                                                   // 691
//                                                                                                                    // 692
// options.update {Function(userId, docs, fields, modifier)}                                                          // 693
//   return true to allow/deny updating these documents.                                                              // 694
//   `fields` is passed as an array of fields that are to be modified                                                 // 695
//                                                                                                                    // 696
// options.remove {Function(userId, docs)}                                                                            // 697
//   return true to allow/deny removing these documents                                                               // 698
//                                                                                                                    // 699
// options.fetch {Array}                                                                                              // 700
//   Fields to fetch for these validators. If any call to allow or deny                                               // 701
//   does not have this option then all fields are loaded.                                                            // 702
//                                                                                                                    // 703
// allow and deny can be called multiple times. The validators are                                                    // 704
// evaluated as follows:                                                                                              // 705
// - If neither deny() nor allow() has been called on the collection,                                                 // 706
//   then the request is allowed if and only if the "insecure" smart                                                  // 707
//   package is in use.                                                                                               // 708
// - Otherwise, if any deny() function returns true, the request is denied.                                           // 709
// - Otherwise, if any allow() function returns true, the request is allowed.                                         // 710
// - Otherwise, the request is denied.                                                                                // 711
//                                                                                                                    // 712
// Meteor may call your deny() and allow() functions in any order, and may not                                        // 713
// call all of them if it is able to make a decision without calling them all                                         // 714
// (so don't include side effects).                                                                                   // 715
                                                                                                                      // 716
(function () {                                                                                                        // 717
  var addValidator = function(allowOrDeny, options) {                                                                 // 718
    // validate keys                                                                                                  // 719
    var VALID_KEYS = ['insert', 'update', 'remove', 'fetch', 'transform'];                                            // 720
    _.each(_.keys(options), function (key) {                                                                          // 721
      if (!_.contains(VALID_KEYS, key))                                                                               // 722
        throw new Error(allowOrDeny + ": Invalid key: " + key);                                                       // 723
    });                                                                                                               // 724
                                                                                                                      // 725
    var self = this;                                                                                                  // 726
    self._restricted = true;                                                                                          // 727
                                                                                                                      // 728
    _.each(['insert', 'update', 'remove'], function (name) {                                                          // 729
      if (options[name]) {                                                                                            // 730
        if (!(options[name] instanceof Function)) {                                                                   // 731
          throw new Error(allowOrDeny + ": Value for `" + name + "` must be a function");                             // 732
        }                                                                                                             // 733
                                                                                                                      // 734
        // If the transform is specified at all (including as 'null') in this                                         // 735
        // call, then take that; otherwise, take the transform from the                                               // 736
        // collection.                                                                                                // 737
        if (options.transform === undefined) {                                                                        // 738
          options[name].transform = self._transform;  // already wrapped                                              // 739
        } else {                                                                                                      // 740
          options[name].transform = LocalCollection.wrapTransform(                                                    // 741
            options.transform);                                                                                       // 742
        }                                                                                                             // 743
                                                                                                                      // 744
        self._validators[name][allowOrDeny].push(options[name]);                                                      // 745
      }                                                                                                               // 746
    });                                                                                                               // 747
                                                                                                                      // 748
    // Only update the fetch fields if we're passed things that affect                                                // 749
    // fetching. This way allow({}) and allow({insert: f}) don't result in                                            // 750
    // setting fetchAllFields                                                                                         // 751
    if (options.update || options.remove || options.fetch) {                                                          // 752
      if (options.fetch && !(options.fetch instanceof Array)) {                                                       // 753
        throw new Error(allowOrDeny + ": Value for `fetch` must be an array");                                        // 754
      }                                                                                                               // 755
      self._updateFetch(options.fetch);                                                                               // 756
    }                                                                                                                 // 757
  };                                                                                                                  // 758
                                                                                                                      // 759
  /**                                                                                                                 // 760
   * @summary Allow users to write directly to this collection from client code, subject to limitations you define.   // 761
   * @locus Server                                                                                                    // 762
   * @param {Object} options                                                                                          // 763
   * @param {Function} options.insert,update,remove Functions that look at a proposed modification to the database and return true if it should be allowed.
   * @param {String[]} options.fetch Optional performance enhancement. Limits the fields that will be fetched from the database for inspection by your `update` and `remove` functions.
   * @param {Function} options.transform Overrides `transform` on the  [`Collection`](#collections).  Pass `null` to disable transformation.
   */                                                                                                                 // 767
  Mongo.Collection.prototype.allow = function(options) {                                                              // 768
    addValidator.call(this, 'allow', options);                                                                        // 769
  };                                                                                                                  // 770
                                                                                                                      // 771
  /**                                                                                                                 // 772
   * @summary Override `allow` rules.                                                                                 // 773
   * @locus Server                                                                                                    // 774
   * @param {Object} options                                                                                          // 775
   * @param {Function} options.insert,update,remove Functions that look at a proposed modification to the database and return true if it should be denied, even if an [allow](#allow) rule says otherwise.
   * @param {String[]} options.fetch Optional performance enhancement. Limits the fields that will be fetched from the database for inspection by your `update` and `remove` functions.
   * @param {Function} options.transform Overrides `transform` on the  [`Collection`](#collections).  Pass `null` to disable transformation.
   */                                                                                                                 // 779
  Mongo.Collection.prototype.deny = function(options) {                                                               // 780
    addValidator.call(this, 'deny', options);                                                                         // 781
  };                                                                                                                  // 782
})();                                                                                                                 // 783
                                                                                                                      // 784
                                                                                                                      // 785
Mongo.Collection.prototype._defineMutationMethods = function() {                                                      // 786
  var self = this;                                                                                                    // 787
                                                                                                                      // 788
  // set to true once we call any allow or deny methods. If true, use                                                 // 789
  // allow/deny semantics. If false, use insecure mode semantics.                                                     // 790
  self._restricted = false;                                                                                           // 791
                                                                                                                      // 792
  // Insecure mode (default to allowing writes). Defaults to 'undefined' which                                        // 793
  // means insecure iff the insecure package is loaded. This property can be                                          // 794
  // overriden by tests or packages wishing to change insecure mode behavior of                                       // 795
  // their collections.                                                                                               // 796
  self._insecure = undefined;                                                                                         // 797
                                                                                                                      // 798
  self._validators = {                                                                                                // 799
    insert: {allow: [], deny: []},                                                                                    // 800
    update: {allow: [], deny: []},                                                                                    // 801
    remove: {allow: [], deny: []},                                                                                    // 802
    upsert: {allow: [], deny: []}, // dummy arrays; can't set these!                                                  // 803
    fetch: [],                                                                                                        // 804
    fetchAllFields: false                                                                                             // 805
  };                                                                                                                  // 806
                                                                                                                      // 807
  if (!self._name)                                                                                                    // 808
    return; // anonymous collection                                                                                   // 809
                                                                                                                      // 810
  // XXX Think about method namespacing. Maybe methods should be                                                      // 811
  // "Meteor:Mongo:insert/NAME"?                                                                                      // 812
  self._prefix = '/' + self._name + '/';                                                                              // 813
                                                                                                                      // 814
  // mutation methods                                                                                                 // 815
  if (self._connection) {                                                                                             // 816
    var m = {};                                                                                                       // 817
                                                                                                                      // 818
    _.each(['insert', 'update', 'remove'], function (method) {                                                        // 819
      m[self._prefix + method] = function (/* ... */) {                                                               // 820
        // All the methods do their own validation, instead of using check().                                         // 821
        check(arguments, [Match.Any]);                                                                                // 822
        var args = _.toArray(arguments);                                                                              // 823
        try {                                                                                                         // 824
          // For an insert, if the client didn't specify an _id, generate one                                         // 825
          // now; because this uses DDP.randomStream, it will be consistent with                                      // 826
          // what the client generated. We generate it now rather than later so                                       // 827
          // that if (eg) an allow/deny rule does an insert to the same                                               // 828
          // collection (not that it really should), the generated _id will                                           // 829
          // still be the first use of the stream and will be consistent.                                             // 830
          //                                                                                                          // 831
          // However, we don't actually stick the _id onto the document yet,                                          // 832
          // because we want allow/deny rules to be able to differentiate                                             // 833
          // between arbitrary client-specified _id fields and merely                                                 // 834
          // client-controlled-via-randomSeed fields.                                                                 // 835
          var generatedId = null;                                                                                     // 836
          if (method === "insert" && !_.has(args[0], '_id')) {                                                        // 837
            generatedId = self._makeNewID();                                                                          // 838
          }                                                                                                           // 839
                                                                                                                      // 840
          if (this.isSimulation) {                                                                                    // 841
            // In a client simulation, you can do any mutation (even with a                                           // 842
            // complex selector).                                                                                     // 843
            if (generatedId !== null)                                                                                 // 844
              args[0]._id = generatedId;                                                                              // 845
            return self._collection[method].apply(                                                                    // 846
              self._collection, args);                                                                                // 847
          }                                                                                                           // 848
                                                                                                                      // 849
          // This is the server receiving a method call from the client.                                              // 850
                                                                                                                      // 851
          // We don't allow arbitrary selectors in mutations from the client: only                                    // 852
          // single-ID selectors.                                                                                     // 853
          if (method !== 'insert')                                                                                    // 854
            throwIfSelectorIsNotId(args[0], method);                                                                  // 855
                                                                                                                      // 856
          if (self._restricted) {                                                                                     // 857
            // short circuit if there is no way it will pass.                                                         // 858
            if (self._validators[method].allow.length === 0) {                                                        // 859
              throw new Meteor.Error(                                                                                 // 860
                403, "Access denied. No allow validators set on restricted " +                                        // 861
                  "collection for method '" + method + "'.");                                                         // 862
            }                                                                                                         // 863
                                                                                                                      // 864
            var validatedMethodName =                                                                                 // 865
                  '_validated' + method.charAt(0).toUpperCase() + method.slice(1);                                    // 866
            args.unshift(this.userId);                                                                                // 867
            method === 'insert' && args.push(generatedId);                                                            // 868
            return self[validatedMethodName].apply(self, args);                                                       // 869
          } else if (self._isInsecure()) {                                                                            // 870
            if (generatedId !== null)                                                                                 // 871
              args[0]._id = generatedId;                                                                              // 872
            // In insecure mode, allow any mutation (with a simple selector).                                         // 873
            // XXX This is kind of bogus.  Instead of blindly passing whatever                                        // 874
            //     we get from the network to this function, we should actually                                       // 875
            //     know the correct arguments for the function and pass just                                          // 876
            //     them.  For example, if you have an extraneous extra null                                           // 877
            //     argument and this is Mongo on the server, the .wrapAsync'd                                         // 878
            //     functions like update will get confused and pass the                                               // 879
            //     "fut.resolver()" in the wrong slot, where _update will never                                       // 880
            //     invoke it. Bam, broken DDP connection.  Probably should just                                       // 881
            //     take this whole method and write it three times, invoking                                          // 882
            //     helpers for the common code.                                                                       // 883
            return self._collection[method].apply(self._collection, args);                                            // 884
          } else {                                                                                                    // 885
            // In secure mode, if we haven't called allow or deny, then nothing                                       // 886
            // is permitted.                                                                                          // 887
            throw new Meteor.Error(403, "Access denied");                                                             // 888
          }                                                                                                           // 889
        } catch (e) {                                                                                                 // 890
          if (e.name === 'MongoError' || e.name === 'MinimongoError') {                                               // 891
            throw new Meteor.Error(409, e.toString());                                                                // 892
          } else {                                                                                                    // 893
            throw e;                                                                                                  // 894
          }                                                                                                           // 895
        }                                                                                                             // 896
      };                                                                                                              // 897
    });                                                                                                               // 898
    // Minimongo on the server gets no stubs; instead, by default                                                     // 899
    // it wait()s until its result is ready, yielding.                                                                // 900
    // This matches the behavior of macromongo on the server better.                                                  // 901
    // XXX see #MeteorServerNull                                                                                      // 902
    if (Meteor.isClient || self._connection === Meteor.server)                                                        // 903
      self._connection.methods(m);                                                                                    // 904
  }                                                                                                                   // 905
};                                                                                                                    // 906
                                                                                                                      // 907
                                                                                                                      // 908
Mongo.Collection.prototype._updateFetch = function (fields) {                                                         // 909
  var self = this;                                                                                                    // 910
                                                                                                                      // 911
  if (!self._validators.fetchAllFields) {                                                                             // 912
    if (fields) {                                                                                                     // 913
      self._validators.fetch = _.union(self._validators.fetch, fields);                                               // 914
    } else {                                                                                                          // 915
      self._validators.fetchAllFields = true;                                                                         // 916
      // clear fetch just to make sure we don't accidentally read it                                                  // 917
      self._validators.fetch = null;                                                                                  // 918
    }                                                                                                                 // 919
  }                                                                                                                   // 920
};                                                                                                                    // 921
                                                                                                                      // 922
Mongo.Collection.prototype._isInsecure = function () {                                                                // 923
  var self = this;                                                                                                    // 924
  if (self._insecure === undefined)                                                                                   // 925
    return !!Package.insecure;                                                                                        // 926
  return self._insecure;                                                                                              // 927
};                                                                                                                    // 928
                                                                                                                      // 929
var docToValidate = function (validator, doc, generatedId) {                                                          // 930
  var ret = doc;                                                                                                      // 931
  if (validator.transform) {                                                                                          // 932
    ret = EJSON.clone(doc);                                                                                           // 933
    // If you set a server-side transform on your collection, then you don't get                                      // 934
    // to tell the difference between "client specified the ID" and "server                                           // 935
    // generated the ID", because transforms expect to get _id.  If you want to                                       // 936
    // do that check, you can do it with a specific                                                                   // 937
    // `C.allow({insert: f, transform: null})` validator.                                                             // 938
    if (generatedId !== null) {                                                                                       // 939
      ret._id = generatedId;                                                                                          // 940
    }                                                                                                                 // 941
    ret = validator.transform(ret);                                                                                   // 942
  }                                                                                                                   // 943
  return ret;                                                                                                         // 944
};                                                                                                                    // 945
                                                                                                                      // 946
Mongo.Collection.prototype._validatedInsert = function (userId, doc,                                                  // 947
                                                         generatedId) {                                               // 948
  var self = this;                                                                                                    // 949
                                                                                                                      // 950
  // call user validators.                                                                                            // 951
  // Any deny returns true means denied.                                                                              // 952
  if (_.any(self._validators.insert.deny, function(validator) {                                                       // 953
    return validator(userId, docToValidate(validator, doc, generatedId));                                             // 954
  })) {                                                                                                               // 955
    throw new Meteor.Error(403, "Access denied");                                                                     // 956
  }                                                                                                                   // 957
  // Any allow returns true means proceed. Throw error if they all fail.                                              // 958
  if (_.all(self._validators.insert.allow, function(validator) {                                                      // 959
    return !validator(userId, docToValidate(validator, doc, generatedId));                                            // 960
  })) {                                                                                                               // 961
    throw new Meteor.Error(403, "Access denied");                                                                     // 962
  }                                                                                                                   // 963
                                                                                                                      // 964
  // If we generated an ID above, insert it now: after the validation, but                                            // 965
  // before actually inserting.                                                                                       // 966
  if (generatedId !== null)                                                                                           // 967
    doc._id = generatedId;                                                                                            // 968
                                                                                                                      // 969
  self._collection.insert.call(self._collection, doc);                                                                // 970
};                                                                                                                    // 971
                                                                                                                      // 972
var transformDoc = function (validator, doc) {                                                                        // 973
  if (validator.transform)                                                                                            // 974
    return validator.transform(doc);                                                                                  // 975
  return doc;                                                                                                         // 976
};                                                                                                                    // 977
                                                                                                                      // 978
// Simulate a mongo `update` operation while validating that the access                                               // 979
// control rules set by calls to `allow/deny` are satisfied. If all                                                   // 980
// pass, rewrite the mongo operation to use $in to set the list of                                                    // 981
// document ids to change ##ValidatedChange                                                                           // 982
Mongo.Collection.prototype._validatedUpdate = function(                                                               // 983
    userId, selector, mutator, options) {                                                                             // 984
  var self = this;                                                                                                    // 985
                                                                                                                      // 986
  check(mutator, Object);                                                                                             // 987
                                                                                                                      // 988
  options = _.clone(options) || {};                                                                                   // 989
                                                                                                                      // 990
  if (!LocalCollection._selectorIsIdPerhapsAsObject(selector))                                                        // 991
    throw new Error("validated update should be of a single ID");                                                     // 992
                                                                                                                      // 993
  // We don't support upserts because they don't fit nicely into allow/deny                                           // 994
  // rules.                                                                                                           // 995
  if (options.upsert)                                                                                                 // 996
    throw new Meteor.Error(403, "Access denied. Upserts not " +                                                       // 997
                           "allowed in a restricted collection.");                                                    // 998
                                                                                                                      // 999
  var noReplaceError = "Access denied. In a restricted collection you can only" +                                     // 1000
        " update documents, not replace them. Use a Mongo update operator, such " +                                   // 1001
        "as '$set'.";                                                                                                 // 1002
                                                                                                                      // 1003
  // compute modified fields                                                                                          // 1004
  var fields = [];                                                                                                    // 1005
  if (_.isEmpty(mutator)) {                                                                                           // 1006
    throw new Meteor.Error(403, noReplaceError);                                                                      // 1007
  }                                                                                                                   // 1008
  _.each(mutator, function (params, op) {                                                                             // 1009
    if (op.charAt(0) !== '$') {                                                                                       // 1010
      throw new Meteor.Error(403, noReplaceError);                                                                    // 1011
    } else if (!_.has(ALLOWED_UPDATE_OPERATIONS, op)) {                                                               // 1012
      throw new Meteor.Error(                                                                                         // 1013
        403, "Access denied. Operator " + op + " not allowed in a restricted collection.");                           // 1014
    } else {                                                                                                          // 1015
      _.each(_.keys(params), function (field) {                                                                       // 1016
        // treat dotted fields as if they are replacing their                                                         // 1017
        // top-level part                                                                                             // 1018
        if (field.indexOf('.') !== -1)                                                                                // 1019
          field = field.substring(0, field.indexOf('.'));                                                             // 1020
                                                                                                                      // 1021
        // record the field we are trying to change                                                                   // 1022
        if (!_.contains(fields, field))                                                                               // 1023
          fields.push(field);                                                                                         // 1024
      });                                                                                                             // 1025
    }                                                                                                                 // 1026
  });                                                                                                                 // 1027
                                                                                                                      // 1028
  var findOptions = {transform: null};                                                                                // 1029
  if (!self._validators.fetchAllFields) {                                                                             // 1030
    findOptions.fields = {};                                                                                          // 1031
    _.each(self._validators.fetch, function(fieldName) {                                                              // 1032
      findOptions.fields[fieldName] = 1;                                                                              // 1033
    });                                                                                                               // 1034
  }                                                                                                                   // 1035
                                                                                                                      // 1036
  var doc = self._collection.findOne(selector, findOptions);                                                          // 1037
  if (!doc)  // none satisfied!                                                                                       // 1038
    return 0;                                                                                                         // 1039
                                                                                                                      // 1040
  // call user validators.                                                                                            // 1041
  // Any deny returns true means denied.                                                                              // 1042
  if (_.any(self._validators.update.deny, function(validator) {                                                       // 1043
    var factoriedDoc = transformDoc(validator, doc);                                                                  // 1044
    return validator(userId,                                                                                          // 1045
                     factoriedDoc,                                                                                    // 1046
                     fields,                                                                                          // 1047
                     mutator);                                                                                        // 1048
  })) {                                                                                                               // 1049
    throw new Meteor.Error(403, "Access denied");                                                                     // 1050
  }                                                                                                                   // 1051
  // Any allow returns true means proceed. Throw error if they all fail.                                              // 1052
  if (_.all(self._validators.update.allow, function(validator) {                                                      // 1053
    var factoriedDoc = transformDoc(validator, doc);                                                                  // 1054
    return !validator(userId,                                                                                         // 1055
                      factoriedDoc,                                                                                   // 1056
                      fields,                                                                                         // 1057
                      mutator);                                                                                       // 1058
  })) {                                                                                                               // 1059
    throw new Meteor.Error(403, "Access denied");                                                                     // 1060
  }                                                                                                                   // 1061
                                                                                                                      // 1062
  options._forbidReplace = true;                                                                                      // 1063
                                                                                                                      // 1064
  // Back when we supported arbitrary client-provided selectors, we actually                                          // 1065
  // rewrote the selector to include an _id clause before passing to Mongo to                                         // 1066
  // avoid races, but since selector is guaranteed to already just be an ID, we                                       // 1067
  // don't have to any more.                                                                                          // 1068
                                                                                                                      // 1069
  return self._collection.update.call(                                                                                // 1070
    self._collection, selector, mutator, options);                                                                    // 1071
};                                                                                                                    // 1072
                                                                                                                      // 1073
// Only allow these operations in validated updates. Specifically                                                     // 1074
// whitelist operations, rather than blacklist, so new complex                                                        // 1075
// operations that are added aren't automatically allowed. A complex                                                  // 1076
// operation is one that does more than just modify its target                                                        // 1077
// field. For now this contains all update operations except '$rename'.                                               // 1078
// http://docs.mongodb.org/manual/reference/operators/#update                                                         // 1079
var ALLOWED_UPDATE_OPERATIONS = {                                                                                     // 1080
  $inc:1, $set:1, $unset:1, $addToSet:1, $pop:1, $pullAll:1, $pull:1,                                                 // 1081
  $pushAll:1, $push:1, $bit:1                                                                                         // 1082
};                                                                                                                    // 1083
                                                                                                                      // 1084
// Simulate a mongo `remove` operation while validating access control                                                // 1085
// rules. See #ValidatedChange                                                                                        // 1086
Mongo.Collection.prototype._validatedRemove = function(userId, selector) {                                            // 1087
  var self = this;                                                                                                    // 1088
                                                                                                                      // 1089
  var findOptions = {transform: null};                                                                                // 1090
  if (!self._validators.fetchAllFields) {                                                                             // 1091
    findOptions.fields = {};                                                                                          // 1092
    _.each(self._validators.fetch, function(fieldName) {                                                              // 1093
      findOptions.fields[fieldName] = 1;                                                                              // 1094
    });                                                                                                               // 1095
  }                                                                                                                   // 1096
                                                                                                                      // 1097
  var doc = self._collection.findOne(selector, findOptions);                                                          // 1098
  if (!doc)                                                                                                           // 1099
    return 0;                                                                                                         // 1100
                                                                                                                      // 1101
  // call user validators.                                                                                            // 1102
  // Any deny returns true means denied.                                                                              // 1103
  if (_.any(self._validators.remove.deny, function(validator) {                                                       // 1104
    return validator(userId, transformDoc(validator, doc));                                                           // 1105
  })) {                                                                                                               // 1106
    throw new Meteor.Error(403, "Access denied");                                                                     // 1107
  }                                                                                                                   // 1108
  // Any allow returns true means proceed. Throw error if they all fail.                                              // 1109
  if (_.all(self._validators.remove.allow, function(validator) {                                                      // 1110
    return !validator(userId, transformDoc(validator, doc));                                                          // 1111
  })) {                                                                                                               // 1112
    throw new Meteor.Error(403, "Access denied");                                                                     // 1113
  }                                                                                                                   // 1114
                                                                                                                      // 1115
  // Back when we supported arbitrary client-provided selectors, we actually                                          // 1116
  // rewrote the selector to {_id: {$in: [ids that we found]}} before passing to                                      // 1117
  // Mongo to avoid races, but since selector is guaranteed to already just be                                        // 1118
  // an ID, we don't have to any more.                                                                                // 1119
                                                                                                                      // 1120
  return self._collection.remove.call(self._collection, selector);                                                    // 1121
};                                                                                                                    // 1122
                                                                                                                      // 1123
/**                                                                                                                   // 1124
 * @deprecated in 0.9.1                                                                                               // 1125
 */                                                                                                                   // 1126
Meteor.Collection = Mongo.Collection;                                                                                 // 1127
                                                                                                                      // 1128
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);


/* Exports */
if (typeof Package === 'undefined') Package = {};
Package.mongo = {
  Mongo: Mongo
};

})();