<commit_before>/*global define*/
(function (root, factory) {
  if (typeof exports === 'object') {
    module.exports = factory(require('chance'))
  } else if (typeof define === 'function' && define.amd) {
    define(['chance'], factory)
  } else {
    root.weknowhow = root.weknowhow || {}
    root.weknowhow.chanceGenerators = factory(root.chance)
  }
})(this, function (Chance) {
  function copy (source) {
    var result = {}
    Object.keys(source).forEach(function (key) {
      result[key] = source[key]
    })
    return result
  }

  function unwrap (v) {
    if (Array.isArray(v)) {
      return v.map(unwrap)
    } else if (v && typeof v === 'object') {
      return Object.keys(v).reduce(function (result, key) {
        result[key] = unwrap(v[key])
        return result
      }, {})
    } else {
      return v && v.isGenerator ? v() : v
    }
  }

  function ExtendedChance (seed) {
    if (!(this instanceof ExtendedChance)) {
      return new ExtendedChance(seed)
    }

    var that = this
    var chance = typeof seed === 'undefined'
        ? new Chance()
        : new Chance(seed)

    // Fix that pick provided a count of zero or one does not return an array
    var originalPick = Chance.prototype.pick
    chance.pick = function (array, count) {
      if (count === 0) {
        return []
      }

      if (count === 1) {
        return [originalPick.call(chance, array, count)]
      }

      return originalPick.call(chance, array, count)
    }

    chance.shape = function (data) {
      return unwrap(data)
    }

    function generatorFunction (name, args, f) {
      f.isGenerator = true
      f.generatorName = name
      f.args = args
      f.toString = function () {
        return name
      }
      return f
    }

    function installMapFunction (generator) {
      generator.map = function (f) {
        var lastValue, lastMappedValue
        var mapGenerator = generatorFunction(generator.name + '.map', [], function () {
          lastValue = generator()
          lastMappedValue = f(lastValue)
          return lastMappedValue
        })

        mapGenerator.shrink = function (value) {
          if (value === lastMappedValue) {
            return generator.shrink(lastValue).map(f)
          } else {
            return generator
          }
        }

        installMapFunction(mapGenerator)

        return mapGenerator
      }
    }

    var overrides = {
      n: function (generator, count) {
        return createGenerator('n', [generator, count], [generator])
      },
      pickset: function (data, count) {
        var picksetGenerator = generatorFunction('pickset', [data, count], function () {
          picksetGenerator.lastValue = chance.pickset(data, unwrap(count))
          picksetGenerator.lastUnwrappedValue = unwrap(picksetGenerator.lastValue)
          return picksetGenerator.lastUnwrappedValue
        })

        installMapFunction(picksetGenerator)

        picksetGenerator.shrink = function (data) {
          return shrinkers.pickset(picksetGenerator, data)
        }

        return picksetGenerator
      },
      unique: function (generator, count, options) {
        return createGenerator('unique', [generator, count], [generator])
      }
    }

    function minMaxShrinker (generator, data) {
      var currentLimits = generator.args[0] || {}
      var limits = copy(currentLimits)

      var value = typeof data === 'string'
        ? parseFloat(data.replace(/^[^\d]*/, ''))
        : data

      if (value < 0 && value < (currentLimits.max || 0)) {
        var max = currentLimits.max || 0
        limits = {
          min: value,
          max: Math.min(0, max)
        }
      } else if (value > 0 && value > (currentLimits.min || 0)) {
        var min = currentLimits.min || 0
        limits = {
          min: Math.max(0, min),
          max: value
        }
      } else {
        return that.identity(data)
      }

      return that[generator.generatorName](limits)
    }

    var shrinkers = {
      n: function (generator, data) {
        if (data.length === 0) {
          return that.identity(data)
        }

        var count = generator.args[1]
        if (count && count.shrink) {
          count = count.shrink(data.length)
        } else {
          count = data.length
        }

        var dataGenerator = generator.args[0]
        if (dataGenerator.shrink) {
          return that.pickset(data.map(dataGenerator.shrink), count)
        } else {
          return that.pickset(data, count)
        }
      },
      pick: function (generator, data) {
        if (Array.isArray(data)) {
          return shrinkers.pickset(generator, data)
        } else {
          return shrinkers.pickone(generator, data)
        }
      },
      pickone: function (generator, data) {
        return that.identity(data)
      },
      pickset: function (generator, data) {
        if (data.length === 0) {
          return that.identity(data)
        }

        var shrinkable = false
        var count = generator.args[1]
        if (count && count.shrink) {
          shrinkable = true
          count = count.shrink(data.length)
        }

        var shrinkableData = (generator.lastValue || []).some(function (g) {
          return g && g.shrink
        })

        shrinkable = shrinkable || shrinkableData

        if (shrinkableData && data.length < 10 && generator.lastUnwrappedValue === data) {
          data = generator.lastValue.map(function (g, i) {
            return g && g.shrink
              ? g.shrink(data[i])
              : data[i]
          })
        }

        if (!shrinkable) {
          return that.identity(data)
        }

        return that.pickset(data, count)
      },
      unique: function (generator, data) {
        var count = generator.args[1]
        if (count && count.shrink) {
          count = count.shrink(data.length)
        } else {
          count = data.length
        }

        var dataGenerator = generator.args[0]
        if (dataGenerator.shrink) {
          return that.pickset(data.map(dataGenerator.shrink), count)
        } else {
          return that.pickset(data, count)
        }
      },
      shape: function (generator, data) {
        var shapeGenerators = generator.args[0]
        var shrunk = false
        var newShape = Object.keys(shapeGenerators).reduce(function (result, key) {
          var entry = shapeGenerators[key]
          if (entry && typeof entry.shrink === 'function') {
            shrunk = true
            result[key] = entry.shrink(data[key])
          } else {
            result[key] = entry
          }

          return result
        }, {})

        if (shrunk) {
          return that.shape(newShape)
        } else {
          return that.identity(data)
        }
      },
      string: function (generator, data) {
        var currentLimits = generator.args[0] || {}
        var limits = copy(currentLimits)

        var pool = {}
        for (var i = 0; i < data.length; i += 1) {
          pool[data[i]] = true
        }
        limits.pool = Object.keys(pool).join('')

        if (data.length === 0) {
          return that.identity(data)
        } else if (typeof limits.length === 'undefined') {
          limits.length = that.integer({ min: 0, max: data.length })
        } else if (limits.length && limits.length.shrink) {
          limits.length = limits.length.shrink(data.length)
        } else {
          return that.identity(data)
        }

        return that.string(limits)
      },
      integer: minMaxShrinker,
      natural: minMaxShrinker,
      floating: minMaxShrinker,
      year: minMaxShrinker,
      altitude: minMaxShrinker,
      depth: minMaxShrinker,
      latitude: minMaxShrinker,
      longitude: minMaxShrinker,
      dollar: minMaxShrinker
    }

    function createGenerator (name, args, omitUnwap) {
      var omitUnwrapIndex = {}

      omitUnwap && args.forEach(function (arg, i) {
        if (omitUnwap.indexOf(arg) !== -1) {
          omitUnwrapIndex[i] = true
        }
      })

      var g = generatorFunction(name, args, function () {
        if (arguments.length === 0) {
          return chance[name].apply(chance, args.map(function (arg, i) {
            return omitUnwrapIndex[i] ? arg : unwrap(arg)
          }))
        } else {
          return createGenerator(
            name,
            Array.prototype.slice.call(arguments)
          )
        }
      })

      var shrinker = shrinkers[name]
      if (shrinker) {
        g.shrink = function (data) {
          return shrinker(g, data)
        }
      } else {
        g.shrink = function (data) {
          return that.identity(data)
        }
      }

      installMapFunction(g)

      return g
    }

    ['shape'].concat(Object.keys(Chance.prototype)).forEach(function (key) {
      var property = chance[key]
      if (typeof property === 'function') {
        if (overrides[key]) {
          that[key] = generatorFunction(key, [], overrides[key])
        } else {
          that[key] = createGenerator(key, [])
        }
      } else {
        that[key] = property
      }
    })

    that.identity = generatorFunction('identity', [], function (data) {
      var identityGenerator = generatorFunction('identity', [data], function () {
        return data
      })

      identityGenerator.map = function (fn) {
        return that.identity(fn(data))
      }

      return identityGenerator
    })

    that.array = generatorFunction('array', [], function (generator, count) {
      if (typeof count === 'undefined') {
        return that.n(generator, that.natural({ max: 50 }))
      } else {
        return that.n(generator, count)
      }
    })
  }

  return ExtendedChance
})
<commit_msg>Unwrap results from mapping functions<commit_after>/*global define*/
(function (root, factory) {
  if (typeof exports === 'object') {
    module.exports = factory(require('chance'))
  } else if (typeof define === 'function' && define.amd) {
    define(['chance'], factory)
  } else {
    root.weknowhow = root.weknowhow || {}
    root.weknowhow.chanceGenerators = factory(root.chance)
  }
})(this, function (Chance) {
  function copy (source) {
    var result = {}
    Object.keys(source).forEach(function (key) {
      result[key] = source[key]
    })
    return result
  }

  function unwrap (v) {
    if (Array.isArray(v)) {
      return v.map(unwrap)
    } else if (v && typeof v === 'object') {
      return Object.keys(v).reduce(function (result, key) {
        result[key] = unwrap(v[key])
        return result
      }, {})
    } else {
      return v && v.isGenerator ? v() : v
    }
  }

  function ExtendedChance (seed) {
    if (!(this instanceof ExtendedChance)) {
      return new ExtendedChance(seed)
    }

    var that = this
    var chance = typeof seed === 'undefined'
        ? new Chance()
        : new Chance(seed)

    // Fix that pick provided a count of zero or one does not return an array
    var originalPick = Chance.prototype.pick
    chance.pick = function (array, count) {
      if (count === 0) {
        return []
      }

      if (count === 1) {
        return [originalPick.call(chance, array, count)]
      }

      return originalPick.call(chance, array, count)
    }

    chance.shape = function (data) {
      return unwrap(data)
    }

    function generatorFunction (name, args, f) {
      f.isGenerator = true
      f.generatorName = name
      f.args = args
      f.toString = function () {
        return name
      }
      return f
    }

    function installMapFunction (generator) {
      generator.map = function (f) {
        var lastValue, lastMappedValue
        var mapGenerator = generatorFunction(generator.name + '.map', [], function () {
          lastValue = generator()
          lastMappedValue = unwrap(f(lastValue))
          return lastMappedValue
        })

        mapGenerator.shrink = function (value) {
          if (value === lastMappedValue) {
            return generator.shrink(lastValue).map(f)
          } else {
            return generator
          }
        }

        installMapFunction(mapGenerator)

        return mapGenerator
      }
    }

    var overrides = {
      n: function (generator, count) {
        return createGenerator('n', [generator, count], [generator])
      },
      pickset: function (data, count) {
        var picksetGenerator = generatorFunction('pickset', [data, count], function () {
          picksetGenerator.lastValue = chance.pickset(data, unwrap(count))
          picksetGenerator.lastUnwrappedValue = unwrap(picksetGenerator.lastValue)
          return picksetGenerator.lastUnwrappedValue
        })

        installMapFunction(picksetGenerator)

        picksetGenerator.shrink = function (data) {
          return shrinkers.pickset(picksetGenerator, data)
        }

        return picksetGenerator
      },
      unique: function (generator, count, options) {
        return createGenerator('unique', [generator, count], [generator])
      }
    }

    function minMaxShrinker (generator, data) {
      var currentLimits = generator.args[0] || {}
      var limits = copy(currentLimits)

      var value = typeof data === 'string'
        ? parseFloat(data.replace(/^[^\d]*/, ''))
        : data

      if (value < 0 && value < (currentLimits.max || 0)) {
        var max = currentLimits.max || 0
        limits = {
          min: value,
          max: Math.min(0, max)
        }
      } else if (value > 0 && value > (currentLimits.min || 0)) {
        var min = currentLimits.min || 0
        limits = {
          min: Math.max(0, min),
          max: value
        }
      } else {
        return that.identity(data)
      }

      return that[generator.generatorName](limits)
    }

    var shrinkers = {
      n: function (generator, data) {
        if (data.length === 0) {
          return that.identity(data)
        }

        var count = generator.args[1]
        if (count && count.shrink) {
          count = count.shrink(data.length)
        } else {
          count = data.length
        }

        var dataGenerator = generator.args[0]
        if (dataGenerator.shrink) {
          return that.pickset(data.map(dataGenerator.shrink), count)
        } else {
          return that.pickset(data, count)
        }
      },
      pick: function (generator, data) {
        if (Array.isArray(data)) {
          return shrinkers.pickset(generator, data)
        } else {
          return shrinkers.pickone(generator, data)
        }
      },
      pickone: function (generator, data) {
        return that.identity(data)
      },
      pickset: function (generator, data) {
        if (data.length === 0) {
          return that.identity(data)
        }

        var shrinkable = false
        var count = generator.args[1]
        if (count && count.shrink) {
          shrinkable = true
          count = count.shrink(data.length)
        }

        var shrinkableData = (generator.lastValue || []).some(function (g) {
          return g && g.shrink
        })

        shrinkable = shrinkable || shrinkableData

        if (shrinkableData && data.length < 10 && generator.lastUnwrappedValue === data) {
          data = generator.lastValue.map(function (g, i) {
            return g && g.shrink
              ? g.shrink(data[i])
              : data[i]
          })
        }

        if (!shrinkable) {
          return that.identity(data)
        }

        return that.pickset(data, count)
      },
      unique: function (generator, data) {
        var count = generator.args[1]
        if (count && count.shrink) {
          count = count.shrink(data.length)
        } else {
          count = data.length
        }

        var dataGenerator = generator.args[0]
        if (dataGenerator.shrink) {
          return that.pickset(data.map(dataGenerator.shrink), count)
        } else {
          return that.pickset(data, count)
        }
      },
      shape: function (generator, data) {
        var shapeGenerators = generator.args[0]
        var shrunk = false
        var newShape = Object.keys(shapeGenerators).reduce(function (result, key) {
          var entry = shapeGenerators[key]
          if (entry && typeof entry.shrink === 'function') {
            shrunk = true
            result[key] = entry.shrink(data[key])
          } else {
            result[key] = entry
          }

          return result
        }, {})

        if (shrunk) {
          return that.shape(newShape)
        } else {
          return that.identity(data)
        }
      },
      string: function (generator, data) {
        var currentLimits = generator.args[0] || {}
        var limits = copy(currentLimits)

        var pool = {}
        for (var i = 0; i < data.length; i += 1) {
          pool[data[i]] = true
        }
        limits.pool = Object.keys(pool).join('')

        if (data.length === 0) {
          return that.identity(data)
        } else if (typeof limits.length === 'undefined') {
          limits.length = that.integer({ min: 0, max: data.length })
        } else if (limits.length && limits.length.shrink) {
          limits.length = limits.length.shrink(data.length)
        } else {
          return that.identity(data)
        }

        return that.string(limits)
      },
      integer: minMaxShrinker,
      natural: minMaxShrinker,
      floating: minMaxShrinker,
      year: minMaxShrinker,
      altitude: minMaxShrinker,
      depth: minMaxShrinker,
      latitude: minMaxShrinker,
      longitude: minMaxShrinker,
      dollar: minMaxShrinker
    }

    function createGenerator (name, args, omitUnwap) {
      var omitUnwrapIndex = {}

      omitUnwap && args.forEach(function (arg, i) {
        if (omitUnwap.indexOf(arg) !== -1) {
          omitUnwrapIndex[i] = true
        }
      })

      var g = generatorFunction(name, args, function () {
        if (arguments.length === 0) {
          return chance[name].apply(chance, args.map(function (arg, i) {
            return omitUnwrapIndex[i] ? arg : unwrap(arg)
          }))
        } else {
          return createGenerator(
            name,
            Array.prototype.slice.call(arguments)
          )
        }
      })

      var shrinker = shrinkers[name]
      if (shrinker) {
        g.shrink = function (data) {
          return shrinker(g, data)
        }
      } else {
        g.shrink = function (data) {
          return that.identity(data)
        }
      }

      installMapFunction(g)

      return g
    }

    ['shape'].concat(Object.keys(Chance.prototype)).forEach(function (key) {
      var property = chance[key]
      if (typeof property === 'function') {
        if (overrides[key]) {
          that[key] = generatorFunction(key, [], overrides[key])
        } else {
          that[key] = createGenerator(key, [])
        }
      } else {
        that[key] = property
      }
    })

    that.identity = generatorFunction('identity', [], function (data) {
      var identityGenerator = generatorFunction('identity', [data], function () {
        return data
      })

      identityGenerator.map = function (fn) {
        return that.identity(fn(data))
      }

      return identityGenerator
    })

    that.array = generatorFunction('array', [], function (generator, count) {
      if (typeof count === 'undefined') {
        return that.n(generator, that.natural({ max: 50 }))
      } else {
        return that.n(generator, count)
      }
    })
  }

  return ExtendedChance
})
<|endoftext|>


