<commit_before>Meteor._ClientMethodInvocation = function (name, handler) {
  var self = this;

  // XXX need: user, setRestartHook, setUser (simulated??)

  self._enclosing = null;
  self.isSimulation = null;
  self._name = name;
  self._handler = handler;
  self._callback = null;
  self._id = null;
};

_.extend(Meteor._ClientMethodInvocation.prototype, {
  beginAsync: function () {
    // XXX need a much better error message!
    // duplicated in livedata_server
    throw new Error("Simulated methods may not be asynchronous");
  },

  _run: function (args, callback, enqueue) {
    var self = this;
    self._enclosing = Meteor._CurrentInvocation.get();
    self._callback = callback;
    self.isSimulation = true; // NB: refers to child invocations, not to us

    // run locally (if we have a stub for it)
    if (self._handler) {
      try {
        var ret = Meteor._CurrentInvocation.withValue(self, function () {
          return self._handler.apply(self, args);
        });
      } catch (e) {
        var stub_exception = e;
      }
    }

    if (self._enclosing && self._enclosing.isSimulation) {
      // In simulation mode, never do an RPC. Use the result of
      // running the stub instead.
      if (self._callback) {
        if (stub_exception)
          self._callback({error: 500, reason: "Stub threw exception"});
        else
          self._callback(ret);
      }
    } else {
      // This invocation is real, not a simulation. Do the RPC.

      // Note that it is important that the function totally complete,
      // locally, before the message is sent to the server. (Or at
      // least, we need to guarantee that the snapshot is not restored
      // until the local copy of the function has stopped doing writes.)

      enqueue({msg: 'method', method: self._name, params: args},
              self._callback);
    }

    if (stub_exception)
      throw stub_exception;
    return ret;
  }
});

// list of subscription tokens outstanding during a
// captureDependencies run. only set when we're doing a run. The fact
// that this is a singleton means we can't do recursive
// Meteor.subscriptions(). But what would that even mean?
// XXX namespacing
Meteor._capture_subs = null;

Meteor._LivedataConnection = function (url) {
  var self = this;
  self.url = url;
  self.stores = {}; // name -> object with methods
  self.method_handlers = {}; // name -> func
  self.next_method_id = 1;
  self.outstanding_methods = []; // each item has keys: msg, callback
  self.unsatisfied_methods = {}; // map from method_id -> true
  self.pending_data = []; // array of pending data messages
  self.queued = {}; // name -> updates for (yet to be created) collection
  self.quiesce_callbacks = [];

  self.subs = new LocalCollection;
  // keyed by subs._id. value is unset or an array. if set, sub is not
  // yet ready.
  self.sub_ready_callbacks = {};

  // Setup auto-reload persistence.
  var reload_key = "Server-" + url;
  var reload_data = Meteor._reload.migration_data(reload_key);
  if (typeof reload_data === "object") {
    if (typeof reload_data.next_method_id === "number")
      self.next_method_id = reload_data.next_method_id;
    if (typeof reload_data.outstanding_methods === "object")
      self.outstanding_methods = reload_data.outstanding_methods;
    // pending messages will be transmitted on initial stream 'reset'
  }
  Meteor._reload.on_migrate(reload_key, function () {
    var methods = _.map(self.outstanding_methods, function (m) {
      // filter out callback
      return {msg: m.msg};
    });

    return { next_method_id: self.next_method_id,
             outstanding_methods: methods };
  });

  // Setup stream
  self.stream = new Meteor._Stream(self.url);

  self.stream.on('message', function (raw_msg) {
    try {
      var msg = JSON.parse(raw_msg);
    } catch (err) {
      Meteor._debug("discarding message with invalid JSON", raw_msg);
      return;
    }
    if (typeof msg !== 'object' || !msg.msg) {
      Meteor._debug("discarding invalid livedata message", msg);
      return;
    }

    if (msg.msg === 'connected')
      self._livedata_connected(msg);
    else if (msg.msg === 'data')
      self._livedata_data(msg);
    else if (msg.msg === 'nosub')
      self._livedata_nosub(msg);
    else if (msg.msg === 'result')
      self._livedata_result(msg);
    else if (msg.msg === 'error')
      self._livedata_error(msg);
    else
      Meteor._debug("discarding unknown livedata message type", msg);
  });

  self.stream.on('reset', function () {

    // Send a connect message at the beginning of the stream.
    // NOTE: reset is called even on the first connection, so this is
    // the only place we send this message.
    self.stream.send(JSON.stringify({msg: 'connect'}));

    // Send pending methods.
    _.each(self.outstanding_methods, function (m) {
      self.stream.send(JSON.stringify(m.msg));
    });

    // add new subscriptions at the end. this way they take effect after
    // the handlers and we don't see flicker.
    self.subs.find().forEach(function (sub) {
      self.stream.send(JSON.stringify(
        {msg: 'sub', id: sub._id, name: sub.name, params: sub.args}));
    });

    // clear out the local database!

    // XXX this causes flicker ("database flap") and needs to be
    // rewritten. we should wait until we found out if we successfully
    // resumed the stream. if so, we need do nothing special. if not,
    // we need to put a reset message in pending_data (optionally
    // clearing pending_data and queued first, as an optimization),
    // and defer processing pending_data until all of the
    // subscriptions that we previously told the user were ready, are
    // now once again ready. then, when we do go to process the
    // messages, we need to do it in one atomic batch (the reset and
    // the redeliveries together) so that livequeries don't observe
    // spurious 'added' and 'removed' messages, which would cause, eg,
    // DOM elements to fail to get semantically matched, leading to a
    // loss of focus/input state.
    _.each(self.stores, function (s) { s.reset(); });
    self.pending_data = [];
    self.queued = {};
  });

  // we never terminate the observe(), since there is no way to
  // destroy a LivedataConnection.. but this shouldn't matter, since we're
  // the only one that holds a reference to the self.subs collection
  self.subs_token = self.subs.find({}).observe({
    added: function (sub) {
      self.stream.send(JSON.stringify({
        msg: 'sub', id: sub._id, name: sub.name, params: sub.args}));
    },
    changed: function (sub) {
      if (sub.count <= 0) {
        // minimongo not re-entrant.
        _.defer(function () { self.subs.remove({_id: sub._id}); });
      }
    },
    removed: function (id) {
      self.stream.send(JSON.stringify({msg: 'unsub', id: id}));
    }
  });
};

_.extend(Meteor._LivedataConnection.prototype, {
  // 'name' is the name of the data on the wire that should go in the
  // store. 'store' should be an object with methods beginUpdate,
  // update, endUpdate, reset. see Collection for an example.
  registerStore: function (name, store) {
    var self = this;

    if (name in self.stores)
      return false;
    self.stores[name] = store;

    store.beginUpdate();
    _.each(self.queued[name] || [], function (msg) {
      store.update(msg);
    });
    store.endUpdate();
    delete self.queued[name];

    return true;
  },

  subscribe: function (name, args, callback) {
    var self = this;
    var id;
    var existing = self.subs.find({name: name, args: args}, {reactive: false}).fetch();

    if (existing && existing[0]) {
      // already subbed, inc count.
      id = existing[0]._id;
      self.subs.update({_id: id}, {$inc: {count: 1}});

      if (callback) {
        if (self.sub_ready_callbacks[id])
          self.sub_ready_callbacks[id].push(callback);
        else
          callback(); // XXX maybe _.defer?
      }
    } else {
      // new sub, add object.
      // generate our own id so we can know it w/ a find afterwards.
      id = LocalCollection.uuid();
      self.subs.insert({_id: id, name: name, args: args, count: 1});

      self.sub_ready_callbacks[id] = [];

      if (callback)
        self.sub_ready_callbacks[id].push(callback);
    }

    // return an object with a stop method.
    var token = {stop: function () {
      if (!id) return; // must have an id (local from above).
      // just update the database. observe takes care of the rest.
      self.subs.update({_id: id}, {$inc: {count: -1}});
    }};

    if (Meteor._capture_subs)
      Meteor._capture_subs.push(token);

    return token;
  },

  methods: function (methods) {
    var self = this;
    _.each(methods, function (func, name) {
      if (self.method_handlers[name])
        throw new Error("A method named '" + name + "' is already defined");
      self.method_handlers[name] = func;
    });
  },

  call: function (name /*, arguments */) {
    return this.apply(name, Array.prototype.slice.call(arguments, 1));
  },

  apply: function (name, args) {
    var self = this;
    var enclosing = Meteor._CurrentInvocation.get();
    var handler = self.method_handlers[name];
    var result_func = function () {};

    // if it's a function, the last argument is the result callback,
    // not a parameter to the remote method.
    args = _.clone(args);
    if (args.length && typeof args[args.length - 1] === "function") {
      // XXX would it be better form to do the binding in stream.on,
      // or caller, instead of here?
      result_func = Meteor.bindEnvironment(args.pop(), function (e) {
        // XXX improve error message (and how we report it)
        Meteor._debug("Exception while delivering result of invoking '" +
                      name + "'", e.stack);
      });
    }

    var enqueue = function (msg, callback) {
      msg.id = '' + (self.next_method_id++);
      self.outstanding_methods.push({
        msg: msg, callback: callback});
      self.unsatisfied_methods[msg.id] = true;
      self.stream.send(JSON.stringify(msg));
    };

    var invocation = new Meteor._ClientMethodInvocation(name, handler, self);
    // if _run throws an exception, allow it to propagate
    return invocation._run(args, result_func, enqueue);
  },

  status: function () {
    var self = this;
    return self.stream.status();
  },

  reconnect: function () {
    var self = this;
    return self.stream.reconnect();
  },

  // called when we are up-to-date with the server. intended for use
  // only in tests. currently, you are very limited in what you may do
  // inside your callback -- in particular, don't do anything that
  // could result in another call to onQuiesce, or results are
  // undefined.
  onQuiesce: function (f) {
    var self = this;

    f = Meteor.bindEnvironment(f, function (e) {
      Meteor._debug("Exception in quiesce callback", e.stack);
    });

    for (var method_id in self.unsatisfied_methods) {
      // we are not quiesced -- wait until we are
      self.quiesce_callbacks.push(f);
      return;
    }

    f();
  },

  _livedata_connected: function (msg) {
    var self = this;
    // Meteor._debug("CONNECTED", msg);
  },

  _livedata_data: function (msg) {
    var self = this;

    // Add the data message to the queue
    self.pending_data.push(msg);

    // If there are still method invocations in flight, stop
    _.each(msg.methods || [], function (method_id) {
      delete self.unsatisfied_methods[method_id];
    });
    for (var method_id in self.unsatisfied_methods)
      return;

    // All methods have landed. Blow away local changes and replace
    // with authoritative changes from server.

    _.each(self.stores, function (s) { s.beginUpdate(); });

    _.each(self.pending_data, function (msg) {
      if (msg.collection && msg.id) {
        var store = self.stores[msg.collection];

        if (!store) {
          // Nobody's listening for this data. Queue it up until
          // someone wants it.
          // XXX memory use will grow without bound if you forget to
          // create a collection.. going to have to do something about
          // that.
          if (!(msg.collection in self.queued))
            self.queued[msg.collection] = [];
          self.queued[msg.collection].push(msg);
          return;
        }

        store.update(msg);
      }

      if (msg.subs) {
        _.each(msg.subs, function (id) {
          var arr = self.sub_ready_callbacks[id];
          if (arr) _.each(arr, function (c) { c(); });
          delete self.sub_ready_callbacks[id];
        });
      }
    });

    _.each(self.stores, function (s) { s.endUpdate(); });

    _.each(self.quiesce_callbacks, function (cb) { cb(); });
    self.quiesce_callbacks = [];

    self.pending_data = [];
  },

  _livedata_nosub: function (msg) {
    var self = this;
    // Meteor._debug("NOSUB", msg);
  },

  _livedata_result: function (msg) {
    var self = this;
    // id, result or error. error has error (code), reason, details

    // find the outstanding request
    // should be O(1) in nearly all realistic use cases
    for (var i = 0; i < self.outstanding_methods.length; i++) {
      var m = self.outstanding_methods[i];
      if (m.msg.id === msg.id)
        break;
    }
    if (!m) {
      // XXX write a better error
      Meteor._debug("Can't interpret method response message");
      return;
    }

    // remove
    self.outstanding_methods.splice(i, 1);

    // If a response is to a method we sent from reload pending
    // methods, it won't have a callback. This is OK. This will get
    // revisited when we do more intellegent auto-reloads where we
    // wait to quiesce, pre-stage cached assets, etc.
    if (m.callback) {
      // callback will have already been bindEnvironment'd by apply(),
      // so no need to catch exceptions
      if ('error' in msg)
        m.callback(msg.error);
      else
        m.callback(undefined, msg.result);
    }
  },

  _livedata_error: function (msg) {
    Meteor._debug("Received error from server: " + msg.reason);
  }

});

