<commit_before>var elasticsearch = require('elasticsearch'),
  Generator = require('./mapping-generator'),
  generator = new Generator(),
  serialize = require('./serialize'),
  events = require('events'),
  nop = require('nop'),
  util = require('util');

module.exports = function Mongoosastic(schema, options) {
  options = options || {};

  var bulkTimeout, bulkBuffer = [], esClient,
    mapping = getMapping(schema),
    indexName = options && options.index,
    typeName = options && options.type,
    alwaysHydrate = options && options.hydrate,
    defaultHydrateOptions = options && options.hydrateOptions,
    bulk = options && options.bulk,
    filter = options && options.filter;

  if (options.esClient) {
    esClient = options.esClient;
  } else {
    esClient = createEsClient(options);
  }

  setUpMiddlewareHooks(schema);

  /**
   * ElasticSearch Client
   */
  schema.statics.esClient = esClient;

  /**
   * Create the mapping. Takes an optional settings parameter and a callback that will be called once
   * the mapping is created

   * @param settings Object (optional)
   * @param cb Function
   */
  schema.statics.createMapping = function(settings, cb) {
    if (arguments.length < 2) {
      cb = arguments[0] || nop;
      settings = undefined;
    }

    setIndexNameIfUnset(this.modelName);

    createMappingIfNotPresent({
      client: esClient,
      indexName: indexName,
      typeName: typeName,
      schema: schema,
      settings: settings
    }, cb);
  };

  /**
   * @param options  Object (optional)
   * @param cb Function
   */
  schema.methods.index = function(options, cb) {
    if (arguments.length < 2) {
      cb = arguments[0] || nop;
      options = {};
    }

    if (filter && filter(this)) {
      return cb();
    }

    setIndexNameIfUnset(this.constructor.modelName);

    var index = options.index || indexName,
      type = options.type || typeName;

    if (bulk) {
      /**
       * To serialize in bulk it needs the _id
       */
      var serialModel = serialize(this, mapping);
      serialModel._id = this._id;

      bulkIndex({
        index: index,
        type: type,
        model: serialModel
      });
      setImmediate(cb);
    } else {
      esClient.index({
        index: index,
        type: type,
        id: this._id.toString(),
        body: serialize(this, mapping)
      }, cb);
    }
  };

  /**
   * Unset elasticsearch index
   * @param options - (optional) options for unIndex
   * @param cb - callback when unIndex is complete
   */
  schema.methods.unIndex = function(options, cb) {
    if (arguments.length < 2) {
      cb = arguments[0] || nop;
      options = {};
    }

    setIndexNameIfUnset(this.constructor.modelName);

    options.index = options.index || indexName;
    options.type = options.type || typeName;
    options.model = this;
    options.client = esClient;
    options.tries = 3;

    if (bulk)
      bulkDelete(options, cb);
    else
      deleteByMongoId(options, cb);
  };

  /**
   * Delete all documents from a type/index
   * @param options - (optional) specify index/type
   * @param cb - callback when truncation is complete
   */
  schema.statics.esTruncate = function(options, cb) {
    if (arguments.length < 2) {
      cb = arguments[0] || nop;
      options = {};
    }

    setIndexNameIfUnset(this.modelName);

    var index = options.index || indexName,
      type = options.type || typeName;

    esClient.deleteByQuery({
      index: index,
      type: type,
      body: {
        query: {
          match_all: {}
        }
      }
    }, cb);
  };

  /**
   * Synchronize an existing collection
   *
   * @param query - query for documents you want to synchronize
   */
  schema.statics.synchronize = function(query) {
    var em = new events.EventEmitter(),
      closeValues = [],
      counter = 0,
      close = function() {
        em.emit.apply(em, ['close'].concat(closeValues));
      };

    //Set indexing to be bulk when synchronizing to make synchronizing faster
    bulk = bulk || {
      delay: 1000,
      size: 1000
    };

    query = query || {};

    setIndexNameIfUnset(this.modelName);

    var stream = this.find(query).stream();

    stream.on('data', function(doc) {
      counter++;
      doc.save(function(err) {
        if (err) {
          return em.emit('error', err);
        }

        doc.on('es-indexed', function(err, doc) {
          counter--;
          if (err) {
            em.emit('error', err);
          } else {
            em.emit('data', null, doc);
          }
        });
      });
    });

    stream.on('close', function(a, b) {
      closeValues = [a, b];
      var closeInterval = setInterval(function() {
        if (counter === 0 && bulkBuffer.length === 0) {
          clearInterval(closeInterval);
          close();
        }
      }, 1000);
    });

    stream.on('error', function(err) {
      em.emit('error', err);
    });

    return em;
  };
  /**
   * ElasticSearch search function
   *
   * @param query - query object to perform search with
   * @param options - (optional) special search options, such as hydrate
   * @param cb - callback called with search results
   */
  schema.statics.search = function(query, options, cb) {
    if (arguments.length === 2) {
      cb = arguments[1];
      options = {};
    }

    options.hydrateOptions = options.hydrateOptions || defaultHydrateOptions || {};

    if (query === null)
      query = undefined;

    var _this = this,
      esQuery = {
        body: {
          query: query
        },
        index: options.index || indexName,
        type: options.type || typeName
      };
    if (options.highlight) {
      esQuery.body.highlight = options.highlight;
    }

    Object.keys(options).forEach(function(opt) {
      if (!opt.match(/hydrate/) && options.hasOwnProperty(opt))
        esQuery[opt] = options[opt];
    });

    setIndexNameIfUnset(this.modelName);

    esClient.search(esQuery, function(err, res) {
      if (err) {
        return cb(err);
      }

      if (alwaysHydrate || options.hydrate) {
        hydrate(res, _this, options, cb);
      } else {
        cb(null, res);
      }
    });
  };

  schema.statics.esCount = function(query, cb) {
    setIndexNameIfUnset(this.modelName);

    if (cb == null && typeof query === 'function') {
      cb = query;
      query = null;
    }

    var esQuery = {
      body: {
        query: query
      },
      index: options.index || indexName,
      type:  options.type  || typeName
    };

    esClient.count(esQuery, cb);
  };

  function bulkDelete(options, cb) {
    bulkAdd({
      delete: {
        _index: options.index || indexName,
        _type: options.type || typeName,
        _id: options.model._id.toString()
      }
    });
    cb();
  }

  function bulkIndex(options) {
    bulkAdd({
      index: {
        _index: options.index || indexName,
        _type: options.type || typeName,
        _id: options.model._id.toString()
      }
    });
    bulkAdd(options.model);
  }

  function clearBulkTimeout() {
    clearTimeout(bulkTimeout);
    bulkTimeout = undefined;
  }

  function bulkAdd(instruction) {
    bulkBuffer.push(instruction);

    //Return because we need the doc being indexed
    //Before we start inserting
    if (instruction.index && instruction.index._index)
      return;

    if (bulkBuffer.length >= (bulk.size || 1000)) {
      schema.statics.flush();
      clearBulkTimeout();
    } else if (bulkTimeout === undefined) {
      bulkTimeout = setTimeout(function() {
        schema.statics.flush();
        clearBulkTimeout();
      }, bulk.delay || 1000);
    }
  }

  schema.statics.flush = function(cb) {
    cb = cb || function(err) {
      if (err) {
        console.log(err);
      }
    };

    esClient.bulk({
      body: bulkBuffer
    }, cb);

    bulkBuffer = [];
  };

  schema.statics.refresh = function(options, cb) {
    if (arguments.length < 2) {
      cb = arguments[0] || nop;
      options = {};
    }

    setIndexNameIfUnset(this.modelName);
    esClient.indices.refresh({
      index: options.index || indexName
    }, cb);
  };

  function setIndexNameIfUnset(model) {
    var modelName = model.toLowerCase();
    if (!indexName) {
      indexName = modelName + 's';
    }

    if (!typeName) {
      typeName = modelName;
    }
  }

  /**
   * Use standard Mongoose Middleware hooks
   * to persist to Elasticsearch
   */
  function setUpMiddlewareHooks(schema) {
    schema.post('remove', function(doc) {
      setIndexNameIfUnset(doc.constructor.modelName);

      var options = {
        index: indexName,
        type: typeName,
        tries: 3,
        model: doc,
        client: esClient
      };

      if (bulk) {
        bulkDelete(options, nop);
      } else {
        deleteByMongoId(options, nop);
      }
    });

    /**
     * Save in elasticsearch on save.
     */

    schema.post('save', function(doc) {
      doc.index(function(err, res) {
        if (!filter || !filter(doc)) {
          doc.emit('es-indexed', err, res);
        }
      });
    });
  }

};

function createEsClient(options) {

  var esOptions = {};

  if (util.isArray(options.hosts)) {
    esOptions.host = options.hosts;
  } else {
    esOptions.host = {
      host: options && options.host ? options.host : 'localhost',
      port: options && options.port ? options.port : 9200,
      protocol: options && options.protocol ? options.protocol : 'http',
      auth: options && options.auth ? options.auth : null,
      keepAlive: false
    };
  }

  esOptions.log = (options ? options.log : null);

  return new elasticsearch.Client(esOptions);
}