<commit_before>var exec, load;

(function(global) {
    'use strict';

    if (typeof module !== 'undefined' && module.exports)
        module.exports      = new LoadRemote();
    else
        global.loadRemote   = new LoadRemote();

    function LoadRemote() {
        var Prefix      = '/',
            loadRemote  = function(name, options, callback) {
                var o   = options;

                if (!callback)
                    callback = options;

                if (global[o.name])
                    callback();
                else
                    exec.parallel([loadModules, loadOptions], function(error, modules, config) {
                        var remoteTmpls, local, remote,
                            online, module, isArray, version,

                            funcON      = function() {
                                load.parallel(remote, function(error) {
                                    if (error)
                                        funcOFF();
                                    else
                                        callback();
                                });
                            },

                            funcOFF     = function() {
                                load.parallel(local, callback);
                            };

                        if (error) {
                            if (!module)
                                alert('Error: could not load module!');

                            if (!config)
                                alert('Error: could not load config!');
                        } else {
                            online      = config.online && navigator.onLine,
                            module      = findObjByNameInArr(modules, name),

                            isArray     = Array.isArray(module.local),
                            version     = module.version;

                            if (isArray) {
                                remoteTmpls = module.remote;
                                local       = module.local;
                            } else {
                               remoteTmpls  = [module.remote];
                               local        = [module.local];
                            }

                            local   = local.map(function(url) {
                                return o.noPrefix ? url : Prefix + url;
                            });

                            remote  = remoteTmpls.map(function(tmpl) {
                                return tmpl.replace(/{{\sversion\s}}/g, version);
                            });

                            exec.if(online, funcON, funcOFF);
                        }
                    });
                };

            loadRemote.setPrefix      = function(prefix) {
                Prefix = prefix;

                return loadRemote;
            };

            loadRemote.load         = loadRemote;

            function loadModules(callback) {
                var url = Prefix + '/json/modules.json';

                load.json(url, callback);
            }

            function loadOptions(callback) {
                var url = Prefix + '/options.json';

                load.json(url, callback);
            }

            function findObjByNameInArr(array, name) {
                var ret;

                if (!Array.isArray(array))
                    throw(Error('array should be array!'));

                if (typeof name !== 'string')
                    throw(Error('name should be string!'));

                array.some(function(item) {
                    var is = item.name === name,
                        isArray = Array.isArray(item);

                    if (is)
                        ret = item;
                    else if (isArray)
                        item.some(function(item) {
                            is = item.name === name;

                            if (is)
                                ret = item.data;

                            return is;
                        });

                    return is;
                });

                return ret;
            }

            return loadRemote;
    }

})(this);
<commit_msg>feature(loadremote) findObjByNameInArr -> binom<commit_after>var exec, load;

(function(global) {
    'use strict';

    if (typeof module !== 'undefined' && module.exports)
        module.exports      = new LoadRemote();
    else
        global.loadRemote   = new LoadRemote();

    function LoadRemote() {
        var Prefix      = '/',
            loadRemote  = function(name, options, callback) {
                var o   = options;

                if (!callback)
                    callback = options;

                if (global[o.name])
                    callback();
                else
                    exec.parallel([loadModules, loadOptions], function(error, modules, config) {
                        var remoteTmpls, local, remote,
                            online, module, isArray, version,

                            funcON      = function() {
                                load.parallel(remote, function(error) {
                                    if (error)
                                        funcOFF();
                                    else
                                        callback();
                                });
                            },

                            funcOFF     = function() {
                                load.parallel(local, callback);
                            };

                        if (error) {
                            if (!module)
                                alert('Error: could not load module!');

                            if (!config)
                                alert('Error: could not load config!');
                        } else {
                            online      = config.online && navigator.onLine,
                            module      = binom(name, modules),

                            isArray     = Array.isArray(module.local),
                            version     = module.version;

                            if (isArray) {
                                remoteTmpls = module.remote;
                                local       = module.local;
                            } else {
                               remoteTmpls  = [module.remote];
                               local        = [module.local];
                            }

                            local   = local.map(function(url) {
                                return o.noPrefix ? url : Prefix + url;
                            });

                            remote  = remoteTmpls.map(function(tmpl) {
                                return tmpl.replace(/{{\sversion\s}}/g, version);
                            });

                            exec.if(online, funcON, funcOFF);
                        }
                    });
                };

            loadRemote.setPrefix      = function(prefix) {
                Prefix = prefix;

                return loadRemote;
            };

            loadRemote.load         = loadRemote;

            function loadModules(callback) {
                var url = Prefix + '/json/modules.json';

                load.json(url, callback);
            }

            function loadOptions(callback) {
                var url = Prefix + '/options.json';

                load.json(url, callback);
            }

            function binom(name, array) {
                var ret;

                if (typeof name !== 'string')
                    throw(Error('name should be string!'));

                if (!Array.isArray(array))
                    throw(Error('array should be array!'));

                array.some(function(item) {
                    var is = item.name === name;

                    if (is)
                        ret = item;

                    return is;
                });

                return ret;
            }

            return loadRemote;
    }

})(this);
<|endoftext|>


<commit_before>var _ = require("underscore");
var helpers = require("./nimrodhelpers");
var util = require("util");
var Cursor = require("mongodb").Cursor;
var ObjectID = require("mongodb").ObjectID;

function makeCommandObj(obj, collName) {
  if (typeof obj === "string") {
    var cmd = {};
    cmd[obj] = collName;
    obj = cmd;
  }
  return obj;
}

function BulkWriteResultWrapper(base) {
  this.__proto__ = base;

  var _this = this;
  this.toSingleResult = function() {
    var single = {"nMatched": base.nMatched,
                  "nUpserted": base.nUpserted,
                  "nModified": base.nModified,
                  "_id": base.getUpsertedIdAt(0)._id};
    var singleResult = helpers.extend(single, _this);
    singleResult.getUpsertedId = function() {
      return _this.getUpsertedIdAt(0);
    };
    return singleResult;
  };
};