_.extend(Meteor, {
  connect: function (url) {
    return new Meteor._LivedataConnection(url);
  },

  autosubscribe: function (sub_func) {
    var local_subs = [];
    var context = new Meteor.deps.Context();

    context.on_invalidate(function () {
      // recurse.
      Meteor.autosubscribe(sub_func);
      // unsub after re-subbing, to avoid bouncing.
      _.each(local_subs, function (x) { x.stop() });
    });

    context.run(function () {
      if (Meteor._capture_subs)
        throw new Error("Meteor.autosubscribe may not be called recursively");

      Meteor._capture_subs = [];
      try {
        sub_func();
      } finally {
        local_subs = Meteor._capture_subs;
        Meteor._capture_subs = null;
      }
    });
  }
});

<commit_msg>Implement reconnection on the client.<commit_after>Meteor._ClientMethodInvocation = function (name, handler) {
  var self = this;

  // XXX need: user, setRestartHook, setUser (simulated??)

  self._enclosing = null;
  self.isSimulation = null;
  self._name = name;
  self._handler = handler;
  self._callback = null;
  self._id = null;
};

_.extend(Meteor._ClientMethodInvocation.prototype, {
  beginAsync: function () {
    // XXX need a much better error message!
    // duplicated in livedata_server
    throw new Error("Simulated methods may not be asynchronous");
  },

  _run: function (args, callback, enqueue) {
    var self = this;
    self._enclosing = Meteor._CurrentInvocation.get();
    self._callback = callback;
    self.isSimulation = true; // NB: refers to child invocations, not to us

    // run locally (if we have a stub for it)
    if (self._handler) {
      try {
        var ret = Meteor._CurrentInvocation.withValue(self, function () {
          return self._handler.apply(self, args);
        });
      } catch (e) {
        var stub_exception = e;
      }
    }

    if (self._enclosing && self._enclosing.isSimulation) {
      // In simulation mode, never do an RPC. Use the result of
      // running the stub instead.
      if (self._callback) {
        if (stub_exception)
          self._callback({error: 500, reason: "Stub threw exception"});
        else
          self._callback(ret);
      }
    } else {
      // This invocation is real, not a simulation. Do the RPC.

      // Note that it is important that the function totally complete,
      // locally, before the message is sent to the server. (Or at
      // least, we need to guarantee that the snapshot is not restored
      // until the local copy of the function has stopped doing writes.)

      enqueue({msg: 'method', method: self._name, params: args},
              self._callback);
    }

    if (stub_exception)
      throw stub_exception;
    return ret;
  }
});

// list of subscription tokens outstanding during a
// captureDependencies run. only set when we're doing a run. The fact
// that this is a singleton means we can't do recursive
// Meteor.subscriptions(). But what would that even mean?
// XXX namespacing
Meteor._capture_subs = null;

Meteor._LivedataConnection = function (url) {
  var self = this;
  self.url = url;
  self.last_session_id = null;
  self.stores = {}; // name -> object with methods
  self.method_handlers = {}; // name -> func
  self.next_method_id = 1;
  self.outstanding_methods = []; // each item has keys: msg, callback
  self.unsatisfied_methods = {}; // map from method_id -> true
  self.pending_data = []; // array of pending data messages
  self.queued = {}; // name -> updates for (yet to be created) collection
  self.quiesce_callbacks = [];

  self.subs = new LocalCollection;
  // keyed by subs._id. value is unset or an array. if set, sub is not
  // yet ready.
  self.sub_ready_callbacks = {};

  // Setup auto-reload persistence.
  var reload_key = "Server-" + url;
  var reload_data = Meteor._reload.migration_data(reload_key);
  if (typeof reload_data === "object") {
    if (typeof reload_data.next_method_id === "number")
      self.next_method_id = reload_data.next_method_id;
    if (typeof reload_data.outstanding_methods === "object")
      self.outstanding_methods = reload_data.outstanding_methods;
    // pending messages will be transmitted on initial stream 'reset'
  }
  Meteor._reload.on_migrate(reload_key, function () {
    var methods = _.map(self.outstanding_methods, function (m) {
      // filter out callback
      return {msg: m.msg};
    });

    return { next_method_id: self.next_method_id,
             outstanding_methods: methods };
  });

  // Setup stream
  self.stream = new Meteor._Stream(self.url);

  self.stream.on('message', function (raw_msg) {
    try {
      var msg = JSON.parse(raw_msg);
    } catch (err) {
      Meteor._debug("discarding message with invalid JSON", raw_msg);
      return;
    }
    if (typeof msg !== 'object' || !msg.msg) {
      Meteor._debug("discarding invalid livedata message", msg);
      return;
    }

    if (msg.msg === 'connected')
      self._livedata_connected(msg);
    else if (msg.msg === 'data')
      self._livedata_data(msg);
    else if (msg.msg === 'nosub')
      self._livedata_nosub(msg);
    else if (msg.msg === 'result')
      self._livedata_result(msg);
    else if (msg.msg === 'error')
      self._livedata_error(msg);
    else
      Meteor._debug("discarding unknown livedata message type", msg);
  });

  self.stream.on('reset', function () {

    // Send a connect message at the beginning of the stream.
    // NOTE: reset is called even on the first connection, so this is
    // the only place we send this message.
    var msg = {msg: 'connect'};
    if (self.last_session_id)
      msg.session = self.last_session_id;
    self.stream.send(JSON.stringify(msg));

    // Now, to minimize setup latency, go ahead and blast out all of
    // our pending methods ands subscriptions before we've even taken
    // the necessary RTT to know if we successfully reconnected. (1)
    // They're supposed to be idempotent; (2) even if we did
    // reconnect, we're not sure what messages might have gotten lost
    // (in either direction) since we were disconnected (TCP being
    // sloppy about that.)

    // XXX we may have an issue where we lose 'data' messages sent
    // immediately before disconnection.. do we need to add app-level
    // acking of data messages?

    // Send pending methods.
    _.each(self.outstanding_methods, function (m) {
      self.stream.send(JSON.stringify(m.msg));
    });

    // add new subscriptions at the end. this way they take effect after
    // the handlers and we don't see flicker.
    self.subs.find().forEach(function (sub) {
      self.stream.send(JSON.stringify(
        {msg: 'sub', id: sub._id, name: sub.name, params: sub.args}));
    });
  });

  // we never terminate the observe(), since there is no way to
  // destroy a LivedataConnection.. but this shouldn't matter, since we're
  // the only one that holds a reference to the self.subs collection
  self.subs_token = self.subs.find({}).observe({
    added: function (sub) {
      self.stream.send(JSON.stringify({
        msg: 'sub', id: sub._id, name: sub.name, params: sub.args}));
    },
    changed: function (sub) {
      if (sub.count <= 0) {
        // minimongo not re-entrant.
        _.defer(function () { self.subs.remove({_id: sub._id}); });
      }
    },
    removed: function (id) {
      self.stream.send(JSON.stringify({msg: 'unsub', id: id}));
    }
  });
};