function createMappingIfNotPresent(options, cb) {
  var client = options.client,
    indexName = options.indexName,
    typeName = options.typeName,
    schema = options.schema,
    settings = options.settings;

  generator.generateMapping(schema, function(err, mapping) {
    var completeMapping = {};
    completeMapping[typeName] = mapping;
    client.indices.exists({index: indexName}, function(err, exists) {
      if (err) {
        return cb(err);
      }

      if (exists) {
        client.indices.putMapping({
          index: indexName,
          type: typeName,
          body: completeMapping
        }, cb);

      } else {
        client.indices.create({index: indexName, body: settings}, function(err) {
          if (err)
            return cb(err);

          client.indices.putMapping({
            index: indexName,
            type: typeName,
            body: completeMapping
          }, cb);
        });
      }
    });
  });
}

function hydrate(res, model, options, cb) {
  var results = res.hits,
    resultsMap = {},
    ids = results.hits.map(function(a, i) {
      resultsMap[a._id] = i;
      return a._id;
    }),

    query = model.find({_id: {$in: ids}}),
    hydrateOptions = options.hydrateOptions;

  // Build Mongoose query based on hydrate options
  // Example: {lean: true, sort: '-name', select: 'address name'}
  Object.keys(hydrateOptions).forEach(function(option) {
    query[option](hydrateOptions[option]);
  });

  query.exec(function(err, docs) {
    if (err) {
      return cb(err);
    } else {
      var hits = [];

      docs.forEach(function(doc) {
        var i = resultsMap[doc._id];
        if (options.highlight) {
          doc._highlight = results.hits[i].highlight;
        }

        hits[i] = doc;
      });

      results.hits = hits;
      res.hits = results;
      cb(null, res);
    }
  });
}

function getMapping(schema) {
  var retMapping = {};
  generator.generateMapping(schema, function(err, mapping) {
    retMapping = mapping;
  });

  return retMapping;
}

function deleteByMongoId(options, cb) {
  var index = options.index,
    type = options.type,
    client = options.client,
    model = options.model,
    tries = options.tries;

  client.delete({
    index: index,
    type: type,
    id: model._id.toString()
  }, function(err, res) {
    if (err && err.message.indexOf('404') > -1) {
      setTimeout(function() {
        if (tries <= 0) {
          return cb(err);
        } else {
          options.tries = --tries;
          deleteByMongoId(options, cb);
        }
      }, 500);
    } else {
      model.emit('es-removed', err, res);
      cb(err);
    }
  });
}
<commit_msg>Fix #73<commit_after>var elasticsearch = require('elasticsearch'),
  Generator = require('./mapping-generator'),
  generator = new Generator(),
  serialize = require('./serialize'),
  events = require('events'),
  nop = require('nop'),
  util = require('util');

module.exports = function Mongoosastic(schema, options) {
  options = options || {};

  var bulkTimeout, bulkBuffer = [], esClient,
    mapping = getMapping(schema),
    indexName = options && options.index,
    typeName = options && options.type,
    alwaysHydrate = options && options.hydrate,
    defaultHydrateOptions = options && options.hydrateOptions,
    bulk = options && options.bulk,
    filter = options && options.filter;

  if (options.esClient) {
    esClient = options.esClient;
  } else {
    esClient = createEsClient(options);
  }

  setUpMiddlewareHooks(schema);

  /**
   * ElasticSearch Client
   */
  schema.statics.esClient = esClient;

  /**
   * Create the mapping. Takes an optional settings parameter and a callback that will be called once
   * the mapping is created

   * @param settings Object (optional)
   * @param cb Function
   */
  schema.statics.createMapping = function(settings, cb) {
    if (arguments.length < 2) {
      cb = arguments[0] || nop;
      settings = undefined;
    }

    setIndexNameIfUnset(this.modelName);

    createMappingIfNotPresent({
      client: esClient,
      indexName: indexName,
      typeName: typeName,
      schema: schema,
      settings: settings
    }, cb);
  };

  /**
   * @param options  Object (optional)
   * @param cb Function
   */
  schema.methods.index = function(options, cb) {
    if (arguments.length < 2) {
      cb = arguments[0] || nop;
      options = {};
    }

    if (filter && filter(this)) {
      return cb();
    }

    setIndexNameIfUnset(this.constructor.modelName);

    var index = options.index || indexName,
      type = options.type || typeName;

    if (bulk) {
      /**
       * To serialize in bulk it needs the _id
       */
      var serialModel = serialize(this, mapping);
      serialModel._id = this._id;

      bulkIndex({
        index: index,
        type: type,
        model: serialModel
      });
      setImmediate(cb);
    } else {
      esClient.index({
        index: index,
        type: type,
        id: this._id.toString(),
        body: serialize(this, mapping)
      }, cb);
    }
  };

  /**
   * Unset elasticsearch index
   * @param options - (optional) options for unIndex
   * @param cb - callback when unIndex is complete
   */
  schema.methods.unIndex = function(options, cb) {
    if (arguments.length < 2) {
      cb = arguments[0] || nop;
      options = {};
    }

    setIndexNameIfUnset(this.constructor.modelName);

    options.index = options.index || indexName;
    options.type = options.type || typeName;
    options.model = this;
    options.client = esClient;
    options.tries = 3;

    if (bulk)
      bulkDelete(options, cb);
    else
      deleteByMongoId(options, cb);
  };

  /**
   * Delete all documents from a type/index
   * @param options - (optional) specify index/type
   * @param cb - callback when truncation is complete
   */
  schema.statics.esTruncate = function(options, cb) {
    if (arguments.length < 2) {
      cb = arguments[0] || nop;
      options = {};
    }

    setIndexNameIfUnset(this.modelName);

    var index = options.index || indexName,
      type = options.type || typeName;

    esClient.deleteByQuery({
      index: index,
      type: type,
      body: {
        query: {
          match_all: {}
        }
      }
    }, cb);
  };

  /**
   * Synchronize an existing collection
   *
   * @param query - query for documents you want to synchronize
   */
  schema.statics.synchronize = function(query) {
    var em = new events.EventEmitter(),
      closeValues = [],
      counter = 0,
      close = function() {
        em.emit.apply(em, ['close'].concat(closeValues));
      };

    //Set indexing to be bulk when synchronizing to make synchronizing faster
    bulk = bulk || {
      delay: 1000,
      size: 1000
    };

    query = query || {};

    setIndexNameIfUnset(this.modelName);

    var stream = this.find(query).stream();

    stream.on('data', function(doc) {
      counter++;
      doc.save(function(err) {
        if (err) {
          return em.emit('error', err);
        }

        doc.on('es-indexed', function(err, doc) {
          counter--;
          if (err) {
            em.emit('error', err);
          } else {
            em.emit('data', null, doc);
          }
        });
      });
    });

    stream.on('close', function(a, b) {
      closeValues = [a, b];
      var closeInterval = setInterval(function() {
        if (counter === 0 && bulkBuffer.length === 0) {
          clearInterval(closeInterval);
          close();
        }
      }, 1000);
    });

    stream.on('error', function(err) {
      em.emit('error', err);
    });

    return em;
  };
  /**
   * ElasticSearch search function
   *
   * @param query - query object to perform search with
   * @param options - (optional) special search options, such as hydrate
   * @param cb - callback called with search results
   */
  schema.statics.search = function(query, options, cb) {
    if (arguments.length === 2) {
      cb = arguments[1];
      options = {};
    }

    options.hydrateOptions = options.hydrateOptions || defaultHydrateOptions || {};

    if (query === null)
      query = undefined;

    setIndexNameIfUnset(this.modelName);

    var _this = this,
      esQuery = {
        body: {
          query: query
        },
        index: options.index || indexName,
        type: options.type || typeName
      };
    if (options.highlight) {
      esQuery.body.highlight = options.highlight;
    }

    Object.keys(options).forEach(function(opt) {
      if (!opt.match(/hydrate/) && options.hasOwnProperty(opt))
        esQuery[opt] = options[opt];
    });

    esClient.search(esQuery, function(err, res) {
      if (err) {
        return cb(err);
      }

      if (alwaysHydrate || options.hydrate) {
        hydrate(res, _this, options, cb);
      } else {
        cb(null, res);
      }
    });
  };

  schema.statics.esCount = function(query, cb) {
    setIndexNameIfUnset(this.modelName);

    if (cb == null && typeof query === 'function') {
      cb = query;
      query = null;
    }

    var esQuery = {
      body: {
        query: query
      },
      index: options.index || indexName,
      type:  options.type  || typeName
    };

    esClient.count(esQuery, cb);
  };

  function bulkDelete(options, cb) {
    bulkAdd({
      delete: {
        _index: options.index || indexName,
        _type: options.type || typeName,
        _id: options.model._id.toString()
      }
    });
    cb();
  }

  function bulkIndex(options) {
    bulkAdd({
      index: {
        _index: options.index || indexName,
        _type: options.type || typeName,
        _id: options.model._id.toString()
      }
    });
    bulkAdd(options.model);
  }

  function clearBulkTimeout() {
    clearTimeout(bulkTimeout);
    bulkTimeout = undefined;
  }

  function bulkAdd(instruction) {
    bulkBuffer.push(instruction);

    //Return because we need the doc being indexed
    //Before we start inserting
    if (instruction.index && instruction.index._index)
      return;

    if (bulkBuffer.length >= (bulk.size || 1000)) {
      schema.statics.flush();
      clearBulkTimeout();
    } else if (bulkTimeout === undefined) {
      bulkTimeout = setTimeout(function() {
        schema.statics.flush();
        clearBulkTimeout();
      }, bulk.delay || 1000);
    }
  }

  schema.statics.flush = function(cb) {
    cb = cb || function(err) {
      if (err) {
        console.log(err);
      }
    };

    esClient.bulk({
      body: bulkBuffer
    }, cb);

    bulkBuffer = [];
  };

  schema.statics.refresh = function(options, cb) {
    if (arguments.length < 2) {
      cb = arguments[0] || nop;
      options = {};
    }

    setIndexNameIfUnset(this.modelName);
    esClient.indices.refresh({
      index: options.index || indexName
    }, cb);
  };

  function setIndexNameIfUnset(model) {
    var modelName = model.toLowerCase();
    if (!indexName) {
      indexName = modelName + 's';
    }

    if (!typeName) {
      typeName = modelName;
    }
  }

  /**
   * Use standard Mongoose Middleware hooks
   * to persist to Elasticsearch
   */
  function setUpMiddlewareHooks(schema) {
    schema.post('remove', function(doc) {
      setIndexNameIfUnset(doc.constructor.modelName);

      var options = {
        index: indexName,
        type: typeName,
        tries: 3,
        model: doc,
        client: esClient
      };

      if (bulk) {
        bulkDelete(options, nop);
      } else {
        deleteByMongoId(options, nop);
      }
    });

    /**
     * Save in elasticsearch on save.
     */

    schema.post('save', function(doc) {
      doc.index(function(err, res) {
        if (!filter || !filter(doc)) {
          doc.emit('es-indexed', err, res);
        }
      });
    });
  }

};