function BulkOp(conn, base) {
  this.__proto__ = base;

  this.execute = function(options) {
    if (!options) {
      options = {};
    }
    base.execute(options, conn.flow.addNoError());
    return new BulkWriteResultWrapper(conn.flow.waitProper());
  };
}

function ShellIterator(conn, coll) {
  var cursor = conn.flow.waitProper();

  var nextDoc;
  var hasNextCalled = false;

  var ret = {};
  ret.countReturn = function(){
    var c = this.count();

    if (cursor.skipValue) {
      c = c - cursor.skipValue;
    }
    if (cursor.limitValue > 0 && cursor.limitValue < c) {
        return cursor.limitValue;
    }

    return Math.max(0, c);
  };

  ret.size = ret.countReturn;

  ret.arrayAccess = function (idx) {
    return ret.toArray()[idx];
  };

  ret.hasNext = function() {
    if (!hasNextCalled) {
      cursor.nextObject(conn.flow.addNoError());
      nextDoc = conn.flow.waitProper();
    }
    hasNextCalled = true;
    return !!nextDoc && !(nextDoc instanceof Error);
  };

  ret.next = function() {
    ret.hasNext();
    hasNextCalled = false;
    return nextDoc;
  };

  ret.toArray = function() {
    cursor.s.cmd.explain = false;
    var arr = [];
    if (!ret.hasNext()) {
      cursor.rewind();
      hasNextCalled = false;
    }
    while (ret.hasNext()) {
      arr.push(ret.next());
    }
    return arr;
  };

  ret.itcount = function() {
    return ret.toArray().length;
  };

  ret.length = ret.itcount;

  ret.hint = function(obj) {
    if (typeof obj === "object") {
      var index = coll.getIndexes().filter(function(index) {
        return _.isEqual(index.key, obj);
      });
      if (index.length) {
        obj = index[0].name;
      } else {
        throw new Error("bad hint index name or key");
      }
    }
    cursor.s.options.hint = obj;
    return p;
  };

  ret.explain = function(v) {
    if (cursor.cursorState.notified) {
      cursor.rewind();
      hasNextCalled = false;
    }
    cursor.s.cmd.verbosity = v;
    cursor.explain.call(cursor, conn.flow.addNoError());
    return conn.flow.waitProper();
  };

  _.each(["count"], function(fn) {
    ret[fn] = function() {
      var args = Array.prototype.slice.call(arguments, 0).concat(conn.flow.addNoError());
      cursor[fn].apply(cursor, args);
      return conn.flow.waitProper();
    };
  });

  _.each(["sort", "limit", "skip", "batchSize"], function(fn) {
    ret[fn] = function() {
      cursor[fn].apply(cursor, arguments);
      return p;
    };
  });

  ret.forEach = function(fn) {
    while (ret.hasNext()) {
      fn(ret.next());
    }
  };

  ret.map = function(fn) {
    var arr = [];
    while (ret.hasNext()) {
      arr.push(fn(ret.next()));
    }
    return arr;
  };

  var p = Proxy.create({
    get: function(target, v) {
      if (ret[v]) {
        return ret[v];
      }
      var n = parseInt(v, 10);
      if (!isNaN(n) && n >= 0) {
        return target.arrayAccess(n);
      }
      return undefined;
    }
  });

  return p;
}

function genIndexName(keys) {
  var name = "";
  for (var k in keys) {
    var v = keys[k];
    if (typeof v === "function")
      continue;

    if ( name.length > 0 )
      name += "_";
    name += k + "_" + v;
  }

  return name;
}

function indexSpec(fullCollName, keys, options) {
  var ret = { ns : fullCollName, key : keys , name : genIndexName(keys) };

  if (!options) {
    return ret;
  }

  if (typeof options === "string") {
    ret.name = options;
  } else if (typeof options === "boolean") {
    ret.unique = true;
  } else if (typeof options === "object" ) {
    if (options.length) {
      var nb = 0;
      for (var i = 0; i < options.length; i++) {
        if (typeof options[i] === "string") {
          ret.name = options[i];
        } else if (typeof options[i] === "boolean") {
          if (options[i]) {
            if (nb === 0) {
              ret.unique = true;
            } else if (nb === 1) {
              ret.dropDups = true;
            }
          }
          nb++;
        }
      }
    }
    else {
      helpers.extend(ret , options);
    }
  }
  else {
    throw new Error(util.format("can't handle: %j", typeof options));
  }
  return ret;
};

var methods = function(dbLit, conn) {
  return function(collectionName) {
    return {
      initializeOrderedBulkOp: function(options) {
        if (!options) {
          options = {};
        }
        var bulk = conn.db.collection(collectionName).initializeOrderedBulkOp(options);
        return new BulkOp(conn, bulk);
      },
      initializeUnorderedBulkOp: function(options) {
        if (!options) {
          options = {};
        }
        var bulk = conn.db.collection(collectionName).initializeUnorderedBulkOp(options);
        return new BulkOp(conn, bulk);
      },
      getMongo: function() {
        return this.getDB().getMongo();
      },
      runCommand: function(cmd, params) {
        cmd = makeCommandObj(cmd, collectionName);
        var db = this.getDB();
        if (typeof cmd === "object") {
          return db.runCommand(cmd);
        }
        var c = {};
        c[cmd] = this.getName();
        if (params) {
          helpers.extend(c, params);
        }
        return db.runCommand(c);
      },
      getName: function() {
        return collectionName;
      },
      getDB: function() {
        return Proxy.create(dbLit(conn));
      },
      find: function(q, fields, limit, skip, batchSize, options) {
        options = limit ? (options.limit = limit, options) : options;
        options = skip ? (options.skip = skip, options) : options;
        options = batchSize ? (options.batchSize = batchSize, options) : options;

        conn.db.collection(collectionName).find(q, fields, options, conn.flow.addNoError());

        return new ShellIterator(conn, this);
      },
      findOne: function(q, fields, options) {
        conn.db.collection(collectionName).findOne(q, fields, options, conn.flow.addNoError());
        return conn.flow.waitProper();
      },
      insert: function(doc, options) {
        if (!options) {
          options = {};
        }
        conn.db.collection(collectionName).insert(doc, options, conn.flow.addNoError());
        return conn.flow.waitProper();
      },
      count: function(q) {
        conn.db.collection(collectionName).count(q, conn.flow.addNoError());
        return conn.flow.waitProper();
      },
      remove: function(q, justOne) {
        var options = justOne ? {"single": justOne} : {};
        q = q instanceof ObjectID ? {"_id" : q } : q;
        conn.db.collection(collectionName).remove(q, options, conn.flow.addNoError());
        return conn.flow.waitProper();
      },
      update: function(q, obj, upsert, multi) {
        var options = (upsert || multi) ?
          {"upsert": upsert, "multi": multi} : {};
        conn.db.collection(collectionName).update(q, obj, options, conn.flow.addNoError());
        return conn.flow.waitProper();
      },

      // =================  others  =================
      getFullName: function() {
        return util.format("%s.%s", conn.db.databaseName, collectionName);
      },

      toString: function() {
        return this.getFullName();
      },

      clean: function() {
        return helpers.runCommand(conn, {"clean": collectionName});
      },

      validate: function(full) {
        var cmd = {"validate" : collectionName};
        if (typeof full === "object" ) {
          helpers.extend(cmd, full);
        } else {
          cmd.full = full;
        }
        return helpers.runCommand(conn, cmd);
      },

      aggregate: function(pipeline, opts) {
        opts = opts === undefined ? {} : opts;
        if ("cursor" in opts) {
          var cursor = conn.db.collection(collectionName).aggregate(pipeline, opts);
          return new helpers.AggregationCursor(conn, cursor);
        }
        conn.db.collection(collectionName).aggregate(
          pipeline, opts, conn.flow.addNoError()
        );
        return conn.flow.waitProper();
      },

      ensureIndex: function(fieldOrSpec, options) {
        return this.createIndex(fieldOrSpec, options);
      },

      copyTo: function(newName) {
        var to = conn.db.collection(newName);
        to.ensureIndex({ _id: 1}, conn.flow.addNoError());
        conn.flow.waitProper();

        var count = 0;
        var cursor = this.find();
        while (cursor.hasNext()) {
          var o = cursor.next();
          count++;
          to.update({_id:o._id}, o, { upsert: true } , conn.flow.addNoError());
          conn.flow.waitProper();
        }

        return count;
      },

      createIndex: function(keys, options) {
        if (!keys) {
          throw new Error("you must specify the index keys");
        }
        var o = indexSpec(this.getFullName(), keys, options);
        return helpers.runCommand(conn, { "createIndexes": collectionName, indexes: [o] });
      },

      dataSize: function() {
        return this.stats().size;
      },

      distinct: function(keyString, query) {
        keyStringType = typeof keyString;
        if (keyStringType !== "string") {
          throw new Error(
            util.format("The first argument to the distinct command must be a string but was a %j", keyStringType)
          );;
        }
        queryType = typeof query;
        if (!!query && queryType !== "object") {
          throw new Error(
            util.format("The query argument to the distinct command must be a document but was a %j", queryType)
          );
        }
        var res = helpers.runCommand(conn, {
          "distinct" : collectionName,
          key: keyString,
          query: query || {}
        });
        if (!res.ok) {
          throw new Error(
            util.format("distinct failed: %j", res )
          );
        }
        return res.values;
      },

      drop: function() {
        if (arguments.length) {
          throw new Error("drop takes no argument");
        }
        if (!helpers.collectionExists(conn, collectionName)) {
          return false;
        }

        try {
          helpers.runCommand(conn, { "drop": collectionName });
        } catch(e) {
          if (e.errmsg === "ns not found") {
            return false;
          }
          throw new Error(util.format("drop failed: %j", ret));
        }
        return true;
      },

      dropIndex: function (index) {
        return helpers.runCommand(conn, {"deleteIndexes" : collectionName, "index": index});
      },

      dropIndexes: function() {
        if (arguments.length) {
          throw new Error("dropIndexes doesn't take arguments");
        }
        if (!helpers.collectionExists(conn, collectionName)) {
          return false;
        }
        var res = helpers.runCommand(conn, { "deleteIndexes" : collectionName, index: "*" });
        if (res.ok) {
          return res;
        }
        if (res.errmsg && res.errmsg.match(/not found/)) {
          return res;
        }
        throw new Error(util.format("error dropping indexes: %j", ret));
      },

      findAndModify: function(args) {
        var cmd = { "findandmodify": collectionName };
        for (var key in args) {
          cmd[key] = args[key];
        }

        var ret = helpers.runCommand(conn, cmd);
        if (!ret.ok) {
          if (ret.errmsg === "No matching object found") {
            return null;
          }
          throw new Error(util.format("findAndModify failed: %j", ret));
        }
      },

      getIndexKeys: function (){
        return this.getIndexes().map(
          function(i){
            return i.key;
          }
        );
      },

      getIndexes: function() {
        var dbName = conn.db.databaseName;
        conn.db.collection("system.indexes").find({
          ns: util.format("%s.%s", dbName, collectionName)
        }, conn.flow.addNoError());
        return new ShellIterator(conn, this).toArray();
      },

      indexStats: function(params) {
        var cmd = { "indexStats": collectionName};
        if (typeof params === "object") {
          helpers.extend(cmd, params)
        }
        var res;
        try {
          res = helpers.runCommand(conn, cmd);
        } catch (ex) {
          if (!res || (!res.ok && res.errmsg.match(/no such cmd/))) {
            console.log("this comand requires starting mongod with --enableExperimentalIindexStats");
          }
        }
        return res;
      },

      isCapped: function() {
        var options = flow.sync(function(callback) {
          conn.db.collection(collectionName).options(callback);
        });
        return !!(options && options.capped);
      },

      mapReduce: function(map, reduce, options) {
        return flow.sync(function(callback) {
          conn.db.collection(collectionName).
            mapReduce(map, reduce, options, callback);
        });
      },

      reIndex: function() {
        return flow.sync(function(callback) {
          conn.db.collection(collectionName).reIndex(callback);
        });
      },

      renameCollection: function(newName, options) {
        return flow.sync(function(callback) {
          conn.db.collection(collectionName).rename(newName, options, callback);
        });
      },

      save: function(obj, opts) {
        return flow.sync(function(callback) {
          conn.db.collection(collectionName).save(obj, opts, callback);
        });
      },

      stats: function(opts) {
        return flow.sync(function(callback) {
          conn.db.collection(collectionName).stats(opts, callback);
        });
      },

      storageSize: function() {
        return this.stats().size;
      },

      totalIndexSize: function(verbose) {
        var stats = this.stats();
        if (verbose) {
          for (var ns in stats.indexSizes) {
            console.log(util.format("%s\t%s", ns, stats.indexSizes[ns]));
          }
        }
        return stats.totalIndexSize;
      },

      totalSize: function() {
        return this.totalIndexSize() + this.storageSize();
      }

    }
  };
};