_.extend(Meteor._LivedataConnection.prototype, {
  // 'name' is the name of the data on the wire that should go in the
  // store. 'store' should be an object with methods beginUpdate,
  // update, endUpdate, reset. see Collection for an example.
  registerStore: function (name, store) {
    var self = this;

    if (name in self.stores)
      return false;
    self.stores[name] = store;

    store.beginUpdate();
    _.each(self.queued[name] || [], function (msg) {
      store.update(msg);
    });
    store.endUpdate();
    delete self.queued[name];

    return true;
  },

  subscribe: function (name, args, callback) {
    var self = this;
    var id;
    var existing = self.subs.find({name: name, args: args}, {reactive: false}).fetch();

    if (existing && existing[0]) {
      // already subbed, inc count.
      id = existing[0]._id;
      self.subs.update({_id: id}, {$inc: {count: 1}});

      if (callback) {
        if (self.sub_ready_callbacks[id])
          self.sub_ready_callbacks[id].push(callback);
        else
          callback(); // XXX maybe _.defer?
      }
    } else {
      // new sub, add object.
      // generate our own id so we can know it w/ a find afterwards.
      id = LocalCollection.uuid();
      self.subs.insert({_id: id, name: name, args: args, count: 1});

      self.sub_ready_callbacks[id] = [];

      if (callback)
        self.sub_ready_callbacks[id].push(callback);
    }

    // return an object with a stop method.
    var token = {stop: function () {
      if (!id) return; // must have an id (local from above).
      // just update the database. observe takes care of the rest.
      self.subs.update({_id: id}, {$inc: {count: -1}});
    }};

    if (Meteor._capture_subs)
      Meteor._capture_subs.push(token);

    return token;
  },

  methods: function (methods) {
    var self = this;
    _.each(methods, function (func, name) {
      if (self.method_handlers[name])
        throw new Error("A method named '" + name + "' is already defined");
      self.method_handlers[name] = func;
    });
  },

  call: function (name /*, arguments */) {
    return this.apply(name, Array.prototype.slice.call(arguments, 1));
  },

  apply: function (name, args) {
    var self = this;
    var enclosing = Meteor._CurrentInvocation.get();
    var handler = self.method_handlers[name];
    var result_func = function () {};

    // if it's a function, the last argument is the result callback,
    // not a parameter to the remote method.
    args = _.clone(args);
    if (args.length && typeof args[args.length - 1] === "function") {
      // XXX would it be better form to do the binding in stream.on,
      // or caller, instead of here?
      result_func = Meteor.bindEnvironment(args.pop(), function (e) {
        // XXX improve error message (and how we report it)
        Meteor._debug("Exception while delivering result of invoking '" +
                      name + "'", e.stack);
      });
    }

    var enqueue = function (msg, callback) {
      msg.id = '' + (self.next_method_id++);
      self.outstanding_methods.push({
        msg: msg, callback: callback});
      self.unsatisfied_methods[msg.id] = true;
      self.stream.send(JSON.stringify(msg));
    };

    var invocation = new Meteor._ClientMethodInvocation(name, handler, self);
    // if _run throws an exception, allow it to propagate
    return invocation._run(args, result_func, enqueue);
  },

  status: function () {
    var self = this;
    return self.stream.status();
  },

  reconnect: function () {
    var self = this;
    return self.stream.reconnect();
  },

  // called when we are up-to-date with the server. intended for use
  // only in tests. currently, you are very limited in what you may do
  // inside your callback -- in particular, don't do anything that
  // could result in another call to onQuiesce, or results are
  // undefined.
  onQuiesce: function (f) {
    var self = this;

    f = Meteor.bindEnvironment(f, function (e) {
      Meteor._debug("Exception in quiesce callback", e.stack);
    });

    for (var method_id in self.unsatisfied_methods) {
      // we are not quiesced -- wait until we are
      self.quiesce_callbacks.push(f);
      return;
    }

    f();
  },

  _livedata_connected: function (msg) {
    var self = this;

    if (typeof (msg.session) === "string") {
      var reconnected = (self.last_session_id === msg.session);
      self.last_session_id = msg.session;
    }

    if (reconnected)
      // successful reconnection -- pick up where we left off.
      return;

    // clear out the local database!

    // XXX this causes flicker ("database flap") and needs to be
    // rewritten. we need to put a reset message in pending_data
    // (optionally clearing pending_data and queued first, as an
    // optimization), and defer processing pending_data until all of
    // the subscriptions that we previously told the user were ready,
    // are now once again ready. then, when we do go to process the
    // messages, we need to do it in one atomic batch (the reset and
    // the redeliveries together) so that livequeries don't observe
    // spurious 'added' and 'removed' messages, which would cause, eg,
    // DOM elements to fail to get semantically matched, leading to a
    // loss of focus/input state.
    _.each(self.stores, function (s) { s.reset(); });
    self.pending_data = [];
    self.queued = {};
  },

  _livedata_data: function (msg) {
    var self = this;

    // Add the data message to the queue
    self.pending_data.push(msg);

    // If there are still method invocations in flight, stop
    _.each(msg.methods || [], function (method_id) {
      delete self.unsatisfied_methods[method_id];
    });
    for (var method_id in self.unsatisfied_methods)
      return;

    // All methods have landed. Blow away local changes and replace
    // with authoritative changes from server.

    _.each(self.stores, function (s) { s.beginUpdate(); });

    _.each(self.pending_data, function (msg) {
      if (msg.collection && msg.id) {
        var store = self.stores[msg.collection];

        if (!store) {
          // Nobody's listening for this data. Queue it up until
          // someone wants it.
          // XXX memory use will grow without bound if you forget to
          // create a collection.. going to have to do something about
          // that.
          if (!(msg.collection in self.queued))
            self.queued[msg.collection] = [];
          self.queued[msg.collection].push(msg);
          return;
        }

        store.update(msg);
      }

      if (msg.subs) {
        _.each(msg.subs, function (id) {
          var arr = self.sub_ready_callbacks[id];
          if (arr) _.each(arr, function (c) { c(); });
          delete self.sub_ready_callbacks[id];
        });
      }
    });

    _.each(self.stores, function (s) { s.endUpdate(); });

    _.each(self.quiesce_callbacks, function (cb) { cb(); });
    self.quiesce_callbacks = [];

    self.pending_data = [];
  },

  _livedata_nosub: function (msg) {
    var self = this;
    // Meteor._debug("NOSUB", msg);
  },

  _livedata_result: function (msg) {
    var self = this;
    // id, result or error. error has error (code), reason, details

    // find the outstanding request
    // should be O(1) in nearly all realistic use cases
    for (var i = 0; i < self.outstanding_methods.length; i++) {
      var m = self.outstanding_methods[i];
      if (m.msg.id === msg.id)
        break;
    }
    if (!m) {
      // XXX write a better error
      Meteor._debug("Can't interpret method response message");
      return;
    }

    // remove
    self.outstanding_methods.splice(i, 1);

    // If a response is to a method we sent from reload pending
    // methods, it won't have a callback. This is OK. This will get
    // revisited when we do more intellegent auto-reloads where we
    // wait to quiesce, pre-stage cached assets, etc.
    if (m.callback) {
      // callback will have already been bindEnvironment'd by apply(),
      // so no need to catch exceptions
      if ('error' in msg)
        m.callback(msg.error);
      else
        m.callback(undefined, msg.result);
    }
  },

  _livedata_error: function (msg) {
    Meteor._debug("Received error from server: " + msg.reason);
  }

});

_.extend(Meteor, {
  connect: function (url) {
    return new Meteor._LivedataConnection(url);
  },

  autosubscribe: function (sub_func) {
    var local_subs = [];
    var context = new Meteor.deps.Context();

    context.on_invalidate(function () {
      // recurse.
      Meteor.autosubscribe(sub_func);
      // unsub after re-subbing, to avoid bouncing.
      _.each(local_subs, function (x) { x.stop() });
    });

    context.run(function () {
      if (Meteor._capture_subs)
        throw new Error("Meteor.autosubscribe may not be called recursively");

      Meteor._capture_subs = [];
      try {
        sub_func();
      } finally {
        local_subs = Meteor._capture_subs;
        Meteor._capture_subs = null;
      }
    });
  }
});

<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc directive
 * @name ngClick
 *
 * @description
 * The ngClick directive allows you to specify custom behavior when
 * an element is clicked.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
 * click. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-click="count = count + 1" ng-init="count=0">
        Increment
      </button>
      count: {{count}}
     </file>
     <file name="protractor.js" type="protractor">
       it('should check ng-click', function() {
         expect(element(by.binding('count')).getText()).toMatch('0');
         element(by.css('button')).click();
         expect(element(by.binding('count')).getText()).toMatch('1');
       });
     </file>
   </example>
 */
/*
 * A directive that allows creation of custom onclick handlers that are defined as angular
 * expressions and are compiled and executed within the current scope.
 *
 * Events that are handled via these handler are always configured not to propagate further.
 */
var ngEventDirectives = {};
forEach(
  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
  function(name) {
    var directiveName = directiveNormalize('ng-' + name);
    ngEventDirectives[directiveName] = ['$parse', function($parse) {
      return {
        compile: function($element, attr) {
          var fn = $parse(attr[directiveName]);
          return function ngEventHandler(scope, element) {
            element.on(lowercase(name), function(event) {
              scope.$apply(function() {
                fn(scope, {$event:event});
              });
            });
          };
        }
      };
    }];
  }
);

/**
 * @ngdoc directive
 * @name ngDblclick
 *
 * @description
 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
 * a dblclick. (The Event object is available as `$event`)
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-dblclick="count = count + 1" ng-init="count=0">
        Increment (on double click)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngMousedown
 *
 * @description
 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-mousedown="count = count + 1" ng-init="count=0">
        Increment (on mouse down)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngMouseup
 *
 * @description
 * Specify custom behavior on mouseup event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-mouseup="count = count + 1" ng-init="count=0">
        Increment (on mouse up)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngMouseover
 *
 * @description
 * Specify custom behavior on mouseover event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-mouseover="count = count + 1" ng-init="count=0">
        Increment (when mouse is over)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngMouseenter
 *
 * @description
 * Specify custom behavior on mouseenter event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-mouseenter="count = count + 1" ng-init="count=0">
        Increment (when mouse enters)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngMouseleave
 *
 * @description
 * Specify custom behavior on mouseleave event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-mouseleave="count = count + 1" ng-init="count=0">
        Increment (when mouse leaves)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngMousemove
 *
 * @description
 * Specify custom behavior on mousemove event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-mousemove="count = count + 1" ng-init="count=0">
        Increment (when mouse moves)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngKeydown
 *
 * @description
 * Specify custom behavior on keydown event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
 *
 * @example
   <example>
     <file name="index.html">
      <input ng-keydown="count = count + 1" ng-init="count=0">
      key down count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngKeyup
 *
 * @description
 * Specify custom behavior on keyup event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
 *
 * @example
   <example>
     <file name="index.html">

Typing in the input box below updates the key count

       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}

Typing in the input box below updates the keycode

       <input ng-keyup="event=$event">

event keyCode: {{ event.keyCode }}

event altKey: {{ event.altKey }}

     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngKeypress
 *
 * @description
 * Specify custom behavior on keypress event.
 *
 * @element ANY
 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
 * and can be interrogated for keyCode, altKey, etc.)
 *
 * @example
   <example>
     <file name="index.html">
      <input ng-keypress="count = count + 1" ng-init="count=0">
      key press count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngSubmit
 *
 * @description
 * Enables binding angular expressions to onsubmit events.
 *
 * Additionally it prevents the default action (which for form means sending the request to the
 * server and reloading the current page), but only if the form does not contain `action`,
 * `data-action`, or `x-action` attributes.
 *
 * @element form
 * @priority 0
 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
 * ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">

      <form ng-submit="submit()" ng-controller="Ctrl">
        Enter text and hit enter:
        <input type="text" ng-model="text" name="text" />
        <input type="submit" id="submit" value="Submit" />

list={{list}}

      </form>
     </file>
     <file name="protractor.js" type="protractor">
       it('should check ng-submit', function() {
         expect(element(by.binding('list')).getText()).toBe('list=[]');
         element(by.css('#submit')).click();
         expect(element(by.binding('list')).getText()).toContain('hello');
         expect(element(by.model('text')).getAttribute('value')).toBe('');
       });
       it('should ignore empty strings', function() {
         expect(element(by.binding('list')).getText()).toBe('list=[]');
         element(by.css('#submit')).click();
         element(by.css('#submit')).click();
         expect(element(by.binding('list')).getText()).toContain('hello');
        });
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngFocus
 *
 * @description
 * Specify custom behavior on focus event.
 *
 * @element window, input, select, textarea, a
 * @priority 0
 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
 * See {@link ng.directive:ngClick ngClick}
 */