function createEsClient(options) {

  var esOptions = {};

  if (util.isArray(options.hosts)) {
    esOptions.host = options.hosts;
  } else {
    esOptions.host = {
      host: options && options.host ? options.host : 'localhost',
      port: options && options.port ? options.port : 9200,
      protocol: options && options.protocol ? options.protocol : 'http',
      auth: options && options.auth ? options.auth : null,
      keepAlive: false
    };
  }

  esOptions.log = (options ? options.log : null);

  return new elasticsearch.Client(esOptions);
}

function createMappingIfNotPresent(options, cb) {
  var client = options.client,
    indexName = options.indexName,
    typeName = options.typeName,
    schema = options.schema,
    settings = options.settings;

  generator.generateMapping(schema, function(err, mapping) {
    var completeMapping = {};
    completeMapping[typeName] = mapping;
    client.indices.exists({index: indexName}, function(err, exists) {
      if (err) {
        return cb(err);
      }

      if (exists) {
        client.indices.putMapping({
          index: indexName,
          type: typeName,
          body: completeMapping
        }, cb);

      } else {
        client.indices.create({index: indexName, body: settings}, function(err) {
          if (err)
            return cb(err);

          client.indices.putMapping({
            index: indexName,
            type: typeName,
            body: completeMapping
          }, cb);
        });
      }
    });
  });
}

function hydrate(res, model, options, cb) {
  var results = res.hits,
    resultsMap = {},
    ids = results.hits.map(function(a, i) {
      resultsMap[a._id] = i;
      return a._id;
    }),

    query = model.find({_id: {$in: ids}}),
    hydrateOptions = options.hydrateOptions;

  // Build Mongoose query based on hydrate options
  // Example: {lean: true, sort: '-name', select: 'address name'}
  Object.keys(hydrateOptions).forEach(function(option) {
    query[option](hydrateOptions[option]);
  });

  query.exec(function(err, docs) {
    if (err) {
      return cb(err);
    } else {
      var hits = [];

      docs.forEach(function(doc) {
        var i = resultsMap[doc._id];
        if (options.highlight) {
          doc._highlight = results.hits[i].highlight;
        }

        hits[i] = doc;
      });

      results.hits = hits;
      res.hits = results;
      cb(null, res);
    }
  });
}

function getMapping(schema) {
  var retMapping = {};
  generator.generateMapping(schema, function(err, mapping) {
    retMapping = mapping;
  });

  return retMapping;
}

function deleteByMongoId(options, cb) {
  var index = options.index,
    type = options.type,
    client = options.client,
    model = options.model,
    tries = options.tries;

  client.delete({
    index: index,
    type: type,
    id: model._id.toString()
  }, function(err, res) {
    if (err && err.message.indexOf('404') > -1) {
      setTimeout(function() {
        if (tries <= 0) {
          return cb(err);
        } else {
          options.tries = --tries;
          deleteByMongoId(options, cb);
        }
      }, 500);
    } else {
      model.emit('es-removed', err, res);
      cb(err);
    }
  });
}
<|endoftext|>


<commit_before>const getTop = require('./../../api/reddit-api').getTop;

const mySubreddits = [
    'nottheonion',
    'videos',
    'funny',
    'gifs',
    'pics',
    'gaming',
    'aww',
    'dankmemes',
    'programmerhumor',
    'crappydesign',
    'upliftingnews',
    'mildyinteresting',
    'food'
];

function getRandomNumber(max){
    return Math.floor(Math.random() * max);
}

function getRandomSubreddit(x = 3) {
    let subredditList = [];
    for (let i = 0; i < x ; i++) {
        let randomNum = getRandomNumber(mySubreddits.length);
        while (subredditList.includes(mySubreddits[randomNum])) {
            randomNum = getRandomNumber(mySubreddits.length);
        }
        subredditList.push(mySubreddits[randomNum]);
    }
    return subredditList;
}

const fetchTopContents = async() => {
    let list = getRandomSubreddit();
    let getTopPromise = [];
    let topContents = [];

    for (let subreddit of list) {
        getTopPromise.push(getTop(subreddit));
    }

    let contentRes = await Promise.all(getTopPromise);
    for (let content of contentRes) {
        topContents.push(content[getRandomNumber(content.length)]);
    }
    return topContents;
};

module.exports = fetchTopContents;
<commit_msg>Remove dankmemes subreddit in fetchContent<commit_after>const getTop = require('./../../api/reddit-api').getTop;

const mySubreddits = [
    'nottheonion',
    'videos',
    'funny',
    'gifs',
    'pics',
    'gaming',
    'aww',
    'programmerhumor',
    'crappydesign',
    'upliftingnews',
    'mildyinteresting',
    'food'
];

function getRandomNumber(max){
    return Math.floor(Math.random() * max);
}

function getRandomSubreddit(x = 3) {
    let subredditList = [];
    for (let i = 0; i < x ; i++) {
        let randomNum = getRandomNumber(mySubreddits.length);
        while (subredditList.includes(mySubreddits[randomNum])) {
            randomNum = getRandomNumber(mySubreddits.length);
        }
        subredditList.push(mySubreddits[randomNum]);
    }
    return subredditList;
}

const fetchTopContents = async() => {
    let list = getRandomSubreddit();
    let getTopPromise = [];
    let topContents = [];

    for (let subreddit of list) {
        getTopPromise.push(getTop(subreddit));
    }

    let contentRes = await Promise.all(getTopPromise);
    for (let content of contentRes) {
        topContents.push(content[getRandomNumber(content.length)]);
    }
    return topContents;
};

module.exports = fetchTopContents;
<|endoftext|>


<commit_before>/*
 * Screens.js - v0.9b
 * Copyright (C) 2013 Andreu Santaren Llop
 */

// Init Screens.js library
{
    Array.prototype.last = function()
    {
        return this[this.length - 1];
    };

    Array.prototype.remove = function(index)
    {
        this.splice(index, 1);
    };

    window.BROADCAST_REGISTER_ARRAY = new Array();
    window.SCREEN_STACK = new Array();
}

// Create a class
function __CONTEXT_CLASS__(context, name, obj1, obj2)
{
    if (obj2 !== undefined)            // New class by heritance
    {
        if (typeof(obj1) !== "object")
        {
            throw "Extending from a non object";
        }

        // Clone the object
        var _asc_ = __CLONE__(obj1);

        // Get all element of obj and push it in _asc_
        for (var index in obj2)
        {
            var attr = obj2[index];
            _asc_[index] = attr;
        }

        // Init base properties
        _asc_.CLASS_TYPE.push(_asc_.CLASS_NAME);
        _asc_.CLASS_NAME = name;

        // Create the variable for the class
        context[name] = _asc_;
        return _asc_;
    }
    else                        // New base class
    {
        // Init base methods
        obj1.New = function()
        {
            var newObj = __CLONE__(this);

            delete newObj.New;

            newObj.OBJECT_UNIQUE_KEY = Math.floor(Math.random() * 10000000000000000).toString();

            // Call constructor
            newObj['Init'].apply(newObj, arguments);

            return newObj;
        };

        obj1.Super = function()
        {
            var superClass = this.CLASS_TYPE.last();

            if (superClass === "__CLASS__")
            {
                return null;
            }
            else
            {
                var object = __CLONE__(eval(superClass));
                delete object.New;

                __MERGE__(this, object);

                return object;
            }
        };

        if (!('Init' in obj1))
        {
            obj1.Init = function() {};
        }

        // Init base properties
        obj1.CLASS_TYPE = new Array("__CLASS__");
        obj1.CLASS_NAME = name;

        // Create the variable for the class
        context[name] = obj1;
        return obj1;
    }
}

// Create a class in the global context
function __CLASS__(name, obj1, obj2)
{
    return __CONTEXT_CLASS__(window, name, obj1, obj2);
}