exports.ShellIterator = ShellIterator;
exports.Instance = methods;
exports.BulkWriteResult = BulkWriteResultWrapper;
<commit_msg>Throw exception on invalid query<commit_after>var _ = require("underscore");
var helpers = require("./nimrodhelpers");
var util = require("util");
var Cursor = require("mongodb").Cursor;
var ObjectID = require("mongodb").ObjectID;

function makeCommandObj(obj, collName) {
  if (typeof obj === "string") {
    var cmd = {};
    cmd[obj] = collName;
    obj = cmd;
  }
  return obj;
}

function BulkWriteResultWrapper(base) {
  this.__proto__ = base;

  var _this = this;
  this.toSingleResult = function() {
    var single = {"nMatched": base.nMatched,
                  "nUpserted": base.nUpserted,
                  "nModified": base.nModified,
                  "_id": base.getUpsertedIdAt(0)._id};
    var singleResult = helpers.extend(single, _this);
    singleResult.getUpsertedId = function() {
      return _this.getUpsertedIdAt(0);
    };
    return singleResult;
  };
};

function BulkOp(conn, base) {
  this.__proto__ = base;

  this.execute = function(options) {
    if (!options) {
      options = {};
    }
    base.execute(options, conn.flow.addNoError());
    return new BulkWriteResultWrapper(conn.flow.waitProper());
  };
}

function ShellIterator(conn, coll) {
  var cursor = conn.flow.waitProper();

  var nextDoc;
  var hasNextCalled = false;

  var ret = {};
  ret.countReturn = function(){
    var c = this.count();

    if (cursor.skipValue) {
      c = c - cursor.skipValue;
    }
    if (cursor.limitValue > 0 && cursor.limitValue < c) {
        return cursor.limitValue;
    }

    return Math.max(0, c);
  };

  ret.size = ret.countReturn;

  ret.arrayAccess = function (idx) {
    return ret.toArray()[idx];
  };

  ret.hasNext = function() {
    if (!hasNextCalled) {
      cursor.nextObject(conn.flow.addNoError());
      nextDoc = conn.flow.waitProper();
    }
    hasNextCalled = true;
    return !!nextDoc && !(nextDoc instanceof Error);
  };

  ret.next = function() {
    ret.hasNext();
    hasNextCalled = false;
    return nextDoc;
  };

  ret.toArray = function() {
    cursor.s.cmd.explain = false;
    var arr = [];
    if (!ret.hasNext()) {
      cursor.rewind();
      hasNextCalled = false;
    }
    while (ret.hasNext()) {
      arr.push(ret.next());
    }
    return arr;
  };

  ret.itcount = function() {
    return ret.toArray().length;
  };

  ret.length = ret.itcount;

  ret.hint = function(obj) {
    if (typeof obj === "object") {
      var index = coll.getIndexes().filter(function(index) {
        return _.isEqual(index.key, obj);
      });
      if (index.length) {
        obj = index[0].name;
      } else {
        throw new Error("bad hint index name or key");
      }
    }
    cursor.s.options.hint = obj;
    return p;
  };

  ret.explain = function(v) {
    if (cursor.cursorState.notified) {
      cursor.rewind();
      hasNextCalled = false;
    }
    cursor.s.cmd.verbosity = v;
    cursor.explain.call(cursor, conn.flow.addNoError());
    return conn.flow.waitProper();
  };

  _.each(["count"], function(fn) {
    ret[fn] = function() {
      var args = Array.prototype.slice.call(arguments, 0).concat(conn.flow.addNoError());
      cursor[fn].apply(cursor, args);
      return conn.flow.waitProper();
    };
  });

  _.each(["sort", "limit", "skip", "batchSize"], function(fn) {
    ret[fn] = function() {
      cursor[fn].apply(cursor, arguments);
      return p;
    };
  });

  ret.forEach = function(fn) {
    while (ret.hasNext()) {
      fn(ret.next());
    }
  };

  ret.map = function(fn) {
    var arr = [];
    while (ret.hasNext()) {
      arr.push(fn(ret.next()));
    }
    return arr;
  };

  var p = Proxy.create({
    get: function(target, v) {
      if (ret[v]) {
        return ret[v];
      }
      var n = parseInt(v, 10);
      if (!isNaN(n) && n >= 0) {
        return target.arrayAccess(n);
      }
      return undefined;
    }
  });

  return p;
}

function genIndexName(keys) {
  var name = "";
  for (var k in keys) {
    var v = keys[k];
    if (typeof v === "function")
      continue;

    if ( name.length > 0 )
      name += "_";
    name += k + "_" + v;
  }

  return name;
}

function indexSpec(fullCollName, keys, options) {
  var ret = { ns : fullCollName, key : keys , name : genIndexName(keys) };

  if (!options) {
    return ret;
  }

  if (typeof options === "string") {
    ret.name = options;
  } else if (typeof options === "boolean") {
    ret.unique = true;
  } else if (typeof options === "object" ) {
    if (options.length) {
      var nb = 0;
      for (var i = 0; i < options.length; i++) {
        if (typeof options[i] === "string") {
          ret.name = options[i];
        } else if (typeof options[i] === "boolean") {
          if (options[i]) {
            if (nb === 0) {
              ret.unique = true;
            } else if (nb === 1) {
              ret.dropDups = true;
            }
          }
          nb++;
        }
      }
    }
    else {
      helpers.extend(ret , options);
    }
  }
  else {
    throw new Error(util.format("can't handle: %j", typeof options));
  }
  return ret;
};