/**
 * @ngdoc directive
 * @name ngBlur
 *
 * @description
 * Specify custom behavior on blur event.
 *
 * @element window, input, select, textarea, a
 * @priority 0
 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
 * See {@link ng.directive:ngClick ngClick}
 */

/**
 * @ngdoc directive
 * @name ngCopy
 *
 * @description
 * Specify custom behavior on copy event.
 *
 * @element window, input, select, textarea, a
 * @priority 0
 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
      copied: {{copied}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngCut
 *
 * @description
 * Specify custom behavior on cut event.
 *
 * @element window, input, select, textarea, a
 * @priority 0
 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
      cut: {{cut}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngPaste
 *
 * @description
 * Specify custom behavior on paste event.
 *
 * @element window, input, select, textarea, a
 * @priority 0
 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
      pasted: {{paste}}
     </file>
   </example>
 */
<commit_msg>docs(ngSubmit): update example to use a module<commit_after>'use strict';

/**
 * @ngdoc directive
 * @name ngClick
 *
 * @description
 * The ngClick directive allows you to specify custom behavior when
 * an element is clicked.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
 * click. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-click="count = count + 1" ng-init="count=0">
        Increment
      </button>
      count: {{count}}
     </file>
     <file name="protractor.js" type="protractor">
       it('should check ng-click', function() {
         expect(element(by.binding('count')).getText()).toMatch('0');
         element(by.css('button')).click();
         expect(element(by.binding('count')).getText()).toMatch('1');
       });
     </file>
   </example>
 */
/*
 * A directive that allows creation of custom onclick handlers that are defined as angular
 * expressions and are compiled and executed within the current scope.
 *
 * Events that are handled via these handler are always configured not to propagate further.
 */
var ngEventDirectives = {};
forEach(
  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
  function(name) {
    var directiveName = directiveNormalize('ng-' + name);
    ngEventDirectives[directiveName] = ['$parse', function($parse) {
      return {
        compile: function($element, attr) {
          var fn = $parse(attr[directiveName]);
          return function ngEventHandler(scope, element) {
            element.on(lowercase(name), function(event) {
              scope.$apply(function() {
                fn(scope, {$event:event});
              });
            });
          };
        }
      };
    }];
  }
);

/**
 * @ngdoc directive
 * @name ngDblclick
 *
 * @description
 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
 * a dblclick. (The Event object is available as `$event`)
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-dblclick="count = count + 1" ng-init="count=0">
        Increment (on double click)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngMousedown
 *
 * @description
 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-mousedown="count = count + 1" ng-init="count=0">
        Increment (on mouse down)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngMouseup
 *
 * @description
 * Specify custom behavior on mouseup event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-mouseup="count = count + 1" ng-init="count=0">
        Increment (on mouse up)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngMouseover
 *
 * @description
 * Specify custom behavior on mouseover event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-mouseover="count = count + 1" ng-init="count=0">
        Increment (when mouse is over)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngMouseenter
 *
 * @description
 * Specify custom behavior on mouseenter event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-mouseenter="count = count + 1" ng-init="count=0">
        Increment (when mouse enters)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngMouseleave
 *
 * @description
 * Specify custom behavior on mouseleave event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-mouseleave="count = count + 1" ng-init="count=0">
        Increment (when mouse leaves)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngMousemove
 *
 * @description
 * Specify custom behavior on mousemove event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <button ng-mousemove="count = count + 1" ng-init="count=0">
        Increment (when mouse moves)
      </button>
      count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngKeydown
 *
 * @description
 * Specify custom behavior on keydown event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
 *
 * @example
   <example>
     <file name="index.html">
      <input ng-keydown="count = count + 1" ng-init="count=0">
      key down count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngKeyup
 *
 * @description
 * Specify custom behavior on keyup event.
 *
 * @element ANY
 * @priority 0
 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
 *
 * @example
   <example>
     <file name="index.html">

Typing in the input box below updates the key count

       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}

Typing in the input box below updates the keycode

       <input ng-keyup="event=$event">

event keyCode: {{ event.keyCode }}

event altKey: {{ event.altKey }}

     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngKeypress
 *
 * @description
 * Specify custom behavior on keypress event.
 *
 * @element ANY
 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
 * and can be interrogated for keyCode, altKey, etc.)
 *
 * @example
   <example>
     <file name="index.html">
      <input ng-keypress="count = count + 1" ng-init="count=0">
      key press count: {{count}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngSubmit
 *
 * @description
 * Enables binding angular expressions to onsubmit events.
 *
 * Additionally it prevents the default action (which for form means sending the request to the
 * server and reloading the current page), but only if the form does not contain `action`,
 * `data-action`, or `x-action` attributes.
 *
 * @element form
 * @priority 0
 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
 * ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example module="submitExample">
     <file name="index.html">

      <form ng-submit="submit()" ng-controller="ExampleController">
        Enter text and hit enter:
        <input type="text" ng-model="text" name="text" />
        <input type="submit" id="submit" value="Submit" />

list={{list}}

      </form>
     </file>
     <file name="protractor.js" type="protractor">
       it('should check ng-submit', function() {
         expect(element(by.binding('list')).getText()).toBe('list=[]');
         element(by.css('#submit')).click();
         expect(element(by.binding('list')).getText()).toContain('hello');
         expect(element(by.model('text')).getAttribute('value')).toBe('');
       });
       it('should ignore empty strings', function() {
         expect(element(by.binding('list')).getText()).toBe('list=[]');
         element(by.css('#submit')).click();
         element(by.css('#submit')).click();
         expect(element(by.binding('list')).getText()).toContain('hello');
        });
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngFocus
 *
 * @description
 * Specify custom behavior on focus event.
 *
 * @element window, input, select, textarea, a
 * @priority 0
 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
 * See {@link ng.directive:ngClick ngClick}
 */

/**
 * @ngdoc directive
 * @name ngBlur
 *
 * @description
 * Specify custom behavior on blur event.
 *
 * @element window, input, select, textarea, a
 * @priority 0
 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
 * See {@link ng.directive:ngClick ngClick}
 */

/**
 * @ngdoc directive
 * @name ngCopy
 *
 * @description
 * Specify custom behavior on copy event.
 *
 * @element window, input, select, textarea, a
 * @priority 0
 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
      copied: {{copied}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngCut
 *
 * @description
 * Specify custom behavior on cut event.
 *
 * @element window, input, select, textarea, a
 * @priority 0
 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
      cut: {{cut}}
     </file>
   </example>
 */

/**
 * @ngdoc directive
 * @name ngPaste
 *
 * @description
 * Specify custom behavior on paste event.
 *
 * @element window, input, select, textarea, a
 * @priority 0
 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
 *
 * @example
   <example>
     <file name="index.html">
      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
      pasted: {{paste}}
     </file>
   </example>
 */
<|endoftext|>


<commit_before>
var fs = require('fs');
var path = require('path');
var xtend = require('xtend');
var mkdirp = require('mkdirp');
var through = require('through2');
var bresolve = require('browser-resolve');
var transformTools = require('browserify-transform-tools');

module.exports = mendelExtractBundles;

function mendelExtractBundles(referenceBundle, opts) {
    var browserify = referenceBundle.constructor;
    var pipeline = referenceBundle.pipeline.constructor;
    var bopts = referenceBundle._options;

    referenceBundle._transformOrder += 50;
    referenceBundle.transform(absoluteRequires);
    referenceBundle._transformOrder -= 50;

    var allFiltered = [];
    var allIncluded = [];
    var extractions = opts.extractions||{};

    delete extractions._;
    extractions = Object.keys(extractions)
    .filter(Boolean).map(function(extraction) {
        var entries = opts.extractions[extraction];
        if (entries._) entries = entries._;
        allFiltered = allFiltered.concat(entries);
        return {
            id: extraction,
            entries: entries,
        }
    });

    bopts.filter = function(id) {
        for (var i = 0; i < allFiltered.length; i++) {
            if (id.indexOf(allFiltered[i]) === id.length - allFiltered[i].length) {
                return false;
            }
        }
        allIncluded.push(id);
        return true;
    };

    extractions.forEach(function(extraction) {
        var xbopts = xtend(bopts);

        xbopts.entries = extraction.entries;
        delete xbopts.require;

        xbopts.filter = function(id) {
            if (-1 !== allIncluded.indexOf(id)) {
                return false;
            }
            return true;
        };

        var xb = browserify(xbopts);

        xb._transformOrder += 50;
        xb.transform(absoluteRequires);
        xb._transformOrder -= 50;

        Object.keys(browserify.prototype)
            .filter(onlyPublicMethods)
            .filter(notBundle)
            .forEach(function(method) {
                proxyMethod(method, referenceBundle, xb)
            });

        Object.keys(pipeline.prototype)
            .filter(onlyPublicMethods)
            .forEach(function(method) {
                proxyMethod(method, referenceBundle.pipeline, xb.pipeline)
            });

        referenceBundle.on('bundle', function() {
            if (referenceBundle.argv.list && !referenceBundle._logbase) {
                console.log('original bundle');
                referenceBundle._logbase = true;
            }
        });

        referenceBundle.pipeline.get('wrap').once('end', function() {
            if (referenceBundle.argv.list) {
                return listBundle(referenceBundle, xb, extraction);
            }

            if (false && referenceBundle.argv.o || referenceBundle.argv.outfile) {
                writeExtraction(referenceBundle, opts, extraction, xb);
            } else {
                return xb.bundle().pipe(process.stdout);
            }
        });
    });
}

function writeExtraction(referenceBundle, opts, extraction, xb) {
    var extractionOut = extractionDest(referenceBundle, opts, extraction);

    mkdirp.sync(path.dirname(extractionOut));

    return xb.bundle().pipe(fs.createWriteStream(extractionOut));
}

function extractionDest(referenceBundle, opts, extraction) {
    var baseOut = path.parse(referenceBundle.argv.o || referenceBundle.argv.outfile);
    var extractionOut = path.join(
        baseOut.dir,
        extraction.id+'.'+baseOut.base
    );

    if (opts.outdir) {
        extractionOut = path.join(
            opts.outdir,
            extraction.id,
            baseOut.base
        );
    }

    return extractionOut;
}