// Base class
__CLASS__('Obj',
{
    Clone: function(obj)
    {
        return __CLONE__(obj);
    },

	Serialize: function(obj)
	{
		return __SERIALIZE__(obj);
	},

	Deserialize: function(obj)
	{
		return __DESERIALIZE__(obj);
	},

	Merge: function(org, dest)
	{
    	__MERGE__(org, dest);
	},

    LoadClass: function(name, baseurl, readyCallBack)
    {
        return __LOAD_CLASS__(name, baseurl, this, readyCallBack);
    },

    UnloadClass: function(classname)
    {
        return __UNLOAD_CLASS__(classname);
    },

	RegisterReceiver: function(delegate, filter)
    {
        return __REGISTER_RECEIVER__(this, delegate, filter);
    },

    UnregisterReceiver: function(receiver)
    {
        __UNREGISTER_RECEIVER__(receiver);
    },

    SendOrderedBroadcast: function(filter, message)
    {
        __SEND_BROADCAST__(filter, message, false);
    },

    SendBroadcast: function(filter, message)
    {
        __SEND_BROADCAST__(filter, message, true);
    },

    FindReceiver: function(receiver)
    {
        return __FIND_RECEIVER__(receiver);
    }
});

// Base class for screens
__CLASS__('Screen', Obj,
{
    OnLoad: function()
    {},

    OnClose: function()
    {},

    OnReturn: function(retdata)
    {},

    CloseScreen: function(screen)
    {
        screen.OnClose();
    },

    PushScreen: function(selector, screen, loader, args)
    {
        __PUSH_SCREEN__(selector, screen, loader, args);
    },

    PopScreen: function(retdata, args)
    {
        __POP_SCREEN__(retdata, args);
    },

    SetScreen: function(selector, screen)
    {
        return __SET_SCREEN__(selector, screen);
    },

    SetHtml: function(selector, html)
    {
	    __SET_HTML__(this, selector, html);
    },

    Brand: function(selector)
    {
	    __BRAND__(seletor, this);
    },

    Unbrand: function(selector)
    {
	    __UNBRAND__(selector);
    },

    Callback: function(callback)
    {
        return __CALLBACK__(this, callback);
    },

    Ref: function(selector)
    {
        return __REF__(this, selector);
    },

    Click: function(delegate, selector)
    {
        __CLICK__(this, delegate, selector);
    },

    DoubleClick: function(delegate, selector)
    {
        __DOUBLE_CLICK__(this, delegate, selector);
    },

    ListClick: function(delegate, selector)
    {
        __LIST_CLICK__(this, delegate, selector);
    },

    Change: function(delegate, selector)
    {
        __CHANGE__(this, delegate, selector);
    },

    CheckboxClick: function(delegate, selector)
    {
        __CHECKER_CLICK__(this, delegate, selector, 'checkbox');
    },

    RadioClick: function(delegate, selector)
    {
        __CHECKER_CLICK__(this, delegate, selector, 'radio');
    }
});

// Clone an object
function __CLONE__(obj)
{
   return __DESERIALIZE__(__SERIALIZE__(obj));
}

// Convert a class object to string
function __SERIALIZE__(obj)
{
    var fooArray = new Array();
    var attrArray = new Array();

    // Find all functions and convert to string
    for (var attr in obj)
    {
        if (typeof(obj[attr]) === 'function')
        {
            fooArray.push(obj[attr].toString());
            attrArray.push(attr);
        }
    }

    // Obtain a copy of the object (ignoring functions)
    var json = JSON.stringify(obj);
    var copy = JSON.parse(json);

    // Replace the original functions by the strings
    for (var i = 0 ; i < fooArray.length ; i++)
    {
        copy[attrArray[i]] = '(' + fooArray[i] + ')';
    }

    // Add an array for function names
    copy.OBJ_FOO_NAMES = attrArray;

    var serialized = JSON.stringify(copy);

    return serialized;
}

// Convert a string to a class object
function __DESERIALIZE__(obj)
{
    var copy = JSON.parse(obj);

    if (typeof(copy.OBJ_FOO_NAMES) !== 'object')
    {
        throw 'Not a valid serialized object';
    }

    // Take function string and evaluate to obtain the originals
    for (var i = 0 ; i < copy.OBJ_FOO_NAMES.length ; i++)
    {
        var fooStr = copy[copy.OBJ_FOO_NAMES[i]];
        copy[copy.OBJ_FOO_NAMES[i]] = eval('(' + fooStr + ')');
    }

    delete copy.OBJ_FOO_NAMES;

    return copy;
}

// Copy existing propierties of org object to dest object
function __MERGE__(org, dest)
{
    for (var attr in org)
    {
        if (typeof(org[attr]) !== 'function')
        {
            if (attr != 'CLASS_NAME' && attr != 'CLASS_TYPE' && attr != '__proto__')
            {
                if (dest[attr] !== undefined)
                {
                    dest[attr] = org[attr];
                }
            }
        }
    }
}

function __PUSH_SCREEN__(selector, screen, loader, args)
{
    var name = screen.CLASS_NAME;

    // Create an instance of screen class and save it in the stack
    var callname = name + '.New()';
    var screenObj = eval(callname);

    __BRAND__(selector, screenObj);

    loader(selector, args);     // execute loader callback
    screenObj.OnLoad();         // Screen OnLoad methode

    // Add screen to stack
    window.SCREEN_STACK.push({screen: screenObj, selector: selector, loader: loader});
}

function __POP_SCREEN__(retdata, args)
{
    // It is the main screen, no one in the back
    if (window.SCREEN_STACK.length <= 1)
    {
        return;
    }

    // Close the current screen
    var lastScreen = window.SCREEN_STACK.pop();
    lastScreen.screen.OnClose();

    // Get de previus screen object
    var stackObj = window.SCREEN_STACK.last();

    var pageSelector = stackObj.selector;
    var lastPageSelector = lastScreen.selector;

    // Disable all events
    $(lastPageSelector).add("*").off();

    __UNBRAND__(lastScreen.screen);

    stackObj.loader(pageSelector, args);
    stackObj.screen.OnLoad();      // Screen OnLoad methode

    // Call the return function
    if (retdata !== null)
    {
        stackObj.screen.OnReturn(retdata);
    }
}

// Set a Screen class to control a part of an existing page
function __SET_SCREEN__(selector, screen)
{
    var name = screen.CLASS_NAME;

    // Disable all events
    $(selector).find("*").off();

    // Create an instance of screen class and save it in the stack
    var callname = name + '.New()';
    var screenObj = eval(callname);

    __BRAND__(selector, screenObj);

    screenObj.OnLoad();      // Screen OnLoad methode

    return screenObj;
}

// Insert HTML in the DOM and update screen classes
function __SET_HTML__(screen, selector, html)
{
	__REF__(screen, selector).html(html);
    __BRAND__(selector, screen);
}

// Set the Screens brand for a piece of DOM
function __BRAND__(selector, screen)
{
	$(selector).attr('screensclass', screen.CLASS_NAME);
    $(selector).attr('screensid', screen.OBJECT_UNIQUE_KEY);
	$(selector).find('*').attr('screensclass', screen.CLASS_NAME);
    $(selector).find('*').attr('screensid', screen.OBJECT_UNIQUE_KEY);
}

// Remove the Screens brand
function __UNBRAND__(selector)
{
	$(selector).removeAttr('screensclass');
    $(selector).removeAttr('screensid');
	$(selector).find('*').removeAttr('screensclass');
    $(selector).find('*').removeAttr('screensid');
}

// Load a class
function __LOAD_CLASS__(name, baseurl, obj, readyCallBack)
{
    var jsurl = baseurl + "/" + name + ".js";

    if (typeof(window[name]) === 'undefined')
    {
        if (readyCallBack == null)
        {
            jQuery.ajaxSetup({async:false});

            // Load JavaScript class
            $.getScript(jsurl);

            jQuery.ajaxSetup({async:true});

            return true;
        }
        else
        {
            // Load JavaScript class
            $.getScript(jsurl, $.proxy(readyCallBack, obj));

            return true;
        }
    }
    else
    {
        // Class already loaded
        return false;
    }
}

function __UNLOAD_CLASS__(classname)
{
    if (typeof(classname) === 'string')
    {
        return delete window[classname];
    }
    else
    {
        return false;
    }
}

// Callback in the obj context setting the original 'this' as argument
function __CALLBACK__(obj, callback)
{
    return function(args)
    {
        $.proxy(callback, obj)(this, args);
    };
}

// Get an element of a screen by a selector
function __REF__(obj, selector)
{
    return $(
    			"[screensclass='" + obj.CLASS_NAME + "']" +
    			"[screensid='" + obj.OBJECT_UNIQUE_KEY + "']" +
    			selector
    		);
}

// Set click event delegate
function __CLICK__(obj, delegate, selector)
{
    __REF__(obj, selector).click(__CALLBACK__(obj, delegate));
}

// Set double click event delegate
function __DOUBLE_CLICK__(obj, delegate, selector)
{
    __REF__(obj, selector).dblclick(__CALLBACK__(obj, delegate));
}

// Set list click event delegate
function __LIST_CLICK__(obj, delegate, selector)
{
    __REF__(obj, selector).on('click', 'li', __CALLBACK__(obj, delegate));
}

// Element changed delegate
function __CHANGE__(obj, delegate, selector)
{
    __REF__(obj, selector).on('change', __CALLBACK__(obj, delegate));
}

// Set checkbox/radio click event delegate
function __CHECKER_CLICK__(obj, delegate, selector, type)
{
	__REF__(obj, selector).find('input[type=' + type + ']').on('click', __CALLBACK__(obj, delegate));
}

// Register a broadcast receiver
function __REGISTER_RECEIVER__(obj, delegate, filter)
{
    var broadcastObj = {callback: $.proxy(delegate, obj), filter: filter, id: Math.random()};
    window.BROADCAST_REGISTER_ARRAY.push(broadcastObj);

    return broadcastObj.id;
}