var methods = function(dbLit, conn) {
  return function(collectionName) {
    return {
      initializeOrderedBulkOp: function(options) {
        if (!options) {
          options = {};
        }
        var bulk = conn.db.collection(collectionName).initializeOrderedBulkOp(options);
        return new BulkOp(conn, bulk);
      },
      initializeUnorderedBulkOp: function(options) {
        if (!options) {
          options = {};
        }
        var bulk = conn.db.collection(collectionName).initializeUnorderedBulkOp(options);
        return new BulkOp(conn, bulk);
      },
      getMongo: function() {
        return this.getDB().getMongo();
      },
      runCommand: function(cmd, params) {
        cmd = makeCommandObj(cmd, collectionName);
        var db = this.getDB();
        if (typeof cmd === "object") {
          return db.runCommand(cmd);
        }
        var c = {};
        c[cmd] = this.getName();
        if (params) {
          helpers.extend(c, params);
        }
        return db.runCommand(c);
      },
      getName: function() {
        return collectionName;
      },
      getDB: function() {
        return Proxy.create(dbLit(conn));
      },
      find: function(q, fields, limit, skip, batchSize, options) {
        checkQueryErrors(q);
        options = limit ? (options.limit = limit, options) : options;
        options = skip ? (options.skip = skip, options) : options;
        options = batchSize ? (options.batchSize = batchSize, options) : options;

        conn.db.collection(collectionName).find(q, fields, options, conn.flow.addNoError());

        return new ShellIterator(conn, this);
      },
      findOne: function(q, fields, options) {
        checkQueryErrors(q);
        conn.db.collection(collectionName).findOne(q, fields, options, conn.flow.addNoError());
        return conn.flow.waitProper();
      },
      insert: function(doc, options) {
        if (!options) {
          options = {};
        }
        conn.db.collection(collectionName).insert(doc, options, conn.flow.addNoError());
        return conn.flow.waitProper();
      },
      count: function(q) {
        checkQueryErrors(q);
        conn.db.collection(collectionName).count(q, conn.flow.addNoError());
        return conn.flow.waitProper();
      },
      remove: function(q, justOne) {
        var options = justOne ? {"single": justOne} : {};
        q = q instanceof ObjectID ? {"_id" : q } : q;
        conn.db.collection(collectionName).remove(q, options, conn.flow.addNoError());
        return conn.flow.waitProper();
      },
      update: function(q, obj, upsert, multi) {
        var options = (upsert || multi) ?
          {"upsert": upsert, "multi": multi} : {};
        conn.db.collection(collectionName).update(q, obj, options, conn.flow.addNoError());
        return conn.flow.waitProper();
      },

      // =================  others  =================
      getFullName: function() {
        return util.format("%s.%s", conn.db.databaseName, collectionName);
      },

      toString: function() {
        return this.getFullName();
      },

      clean: function() {
        return helpers.runCommand(conn, {"clean": collectionName});
      },

      validate: function(full) {
        var cmd = {"validate" : collectionName};
        if (typeof full === "object" ) {
          helpers.extend(cmd, full);
        } else {
          cmd.full = full;
        }
        return helpers.runCommand(conn, cmd);
      },

      aggregate: function(pipeline, opts) {
        opts = opts === undefined ? {} : opts;
        if ("cursor" in opts) {
          var cursor = conn.db.collection(collectionName).aggregate(pipeline, opts);
          return new helpers.AggregationCursor(conn, cursor);
        }
        conn.db.collection(collectionName).aggregate(
          pipeline, opts, conn.flow.addNoError()
        );
        return conn.flow.waitProper();
      },

      ensureIndex: function(fieldOrSpec, options) {
        return this.createIndex(fieldOrSpec, options);
      },

      copyTo: function(newName) {
        var to = conn.db.collection(newName);
        to.ensureIndex({ _id: 1}, conn.flow.addNoError());
        conn.flow.waitProper();

        var count = 0;
        var cursor = this.find();
        while (cursor.hasNext()) {
          var o = cursor.next();
          count++;
          to.update({_id:o._id}, o, { upsert: true } , conn.flow.addNoError());
          conn.flow.waitProper();
        }

        return count;
      },

      createIndex: function(keys, options) {
        if (!keys) {
          throw new Error("you must specify the index keys");
        }
        var o = indexSpec(this.getFullName(), keys, options);
        return helpers.runCommand(conn, { "createIndexes": collectionName, indexes: [o] });
      },

      dataSize: function() {
        return this.stats().size;
      },

      distinct: function(keyString, query) {
        keyStringType = typeof keyString;
        if (keyStringType !== "string") {
          throw new Error(
            util.format("The first argument to the distinct command must be a string but was a %j", keyStringType)
          );;
        }
        queryType = typeof query;
        if (!!query && queryType !== "object") {
          throw new Error(
            util.format("The query argument to the distinct command must be a document but was a %j", queryType)
          );
        }
        var res = helpers.runCommand(conn, {
          "distinct" : collectionName,
          key: keyString,
          query: query || {}
        });
        if (!res.ok) {
          throw new Error(
            util.format("distinct failed: %j", res )
          );
        }
        return res.values;
      },

      drop: function() {
        if (arguments.length) {
          throw new Error("drop takes no argument");
        }
        if (!helpers.collectionExists(conn, collectionName)) {
          return false;
        }

        try {
          helpers.runCommand(conn, { "drop": collectionName });
        } catch(e) {
          if (e.errmsg === "ns not found") {
            return false;
          }
          throw new Error(util.format("drop failed: %j", ret));
        }
        return true;
      },

      dropIndex: function (index) {
        return helpers.runCommand(conn, {"deleteIndexes" : collectionName, "index": index});
      },

      dropIndexes: function() {
        if (arguments.length) {
          throw new Error("dropIndexes doesn't take arguments");
        }
        if (!helpers.collectionExists(conn, collectionName)) {
          return false;
        }
        var res = helpers.runCommand(conn, { "deleteIndexes" : collectionName, index: "*" });
        if (res.ok) {
          return res;
        }
        if (res.errmsg && res.errmsg.match(/not found/)) {
          return res;
        }
        throw new Error(util.format("error dropping indexes: %j", ret));
      },

      findAndModify: function(args) {
        var cmd = { "findandmodify": collectionName };
        for (var key in args) {
          cmd[key] = args[key];
        }

        var ret = helpers.runCommand(conn, cmd);
        if (!ret.ok) {
          if (ret.errmsg === "No matching object found") {
            return null;
          }
          throw new Error(util.format("findAndModify failed: %j", ret));
        }
      },

      getIndexKeys: function (){
        return this.getIndexes().map(
          function(i){
            return i.key;
          }
        );
      },

      getIndexes: function() {
        var dbName = conn.db.databaseName;
        conn.db.collection("system.indexes").find({
          ns: util.format("%s.%s", dbName, collectionName)
        }, conn.flow.addNoError());
        return new ShellIterator(conn, this).toArray();
      },

      indexStats: function(params) {
        var cmd = { "indexStats": collectionName};
        if (typeof params === "object") {
          helpers.extend(cmd, params)
        }
        var res;
        try {
          res = helpers.runCommand(conn, cmd);
        } catch (ex) {
          if (!res || (!res.ok && res.errmsg.match(/no such cmd/))) {
            console.log("this comand requires starting mongod with --enableExperimentalIindexStats");
          }
        }
        return res;
      },

      isCapped: function() {
        var options = flow.sync(function(callback) {
          conn.db.collection(collectionName).options(callback);
        });
        return !!(options && options.capped);
      },

      mapReduce: function(map, reduce, options) {
        return flow.sync(function(callback) {
          conn.db.collection(collectionName).
            mapReduce(map, reduce, options, callback);
        });
      },

      reIndex: function() {
        return flow.sync(function(callback) {
          conn.db.collection(collectionName).reIndex(callback);
        });
      },

      renameCollection: function(newName, options) {
        return flow.sync(function(callback) {
          conn.db.collection(collectionName).rename(newName, options, callback);
        });
      },

      save: function(obj, opts) {
        return flow.sync(function(callback) {
          conn.db.collection(collectionName).save(obj, opts, callback);
        });
      },

      stats: function(opts) {
        return flow.sync(function(callback) {
          conn.db.collection(collectionName).stats(opts, callback);
        });
      },

      storageSize: function() {
        return this.stats().size;
      },

      totalIndexSize: function(verbose) {
        var stats = this.stats();
        if (verbose) {
          for (var ns in stats.indexSizes) {
            console.log(util.format("%s\t%s", ns, stats.indexSizes[ns]));
          }
        }
        return stats.totalIndexSize;
      },

      totalSize: function() {
        return this.totalIndexSize() + this.storageSize();
      }

    }
  };
};

function checkQueryErrors(q) {
  if (!q) {
    return;
  }
  if (q.$and) {
    if (!Array.isArray(q.$and)) {
      throw new Error('$and must take an array!');
    }
    if (q.$and.length === 0) {
      throw new Error('$and must not be empty!');
    }
    for (var i = 0; i < q.$and.length; ++i) {
      if (typeof q.$and[i] !== 'object') {
        throw new Error('$and must be an array of objects!');
      }
      checkQueryErrors(q.$and[i]);
    }
  }
}

exports.ShellIterator = ShellIterator;
exports.Instance = methods;
exports.BulkWriteResult = BulkWriteResultWrapper;
<|endoftext|>


<commit_before>Meteor.methods({
  userVerify: function (userId, verify) {
    check(userId, String);
    check(verify, Boolean);
    if (!isAdmin(Meteor.user())) {
      throw new Meteor.Error("unauthorized", "You don't have permissions to do this");
    }

    var updated = Meteor.users.update(userId, {
      $set: {'profile.verified': verify}
    });
    return {updated: updated};
  },
  addAlt: function (userId, altName) {
    check(userId, String);
    check(altName, String);
    if (!isAdmin(Meteor.user())) {
      throw new Meteor.Error("unauthorized", "You don't have permissions to do this");
    }
    var updated = Meteor.users.update(userId, {
      $addToSet: {'profile.alts': altName}
    });
    return {updated: updated};
  },
  removeAlt: function (userId, altName) {
    check(userId, String);
    check(altName, String);
    if (!isAdmin(Meteor.user())) {
      throw new Meteor.Error("unauthorized", "You don't have permissions to do this");
    }
    var updated = Meteor.users.update(userId, {
      $pull: {'profile.alts': altName}
    });
    return {updated: updated};
  }
});<commit_msg>Prevents users from modifying their profile field and check for permissions first in each method<commit_after>// Prevents users from modifying their profile
Meteor.users.deny({
  update: function() {
    return true;
  }
});

Meteor.methods({
  userVerify: function (userId, verify) {
    if (!isAdmin())
      throw new Meteor.Error("unauthorized", "You don't have permissions to do this");
    check(userId, String);
    check(verify, Boolean);

    var updated = Meteor.users.update(userId, {
      $set: {'profile.verified': verify}
    });
    return {updated: updated};
  },
  addAlt: function (userId, altName) {
    if (!isAdmin())
      throw new Meteor.Error("unauthorized", "You don't have permissions to do this");
    check(userId, String);
    check(altName, String);

    var updated = Meteor.users.update(userId, {
      $addToSet: {'profile.alts': altName}
    });
    return {updated: updated};
  },
  removeAlt: function (userId, altName) {
    if (!isAdmin())
      throw new Meteor.Error("unauthorized", "You don't have permissions to do this");
    check(userId, String);
    check(altName, String);

    var updated = Meteor.users.update(userId, {
      $pull: {'profile.alts': altName}
    });
    return {updated: updated};
  }
});
<|endoftext|>


<commit_before>var path = require('path');
const root = path.dirname(__dirname);

exports.config = {
	seleniumArgs: ['-browserTimeout=120'],
	//seleniumAddress: 'http://127.0.0.1:4444/wd/hub',
	allScriptsTimeout: 30000,
	capabilities: {
		'phantomjs.binary.path': root + "/ui/test/phantomjs/phantomjs.exe",
		'phantomjs.cli.args': ['--ignore-ssl-errors=true',  '--web-security=false'],
		'version' : '',
		'platform': 'ANY'
	},
	keepAlive: true,
	framework: 'mocha',
	mochaOpts:{
		reporter:'min',
		slow:5000,
		timeout: 30000,
		enableTimeouts: false,
		fullTrace: false,
		colors: false
	},
	specs: ["app/**/*.spec.js"],
	plugins: [
		// 	{
		// 	path: './node_modules/protractor/plugins/console/index.js',
		// 	failOnWarning: true,
		// 	failOnError: true
		// }
	],

	/**
	 * A callback function called once protractor is ready and available,
	 * and before the specs are executed.
	 *
	 * You can specify a file containing code to run by setting onPrepare to
	 * the filename string.
	 */
	onPrepare: function() {
		browser.driver.manage().window().setSize(1024,768);

		// Adds ES6 features to testing
		require('babel-register')({
			presets: [ 'es2015' ]
		});

		// protractor.globals = require('./src/app/globals');
		// global.TestPage = require('./src/app/core/tests/route/test-page');
	//	global.mocha = require("mocha");
		global.chai = require('chai');
		var promised = require('chai-as-promised');
		global.chai.use(promised);
		global.expect = chai.expect;

		/**
		 * At this point, global 'protractor' object will be set up, and
		 * jasmine will be available.
		 *
		 * The require statement must be down here, since jasmine-reporters
		 * needs jasmine to be in the global and protractor does not guarantee
		 * this until inside the onPrepare function.
		 */
		//var jasmineReporters = require('jasmine-reporters');
		//jasmine.getEnv().addReporter(new jasmineReporters.JUnitXmlReporter({
		//    consolidateAll: true,
		//    filePrefix: 'xmloutput',
		//    savePath: './test-reports/e2e-test-report'
		//}));
		//
		//// Add a screenshot reporter and store screenshots to `/test/test-reports/screenshots`:
		//jasmine.getEnv().addReporter(new ScreenShotReporter({
		//    baseDirectory: './test-reports/screenshots',
		//    pathBuilder: function pathBuilder(spec, descriptions, results, capabilities) {
		//        // Return '<browser>/<specname>' as path for screenshots:
		//        // Example: 'firefox/list-should work'.
		//        return path.join(capabilities.caps_.browserName, descriptions.join('-'));
		//    },
		//    takeScreenShotsOnlyForFailedSpecs: true
		//}));
	}
	// ----- Options to be passed to minijasminenode -----
	//jasmineNodeOpts: {
	//    // If true, display spec names.
	//    isVerbose: true
	//}
};
<commit_msg>Added hook to stop build if error<commit_after>var path = require('path');
const root = path.dirname(__dirname);