function listBundle(referenceBundle, xb, extraction) {
    xb._log = [];
    xb.pipeline.get('deps').push(through.obj(
        function (row, enc, next) {
            xb._log.push(row.file || row.id);
            next();
        }
    ));
    xb.bundle(function() {
        console.log(extraction.id);
        xb._log.forEach(function(l){console.log(l)});
    });
}

var absoluteRequires = transformTools.makeRequireTransform(
    "absoluteRequires",
    {evaluateArguments: true},
    function(args, opts, transformDone) {
        var parent = opts.file;
        var module = args[0];

        if (isExternalModule(module)) {
            return transformDone();
        }
        bresolve(module, {filename: parent}, function(err, finalPath) {
            finalPath = path.resolve(finalPath);
            transformDone(null, "require('"+finalPath+"')");
        });
    }
);

function proxyMethod(method, source, destination) {
    var oldMethod = source[method];
    source[method] = function() {
        var args = Array.prototype.slice.call(arguments);
        destination[method].apply(destination, args);
        return oldMethod.apply(source, args);
    }
}

function notBundle(method) {
    return method !== 'bundle';
}

function onlyPublicMethods(method) {
    return method.indexOf('_') !== 0;
}

function isExternalModule (file) {
    var regexp = process.platform === 'win32' ?
        /^(\.|\w:)/ :
        /^[\/.]/;
    return !regexp.test(file);
}
<commit_msg>Save files correctly<commit_after>
var fs = require('fs');
var path = require('path');
var xtend = require('xtend');
var through = require('through2');
var bresolve = require('browser-resolve');
var transformTools = require('browserify-transform-tools');

module.exports = mendelExtractBundles;

function mendelExtractBundles(referenceBundle, opts) {
    var browserify = referenceBundle.constructor;
    var pipeline = referenceBundle.pipeline.constructor;
    var bopts = referenceBundle._options;

    referenceBundle._transformOrder += 50;
    referenceBundle.transform(absoluteRequires);
    referenceBundle._transformOrder -= 50;

    var allFiltered = [];
    var allIncluded = [];
    var extractions = opts.extractions||{};

    delete extractions._;
    extractions = Object.keys(extractions)
    .filter(Boolean).map(function(extraction) {
        var entries = opts.extractions[extraction];
        if (entries._) entries = entries._;
        allFiltered = allFiltered.concat(entries);
        return {
            id: extraction,
            entries: entries,
        }
    });

    bopts.filter = function(id) {
        for (var i = 0; i < allFiltered.length; i++) {
            if (id.indexOf(allFiltered[i]) === id.length - allFiltered[i].length) {
                return false;
            }
        }
        allIncluded.push(id);
        return true;
    };

    extractions.forEach(function(extraction) {
        var xbopts = xtend(bopts);

        xbopts.entries = extraction.entries;
        delete xbopts.require;

        xbopts.filter = function(id) {
            if (-1 !== allIncluded.indexOf(id)) {
                return false;
            }
            return true;
        };

        var xb = browserify(xbopts);

        xb._transformOrder += 50;
        xb.transform(absoluteRequires);
        xb._transformOrder -= 50;

        Object.keys(browserify.prototype)
            .filter(onlyPublicMethods)
            .filter(notBundle)
            .forEach(function(method) {
                proxyMethod(method, referenceBundle, xb)
            });

        Object.keys(pipeline.prototype)
            .filter(onlyPublicMethods)
            .forEach(function(method) {
                proxyMethod(method, referenceBundle.pipeline, xb.pipeline)
            });

        referenceBundle.on('bundle', function() {
            if (referenceBundle.argv.list && !referenceBundle._logbase) {
                console.log('original bundle');
                referenceBundle._logbase = true;
            }
        });

        referenceBundle.pipeline.get('wrap').once('end', function() {
            if (referenceBundle.argv.list) {
                return listBundle(referenceBundle, xb, extraction);
            }

            if (false && referenceBundle.argv.o || referenceBundle.argv.outfile) {
                writeExtraction(referenceBundle, opts, extraction, xb);
            } else {
                return xb.bundle().pipe(process.stdout);
            }
        });
    });
}

function writeExtraction(referenceBundle, opts, extraction, xb) {
    var extractionOut = extractionDest(referenceBundle, opts, extraction);
    return xb.bundle().pipe(fs.createWriteStream(extractionOut));
}

function extractionDest(referenceBundle, opts, extraction) {
    var baseOut = path.parse(referenceBundle.argv.o || referenceBundle.argv.outfile);
    return path.join(
        baseOut.dir,
        extraction.id+'.'+baseOut.ext
    );
}

function listBundle(referenceBundle, xb, extraction) {
    xb._log = [];
    xb.pipeline.get('deps').push(through.obj(
        function (row, enc, next) {
            xb._log.push(row.file || row.id);
            next();
        }
    ));
    xb.bundle(function() {
        console.log(extraction.id);
        xb._log.forEach(function(l){console.log(l)});
    });
}

var absoluteRequires = transformTools.makeRequireTransform(
    "absoluteRequires",
    {evaluateArguments: true},
    function(args, opts, transformDone) {
        var parent = opts.file;
        var module = args[0];

        if (isExternalModule(module)) {
            return transformDone();
        }
        bresolve(module, {filename: parent}, function(err, finalPath) {
            finalPath = path.resolve(finalPath);
            transformDone(null, "require('"+finalPath+"')");
        });
    }
);

function proxyMethod(method, source, destination) {
    var oldMethod = source[method];
    source[method] = function() {
        var args = Array.prototype.slice.call(arguments);
        destination[method].apply(destination, args);
        return oldMethod.apply(source, args);
    }
}

function notBundle(method) {
    return method !== 'bundle';
}

function onlyPublicMethods(method) {
    return method.indexOf('_') !== 0;
}

function isExternalModule (file) {
    var regexp = process.platform === 'win32' ?
        /^(\.|\w:)/ :
        /^[\/.]/;
    return !regexp.test(file);
}
<|endoftext|>


<commit_before>var _ = require('lodash');
var rmLoc = require('./rmLoc');
var parser = require('./');
var normalizeAST = require('./normalizeASTForTestCompare');

var ruleExample = function(src){
  return function(){
    var ast = parser('ruleset rs{' + src + '}');
    return [src, ast[0].rules[0]];
  };
};

var select_whenExample = function(src){
  return function(){
    var ast = parser('ruleset rs{rule r0{' + src + '}}');
    return [src, ast[0].rules[0].select_when];
  };
};

var examples = {
  '### Ruleset': [
    'ruleset NAME {\n}',
      [
        'ruleset hello {',
        '  meta {',
        '    name "Hello World"',
        '    description <<',
        'Hello parser!',
        '>>',
        '  }',
        '  global {',
        '    a = 1',
        '  }',
        '}'
      ].join('\n')
  ],
  '### Rule': [
    ruleExample('rule NAME {\n}'),
    ruleExample('rule NAME is inactive {\n}'),
    ruleExample([
      'rule hello {',
      '  pre {',
      '    a = 1',
      '  }',
      '',
      '  if COND then',
      '    choose',
      '      one =>',
      '         action(1)',
      '      two =>',
      '         action(2)',
      '',
      '  fired {',
      '    FIRED',
      '  }',
      '}'
    ].join('\n'))
  ],
  '### EventExpression': [
    select_whenExample('select when A B'),
    select_whenExample('select when A B attr re#^(.*)$# setting(val)'),
    select_whenExample('select when A A or B B'),
    select_whenExample('select when any 2 (A A, B B, C C)')
  ],
  '### KRL Expression language': [],
  '#### Literals': [
    '"hello world"',
    '-12.3',
    'thing',
    'true',
    're#^My name is (.*)#i',
    '[A, B, C]',
    '{"one": A}',
    '<<\n  hello #{name}!\n  >>'
  ],
  '#### Assignment': [
    'A = B'
  ],
  '#### Infix Operators': [
    'A && B',
    'A + B + C',
    'A + B * C',
    'A < B',
    'A cmp B',
    'A <=> B'
  ],
  '#### Conditionals': [
    'A => B | C',
    'A => B |\nC => D |\n     E'
  ],
  '#### Functions': [
    'function(A){\n  B\n}',
    'A(B,C)'
  ]
};

var ind = function(n){
  var s = '';
  var i;
  for(i = 0; i < n; i++){
    s += ' ';
  }
  return s;
};

var printAST = function(ast, i, indent_size){
  indent_size = indent_size || 2;
  if(_.isArray(ast)){
    var arr_strs = _.map(ast, function(ast){
      return printAST(ast, i + indent_size, indent_size);
    });
    var flat_array = '[ ' + arr_strs.join(' , ') + ' ]';
    if((flat_array.indexOf('\n') < 0) && (flat_array.length < 20)){
      return flat_array;
    }
    return '[\n'
      + _.map(arr_strs, function(str){
        return ind(i + indent_size) + str;
      }).join(',\n')
      + '\n' + ind(i) + ']';
  }
  if(_.isPlainObject(ast)){
    if(ast.type === 'Identifier' && /^[A-Z]+$/.test(ast.value)){
      return ast.value;
    }
    if(i !== 0 && _.includes(['String', 'Number', 'Boolean', 'Identifier', 'RegExp'], ast.type)){
      var v = ast.type === 'RegExp'
        ? ast.value
        : JSON.stringify(ast.value);
      return '{value: ' + v + ', type:"' + ast.type + '"}';
    }
    return '{\n'
      + _.map(ast, function(value, key){
        var k = JSON.stringify(key);
        var v = printAST(value, i + indent_size, indent_size);
        if(key === 'value' && ast.type === 'RegExp'){
          v = ast.value;
        }
        return ind(i + indent_size) + k + ': ' + v;
      }).join(',\n')
      + '\n' + ind(i) + '}';
  }
  return JSON.stringify(ast);
};

_.each(examples, function(srcs, head){
  console.log();
  console.log(head);
  if(_.isEmpty(srcs)){
    return;
  }
  console.log();
  console.log('```js\n' + _.map(srcs, function(src){
    var ast;
    if(_.isFunction(src)){
      var src_ast = src();
      src = src_ast[0];
      ast = src_ast[1];
    }else{
      ast = parser(src);
    }
    ast = normalizeAST(rmLoc(ast));
    ast = _.isArray(ast) && _.size(ast) === 1 ? _.head(ast) : ast;

    return src + '\n' + printAST(ast, 0, 2);
  }).join('\n\n') + '\n```');
})
<commit_msg>adding select to rule hello example<commit_after>var _ = require('lodash');
var rmLoc = require('./rmLoc');
var parser = require('./');
var normalizeAST = require('./normalizeASTForTestCompare');