// Unregister a broadcast receiver
function __UNREGISTER_RECEIVER__(receiver)
{
    for (var i = 0 ; i < window.BROADCAST_REGISTER_ARRAY.length ; i++)
    {
        if (window.BROADCAST_REGISTER_ARRAY[i].id === receiver)
        {
            window.BROADCAST_REGISTER_ARRAY.remove(i);
            return;
        }
    }
}

// Send broadcast message
function __SEND_BROADCAST__(filter, message, massive)
{
    // Make a copy of the array to avoid problems when others change it
    var arrCopy = window.BROADCAST_REGISTER_ARRAY.slice();

    for (var i = 0 ; i < arrCopy.length ; i++)
    {
        if (arrCopy[i].filter === filter)
        {
            if (massive)
            {
                arrCopy[i].callback(message);
            }
            else
            {
                // If ordered broadcast and callback returns true, halt broadcast
                if (arrCopy[i].callback(message)) return;
            }
        }
    }
}

function __FIND_RECEIVER__(receiver)
{
    for (var i = 0 ; i < window.BROADCAST_REGISTER_ARRAY.length ; i++)
    {
        if (window.BROADCAST_REGISTER_ARRAY[i].id === receiver)
        {
            return window.BROADCAST_REGISTER_ARRAY[i];
        }
    }
}
<commit_msg>Loader can be null.<commit_after>/*
 * Screens.js - v0.9b
 * Copyright (C) 2013 Andreu Santaren Llop
 */

// Init Screens.js library
{
    Array.prototype.last = function()
    {
        return this[this.length - 1];
    };

    Array.prototype.remove = function(index)
    {
        this.splice(index, 1);
    };

    window.BROADCAST_REGISTER_ARRAY = new Array();
    window.SCREEN_STACK = new Array();
}

// Create a class
function __CONTEXT_CLASS__(context, name, obj1, obj2)
{
    if (obj2 !== undefined)            // New class by heritance
    {
        if (typeof(obj1) !== "object")
        {
            throw "Extending from a non object";
        }

        // Clone the object
        var _asc_ = __CLONE__(obj1);

        // Get all element of obj and push it in _asc_
        for (var index in obj2)
        {
            var attr = obj2[index];
            _asc_[index] = attr;
        }

        // Init base properties
        _asc_.CLASS_TYPE.push(_asc_.CLASS_NAME);
        _asc_.CLASS_NAME = name;

        // Create the variable for the class
        context[name] = _asc_;
        return _asc_;
    }
    else                        // New base class
    {
        // Init base methods
        obj1.New = function()
        {
            var newObj = __CLONE__(this);

            delete newObj.New;

            newObj.OBJECT_UNIQUE_KEY = Math.floor(Math.random() * 10000000000000000).toString();

            // Call constructor
            newObj['Init'].apply(newObj, arguments);

            return newObj;
        };

        obj1.Super = function()
        {
            var superClass = this.CLASS_TYPE.last();

            if (superClass === "__CLASS__")
            {
                return null;
            }
            else
            {
                var object = __CLONE__(eval(superClass));
                delete object.New;

                __MERGE__(this, object);

                return object;
            }
        };

        if (!('Init' in obj1))
        {
            obj1.Init = function() {};
        }

        // Init base properties
        obj1.CLASS_TYPE = new Array("__CLASS__");
        obj1.CLASS_NAME = name;

        // Create the variable for the class
        context[name] = obj1;
        return obj1;
    }
}

// Create a class in the global context
function __CLASS__(name, obj1, obj2)
{
    return __CONTEXT_CLASS__(window, name, obj1, obj2);
}

// Base class
__CLASS__('Obj',
{
    Clone: function(obj)
    {
        return __CLONE__(obj);
    },

	Serialize: function(obj)
	{
		return __SERIALIZE__(obj);
	},

	Deserialize: function(obj)
	{
		return __DESERIALIZE__(obj);
	},

	Merge: function(org, dest)
	{
    	__MERGE__(org, dest);
	},

    LoadClass: function(name, baseurl, readyCallBack)
    {
        return __LOAD_CLASS__(name, baseurl, this, readyCallBack);
    },

    UnloadClass: function(classname)
    {
        return __UNLOAD_CLASS__(classname);
    },

	RegisterReceiver: function(delegate, filter)
    {
        return __REGISTER_RECEIVER__(this, delegate, filter);
    },

    UnregisterReceiver: function(receiver)
    {
        __UNREGISTER_RECEIVER__(receiver);
    },

    SendOrderedBroadcast: function(filter, message)
    {
        __SEND_BROADCAST__(filter, message, false);
    },

    SendBroadcast: function(filter, message)
    {
        __SEND_BROADCAST__(filter, message, true);
    },

    FindReceiver: function(receiver)
    {
        return __FIND_RECEIVER__(receiver);
    }
});

// Base class for screens
__CLASS__('Screen', Obj,
{
    OnLoad: function()
    {},

    OnClose: function()
    {},

    OnReturn: function(retdata)
    {},

    CloseScreen: function(screen)
    {
        screen.OnClose();
    },

    PushScreen: function(selector, screen, loader, args)
    {
        __PUSH_SCREEN__(selector, screen, loader, args);
    },

    PopScreen: function(retdata, args)
    {
        __POP_SCREEN__(retdata, args);
    },

    SetScreen: function(selector, screen)
    {
        return __SET_SCREEN__(selector, screen);
    },

    SetHtml: function(selector, html)
    {
	    __SET_HTML__(this, selector, html);
    },

    Brand: function(selector)
    {
	    __BRAND__(seletor, this);
    },

    Unbrand: function(selector)
    {
	    __UNBRAND__(selector);
    },

    Callback: function(callback)
    {
        return __CALLBACK__(this, callback);
    },

    Ref: function(selector)
    {
        return __REF__(this, selector);
    },

    Click: function(delegate, selector)
    {
        __CLICK__(this, delegate, selector);
    },

    DoubleClick: function(delegate, selector)
    {
        __DOUBLE_CLICK__(this, delegate, selector);
    },

    ListClick: function(delegate, selector)
    {
        __LIST_CLICK__(this, delegate, selector);
    },

    Change: function(delegate, selector)
    {
        __CHANGE__(this, delegate, selector);
    },

    CheckboxClick: function(delegate, selector)
    {
        __CHECKER_CLICK__(this, delegate, selector, 'checkbox');
    },

    RadioClick: function(delegate, selector)
    {
        __CHECKER_CLICK__(this, delegate, selector, 'radio');
    }
});

// Clone an object
function __CLONE__(obj)
{
   return __DESERIALIZE__(__SERIALIZE__(obj));
}

// Convert a class object to string
function __SERIALIZE__(obj)
{
    var fooArray = new Array();
    var attrArray = new Array();

    // Find all functions and convert to string
    for (var attr in obj)
    {
        if (typeof(obj[attr]) === 'function')
        {
            fooArray.push(obj[attr].toString());
            attrArray.push(attr);
        }
    }

    // Obtain a copy of the object (ignoring functions)
    var json = JSON.stringify(obj);
    var copy = JSON.parse(json);

    // Replace the original functions by the strings
    for (var i = 0 ; i < fooArray.length ; i++)
    {
        copy[attrArray[i]] = '(' + fooArray[i] + ')';
    }

    // Add an array for function names
    copy.OBJ_FOO_NAMES = attrArray;

    var serialized = JSON.stringify(copy);

    return serialized;
}

// Convert a string to a class object
function __DESERIALIZE__(obj)
{
    var copy = JSON.parse(obj);

    if (typeof(copy.OBJ_FOO_NAMES) !== 'object')
    {
        throw 'Not a valid serialized object';
    }

    // Take function string and evaluate to obtain the originals
    for (var i = 0 ; i < copy.OBJ_FOO_NAMES.length ; i++)
    {
        var fooStr = copy[copy.OBJ_FOO_NAMES[i]];
        copy[copy.OBJ_FOO_NAMES[i]] = eval('(' + fooStr + ')');
    }

    delete copy.OBJ_FOO_NAMES;

    return copy;
}

// Copy existing propierties of org object to dest object
function __MERGE__(org, dest)
{
    for (var attr in org)
    {
        if (typeof(org[attr]) !== 'function')
        {
            if (attr != 'CLASS_NAME' && attr != 'CLASS_TYPE' && attr != '__proto__')
            {
                if (dest[attr] !== undefined)
                {
                    dest[attr] = org[attr];
                }
            }
        }
    }
}

function __PUSH_SCREEN__(selector, screen, loader, args)
{
    var name = screen.CLASS_NAME;

    // Create an instance of screen class and save it in the stack
    var callname = name + '.New()';
    var screenObj = eval(callname);

    __BRAND__(selector, screenObj);

    if (loader != null)
    {
        loader(selector, args);     // execute loader callback
    }

    screenObj.OnLoad();         // Screen OnLoad methode

    // Add screen to stack
    window.SCREEN_STACK.push({screen: screenObj, selector: selector, loader: loader});
}

function __POP_SCREEN__(retdata, args)
{
    // It is the main screen, no one in the back
    if (window.SCREEN_STACK.length <= 1)
    {
        return;
    }

    // Close the current screen
    var lastScreen = window.SCREEN_STACK.pop();
    lastScreen.screen.OnClose();

    // Get de previus screen object
    var stackObj = window.SCREEN_STACK.last();

    var pageSelector = stackObj.selector;
    var lastPageSelector = lastScreen.selector;

    // Disable all events
    $(lastPageSelector).add("*").off();

    __UNBRAND__(lastScreen.screen);

    if (stackObj.loader != null)
    {
        stackObj.loader(pageSelector, args);
    }

    stackObj.screen.OnLoad();      // Screen OnLoad methode

    // Call the return function
    if (retdata !== null)
    {
        stackObj.screen.OnReturn(retdata);
    }
}