exports.config = {
	seleniumArgs: ['-browserTimeout=120'],
	//seleniumAddress: 'http://127.0.0.1:4444/wd/hub',
	allScriptsTimeout: 30000,
	capabilities: {
		'phantomjs.binary.path': root + "/ui/test/phantomjs/phantomjs.exe",
		'phantomjs.cli.args': ['--ignore-ssl-errors=true',  '--web-security=false'],
		'version' : '',
		'platform': 'ANY'
	},
	keepAlive: true,
	framework: 'mocha',
	mochaOpts:{
		reporter:'min',
		slow:5000,
		timeout: 30000,
		enableTimeouts: false,
		fullTrace: false,
		colors: false
	},
	specs: ["app/**/*.spec.js"],
	plugins: [
		// 	{
		// 	path: './node_modules/protractor/plugins/console/index.js',
		// 	failOnWarning: true,
		// 	failOnError: true
		// }
	],

	/**
	 * A callback function called once protractor is ready and available,
	 * and before the specs are executed.
	 *
	 * You can specify a file containing code to run by setting onPrepare to
	 * the filename string.
	 */
	onPrepare: function() {
		browser.driver.manage().window().setSize(1024,768);

		// Adds ES6 features to testing
		require('babel-register')({
			presets: [ 'es2015' ]
		});

		// protractor.globals = require('./src/app/globals');
		// global.TestPage = require('./src/app/core/tests/route/test-page');
	//	global.mocha = require("mocha");
		global.chai = require('chai');
		var promised = require('chai-as-promised');
		global.chai.use(promised);
		global.expect = chai.expect;

		/**
		 * At this point, global 'protractor' object will be set up, and
		 * jasmine will be available.
		 *
		 * The require statement must be down here, since jasmine-reporters
		 * needs jasmine to be in the global and protractor does not guarantee
		 * this until inside the onPrepare function.
		 */
		//var jasmineReporters = require('jasmine-reporters');
		//jasmine.getEnv().addReporter(new jasmineReporters.JUnitXmlReporter({
		//    consolidateAll: true,
		//    filePrefix: 'xmloutput',
		//    savePath: './test-reports/e2e-test-report'
		//}));
		//
		//// Add a screenshot reporter and store screenshots to `/test/test-reports/screenshots`:
		//jasmine.getEnv().addReporter(new ScreenShotReporter({
		//    baseDirectory: './test-reports/screenshots',
		//    pathBuilder: function pathBuilder(spec, descriptions, results, capabilities) {
		//        // Return '<browser>/<specname>' as path for screenshots:
		//        // Example: 'firefox/list-should work'.
		//        return path.join(capabilities.caps_.browserName, descriptions.join('-'));
		//    },
		//    takeScreenShotsOnlyForFailedSpecs: true
		//}));
	},
	afterLaunch: function(exitCode) {
		if(exitCode == 1){
			console.log("TESTING HAS FAILED; KILL BUILD");
			throw new Error("TESTING HAS FAILED; KILL BUILD");
		}
	}
};
<|endoftext|>


<commit_before>var _ = require('underscore')
  , fs = require('fs')
  , path = require('path')
  , mkdirp = require('mkdirp')
  , File = require('./file')
  , DepTree = require('./deptree')
;

var rLeadingSlash = /^\//
  , rJSExt = /\.js$/
  , rLineComment  = new RegExp('[ \\t]*//.*', 'g')
  , rTrailingWhitespace  = /\s+$/g
;

var _utils = {
    readFileSyncRel: function(pathRelativeToThisFile) {
        return fs.readFileSync(path.resolve(__dirname, pathRelativeToThisFile), { encoding: 'utf8' });
    },

    indent: function(str) {
        var indent = '    ';
        return indent + str.split('\n').join('\n' + indent);
    },

    normalizePath: function(path) {
        var norm = path;
        norm = norm.replace(rLeadingSlash, '');
        norm = rJSExt.test(norm) ? norm : (norm + '.js');
        return norm;
    },

    resolveModule: function(modulesDir, moduleName) {
        var modulesDirNorm = modulesDir;
        modulesDirNorm = (modulesDirNorm[modulesDirNorm.length - 1] === '/') ? modulesDirNorm : (modulesDirNorm + '/');
        return _utils.normalizePath(modulesDirNorm + moduleName);
    },

    resolveModules: function(modulesDir, moduleNames) {
        var utils = this;
        return moduleNames.map(function(moduleName) {
            return utils.resolveModule(modulesDir, moduleName);
        });
    },

    resolveAliases: function(aliases) {
        return _
            .values(aliases)
            .filter(function(value) {
                return rLeadingSlash.test(value) || rJSExt.test(value);
            })
            .map(function(pathDirty) {
                var pathRel = pathDirty;
                pathRel = pathRel.replace(rLeadingSlash, '');
                pathRel = rJSExt.test(pathRel) ? pathRel : (pathRel + '.js');
                return pathRel;
            })
        ;
    },

    validateConfig: function(config, propNames) {
        propNames.forEach(function(propName) {
            if (!config[propName]) {
                throw new Error('Required config property "' + propName + '" not found');
            }
        });
    },

    replace: function(source, find, replacement) {
        var regex = new RegExp('[ \\t]*(?:/\\*!?)?__' + find + '__(?:!?\\*/)?[ \\t]*', 'g');
        return source.replace(regex, replacement);
    },

    stripLineComments: function(source) {
        return source.replace(rLineComment, '');
    },

    stripTrailingSpaces: function(source) {
        return source
            .split('\n')
            .map(function(line) { return line.replace(rTrailingWhitespace, ''); })
            .join('\n')
        ;
    }
};

var Compiler = function(config) {
    _utils.validateConfig(config, [ 'src_root', 'main', 'dest' ]);

    this.config = config;

    config.user_modules = config.user_modules || [];
    config.aliases = config.aliases || {};
    config.globals = config.globals || {};

    var srcRootAbs = config.src_root,
        main = _utils.normalizePath(config.main),
        userModules = _utils.resolveModules(config.modules_dir, config.user_modules),
        aliasModules = _utils.resolveAliases(config.aliases),
        srcFilePathsRel = [].concat(main, userModules, aliasModules);

    srcFilePathsRel = _.uniq(srcFilePathsRel);

    this.depTree = new DepTree(srcRootAbs, srcFilePathsRel, config.micro_paths);
};

Compiler.prototype = {

    compile: function(done) {
        var complete = this._onScanComplete.bind(this);
        this.depTree.scan(function() {
            complete();
            done && done();
        });
    },

    _addGlobals: function(runtime) {
        var names = _.keys(this.config.globals),
            values = _.values(this.config.globals);

        if (this.config.safe_undefined === true) {
            names.push('undefined');
        }

        var retVal = runtime
          , intro = ''
          , outro = ''
        ;

        if (names.length) {
            retVal = _utils.indent(retVal);
            intro = '(function(' + names.join(', ') + ') {';
            outro = '}(' + values.join(', ') + '));';
        }

        retVal = _utils.replace(retVal, 'GLOBAL_NAMES',  intro);
        retVal = _utils.replace(retVal, 'GLOBAL_VALUES', outro);

        return retVal;
    },

    _onScanComplete: function() {
        var moduleTpl = _utils.readFileSyncRel('../runtime/module.tpl.js')
          , runtimeTpl = _utils.readFileSyncRel('../runtime/runtime.tpl.js')
        ;
        var moduleDefs = this.depTree.dependencies.map(
            /**
             * @param {File} file
             * @returns {String}
             */
            function(file) {
                return moduleTpl
                    .replace(/__PATH__/g, file.pathCanonical)
                    .replace(/__SOURCE__/g, _utils.indent(file.canonicalSource.trim()))
                    .trim()
                ;
            }
        );

        var aliases = {};
        for (var alias in this.config.aliases) {
            var pathRel = this.config.aliases[alias];
            aliases[alias] = File.canonicalizePath(pathRel, this.config.micro_paths);
        }

        var config = {
            main: File.canonicalizePath('/' + _utils.normalizePath(this.config.main), this.config.micro_paths),
            aliases: aliases
        };

        var configArg = JSON.stringify(config, null, 4);
        var moduleDefsArg = '{\n' + _utils.indent(moduleDefs.join(',\n')) + '\n}';

        var runtime = runtimeTpl;
        runtime = _utils.stripLineComments(runtime);
        runtime = _utils.replace(runtime, 'CONFIG', _utils.indent(configArg));
        runtime = _utils.replace(runtime, 'MODULE_DEFINITIONS', _utils.indent(moduleDefsArg));
        runtime = this._addGlobals(runtime);
        runtime = _utils.stripTrailingSpaces(runtime);

        mkdirp.sync(path.dirname(this.config.dest));
        fs.writeFileSync(this.config.dest, runtime);
    }

};

module.exports = Compiler;
<commit_msg>Handle splitting Windows-style newlines (\r\n)<commit_after>var _ = require('underscore')
  , fs = require('fs')
  , path = require('path')
  , mkdirp = require('mkdirp')
  , File = require('./file')
  , DepTree = require('./deptree')
;

var rLeadingSlash = /^\//
  , rJSExt = /\.js$/
  , rLineComment  = new RegExp('[ \\t]*//.*', 'g')
  , rTrailingWhitespace  = /\s+$/g
;

var _utils = {
    readFileSyncRel: function(pathRelativeToThisFile) {
        return fs.readFileSync(path.resolve(__dirname, pathRelativeToThisFile), { encoding: 'utf8' });
    },

    indent: function(str) {
        var indent = '    ';
        return indent + str.replace(/\r\n/g, '\n').split('\n').join('\n' + indent);
    },

    normalizePath: function(path) {
        var norm = path;
        norm = norm.replace(rLeadingSlash, '');
        norm = rJSExt.test(norm) ? norm : (norm + '.js');
        return norm;
    },

    resolveModule: function(modulesDir, moduleName) {
        var modulesDirNorm = modulesDir;
        modulesDirNorm = (modulesDirNorm[modulesDirNorm.length - 1] === '/') ? modulesDirNorm : (modulesDirNorm + '/');
        return _utils.normalizePath(modulesDirNorm + moduleName);
    },

    resolveModules: function(modulesDir, moduleNames) {
        var utils = this;
        return moduleNames.map(function(moduleName) {
            return utils.resolveModule(modulesDir, moduleName);
        });
    },

    resolveAliases: function(aliases) {
        return _
            .values(aliases)
            .filter(function(value) {
                return rLeadingSlash.test(value) || rJSExt.test(value);
            })
            .map(function(pathDirty) {
                var pathRel = pathDirty;
                pathRel = pathRel.replace(rLeadingSlash, '');
                pathRel = rJSExt.test(pathRel) ? pathRel : (pathRel + '.js');
                return pathRel;
            })
        ;
    },

    validateConfig: function(config, propNames) {
        propNames.forEach(function(propName) {
            if (!config[propName]) {
                throw new Error('Required config property "' + propName + '" not found');
            }
        });
    },

    replace: function(source, find, replacement) {
        var regex = new RegExp('[ \\t]*(?:/\\*!?)?__' + find + '__(?:!?\\*/)?[ \\t]*', 'g');
        return source.replace(regex, replacement);
    },

    stripLineComments: function(source) {
        return source.replace(rLineComment, '');
    },

    stripTrailingSpaces: function(source) {
        return source
            .split('\n')
            .map(function(line) { return line.replace(rTrailingWhitespace, ''); })
            .join('\n')
        ;
    }
};

var Compiler = function(config) {
    _utils.validateConfig(config, [ 'src_root', 'main', 'dest' ]);

    this.config = config;

    config.user_modules = config.user_modules || [];
    config.aliases = config.aliases || {};
    config.globals = config.globals || {};

    var srcRootAbs = config.src_root,
        main = _utils.normalizePath(config.main),
        userModules = _utils.resolveModules(config.modules_dir, config.user_modules),
        aliasModules = _utils.resolveAliases(config.aliases),
        srcFilePathsRel = [].concat(main, userModules, aliasModules);

    srcFilePathsRel = _.uniq(srcFilePathsRel);

    this.depTree = new DepTree(srcRootAbs, srcFilePathsRel, config.micro_paths);
};