var ruleExample = function(src){
  return function(){
    var ast = parser('ruleset rs{' + src + '}');
    return [src, ast[0].rules[0]];
  };
};

var select_whenExample = function(src){
  return function(){
    var ast = parser('ruleset rs{rule r0{' + src + '}}');
    return [src, ast[0].rules[0].select_when];
  };
};

var examples = {
  '### Ruleset': [
    'ruleset NAME {\n}',
      [
        'ruleset hello {',
        '  meta {',
        '    name "Hello World"',
        '    description <<',
        'Hello parser!',
        '>>',
        '  }',
        '  global {',
        '    a = 1',
        '  }',
        '}'
      ].join('\n')
  ],
  '### Rule': [
    ruleExample('rule NAME {\n}'),
    ruleExample('rule NAME is inactive {\n}'),
    ruleExample([
      'rule hello {',
      '  select when DOMAIN TYPE',
      '',
      '  pre {',
      '    a = 1',
      '  }',
      '',
      '  if COND then',
      '    choose',
      '      one =>',
      '         action(1)',
      '      two =>',
      '         action(2)',
      '',
      '  fired {',
      '    FIRED',
      '  }',
      '}'
    ].join('\n'))
  ],
  '### EventExpression': [
    select_whenExample('select when A B'),
    select_whenExample('select when A B attr re#^(.*)$# setting(val)'),
    select_whenExample('select when A A or B B'),
    select_whenExample('select when any 2 (A A, B B, C C)')
  ],
  '### KRL Expression language': [],
  '#### Literals': [
    '"hello world"',
    '-12.3',
    'thing',
    'true',
    're#^My name is (.*)#i',
    '[A, B, C]',
    '{"one": A}',
    '<<\n  hello #{name}!\n  >>'
  ],
  '#### Assignment': [
    'A = B'
  ],
  '#### Infix Operators': [
    'A && B',
    'A + B + C',
    'A + B * C',
    'A < B',
    'A cmp B',
    'A <=> B'
  ],
  '#### Conditionals': [
    'A => B | C',
    'A => B |\nC => D |\n     E'
  ],
  '#### Functions': [
    'function(A){\n  B\n}',
    'A(B,C)'
  ]
};

var ind = function(n){
  var s = '';
  var i;
  for(i = 0; i < n; i++){
    s += ' ';
  }
  return s;
};

var printAST = function(ast, i, indent_size){
  indent_size = indent_size || 2;
  if(_.isArray(ast)){
    var arr_strs = _.map(ast, function(ast){
      return printAST(ast, i + indent_size, indent_size);
    });
    var flat_array = '[ ' + arr_strs.join(' , ') + ' ]';
    if((flat_array.indexOf('\n') < 0) && (flat_array.length < 20)){
      return flat_array;
    }
    return '[\n'
      + _.map(arr_strs, function(str){
        return ind(i + indent_size) + str;
      }).join(',\n')
      + '\n' + ind(i) + ']';
  }
  if(_.isPlainObject(ast)){
    if(ast.type === 'Identifier' && /^[A-Z]+$/.test(ast.value)){
      return ast.value;
    }
    if(i !== 0 && _.includes(['String', 'Number', 'Boolean', 'Identifier', 'RegExp'], ast.type)){
      var v = ast.type === 'RegExp'
        ? ast.value
        : JSON.stringify(ast.value);
      return '{value: ' + v + ', type:"' + ast.type + '"}';
    }
    return '{\n'
      + _.map(ast, function(value, key){
        var k = JSON.stringify(key);
        var v = printAST(value, i + indent_size, indent_size);
        if(key === 'value' && ast.type === 'RegExp'){
          v = ast.value;
        }
        return ind(i + indent_size) + k + ': ' + v;
      }).join(',\n')
      + '\n' + ind(i) + '}';
  }
  return JSON.stringify(ast);
};

_.each(examples, function(srcs, head){
  console.log();
  console.log(head);
  if(_.isEmpty(srcs)){
    return;
  }
  console.log();
  console.log('```js\n' + _.map(srcs, function(src){
    var ast;
    if(_.isFunction(src)){
      var src_ast = src();
      src = src_ast[0];
      ast = src_ast[1];
    }else{
      ast = parser(src);
    }
    ast = normalizeAST(rmLoc(ast));
    ast = _.isArray(ast) && _.size(ast) === 1 ? _.head(ast) : ast;

    return src + '\n' + printAST(ast, 0, 2);
  }).join('\n\n') + '\n```');
})
<|endoftext|>


<commit_before>/*
Firebase storage module for bots.

Supports storage of data on a team-by-team, user-by-user, and chnnel-by-channel basis.

save can be used to store arbitrary object.
These objects must include an id by which they can be looked up.
It is recommended to use the team/user/channel id for this purpose.
Example usage of save:
controller.storage.teams.save({id: message.team, foo:"bar"}, function(err){
  if (err)
    console.log(err)
});

get looks up an object by id.
Example usage of get:
controller.storage.teams.get(message.team, function(err, team_data){
  if (err)
    console.log(err)
  else
    console.log(team_data)
});
*/

var Firebase = require("firebase");

module.exports = function(config) {

  if (!config && !config.firebase_uri) throw new Error('Need to provide firebase address. This should look something like "https://botkit-example.firebaseio.com/"');

  var rootRef = new Firebase(config.firebase_uri);
  var teamsRef = rootRef.child("teams");
  var usersRef = rootRef.child("users");
  var channelsRef = rootRef.child("channels");

  var get = function(firebaseRef){
    return function(id, cb){
      firebaseRef.child(id).once('value',
          function(records){
            cb(undefined, records.val());
          },
          function(err){
            cb(err, undefined);
          }
        );
    };
  };

  var save = function(firebaseRef){
    return function(data,cb) {
      var firebase_update = {};
      firebase_update[data.id] = data;
      firebaseRef.update(firebase_update, cb);
    };
  };

  var all = function(firebaseRef){
    return function(cb) {
      firebaseRef.once('value',
        function(records){
          var list = [];
          for (key of Object.keys(records.val())) {
            list.push (records.val()[key]);
          }
          cb(undefined, list);
        },
        function(err){
          cb(err, undefined);
        }
      );
    };
  };

  var storage = {
    teams: {
      get: get(teamsRef),
      save: save(teamsRef),
      all: all(teamsRef)
    },
    channels: {
      get: get(channelsRef),
      save: save(channelsRef),
      all: all(channelsRef)
    },
    users: {
      get: get(usersRef),
      save: save(usersRef),
      all: all(usersRef)
    }
  };

  return storage;

};
<commit_msg>Adds comment about authentication for firebase<commit_after>/*
Firebase storage module for bots.

Note that this storage module does not specify how to authenticate to Firebase.
There are many methods of user authentication for Firebase.
Please read: https://www.firebase.com/docs/web/guide/user-auth.html

Supports storage of data on a team-by-team, user-by-user, and chnnel-by-channel basis.

save can be used to store arbitrary object.
These objects must include an id by which they can be looked up.
It is recommended to use the team/user/channel id for this purpose.
Example usage of save:
controller.storage.teams.save({id: message.team, foo:"bar"}, function(err){
  if (err)
    console.log(err)`
});

get looks up an object by id.
Example usage of get:
controller.storage.teams.get(message.team, function(err, team_data){
  if (err)
    console.log(err)
  else
    console.log(team_data)
});
*/

var Firebase = require("firebase");

module.exports = function(config) {

  if (!config && !config.firebase_uri) throw new Error('Need to provide firebase address. This should look something like "https://botkit-example.firebaseio.com/"');

  var rootRef = new Firebase(config.firebase_uri);
  var teamsRef = rootRef.child("teams");
  var usersRef = rootRef.child("users");
  var channelsRef = rootRef.child("channels");

  var get = function(firebaseRef){
    return function(id, cb){
      firebaseRef.child(id).once('value',
          function(records){
            cb(undefined, records.val());
          },
          function(err){
            cb(err, undefined);
          }
        );
    };
  };

  var save = function(firebaseRef){
    return function(data,cb) {
      var firebase_update = {};
      firebase_update[data.id] = data;
      firebaseRef.update(firebase_update, cb);
    };
  };

  var all = function(firebaseRef){
    return function(cb) {
      firebaseRef.once('value',
        function(records){
          var list = [];
          for (key of Object.keys(records.val())) {
            list.push (records.val()[key]);
          }
          cb(undefined, list);
        },
        function(err){
          cb(err, undefined);
        }
      );
    };
  };

  var storage = {
    teams: {
      get: get(teamsRef),
      save: save(teamsRef),
      all: all(teamsRef)
    },
    channels: {
      get: get(channelsRef),
      save: save(channelsRef),
      all: all(channelsRef)
    },
    users: {
      get: get(usersRef),
      save: save(usersRef),
      all: all(usersRef)
    }
  };

  return storage;

};
<|endoftext|>


<commit_before>import { getDefaultMutations } from '../lib/modules/default_mutations';

import expect from 'expect';

describe('vulcan:core/default_mutations', function() {

    it('returns mutations', function(){
        const mutations = getDefaultMutations({
            typeName:'Foo',
            collectionName:'Foos',
            options: {}
        });
        expect(mutations.create).toBeDefined();
        expect(mutations.update).toBeDefined();
        expect(mutations.delete).toBeDefined();
    });
    it('preserves openCRUD backward compatibility', function(){
        const mutations = getDefaultMutations({
            typeName:'Foo',
            collectionName:'Foos',
            options: {}
        });
        expect(mutations.new).toBeDefined();
        expect(mutations.edit).toBeDefined();
        expect(mutations.remove).toBeDefined();
    });

});<commit_msg>add empty selector test to default mutations<commit_after>import { getDefaultMutations } from '../lib/modules/default_mutations';
import SimpleSchema from 'simpl-schema';

import expect from 'expect';
const test = it;