// Set a Screen class to control a part of an existing page
function __SET_SCREEN__(selector, screen)
{
    var name = screen.CLASS_NAME;

    // Disable all events
    $(selector).find("*").off();

    // Create an instance of screen class and save it in the stack
    var callname = name + '.New()';
    var screenObj = eval(callname);

    __BRAND__(selector, screenObj);

    screenObj.OnLoad();      // Screen OnLoad methode

    return screenObj;
}

// Insert HTML in the DOM and update screen classes
function __SET_HTML__(screen, selector, html)
{
	__REF__(screen, selector).html(html);
    __BRAND__(selector, screen);
}

// Set the Screens brand for a piece of DOM
function __BRAND__(selector, screen)
{
	$(selector).attr('screensclass', screen.CLASS_NAME);
    $(selector).attr('screensid', screen.OBJECT_UNIQUE_KEY);
	$(selector).find('*').attr('screensclass', screen.CLASS_NAME);
    $(selector).find('*').attr('screensid', screen.OBJECT_UNIQUE_KEY);
}

// Remove the Screens brand
function __UNBRAND__(selector)
{
	$(selector).removeAttr('screensclass');
    $(selector).removeAttr('screensid');
	$(selector).find('*').removeAttr('screensclass');
    $(selector).find('*').removeAttr('screensid');
}

// Load a class
function __LOAD_CLASS__(name, baseurl, obj, readyCallBack)
{
    var jsurl = baseurl + "/" + name + ".js";

    if (typeof(window[name]) === 'undefined')
    {
        if (readyCallBack == null)
        {
            jQuery.ajaxSetup({async:false});

            // Load JavaScript class
            $.getScript(jsurl);

            jQuery.ajaxSetup({async:true});

            return true;
        }
        else
        {
            // Load JavaScript class
            $.getScript(jsurl, $.proxy(readyCallBack, obj));

            return true;
        }
    }
    else
    {
        // Class already loaded
        return false;
    }
}

function __UNLOAD_CLASS__(classname)
{
    if (typeof(classname) === 'string')
    {
        return delete window[classname];
    }
    else
    {
        return false;
    }
}

// Callback in the obj context setting the original 'this' as argument
function __CALLBACK__(obj, callback)
{
    return function(args)
    {
        $.proxy(callback, obj)(this, args);
    };
}

// Get an element of a screen by a selector
function __REF__(obj, selector)
{
    return $(
    			"[screensclass='" + obj.CLASS_NAME + "']" +
    			"[screensid='" + obj.OBJECT_UNIQUE_KEY + "']" +
    			selector
    		);
}

// Set click event delegate
function __CLICK__(obj, delegate, selector)
{
    __REF__(obj, selector).click(__CALLBACK__(obj, delegate));
}

// Set double click event delegate
function __DOUBLE_CLICK__(obj, delegate, selector)
{
    __REF__(obj, selector).dblclick(__CALLBACK__(obj, delegate));
}

// Set list click event delegate
function __LIST_CLICK__(obj, delegate, selector)
{
    __REF__(obj, selector).on('click', 'li', __CALLBACK__(obj, delegate));
}

// Element changed delegate
function __CHANGE__(obj, delegate, selector)
{
    __REF__(obj, selector).on('change', __CALLBACK__(obj, delegate));
}

// Set checkbox/radio click event delegate
function __CHECKER_CLICK__(obj, delegate, selector, type)
{
	__REF__(obj, selector).find('input[type=' + type + ']').on('click', __CALLBACK__(obj, delegate));
}

// Register a broadcast receiver
function __REGISTER_RECEIVER__(obj, delegate, filter)
{
    var broadcastObj = {callback: $.proxy(delegate, obj), filter: filter, id: Math.random()};
    window.BROADCAST_REGISTER_ARRAY.push(broadcastObj);

    return broadcastObj.id;
}

// Unregister a broadcast receiver
function __UNREGISTER_RECEIVER__(receiver)
{
    for (var i = 0 ; i < window.BROADCAST_REGISTER_ARRAY.length ; i++)
    {
        if (window.BROADCAST_REGISTER_ARRAY[i].id === receiver)
        {
            window.BROADCAST_REGISTER_ARRAY.remove(i);
            return;
        }
    }
}

// Send broadcast message
function __SEND_BROADCAST__(filter, message, massive)
{
    // Make a copy of the array to avoid problems when others change it
    var arrCopy = window.BROADCAST_REGISTER_ARRAY.slice();

    for (var i = 0 ; i < arrCopy.length ; i++)
    {
        if (arrCopy[i].filter === filter)
        {
            if (massive)
            {
                arrCopy[i].callback(message);
            }
            else
            {
                // If ordered broadcast and callback returns true, halt broadcast
                if (arrCopy[i].callback(message)) return;
            }
        }
    }
}

function __FIND_RECEIVER__(receiver)
{
    for (var i = 0 ; i < window.BROADCAST_REGISTER_ARRAY.length ; i++)
    {
        if (window.BROADCAST_REGISTER_ARRAY[i].id === receiver)
        {
            return window.BROADCAST_REGISTER_ARRAY[i];
        }
    }
}
<|endoftext|>


<commit_before>var _ = require("underscore");
var expect = require("chai").expect;
var React = require("react/addons");
var TestUtils = React.addons.TestUtils;

var config = require("../js/config/config");
var AppsActions = require("../js/actions/AppsActions");
var AppsEvents = require("../js/events/AppsEvents");
var AppsStore = require("../js/stores/AppsStore");
var InfoStore = require("../js/stores/InfoStore");
var States = require("../js/constants/States");
var TasksActions = require("../js/actions/TasksActions");
var TasksEvents = require("../js/events/TasksEvents");
var TaskListItemComponent = require("../js/components/TaskListItemComponent");
var TaskDetailComponent = require("../js/components/TaskDetailComponent");
var TaskListComponent = require("../js/components/TaskListComponent");
var TaskMesosUrlComponent = require("../js/components/TaskMesosUrlComponent");

var expectAsync = require("./helpers/expectAsync");
var HttpServer = require("./helpers/HttpServer").HttpServer;
var ShallowUtils = require("./helpers/ShallowUtils");

var server = new HttpServer(config.localTestserverURI);
config.apiURL = "http://" + server.address + ":" + server.port + "/";

describe("Tasks", function () {

  beforeEach(function (done) {
    this.server = server
    .setup({
      "app": {
        id: "/app-1",
        tasks: [{
          id: "task-1",
          appId: "/app-1"
        },
        {
          id: "task-2",
          appId: "/app-1"
        }]
      }
    }, 200)
    .start(function () {
      AppsStore.once(AppsEvents.CHANGE, done);
      AppsActions.requestApp("/app-1");
    });
  });

  afterEach(function (done) {
    this.server.stop(done);
  });

  describe("on single task deletion", function () {

    it("updates the tasks array on success", function (done) {
      this.server.setup("", 200);

      AppsStore.once(AppsEvents.CHANGE, function () {
        expectAsync(function () {
          expect(AppsStore.currentApp.tasks).to.have.length(1);
          expect(_.where(AppsStore.currentApp.tasks, {
            id: "task-1"
          })).to.be.empty;
        }, done);
      });

      TasksActions.deleteTasks("/app-1", ["task-1"]);
    });

    it("handles failure gracefully", function (done) {
      this.server.setup({message: "Guru Meditation"}, 404);

      AppsStore.once(TasksEvents.DELETE_ERROR, function (error) {
        expectAsync(function () {
          expect(error.message).to.equal("Guru Meditation");
        }, done);
      });

      TasksActions.deleteTasks("/app-1", "task-3");
    });

  });

  describe("on single task deletion and scale", function () {

    it("updates the tasks array on success", function (done) {
      this.server.setup("", 200);

      AppsStore.once(AppsEvents.CHANGE, function () {
        expectAsync(function () {
          expect(AppsStore.currentApp.tasks).to.have.length(1);
          expect(_.where(AppsStore.currentApp.tasks, {
            id: "task-2"
          })).to.be.empty;
        }, done);
      });

      TasksActions.deleteTasksAndScale("/app-1", ["task-2"]);
    });

  });

  describe("on multiple task deletion", function () {

    it("updates the tasks array on success", function (done) {
      this.server.setup("", 200);

      AppsStore.once(AppsEvents.CHANGE, function () {
        expectAsync(function () {
          expect(AppsStore.currentApp.tasks).to.have.length(0);
        }, done);
      });

      TasksActions.deleteTasks("/app-1", ["task-1", "task-2"]);
    });

    it("handles failure gracefully", function (done) {
      this.server.setup({message: "Guru Meditation"}, 404);

      AppsStore.once(TasksEvents.DELETE_ERROR, function (error) {
        expectAsync(function () {
          expect(error.message).to.equal("Guru Meditation");
        }, done);
      });

      TasksActions.deleteTasks("/app-1", "task-3");
    });

  });

  describe("on multiple task deletion and scale", function () {

    it("updates the tasks array on success", function (done) {
      this.server.setup("", 200);

      AppsStore.once(AppsEvents.CHANGE, function () {
        expectAsync(function () {
          expect(AppsStore.currentApp.tasks).to.have.length(0);
        }, done);
      });

      TasksActions.deleteTasksAndScale("/app-1", ["task-1", "task-2"]);
    });

    it("handles failure gracefully", function (done) {
      this.server.setup({message: "Guru Meditation"}, 404);

      AppsStore.once(TasksEvents.DELETE_ERROR, function (error) {
        expectAsync(function () {
          expect(error.message).to.equal("Guru Meditation");
        }, done);
      });

      TasksActions.deleteTasksAndScale("/app-1", "task-3");
    });

  });

});