Compiler.prototype = {

    compile: function(done) {
        var complete = this._onScanComplete.bind(this);
        this.depTree.scan(function() {
            complete();
            done && done();
        });
    },

    _addGlobals: function(runtime) {
        var names = _.keys(this.config.globals),
            values = _.values(this.config.globals);

        if (this.config.safe_undefined === true) {
            names.push('undefined');
        }

        var retVal = runtime
          , intro = ''
          , outro = ''
        ;

        if (names.length) {
            retVal = _utils.indent(retVal);
            intro = '(function(' + names.join(', ') + ') {';
            outro = '}(' + values.join(', ') + '));';
        }

        retVal = _utils.replace(retVal, 'GLOBAL_NAMES',  intro);
        retVal = _utils.replace(retVal, 'GLOBAL_VALUES', outro);

        return retVal;
    },

    _onScanComplete: function() {
        var moduleTpl = _utils.readFileSyncRel('../runtime/module.tpl.js')
          , runtimeTpl = _utils.readFileSyncRel('../runtime/runtime.tpl.js')
        ;
        var moduleDefs = this.depTree.dependencies.map(
            /**
             * @param {File} file
             * @returns {String}
             */
            function(file) {
                return moduleTpl
                    .replace(/__PATH__/g, file.pathCanonical)
                    .replace(/__SOURCE__/g, _utils.indent(file.canonicalSource.trim()))
                    .trim()
                ;
            }
        );

        var aliases = {};
        for (var alias in this.config.aliases) {
            var pathRel = this.config.aliases[alias];
            aliases[alias] = File.canonicalizePath(pathRel, this.config.micro_paths);
        }

        var config = {
            main: File.canonicalizePath('/' + _utils.normalizePath(this.config.main), this.config.micro_paths),
            aliases: aliases
        };

        var configArg = JSON.stringify(config, null, 4);
        var moduleDefsArg = '{\n' + _utils.indent(moduleDefs.join(',\n')) + '\n}';

        var runtime = runtimeTpl;
        runtime = _utils.stripLineComments(runtime);
        runtime = _utils.replace(runtime, 'CONFIG', _utils.indent(configArg));
        runtime = _utils.replace(runtime, 'MODULE_DEFINITIONS', _utils.indent(moduleDefsArg));
        runtime = this._addGlobals(runtime);
        runtime = _utils.stripTrailingSpaces(runtime);

        mkdirp.sync(path.dirname(this.config.dest));
        fs.writeFileSync(this.config.dest, runtime);
    }

};

module.exports = Compiler;
<|endoftext|>


<commit_before>module.exports = {
	'env': {
		'es6': true
	},

	'parserOptions': {
		'sourceType': 'module',
		'ecmaFeatures': {
			'experimentalObjectRestSpread': true,
			'jsx': true
		}
	},

	'plugins': [
		'react'
	],

	'rules': {
		'jsx-uses-react': 2,
		'jsx-uses-vars': 2,

		'arrow-body-style': [0, 'as-needed'],
		'arrow-parens': [2, 'as-needed'],
		'object-shorthand': [2, 'always', { 'ignoreConstructors': true }],
		'prefer-arrow-callback': 2,
		'no-new-symbol': 2,
		'prefer-const': 2,
		'prefer-rest-params': 2,
		'prefer-spread': 2,
		'prefer-template': 2
	}
};<commit_msg>Adding rules for es6<commit_after>module.exports = {
	'env': {
		'es6': true
	},

	'parserOptions': {
		'sourceType': 'module',
		'ecmaFeatures': {
			'experimentalObjectRestSpread': true,
			'jsx': true
		}
	},

	'plugins': [
		'react'
	],

	'rules': {
		'jsx-uses-react': 2,
		'jsx-uses-vars': 2,
		'jsx-curly-spacing': [2, 'never'],
		'jsx-indent': [2, 'tab'],
		'jsx-indent-props': [2, 'tab'],
		'jsx-key': 2,
		'jsx-equals-spacing': [2, 'never'],
		'jsx-no-undef': 2,
		'jsx-no-duplicate-props': 2,
		'jsx-no-literals': 2,
		'jsx-closing-bracket-location': 2,
		'jsx-pascal-case': 2,
		'jsx-space-before-closing': [2, 'always'],
		'jsx-sort-props': [2, {'ignoreCase': true}],
		'no-multi-comp': [2/*, {'ignoreStateless': true}*/],
		'wrap-multilines': [2/*, {'ignoreStateless': true}*/],
		'no-unknown-property': 2,
		'sort-comp': [2, {
			order: [
				'static-methods',
				'lifecycle',
				'everything-else',
				'render'
			],
			groups: {
				lifecycle: [
					'constructor',
					'displayName',
					'mixins',
					'propTypes',
					'statics',
					'defaultProps',
					'getDefaultProps',
					'getInitialState',
					'state',
					'componentWillMount',
					'componentDidMount',
					'componentWillReceiveProps',
					'componentWillUpdate',
					'componentDidUpdate',
					'componentWillUnmount',
					'shouldComponentUpdate',
					'getChildContext',
					'childContextTypes',
					'contextTypes',
				]
			}
		}],

		'arrow-body-style': [0, 'as-needed'],
		'arrow-parens': [2, 'as-needed'],
		'object-shorthand': [2, 'always', { 'ignoreConstructors': true }],
		'prefer-arrow-callback': 2,
		'no-new-symbol': 2,
		'prefer-const': 2,
		'prefer-rest-params': 2,
		'prefer-spread': 2,
		'prefer-template': 2
	}
};<|endoftext|>


<commit_before>/* @flow strict-local */
import differenceInSeconds from 'date-fns/difference_in_seconds';

import type { Dispatch, GetState, Narrow } from '../types';
import { reportPresence, typing } from '../api';
import { PRESENCE_RESPONSE } from '../actionConstants';
import { getAuth } from '../selectors';
import { isPrivateOrGroupNarrow } from '../utils/narrow';

let lastFocusPing = new Date();
let lastTypingStart = new Date();

export const sendFocusPing = (hasFocus: boolean = true, newUserInput: boolean = false) => async (
  dispatch: Dispatch,
  getState: GetState,
) => {
  const auth = getAuth(getState());
  if (auth.realm === '' || auth.apiKey === '') {
    return; // not logged in
  }

  if (differenceInSeconds(new Date(), lastFocusPing) < 60) {
    return;
  }

  lastFocusPing = new Date();

  const response = await reportPresence(auth, hasFocus, newUserInput);
  dispatch({
    type: PRESENCE_RESPONSE,
    presence: response.presences,
    serverTimestamp: response.server_timestamp,
  });
};

export const sendTypingEvent = (narrow: Narrow) => async (
  dispatch: Dispatch,
  getState: GetState,
) => {
  if (!isPrivateOrGroupNarrow(narrow)) {
    return;
  }

  if (differenceInSeconds(new Date(), lastTypingStart) > 15) {
    const auth = getAuth(getState());
    typing(auth, narrow[0].operand, 'start');
    lastTypingStart = new Date();
  }
};
<commit_msg>usersActions: Switch to `import api` style.<commit_after>/* @flow strict-local */
import differenceInSeconds from 'date-fns/difference_in_seconds';

import type { Dispatch, GetState, Narrow } from '../types';
/* eslint-disable import/no-named-as-default-member */
import api from '../api';
import { PRESENCE_RESPONSE } from '../actionConstants';
import { getAuth } from '../selectors';
import { isPrivateOrGroupNarrow } from '../utils/narrow';

let lastFocusPing = new Date();
let lastTypingStart = new Date();

export const sendFocusPing = (hasFocus: boolean = true, newUserInput: boolean = false) => async (
  dispatch: Dispatch,
  getState: GetState,
) => {
  const auth = getAuth(getState());
  if (auth.realm === '' || auth.apiKey === '') {
    return; // not logged in
  }

  if (differenceInSeconds(new Date(), lastFocusPing) < 60) {
    return;
  }

  lastFocusPing = new Date();

  const response = await api.reportPresence(auth, hasFocus, newUserInput);
  dispatch({
    type: PRESENCE_RESPONSE,
    presence: response.presences,
    serverTimestamp: response.server_timestamp,
  });
};

export const sendTypingEvent = (narrow: Narrow) => async (
  dispatch: Dispatch,
  getState: GetState,
) => {
  if (!isPrivateOrGroupNarrow(narrow)) {
    return;
  }

  if (differenceInSeconds(new Date(), lastTypingStart) > 15) {
    const auth = getAuth(getState());
    api.typing(auth, narrow[0].operand, 'start');
    lastTypingStart = new Date();
  }
};
<|endoftext|>


<commit_before>import { click } from '../actions/clickActionCreators';
const DATA_ATTR = 'clicktrack';

function cleanTrackingUrl() {
	// TODO: investigate and refactor to support this feature

	/*
	// remove tracking information from the url to prevent bad link copying
	// and to clean up the url field

	var TRACKING_PARAM = '_xtd';
	if ( history && history.replaceState && window.location.href.indexOf(TRACKING_PARAM) > 0) {

		var baseUrl = window.location.href.split('?');

		//Make sure there was actually a query param.
		if (!baseUrl[1]) {
			return;
		}

		var qs = baseUrl[1].split('&');

		for (var i = 0, len = qs.length; i < len; i++) {
			if (qs[i] && qs[i].split('=')[0] === TRACKING_PARAM) {
				qs.splice(i, 1);
				break;
			}
		}

		history.replaceState({}, document.title, baseUrl[0] + '?' + qs.join('&'));
	}
	*/
}

// set reference to un-modified stopPropagation
const originalStopPropagation = typeof Event !== 'undefined' && Event.prototype.stopPropagation;

/**
 * Overriding stopPropagation for maximum click tracking
 * @param {Function} trackClick function to track clicks
 * @return {undefined} side effects only
 */
export function trackStopPropagation(trackClick) {
	/**
	 * A special Event object method that hooks the click tracking into events
	 * that would never bubble to document.body
	 */
	Event.prototype.stopPropAndTrack = function() {
		trackClick(this);
		return originalStopPropagation.call(this);
	};

	// override stopPropagation to track clicks
	Event.prototype.stopPropagation = function() {
		if (this.type === 'click') {
			trackClick(this);
		}
		return originalStopPropagation.call(this);
	};
}

/**
 * Get a descriptive string for the HTML element passed in, e.g.
 *
 * `tagName#id.class1.class2`
 *
 * @param {HTMLElement} element the element to describe
 * @param {Boolean} idOnly only use id in description
 */
function elementShorthand(element, idOnly) {
	const id = element.id ? `#${element.id}` : '';
	if (id && idOnly) {
		return id;
	}
	const tag = element.tagName.toLowerCase();
	const classes = `.${[ ...element.classList ].join('.')}`;

	return `${tag}${id}${classes}`;
}

function _getData(e) {
	const target = e.target;
	const useChildData = e.type === 'change' && target.tagName.toLowerCase() === 'select';
	const el = useChildData ? target.children[target.selectedIndex] : target;

	try {
		const data = (el.dataset || {})[DATA_ATTR] || '{}';
		return JSON.parse(data);
	} catch(err) {
		// data is not JSON-formatted
		return {};
	}
}