describe('vulcan:core/default_mutations', function () {

    it('returns mutations', function () {
        const mutations = getDefaultMutations({
            typeName: 'Foo',
            collectionName: 'Foos',
            options: {}
        });
        expect(mutations.create).toBeDefined();
        expect(mutations.update).toBeDefined();
        expect(mutations.delete).toBeDefined();
    });
    it('preserves openCRUD backward compatibility', function () {
        const mutations = getDefaultMutations({
            typeName: 'Foo',
            collectionName: 'Foos',
            options: {}
        });
        expect(mutations.new).toBeDefined();
        expect(mutations.edit).toBeDefined();
        expect(mutations.remove).toBeDefined();
    });

    describe('delete mutation', () => {
        const foo = { _id: 'foo' };
        const context = {
            Users: {
                options: {
                    collectionName: 'Users',
                    typeName: 'User'
                },
                simpleSchema: () => new SimpleSchema({ _id: { type: String, canRead: ['admins'] } })
            },
            Foos: {
                findOne: () => foo,
                remove: () => 1,
                options: {
                    collectionName: 'Foos',
                    typeName: 'Foo'
                },
                simpleSchema: () => new SimpleSchema({ _id: { type: String, canRead: ['admins'] } })
            }
            ,
            currentUser: {
                isAdmin: true,
                groups: ['admins']
            }
        };
        const mutations = getDefaultMutations({
            typeName: 'Foo',
            collectionName: 'Foos',
            options: {}
        });
        test('refuse deletion if selector is empty', async () => {
            const { delete: deleteMutation } = mutations;
            const emptySelector = {};
            const nullSelector = { documentId: null };
            const validIdSelector = { _id: 'foobar' };
            const validDocIdSelector = { documentId: 'foobar' };
            const validSlugSelector = { slug: 'foobar' };

            // const { mutation } = deleteMutation; // won't work because "this" must equal deleteMutation to access "check"
            expect(deleteMutation.mutation(null, { selector: emptySelector }, context)).rejects;
            expect(deleteMutation.mutation(null, { selector: nullSelector }, context)).rejects;

            await expect(deleteMutation.mutation(null, { selector: validIdSelector }, context)).resolves.toEqual({ data: foo });
            await expect(deleteMutation.mutation(null, { selector: validDocIdSelector }, context)).resolves.toEqual({ data: foo });
            await expect(deleteMutation.mutation(null, { selector: validSlugSelector }, context)).resolves.toEqual({ data: foo });
        });

    });

});<|endoftext|>


<commit_before>'use strict'

/**
 * Dependencies
 * @ignore
 */
const bignum = require('asn1.js').bignum

/**
 * Module Dependencies
 * @ignore
 */
const KeyType = require('./KeyType')
const InvalidOperationError = require('../InvalidOperationError')
const Converter = require('../Converter')
const asn = require('../asn1')

/**
 * ECDSA
 * @class ECDSA
 *
 * @extends {KeyType}
 *
 * @description
 * ECDSA conversion implementation
 */
class ECDSA extends KeyType {

  static get kty () {
    return 'EC'
  }

  static get oid () {
    return [ 1, 2, 840, 10045, 2, 1 ]
  }

  static get parameters () {
    return Buffer.from('06052b8104000a', 'hex')
  }

  static fromPrivatePKCS1 (base64pem) {
    let ECPrivateKey = asn.normalize('ECPrivateKey')

    let buffer = Buffer.from(base64pem, 'base64')
    let data = ECPrivateKey.decode(buffer, 'der')

    let { privateKey: d, publicKey: { data: publicKey } } = data
    let { x, y } = ECDSA.getPoint(publicKey)

    return new ECDSA({ d, x, y })
  }

  static fromPrivatePKCS8 (base64pem) {
    let PrivateKeyInfo = asn.normalize('PrivateKeyInfo')
    let ECPrivateKey = asn.normalize('ECPrivateKey')

    let buffer = Buffer.from(base64pem, 'base64')
    let info = PrivateKeyInfo.decode(buffer, 'der')
    let data = ECPrivateKey.decode(info.privateKey, 'der')

    let { privateKey: d, publicKey: { data: publicKey } } = data
    let { x, y } = ECDSA.getPoint(publicKey)

    return new ECDSA({ d, x, y })
  }

  static fromPublicPKCS8 (base64pem) {
    let PublicKeyInfo = asn.normalize('PublicKeyInfo')

    let buffer = Buffer.from(base64pem, 'base64')
    let info = PublicKeyInfo.decode(buffer, 'der')
    let data = ECDSA.getPoint(info.publicKey.data)

    return new ECDSA(data)
  }

  static fromJwk (jwk) {
    let { d, x, y, crv } = jwk

    if (!crv) {
      throw new Error('crv is required')
    }

    return new ECDSA({
      d: Converter.convert(d, 'base64url', 'raw'),
      x: Converter.convert(x, 'base64url', 'raw'),
      y: Converter.convert(y, 'base64url', 'raw'),
    }, { crv })
  }

  get isPrivate () {
    return !!this.d
  }

  toPrivatePKCS1 () {
    if (!this.isPrivate) {
      throw new InvalidOperationError('Cannot export a private key from a public key')
    }

    let ECPrivateKey = asn.normalize('ECPrivateKey')

    let { d, x, y } = this
    let publicKey = ECDSA.makePoint(x, y)
    let base64pem = ECPrivateKey.encode({
      version: 1,
      privateKey: d,
      parameters: {
        type: 'namedCurve',
        value: [ 1, 3, 132, 0, 10 ]
      },
      publicKey: {
        unused: 0,
        data: publicKey
      }
    }, 'der').toString('base64')

    return ECDSA.formatPem(base64pem, 'EC PRIVATE')
  }

  toPrivatePKCS8 () {
    if (!this.isPrivate) {
      throw new InvalidOperationError('Cannot export a private key from a public key')
    }

    let PrivateKeyInfo = asn.normalize('PrivateKeyInfo')
    let ECPrivateKey = asn.normalize('ECPrivateKey')

    let { d, x, y, constructor: { oid: algorithm, parameters } } = this
    let publicKey = ECDSA.makePoint(x, y)
    let privateKey = ECPrivateKey.encode({
      version: 1,
      privateKey: d,
      publicKey: {
        unused: 0,
        data: publicKey
      }
    }, 'der')

    let base64pem = PrivateKeyInfo.encode({
      version: 'two-prime',
      algorithm: {
        algorithm,
        parameters
      },
      privateKey
    }, 'der').toString('base64')

    return ECDSA.formatPem(base64pem, 'PRIVATE')
  }

  toPublicPKCS8 () {
    let PublicKeyInfo = asn.normalize('PublicKeyInfo')

    let data = ECDSA.makePoint(this.x, this.y)
    let base64pem = PublicKeyInfo.encode({
      algorithm: {
        algorithm: ECDSA.oid,
        parameters: ECDSA.parameters
      },
      publicKey: {
        unused: 0,
        data
      }
    }, 'der').toString('base64')

    return ECDSA.formatPem(base64pem, 'PUBLIC')
  }

  toPrivateJwk () {
    if (!this.isPrivate) {
      throw new InvalidOperationError('Cannot export a private key from a public key')
    }

    let { d, x, y } = this

    return {
      kty: 'EC',
      crv: 'K-256',
      d: Converter.convert(d, 'raw', 'base64url'),
      x: Converter.convert(x, 'raw', 'base64url'),
      y: Converter.convert(y, 'raw', 'base64url'),
    }
  }

  toPublicJwk () {
    let { x, y } = this

    return {
      kty: 'EC',
      crv: 'K-256',
      x: Converter.convert(x, 'raw', 'base64url'),
      y: Converter.convert(y, 'raw', 'base64url'),
    }
  }

  /**
   * Helper Methods
   * @ignore
   */

  static getPoint (point) {
    let hexstr = Converter.convert(point, 'raw', 'hex')
    let x = hexstr.slice(2, ((hexstr.length - 2) / 2) + 2)
    let y = hexstr.slice(((hexstr.length - 2) / 2) + 2)
    return Converter.convertObject({ x, y }, 'hex', 'raw')
  }

  static makePoint (x, y) {
    let startBuffer = Buffer.from('04', 'hex')
    return Buffer.concat([
      startBuffer,
      x,
      y
    ], startBuffer.length + x.length + y.length)
  }
}

/**
 * Export
 * @ignore
 */
module.exports = ECDSA
<commit_msg>feat(ECDSA): implement fromBlk<commit_after>'use strict'

/**
 * Dependencies
 * @ignore
 */
const bignum = require('asn1.js').bignum

/**
 * Module Dependencies
 * @ignore
 */
const KeyType = require('./KeyType')
const InvalidOperationError = require('../InvalidOperationError')
const Converter = require('../Converter')
const asn = require('../asn1')

/**
 * ECDSA
 * @class ECDSA
 *
 * @extends {KeyType}
 *
 * @description
 * ECDSA conversion implementation
 */
class ECDSA extends KeyType {

  static get kty () {
    return 'EC'
  }

  static get oid () {
    return [ 1, 2, 840, 10045, 2, 1 ]
  }

  static get parameters () {
    return Buffer.from('06052b8104000a', 'hex')
  }

  static fromPrivatePKCS1 (base64pem) {
    let ECPrivateKey = asn.normalize('ECPrivateKey')

    let buffer = Buffer.from(base64pem, 'base64')
    let data = ECPrivateKey.decode(buffer, 'der')

    let { privateKey: d, publicKey: { data: publicKey } } = data
    let { x, y } = ECDSA.getPoint(publicKey)

    return new ECDSA({ d, x, y })
  }

  static fromPrivatePKCS8 (base64pem) {
    let PrivateKeyInfo = asn.normalize('PrivateKeyInfo')
    let ECPrivateKey = asn.normalize('ECPrivateKey')

    let buffer = Buffer.from(base64pem, 'base64')
    let info = PrivateKeyInfo.decode(buffer, 'der')
    let data = ECPrivateKey.decode(info.privateKey, 'der')

    let { privateKey: d, publicKey: { data: publicKey } } = data
    let { x, y } = ECDSA.getPoint(publicKey)

    return new ECDSA({ d, x, y })
  }

  static fromPublicPKCS8 (base64pem) {
    let PublicKeyInfo = asn.normalize('PublicKeyInfo')

    let buffer = Buffer.from(base64pem, 'base64')
    let info = PublicKeyInfo.decode(buffer, 'der')
    let data = ECDSA.getPoint(info.publicKey.data)

    return new ECDSA(data)
  }

  static fromJwk (jwk) {
    let { d, x, y, crv } = jwk

    if (!crv) {
      throw new Error('crv is required')
    }

    return new ECDSA({
      d: Converter.convert(d, 'base64url', 'raw'),
      x: Converter.convert(x, 'base64url', 'raw'),
      y: Converter.convert(y, 'base64url', 'raw'),
    }, { crv })
  }

  static fromBlk (blk) {
    let key

    if (blk.startsWith('04')) {
      key = ECDSA.makePoint(Buffer.from(blk, 'hex'))
    } else {
      key = { d: Converter.convert(blk, 'bn', 'raw') }
    }

    return new ECDSA(key)
  }

  get isPrivate () {
    return !!this.d
  }