describe("Task List Item component", function () {

  beforeEach(function () {
    var model = {
      appId: "/app-1",
      id: "task-123",
      host: "host-1",
      ports: [1, 2, 3],
      status: "status-0",
      updatedAt: "2015-06-29T14:11:58.709Z",
      version: "2015-06-29T13:54:24.171Z"
    };

    this.renderer = TestUtils.createRenderer();
    this.renderer.render(<TaskListItemComponent
      appId={"/app-1"}
      hasHealth={false}
      isActive={false}
      onToggle={()=>{}}
      task={model} />);
    this.component = this.renderer.getRenderOutput();
  });

  afterEach(function () {
    this.renderer.unmount();
  });

  it("has the correct task id", function () {
    var cellContent =
      ShallowUtils.getText(this.component.props.children[1].props.children[0]);

    expect(cellContent).to.equal("task-123");
  });

  it("has the correct status", function () {
    var cellContent =
      ShallowUtils.getText(this.component.props.children[2].props.children);

    expect(cellContent).to.equal("status-0");
  });

  it("has the correct version", function () {
    var cellContent =
      this.component.props.children[3].props.children.props;

    expect(cellContent.title).to.equal("2015-06-29T13:54:24.171Z");
  });

  it("has the correct update timestamp", function () {
    var cellContent =
      this.component.props.children[4].props.children.props;

    expect(cellContent.title).to.equal("2015-06-29T14:11:58.709Z");
    expect(cellContent.dateTime).to.equal("2015-06-29T14:11:58.709Z");
  });

  it("has a health dot", function () {
    var cell =
      this.component.props.children[5].props.children.props.className;

    expect(cell).to.equal("health-dot");
  });

});

describe("Task Detail component", function () {

  beforeEach(function () {
    this.model = {
      appId: "/app-1",
      id: "task-123",
      host: "host-1",
      ports: [1, 2, 3],
      status: "status-0",
      updatedAt: "2015-06-29T14:11:58.709Z",
      stagedAt: "2015-06-29T14:11:58.709Z",
      startedAt: "2015-06-29T14:11:58.709Z",
      version: "2015-06-29T13:54:24.171Z"
    };

    this.renderer = TestUtils.createRenderer();
    this.renderer.render(<TaskDetailComponent
      appId={this.model.appId}
      fetchState={States.STATE_SUCCESS}
      hasHealth={false}
      task={this.model} />);
    this.component = this.renderer.getRenderOutput();
    this.taskDetails = this.component.props.children[1].props.children[0];
  });

  afterEach(function () {
    this.renderer.unmount();
  });

  it("has the correct host", function () {
    var content = ShallowUtils.getText(this.taskDetails.props.children[1]);
    expect(content).to.equal("host-1");
  });

  it("has the correct ports", function () {
    var content = ShallowUtils.getText(this.taskDetails.props.children[3]);
    expect(content).to.equal("[1,2,3]");
  });

  it("has the correct endpoints", function () {
    var list = this.taskDetails.props.children[5];
    var endpoints = [
      ShallowUtils.getText(list[0].props.children),
      ShallowUtils.getText(list[1].props.children),
      ShallowUtils.getText(list[2].props.children)
    ];
    expect(endpoints).to.deep.equal(["host-1:1", "host-1:2", "host-1:3"]);
  });

  it("has the correct status", function () {
    var content = ShallowUtils.getText(this.taskDetails.props.children[7]);
    expect(content).to.equal("status-0");
  });

  it("has the correct timefields", function () {
    var stagedAt = this.taskDetails.props.children[8][0].props;
    var startedAt = this.taskDetails.props.children[8][1].props;
    expect(stagedAt.time).to.equal("2015-06-29T14:11:58.709Z");
    expect(startedAt.time).to.equal("2015-06-29T14:11:58.709Z");
  });

  it("has the correct version", function () {
    var version = this.taskDetails.props.children[10].props.children.props;
    expect(version.dateTime).to.equal("2015-06-29T13:54:24.171Z");
  });

  it("has a loading error", function () {
    this.renderer.render(<TaskDetailComponent
      appId={this.model.appId}
      fetchState={States.STATE_ERROR}
      hasHealth={false}
      task={this.model} />);

    var component = this.renderer.getRenderOutput();

    var content = ShallowUtils.findOne(component, "text-danger");

    expect(content).to.be.an.object;
  });

});

describe("Task List component", function () {

  beforeEach(function () {
    this.model = [{
      appId: "/app-1",
      id: "task-1"
    }, {
      appId: "/app-1",
      id: "task-2"
    }];

    this.renderer = TestUtils.createRenderer();

    this.renderer.render(<TaskListComponent
      currentPage={0}
      fetchState={States.STATE_SUCCESS}
      getTaskHealthMessage={function () {}}
      hasHealth={false}
      itemsPerPage={8}
      onTaskToggle={function () {}}
      selectedTasks={{}}
      tasks={this.model}
      toggleAllTasks={function () {}} />);

    this.component = this.renderer.getRenderOutput();
  });

  afterEach(function () {
    this.renderer.unmount();
  });

  it("has correct TaskListItemComponents keys", function () {
    var tasklistitems =
      this.component.props.children[3].props.children[1].props.children[2];

    expect(tasklistitems[0].key).to.equal("task-1");
    expect(tasklistitems[1].key).to.equal("task-2");
  });

});

describe("Task Mesos Url component", function () {

  beforeEach(function () {
    this.model = {
      appId: "/app-1",
      id: "task-123",
      slaveId: "20150720-125149-3839899402-5050-16758-S1"
    };
    InfoStore.info = {
      "version": "1.2.3",
      "frameworkId": "framework1",
      "leader": "leader1.dcos.io",
      "marathon_config": {
        "marathon_field_1": "mf1",
        "mesos_leader_ui_url": "http://leader1.dcos.io:5050"
      }
    };

    this.renderer = TestUtils.createRenderer();
    this.renderer.render(<TaskMesosUrlComponent task={this.model}/>);
    this.component = this.renderer.getRenderOutput();
  });

  afterEach(function () {
    this.renderer.unmount();
  });

  it("has the correct mesos task url", function () {
    var url = this.component.props.href;
    expect(url).to.equal(
"http://leader1.dcos.io:5050/#/slaves/20150720-125149-3839899402-5050-\
16758-S1/frameworks/framework1/executors/task-123");
  });
  it("has the correct mesos task url when mesosUrl has trailing slash", function () {
    InfoStore.info.marathon_config.mesos_leader_ui_url = "http://leader1.dcos.io:5050/";
    this.renderer = TestUtils.createRenderer();
    this.renderer.render(<TaskMesosUrlComponent task={this.model}/>);
    this.component = this.renderer.getRenderOutput();
    var url = this.component.props.href;
    expect(url).to.equal(
"http://leader1.dcos.io:5050/#/slaves/20150720-125149-3839899402-5050-\
16758-S1/frameworks/framework1/executors/task-123");
  });
});
<commit_msg>Fix task list test<commit_after>var _ = require("underscore");
var expect = require("chai").expect;
var React = require("react/addons");
var TestUtils = React.addons.TestUtils;

var config = require("../js/config/config");
var AppsActions = require("../js/actions/AppsActions");
var AppsEvents = require("../js/events/AppsEvents");
var AppsStore = require("../js/stores/AppsStore");
var InfoStore = require("../js/stores/InfoStore");
var States = require("../js/constants/States");
var TasksActions = require("../js/actions/TasksActions");
var TasksEvents = require("../js/events/TasksEvents");
var TaskListItemComponent = require("../js/components/TaskListItemComponent");
var TaskDetailComponent = require("../js/components/TaskDetailComponent");
var TaskListComponent = require("../js/components/TaskListComponent");
var TaskMesosUrlComponent = require("../js/components/TaskMesosUrlComponent");

var expectAsync = require("./helpers/expectAsync");
var HttpServer = require("./helpers/HttpServer").HttpServer;
var ShallowUtils = require("./helpers/ShallowUtils");

var server = new HttpServer(config.localTestserverURI);
config.apiURL = "http://" + server.address + ":" + server.port + "/";