function getTrackClick(store) {
	/**
	 * Event handler that emits data about each in-page click
	 *
	 * metadata: {
	 *   "lineage": <String representing 'this' location in DOM>,
	 *   "linkText": <this.text>,
	 *   "coords": [x offset from midline (elinate screen-width dependency, y (top) offset],
	 *   "data": { data on clicked element matching the tracking suffix }
	 * }
	 *
	 * @param  {Event} e the click (or other) DOM event
	 * @return {Object} a Redux action describing the click
	 */
	function trackClick(e) {
		const el = e.target;
		if (e.type === 'change' && !e.target.dataset[DATA_ATTR]) {
			// ignore change events on elements without data-clicktrack
			return;
		}

		const linkText = el.textContent.trim().replace(/\s{2,}/g, ' ');
		const clickLineage = [];
		const targetPosition = el.getBoundingClientRect();
		const docMidlineOffset = document.body.clientWidth / 2;
		const x = Math.round(targetPosition.left - docMidlineOffset);
		const y = Math.round(targetPosition.top);
		const data = _getData(e);

		// 1. Build array of DOM tag lineage
		clickLineage.push(elementShorthand(el));  // full shorthand for clicked el
		var currentEl = el;
		while ((currentEl = currentEl.parentNode) && currentEl !== document.body) {
			clickLineage.push(elementShorthand(currentEl, true));  // id/tag only for parents
		}

		// 2. Create click action with metadata
		const clickTrackAction = click({
			lineage: clickLineage.join('<'),
			linkText: linkText,
			coords: [x, y],
			data: data
		});

		// 3. dispatch the action
		store.dispatch(clickTrackAction);

		return clickTrackAction;
	}

	trackStopPropagation(trackClick);
	cleanTrackingUrl();

	return trackClick;
}

export default getTrackClick;

<commit_msg>defer store update until after all event listeners are done<commit_after>import { click } from '../actions/clickActionCreators';
const DATA_ATTR = 'clicktrack';

function cleanTrackingUrl() {
	// TODO: investigate and refactor to support this feature

	/*
	// remove tracking information from the url to prevent bad link copying
	// and to clean up the url field

	var TRACKING_PARAM = '_xtd';
	if ( history && history.replaceState && window.location.href.indexOf(TRACKING_PARAM) > 0) {

		var baseUrl = window.location.href.split('?');

		//Make sure there was actually a query param.
		if (!baseUrl[1]) {
			return;
		}

		var qs = baseUrl[1].split('&');

		for (var i = 0, len = qs.length; i < len; i++) {
			if (qs[i] && qs[i].split('=')[0] === TRACKING_PARAM) {
				qs.splice(i, 1);
				break;
			}
		}

		history.replaceState({}, document.title, baseUrl[0] + '?' + qs.join('&'));
	}
	*/
}

// set reference to un-modified stopPropagation
const originalStopPropagation = typeof Event !== 'undefined' && Event.prototype.stopPropagation;

/**
 * Overriding stopPropagation for maximum click tracking
 * @param {Function} trackClick function to track clicks
 * @return {undefined} side effects only
 */
export function trackStopPropagation(trackClick) {
	/**
	 * A special Event object method that hooks the click tracking into events
	 * that would never bubble to document.body
	 */
	Event.prototype.stopPropAndTrack = function() {
		trackClick(this);
		return originalStopPropagation.call(this);
	};

	// override stopPropagation to track clicks
	Event.prototype.stopPropagation = function() {
		if (this.type === 'click') {
			trackClick(this);
		}
		return originalStopPropagation.call(this);
	};
}

/**
 * Get a descriptive string for the HTML element passed in, e.g.
 *
 * `tagName#id.class1.class2`
 *
 * @param {HTMLElement} element the element to describe
 * @param {Boolean} idOnly only use id in description
 */
function elementShorthand(element, idOnly) {
	const id = element.id ? `#${element.id}` : '';
	if (id && idOnly) {
		return id;
	}
	const tag = element.tagName.toLowerCase();
	const classes = `.${[ ...element.classList ].join('.')}`;

	return `${tag}${id}${classes}`;
}

function _getData(e) {
	const target = e.target;
	const useChildData = e.type === 'change' && target.tagName.toLowerCase() === 'select';
	const el = useChildData ? target.children[target.selectedIndex] : target;

	try {
		const data = (el.dataset || {})[DATA_ATTR] || '{}';
		return JSON.parse(data);
	} catch(err) {
		// data is not JSON-formatted
		return {};
	}
}

function getTrackClick(store) {
	/**
	 * Event handler that emits data about each in-page click
	 *
	 * metadata: {
	 *   "lineage": <String representing 'this' location in DOM>,
	 *   "linkText": <this.text>,
	 *   "coords": [x offset from midline (elinate screen-width dependency, y (top) offset],
	 *   "data": { data on clicked element matching the tracking suffix }
	 * }
	 *
	 * @param  {Event} e the click (or other) DOM event
	 * @return {Object} a Redux action describing the click
	 */
	function trackClick(e) {
		const el = e.target;
		if (e.type === 'change' && !e.target.dataset[DATA_ATTR]) {
			// ignore change events on elements without data-clicktrack
			return;
		}

		const linkText = el.textContent.trim().replace(/\s{2,}/g, ' ');
		const clickLineage = [];
		const targetPosition = el.getBoundingClientRect();
		const docMidlineOffset = document.body.clientWidth / 2;
		const x = Math.round(targetPosition.left - docMidlineOffset);
		const y = Math.round(targetPosition.top);
		const data = _getData(e);

		// 1. Build array of DOM tag lineage
		clickLineage.push(elementShorthand(el));  // full shorthand for clicked el
		var currentEl = el;
		while ((currentEl = currentEl.parentNode) && currentEl !== document.body) {
			clickLineage.push(elementShorthand(currentEl, true));  // id/tag only for parents
		}

		// 2. Create click action with metadata
		const clickTrackAction = click({
			lineage: clickLineage.join('<'),
			linkText: linkText,
			coords: [x, y],
			data: data
		});

		// 3. dispatch the action - must be deferred until after click event has
		//    been processed by all other listeners
		setTimeout(() => store.dispatch(clickTrackAction), 0);

		return clickTrackAction;
	}

	trackStopPropagation(trackClick);
	cleanTrackingUrl();

	return trackClick;
}

export default getTrackClick;

<|endoftext|>


<commit_before>import { dirname, join } from 'path'
import findConfig from 'find-config'

import { isFile } from '../fs'
import { pluck } from '../object'

// constants
const CONFIG = 'northbrook.json'

/**
 * gets a northbrook.json configuration file
 * [options an options object for find-config]
 * @type {Object} contains 'path', 'directory', 'config' properties
 */
export function getConfig (file, options = { home: false }) {
  const path = findConfig(file || CONFIG, options)

  if (!isFile(path)) {
    return { path: null, directory: null, config: null }
  }

  const directory = dirname(path)
  const config = Object.assign({}, require(path))

  return { directory, config }
}

/**
 * A simple helper to throw uniform error if a config does not exist
 */
export function isInitialized (config, commandName) {
  if (!config) {
    throw new Error(`${commandName}: a northbrook.json is required for this command`)
  }
}

export const isConfig = x => x && x.config

/**
 * Trys to require and return a plugin
 */
export function tryRequireExtends (path) {
  try {
    const pkg = require(path)
    return isConfig(pkg) ? pkg : { config: false }
  } catch (e) {
    return { config: null }
  }
}

export function resolveExtends (config, workingDir) {
  function getExtends (extensionName) {
    return tryRequireExtends(extensionName).config || // try normal require
           tryRequireExtends(join(workingDir, extensionName)).config || // try relative require
           tryRequireExtends(join(workingDir, 'node_modules', extensionName)).config || // try manual require from node_modules
           false
  }

  if (!config) return {}

  const prefix = 'northbrook-'
  const scopePrefix = '@northbrook/'

  const extension = pluck('extends', config)

  return extension &&
        getExtends(extension) ||
        getExtends(prefix + extension) ||
        getExtends(scopePrefix + extension) || {}
}
<commit_msg>feat(northbrook): add utility functions to modify configuration files<commit_after>import { dirname, join } from 'path'
import { createWriteStream, readFileSync } from 'fs'
import findConfig from 'find-config'
import jsonbeautify from 'json-beautify'

import { isFile } from '../fs'
import { pluck } from '../object'

// constants
const CONFIG = 'northbrook.json'

const beautify = obj => jsonbeautify(obj, null, 2, 80)

/**
 * gets a northbrook.json configuration file
 * [options an options object for find-config]
 * @type {Object} contains 'path', 'directory', 'config' properties
 */
export function getConfig (file, options = { home: false, json: true }) {
  const json = options && options.json || true
  const path = findConfig(file || CONFIG, options)

  if (!isFile(path)) {
    return { path: null, directory: null, config: null }
  }

  const directory = dirname(path)

  const config = json
    ? Object.assign({}, JSON.parse(readFileSync(path)))
    : readFileSync(path)

  return { path, directory, config }
}

/**
 * A simple helper to throw uniform error if a config does not exist
 */
export function isInitialized (config, commandName) {
  if (!config) {
    throw new Error(`${commandName}: a northbrook.json is required for this command`)
  }
}

export const isConfig = x => x && x.config

/**
 * Trys to require and return a plugin
 */
export function tryRequireExtends (path) {
  try {
    const pkg = require(path)
    return isConfig(pkg) ? pkg : { config: false }
  } catch (e) {
    return { config: null }
  }
}

export function resolveExtends (config, workingDir) {
  function getExtends (extensionName) {
    return tryRequireExtends(extensionName).config || // try normal require
           tryRequireExtends(join(workingDir, extensionName)).config || // try relative require
           tryRequireExtends(join(workingDir, 'node_modules', extensionName)).config || // try manual require from node_modules
           false
  }

  if (!config) return {}

  const prefix = 'northbrook-'
  const scopePrefix = '@northbrook/'

  const extension = pluck('extends', config)

  return extension &&
        getExtends(extension) ||
        getExtends(prefix + extension) ||
        getExtends(scopePrefix + extension) || {}
}

/**
 * Allows making adjustments to a configuration file
 */
export function modifyConfig (configFile, callback) {
  return new Promise((resolve, reject) => {
    if (typeof configFile === 'function') {
      callback = configFile
      configFile = CONFIG
    }

    const { directory, config } = getConfig(configFile)

    const configPath = join(directory, configFile)

    const newConfig = callback(config)

    if (!newConfig && typeof newConfig !== 'object') {
      throw new Error('Callback did not return a new JSON object')
    }

    const fileStream = createWriteStream(configPath)

    fileStream.write(beautify(newConfig))
    fileStream.write('\n')

    fileStream.on('err', (err) => {
      reject(err)
    })

    fileStream.on('end', () => {
      resolve(newConfig)
    })

    fileStream.end()
  })
}

/**
 * shortcut to adding a plugin to a northbrook.json
 */
export function addPlugin (pluginName) {
  return modifyConfig(CONFIG, function (config) {
    if (!config) {
      return console.log('Cannot find a ' + CONFIG + ' to append to')
    }

    if (!pluginName) {
      return config
    }

    if (!Array.isArray(config.plugins)) config.plugins = []

    const plugins = config.plugins

    if (plugins.indexOf(pluginName) > -1) return config

    return Object.assign({}, config, {
      plugins: [...plugins, pluginName]
    })
  })
}

/**
 * shortcut to adding a package to a northbrook.json
 */
export function addPackage (relativePathToPackage) {
  return modifyConfig(CONFIG, function (config) {
    if (!config) {
      return console.log('Cannot find a ' + CONFIG + ' to append to')
    }

    if (!relativePathToPackage) return config

    if (!Array.isArray(config.packages)) config.packages = []

    const packages = config.packages

    if (packages.indexOf(relativePathToPackage) > -1) return config

    return Object.assign({}, config, {
      packages: [...packages, relativePathToPackage]
    })
  })
}
<|endoftext|>