  toPrivatePKCS1 () {
    if (!this.isPrivate) {
      throw new InvalidOperationError('Cannot export a private key from a public key')
    }

    let ECPrivateKey = asn.normalize('ECPrivateKey')

    let { d, x, y } = this
    let publicKey = ECDSA.makePoint(x, y)
    let base64pem = ECPrivateKey.encode({
      version: 1,
      privateKey: d,
      parameters: {
        type: 'namedCurve',
        value: [ 1, 3, 132, 0, 10 ]
      },
      publicKey: {
        unused: 0,
        data: publicKey
      }
    }, 'der').toString('base64')

    return ECDSA.formatPem(base64pem, 'EC PRIVATE')
  }

  toPrivatePKCS8 () {
    if (!this.isPrivate) {
      throw new InvalidOperationError('Cannot export a private key from a public key')
    }

    let PrivateKeyInfo = asn.normalize('PrivateKeyInfo')
    let ECPrivateKey = asn.normalize('ECPrivateKey')

    let { d, x, y, constructor: { oid: algorithm, parameters } } = this
    let publicKey = ECDSA.makePoint(x, y)
    let privateKey = ECPrivateKey.encode({
      version: 1,
      privateKey: d,
      publicKey: {
        unused: 0,
        data: publicKey
      }
    }, 'der')

    let base64pem = PrivateKeyInfo.encode({
      version: 'two-prime',
      algorithm: {
        algorithm,
        parameters
      },
      privateKey
    }, 'der').toString('base64')

    return ECDSA.formatPem(base64pem, 'PRIVATE')
  }

  toPublicPKCS8 () {
    let PublicKeyInfo = asn.normalize('PublicKeyInfo')

    let data = ECDSA.makePoint(this.x, this.y)
    let base64pem = PublicKeyInfo.encode({
      algorithm: {
        algorithm: ECDSA.oid,
        parameters: ECDSA.parameters
      },
      publicKey: {
        unused: 0,
        data
      }
    }, 'der').toString('base64')

    return ECDSA.formatPem(base64pem, 'PUBLIC')
  }

  toPrivateJwk () {
    if (!this.isPrivate) {
      throw new InvalidOperationError('Cannot export a private key from a public key')
    }

    let { d, x, y } = this

    return {
      kty: 'EC',
      crv: 'K-256',
      d: Converter.convert(d, 'raw', 'base64url'),
      x: Converter.convert(x, 'raw', 'base64url'),
      y: Converter.convert(y, 'raw', 'base64url'),
    }
  }

  toPublicJwk () {
    let { x, y } = this

    return {
      kty: 'EC',
      crv: 'K-256',
      x: Converter.convert(x, 'raw', 'base64url'),
      y: Converter.convert(y, 'raw', 'base64url'),
    }
  }

  /**
   * Helper Methods
   * @ignore
   */

  static getPoint (point) {
    let hexstr = Converter.convert(point, 'raw', 'hex')
    let x = hexstr.slice(2, ((hexstr.length - 2) / 2) + 2)
    let y = hexstr.slice(((hexstr.length - 2) / 2) + 2)
    return Converter.convertObject({ x, y }, 'hex', 'raw')
  }

  static makePoint (x, y) {
    let startBuffer = Buffer.from('04', 'hex')
    return Buffer.concat([
      startBuffer,
      x,
      y
    ], startBuffer.length + x.length + y.length)
  }
}

/**
 * Export
 * @ignore
 */
module.exports = ECDSA
<|endoftext|>


<commit_before>const DocumentedItem = require('./item');
const DocumentedItemMeta = require('./item-meta');
const DocumentedConstructor = require('./constructor');
const DocumentedFunction = require('./function');
const DocumentedMember = require('./member');
const DocumentedEvent = require('./event');

class DocumentedClass extends DocumentedItem {
	constructor(docParent, data) {
		super(docParent, data);
		this.props = new Map();
		this.methods = new Map();
		this.events = new Map();
	}

	add(item) {
		if(item instanceof DocumentedConstructor) {
			if(this.classConstructor) {
				throw new Error(`Doc ${this.directData.name} already has constructor - ${this.directData.classConstructor}`);
			}
			this.classConstructor = item;
		} else if(item instanceof DocumentedFunction) {
			if(this.methods.get(item.directData.name)) {
				throw new Error(`Doc ${this.directData.name} already has method ${item.directData.name}`);
			}
			this.methods.set(item.directData.name, item);
		} else if(item instanceof DocumentedMember) {
			if(this.props.get(item.directData.name)) {
				throw new Error(`Doc ${this.directData.name} already has prop ${item.directData.name}`);
			}
			this.props.set(item.directData.name, item);
		} else if(item instanceof DocumentedEvent) {
			if(this.events.get(item.directData.name)) {
				throw new Error(`Doc ${this.directData.name} already has event ${item.directData.name}`);
			}
			this.events.set(item.directData.name, item);
		}
	}

	registerMetaInfo(data) {
		this.directData = data;
		this.directData.meta = new DocumentedItemMeta(this, data.meta);
	}

	serialize() {
		return {
			name: this.directData.name,
			description: this.directData.description,
			extends: this.directData.augments,
			access: this.directData.access,
			classConstructor: this.classConstructor ? this.classConstructor.serialize() : undefined,
			props: this.props.size > 0 ? Array.from(this.props.values()).map(p => p.serialize()) : undefined,
			methods: this.methods.size > 0 ? Array.from(this.methods.values()).map(m => m.serialize()) : undefined,
			events: this.events.size > 0 ? Array.from(this.events.values()).map(e => e.serialize()) : undefined,
			meta: this.directData.meta.serialize()
		};
	}
}

/*
{ id: 'VoiceChannel',
	longname: 'VoiceChannel',
	name: 'VoiceChannel',
	scope: 'global',
	kind: 'class',
	augments: [ 'GuildChannel' ],
	description: 'Represents a Server Voice Channel on Discord.',
	meta:
	 { lineno: 7,
		 filename: 'VoiceChannel.js',
		 path: 'src/structures' },
	order: 232 }
*/

module.exports = DocumentedClass;
<commit_msg>Rename classConstructor to "construct"<commit_after>const DocumentedItem = require('./item');
const DocumentedItemMeta = require('./item-meta');
const DocumentedConstructor = require('./constructor');
const DocumentedFunction = require('./function');
const DocumentedMember = require('./member');
const DocumentedEvent = require('./event');

class DocumentedClass extends DocumentedItem {
	constructor(docParent, data) {
		super(docParent, data);
		this.props = new Map();
		this.methods = new Map();
		this.events = new Map();
	}

	add(item) {
		if(item instanceof DocumentedConstructor) {
			if(this.construct) {
				throw new Error(`Doc ${this.directData.name} already has constructor - ${this.directData.construct}`);
			}
			this.construct = item;
		} else if(item instanceof DocumentedFunction) {
			if(this.methods.get(item.directData.name)) {
				throw new Error(`Doc ${this.directData.name} already has method ${item.directData.name}`);
			}
			this.methods.set(item.directData.name, item);
		} else if(item instanceof DocumentedMember) {
			if(this.props.get(item.directData.name)) {
				throw new Error(`Doc ${this.directData.name} already has prop ${item.directData.name}`);
			}
			this.props.set(item.directData.name, item);
		} else if(item instanceof DocumentedEvent) {
			if(this.events.get(item.directData.name)) {
				throw new Error(`Doc ${this.directData.name} already has event ${item.directData.name}`);
			}
			this.events.set(item.directData.name, item);
		}
	}

	registerMetaInfo(data) {
		this.directData = data;
		this.directData.meta = new DocumentedItemMeta(this, data.meta);
	}

	serialize() {
		return {
			name: this.directData.name,
			description: this.directData.description,
			extends: this.directData.augments,
			access: this.directData.access,
			construct: this.construct ? this.construct.serialize() : undefined,
			props: this.props.size > 0 ? Array.from(this.props.values()).map(p => p.serialize()) : undefined,
			methods: this.methods.size > 0 ? Array.from(this.methods.values()).map(m => m.serialize()) : undefined,
			events: this.events.size > 0 ? Array.from(this.events.values()).map(e => e.serialize()) : undefined,
			meta: this.directData.meta.serialize()
		};
	}
}

/*
{ id: 'VoiceChannel',
	longname: 'VoiceChannel',
	name: 'VoiceChannel',
	scope: 'global',
	kind: 'class',
	augments: [ 'GuildChannel' ],
	description: 'Represents a Server Voice Channel on Discord.',
	meta:
	 { lineno: 7,
		 filename: 'VoiceChannel.js',
		 path: 'src/structures' },
	order: 232 }
*/

module.exports = DocumentedClass;
<|endoftext|>


<commit_before>autocode.state['outputs'] = function() {
  if (autocode.project.outputs) {
    $('#outputs-content-container .table a').slice(1).remove();

    var output, output_filename;
    for (var output_i in autocode.project.outputs) {
      output = autocode.project.outputs[output_i];

      if (autocode.project.outputs instanceof Array) {
        output_name = output.filename;
      } else {
        output_name = output_i;
      }

      if (output.filename) {
        output_filename = output.filename;
      } else {
        output_filename = autocode.imports['crystal/' + output.generator.split('.')[0]].exports[output.generator.split('.')[1]].filename;
      }

      $('#outputs-content-container .table').append(
        ''
          + ''
          + ''
            + '' + output_filename + ''
            + '' + output.generator.split('.')[1] + ''
          + ''
        + ''
      );
    }

    autocode.initState()

    $('#outputs-init').hide();
    $('#outputs-content-container').show();
  } else {
    $('#outputs-init').show();
    $('#outputs-content-container').hide();
  }

  autocode.action.toggleSection('outputs');
};<commit_msg>added icons for outputs<commit_after>autocode.state['outputs'] = function() {
  if (autocode.project.outputs) {
    $('#outputs-content-container .table a').slice(1).remove();

    var output, output_filename;
    for (var output_i in autocode.project.outputs) {
      output = autocode.project.outputs[output_i];

      if (autocode.project.outputs instanceof Array) {
        output_name = output.filename;
      } else {
        output_name = output_i;
      }

      if (output.filename) {
        output_filename = output.filename;
      } else {
        output_filename = autocode.imports['crystal/' + output.generator.split('.')[0]].exports[output.generator.split('.')[1]].filename;
      }

      $('#outputs-content-container .table').append(
        ''
          + ''
          + ''
            + '' + output_filename + ''
            + '' + output.generator.split('.')[1] + ''
          + ''
        + ''
      );
    }

    autocode.initState()

    $('#outputs-init').hide();
    $('#outputs-content-container').show();
  } else {
    $('#outputs-init').show();
    $('#outputs-content-container').hide();
  }

  autocode.action.toggleSection('outputs');
};<|endoftext|>