describe("Tasks", function () {

  beforeEach(function (done) {
    this.server = server
    .setup({
      "app": {
        id: "/app-1",
        tasks: [{
          id: "task-1",
          appId: "/app-1"
        },
        {
          id: "task-2",
          appId: "/app-1"
        }]
      }
    }, 200)
    .start(function () {
      AppsStore.once(AppsEvents.CHANGE, done);
      AppsActions.requestApp("/app-1");
    });
  });

  afterEach(function (done) {
    this.server.stop(done);
  });

  describe("on single task deletion", function () {

    it("updates the tasks array on success", function (done) {
      this.server.setup("", 200);

      AppsStore.once(AppsEvents.CHANGE, function () {
        expectAsync(function () {
          expect(AppsStore.currentApp.tasks).to.have.length(1);
          expect(_.where(AppsStore.currentApp.tasks, {
            id: "task-1"
          })).to.be.empty;
        }, done);
      });

      TasksActions.deleteTasks("/app-1", ["task-1"]);
    });

    it("handles failure gracefully", function (done) {
      this.server.setup({message: "Guru Meditation"}, 404);

      AppsStore.once(TasksEvents.DELETE_ERROR, function (error) {
        expectAsync(function () {
          expect(error.message).to.equal("Guru Meditation");
        }, done);
      });

      TasksActions.deleteTasks("/app-1", "task-3");
    });

  });

  describe("on single task deletion and scale", function () {

    it("updates the tasks array on success", function (done) {
      this.server.setup("", 200);

      AppsStore.once(AppsEvents.CHANGE, function () {
        expectAsync(function () {
          expect(AppsStore.currentApp.tasks).to.have.length(1);
          expect(_.where(AppsStore.currentApp.tasks, {
            id: "task-2"
          })).to.be.empty;
        }, done);
      });

      TasksActions.deleteTasksAndScale("/app-1", ["task-2"]);
    });

  });

  describe("on multiple task deletion", function () {

    it("updates the tasks array on success", function (done) {
      this.server.setup("", 200);

      AppsStore.once(AppsEvents.CHANGE, function () {
        expectAsync(function () {
          expect(AppsStore.currentApp.tasks).to.have.length(0);
        }, done);
      });

      TasksActions.deleteTasks("/app-1", ["task-1", "task-2"]);
    });

    it("handles failure gracefully", function (done) {
      this.server.setup({message: "Guru Meditation"}, 404);

      AppsStore.once(TasksEvents.DELETE_ERROR, function (error) {
        expectAsync(function () {
          expect(error.message).to.equal("Guru Meditation");
        }, done);
      });

      TasksActions.deleteTasks("/app-1", "task-3");
    });

  });

  describe("on multiple task deletion and scale", function () {

    it("updates the tasks array on success", function (done) {
      this.server.setup("", 200);

      AppsStore.once(AppsEvents.CHANGE, function () {
        expectAsync(function () {
          expect(AppsStore.currentApp.tasks).to.have.length(0);
        }, done);
      });

      TasksActions.deleteTasksAndScale("/app-1", ["task-1", "task-2"]);
    });

    it("handles failure gracefully", function (done) {
      this.server.setup({message: "Guru Meditation"}, 404);

      AppsStore.once(TasksEvents.DELETE_ERROR, function (error) {
        expectAsync(function () {
          expect(error.message).to.equal("Guru Meditation");
        }, done);
      });

      TasksActions.deleteTasksAndScale("/app-1", "task-3");
    });

  });

});

describe("Task List Item component", function () {

  beforeEach(function () {
    var model = {
      appId: "/app-1",
      id: "task-123",
      host: "host-1",
      ports: [1, 2, 3],
      status: "status-0",
      updatedAt: "2015-06-29T14:11:58.709Z",
      version: "2015-06-29T13:54:24.171Z"
    };

    this.renderer = TestUtils.createRenderer();
    this.renderer.render(<TaskListItemComponent
      appId={"/app-1"}
      hasHealth={true}
      taskHealthMessage="Healthy"
      isActive={false}
      onToggle={()=>{}}
      task={model} />);
    this.component = this.renderer.getRenderOutput();
  });

  afterEach(function () {
    this.renderer.unmount();
  });

  it("has the correct task id", function () {
    var cellContent =
      ShallowUtils.getText(this.component.props.children[1].props.children[0]);

    expect(cellContent).to.equal("task-123");
  });

  it("has correct health sate", function () {
    var cellContent =
      ShallowUtils.getText(this.component.props.children[2]);

    expect(cellContent).to.equal("Healthy");
  });

  it("has the correct status", function () {
    var cellContent =
      ShallowUtils.getText(this.component.props.children[3].props.children);

    expect(cellContent).to.equal("status-0");
  });

  it("has the correct version", function () {
    var cellContent =
      this.component.props.children[4].props.children.props;

    expect(cellContent.title).to.equal("2015-06-29T13:54:24.171Z");
  });

  it("has the correct update timestamp", function () {
    var cellContent =
      this.component.props.children[5].props.children.props;

    expect(cellContent.title).to.equal("2015-06-29T14:11:58.709Z");
    expect(cellContent.dateTime).to.equal("2015-06-29T14:11:58.709Z");
  });

});

describe("Task Detail component", function () {

  beforeEach(function () {
    this.model = {
      appId: "/app-1",
      id: "task-123",
      host: "host-1",
      ports: [1, 2, 3],
      status: "status-0",
      updatedAt: "2015-06-29T14:11:58.709Z",
      stagedAt: "2015-06-29T14:11:58.709Z",
      startedAt: "2015-06-29T14:11:58.709Z",
      version: "2015-06-29T13:54:24.171Z"
    };

    this.renderer = TestUtils.createRenderer();
    this.renderer.render(<TaskDetailComponent
      appId={this.model.appId}
      fetchState={States.STATE_SUCCESS}
      hasHealth={false}
      task={this.model} />);
    this.component = this.renderer.getRenderOutput();
    this.taskDetails = this.component.props.children[1].props.children[0];
  });

  afterEach(function () {
    this.renderer.unmount();
  });

  it("has the correct host", function () {
    var content = ShallowUtils.getText(this.taskDetails.props.children[1]);
    expect(content).to.equal("host-1");
  });

  it("has the correct ports", function () {
    var content = ShallowUtils.getText(this.taskDetails.props.children[3]);
    expect(content).to.equal("[1,2,3]");
  });

  it("has the correct endpoints", function () {
    var list = this.taskDetails.props.children[5];
    var endpoints = [
      ShallowUtils.getText(list[0].props.children),
      ShallowUtils.getText(list[1].props.children),
      ShallowUtils.getText(list[2].props.children)
    ];
    expect(endpoints).to.deep.equal(["host-1:1", "host-1:2", "host-1:3"]);
  });

  it("has the correct status", function () {
    var content = ShallowUtils.getText(this.taskDetails.props.children[7]);
    expect(content).to.equal("status-0");
  });

  it("has the correct timefields", function () {
    var stagedAt = this.taskDetails.props.children[8][0].props;
    var startedAt = this.taskDetails.props.children[8][1].props;
    expect(stagedAt.time).to.equal("2015-06-29T14:11:58.709Z");
    expect(startedAt.time).to.equal("2015-06-29T14:11:58.709Z");
  });

  it("has the correct version", function () {
    var version = this.taskDetails.props.children[10].props.children.props;
    expect(version.dateTime).to.equal("2015-06-29T13:54:24.171Z");
  });

  it("has a loading error", function () {
    this.renderer.render(<TaskDetailComponent
      appId={this.model.appId}
      fetchState={States.STATE_ERROR}
      hasHealth={false}
      task={this.model} />);

    var component = this.renderer.getRenderOutput();

    var content = ShallowUtils.findOne(component, "text-danger");

    expect(content).to.be.an.object;
  });

});

describe("Task List component", function () {

  beforeEach(function () {
    this.model = [{
      appId: "/app-1",
      id: "task-1"
    }, {
      appId: "/app-1",
      id: "task-2"
    }];

    this.renderer = TestUtils.createRenderer();

    this.renderer.render(<TaskListComponent
      currentPage={0}
      fetchState={States.STATE_SUCCESS}
      getTaskHealthMessage={function () {}}
      hasHealth={false}
      itemsPerPage={8}
      onTaskToggle={function () {}}
      selectedTasks={{}}
      tasks={this.model}
      toggleAllTasks={function () {}} />);

    this.component = this.renderer.getRenderOutput();
  });

  afterEach(function () {
    this.renderer.unmount();
  });

  it("has correct TaskListItemComponents keys", function () {
    var tasklistitems =
      this.component.props.children[3].props.children[1].props.children[2];

    expect(tasklistitems[0].key).to.equal("task-1");
    expect(tasklistitems[1].key).to.equal("task-2");
  });

});

describe("Task Mesos Url component", function () {

  beforeEach(function () {
    this.model = {
      appId: "/app-1",
      id: "task-123",
      slaveId: "20150720-125149-3839899402-5050-16758-S1"
    };
    InfoStore.info = {
      "version": "1.2.3",
      "frameworkId": "framework1",
      "leader": "leader1.dcos.io",
      "marathon_config": {
        "marathon_field_1": "mf1",
        "mesos_leader_ui_url": "http://leader1.dcos.io:5050"
      }
    };

    this.renderer = TestUtils.createRenderer();
    this.renderer.render(<TaskMesosUrlComponent task={this.model}/>);
    this.component = this.renderer.getRenderOutput();
  });

  afterEach(function () {
    this.renderer.unmount();
  });

  it("has the correct mesos task url", function () {
    var url = this.component.props.href;
    expect(url).to.equal(
"http://leader1.dcos.io:5050/#/slaves/20150720-125149-3839899402-5050-\
16758-S1/frameworks/framework1/executors/task-123");
  });
  it("has the correct mesos task url when mesosUrl has trailing slash", function () {
    InfoStore.info.marathon_config.mesos_leader_ui_url = "http://leader1.dcos.io:5050/";
    this.renderer = TestUtils.createRenderer();
    this.renderer.render(<TaskMesosUrlComponent task={this.model}/>);
    this.component = this.renderer.getRenderOutput();
    var url = this.component.props.href;
    expect(url).to.equal(
"http://leader1.dcos.io:5050/#/slaves/20150720-125149-3839899402-5050-\
16758-S1/frameworks/framework1/executors/task-123");
  });
});
<|endoftext|>