<commit_before>/**
 * This file is part of Node Package Comparator.
 *
 * Node Package Comparator is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * Node Package Comparator is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *   along with Node Package Comparator.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

var mongoose = require('mongoose'),
  moment = require('moment'),
  _ = require('lodash'),
  async = require('async'),
  https = require('https'),
  http = require('http'),
  Schema = mongoose.Schema,
  environment = require('../../config/environment'),
  NodePackage = require('../nodePackage/nodepackage.model');

/**
 * @type {Schema}
 * @name SchedulerSchema
 */
var SchedulerSchema = new Schema({
  type: {type: String, enum: ['keywords', 'package'], required: true, index: true},
  keyword: {type: String, index: {unique: true, sparse: true}},
  amount: {type: Number, index: true},
  active: Boolean,
  lastRun: Date,
  lastFinish: Date
});

/**
 * Defined at the end of the file while module.export
 * @typedef Scheduler
 * @type {Model}
 * @property {string} type - Either keywords or nodePackage
 * @property {string} keyword - The keyword for nodePackage type
 * @property {boolean} active - Is update active
 * @property {Date} lastRun - When last update started
 * @property {Date} lastFinish - When last update finished.
 * @property {function) run - Refreshing the data.
 */
var Scheduler;

/**
 * Generic response handler.
 * @param response The http(s) response.
 * @param {function} callback The callback to be called with err and data.
 */
function responseHandler(response, callback) {
  var data = '';
  response.setEncoding('utf8');
  response.on('data', function (chunk) {
    data += chunk;
  });
  response.on('end', function () {
    try {
      data = JSON.parse(data);
      callback(null, data);
    }
    catch (err) {
      callback(err);
    }
  });
}
/**
 * Helper to define if the url is either https or http.
 * @param {string} baseUrl Base url. Has to start with http:// or https://
 * @returns exports request client lib, that means either https or http.
 */
function getRequestClientLib(baseUrl) {
  var match = baseUrl.match(/^https:\/\//i);
  if (match && match.length === 1) {
    return https;
  }
  return http;
}

/**
 * Check if the last run is older than one day or not yet finished.
 * @param {Scheduler} instance of Scheduler
 * @returns {boolean} true if not active and lastRun is older than one day.
 */
function checkIfAllowedToRun(instance) {
  return (!instance.active && (!instance.lastRun || moment().subtract(1, 'day').isAfter(instance.lastRun))) || moment().subtract(2, 'day').isAfter(instance.lastRun);
}

/**
 * Updates the instance to active=true and sets the lastRun to Date.now().
 * Stores the instance directly to the db. Calls the callback with the results of the save function.
 * @param {Scheduler} instance of Scheduler
 * @param {function} callback
 */
function setActiveAndLastRun(instance, callback) {
  instance.active = true;
  instance.lastRun = Date.now();
  instance.save(callback);
}

/**
 * Updates the instance to active=true and sets the lastFinish to Date.now().
 * Stores the instance directly to the db. Calls the callback with the results of the save function.
 * @param {Scheduler} instance of Scheduler
 * @param {function} callback
 */
function setDoneAndLastFinsh(instance, callback) {
  instance.active = false;
  instance.lastFinish = Date.now();
  instance.save(callback);
}

/**
 *
 * @param {String} url the url without query parameters. As example: https://registry.npmjs.org/-/_view/byKeyword
 * @param {Object} queryParams The query params. Each value will be stringified by @{JSON#stringify}
 * @param {function} callback
 */
function getRegistryData(url, queryParams, callback) {
  var clientLib = getRequestClientLib(url);
  var queryParamsAsString = '';

  _.forIn(queryParams, function stringifyForCouch(item, key) {
    queryParamsAsString += queryParamsAsString.length === 0 ? '?' : '&';
    queryParamsAsString += key + '=' + JSON.stringify(item);
  });

  clientLib.get(url + queryParamsAsString, function (res) {
    responseHandler(res, callback);
  });
}

/**
 * Check if the url is a github url. If so, then extract account and project.
 * If not, return null.
 * @param url Found url.
 * @returns {object | null} Returns infos about github if there are some.
 */
function checkUrlForGithubInfos(url) {
  var tmp, projectName;
  if (!url) {
    return null;
  }
  tmp = url.match(/github.com[\/:]([\w\d-]+)\/([a-zA-Z0-9\-_.]+)$/i) || [];
  if (tmp.length === 3) {
    projectName = tmp[2].match(/(.+)\.git$/i) || [];
    return {
      account: tmp[1],
      project: projectName.length === 2 ? projectName[1] : tmp[2]
    };
  }

}

/**
 * Refreshing the keyword schedulers.
 * @param {Scheduler} instance - {Scheduler~type} must be keywords.
 * @returns {boolean} true if refresh occurred, false if not.
 */
function refreshKeywordsScheduler(instance) {
  var toRefresh = checkIfAllowedToRun(instance);
  if (toRefresh) {
    setActiveAndLastRun(instance, function refreshKeywords(err, scheduler) {
      var registry = environment.registry;

      if (err) {
        console.log(err);
        return void 0;
      }

      getRegistryData(registry.uri + registry.byKeywordView, {
        group_level: 1
      }, function updateAllPackageSchedulers(err, data) {
        var keywords = [];
        var amountMap = {};
        _.forEach(data.rows, function selectKeyword(elem) {
          if (elem.key.length > 0) {
            keywords.push(elem.key[0]);
            amountMap[elem.key[0]] = elem.value;
          }
        });

        async.parallel([
          function (cb) {
            Scheduler.remove({keyword: {$nin: keywords}, type: 'package'}, cb);
          },
          function (cb) {
            Scheduler.find({type: 'package', keyword: {$in: keywords}}, 'keyword', function (err, keywordSchedulers) {
              var schedulerKeywords = [], toCreateKeywords, toUpdateKeywordsFnList = [], keywordIdMap = {};

              function splitCreate(schedulerKeywordDocs, cb) {
                var i = 0, maxEach = 10000, total = schedulerKeywordDocs.length, splicedSchedulerDocs = [], functionList = [];
                for (i; i < total; i += maxEach) {
                  splicedSchedulerDocs.push(schedulerKeywordDocs.slice(i, i + maxEach));
                }
                _.forEach(splicedSchedulerDocs, function (splicedDocs) {
                  functionList.push(function createDocs(cb) {
                    Scheduler.create(splicedDocs, cb);
                  });
                });
                async.parallel(functionList, cb);
              }

              function findSchedulerByIdAndUpdateFnCreator(id, update) {
                return function findSchedulerByIdAndUpdateFn(cb) {
                  Scheduler.findByIdAndUpdate(id, update, cb)
                };
              }

              _.forEach(keywordSchedulers, function (extractKeywords) {
                schedulerKeywords.push(extractKeywords.keyword);
                keywordIdMap[extractKeywords.keyword] = extractKeywords._id;
              });

              toCreateKeywords = _.difference(keywords, schedulerKeywords);

              _.forEach(keywords, function (keyword) {
                if (amountMap.hasOwnProperty(keyword)) {
                  toUpdateKeywordsFnList.push(findSchedulerByIdAndUpdateFnCreator(keywordIdMap[keyword],
                    {amount: amountMap[keyword]}));
                }
              });

              schedulerKeywords = [];
              _.forEach(toCreateKeywords, function createDoc(keyword) {
                schedulerKeywords.push({type: 'package', keyword: keyword, amount: amountMap[keyword]});
              });

              async.parallel({
                create: function (cb) {
                  if (schedulerKeywords.length > 1000) {
                    splitCreate(schedulerKeywords, cb);
                  }
                  else {
                    Scheduler.create(schedulerKeywords, cb);
                  }
                },
                update: function (cb) {
                  if (toUpdateKeywordsFnList.length > 0) {
                    async.parallel(toUpdateKeywordsFnList, cb);
                  }
                  else {
                    cb();
                  }
                }

              }, function (err) {
                cb(err);
              });

            });
          }
        ], function finish() {
          setDoneAndLastFinsh(scheduler, function () {
          });
        });

      });
    });
  }
  return toRefresh;
}

function refreshPackageScheduler(instance) {
  console.log('called ' + instance.keyword);
  var toRefresh = checkIfAllowedToRun(instance);
  if (toRefresh) {
    setActiveAndLastRun(instance, function updatePackages(err, scheduler) {
      var registry = environment.registry;
      getRegistryData(registry.uri +
      registry.byKeywordView, {
        group_level: 2,
        startkey: [scheduler.keyword],
        endkey: [scheduler.keyword, {}, {}]
      }, function (err, data) {
        console.log(err);
        var asyncUpdateCalls = [], asyncDBSearchCalls = [];

        /**
         * Creates a callback function for asking the registry and githup.
         * @param {string} name Name of the node package.
         * @returns {Function} callback function for async.
         */
        function createCall(name) {
          return function (cb) {
            getRegistryData(registry.uri + name, {}, function (err, npmInfo) {

              var packageData, githubData;
              if (err) {
                console.log(err);
                cb(err);
                return void 0;
              }
              githubData = checkUrlForGithubInfos(npmInfo.repository ? npmInfo.repository.url : null);
              if (!githubData && npmInfo.homepage) {
                githubData = checkUrlForGithubInfos(npmInfo.homepage);
              }
              packageData = {
                name: npmInfo.name,
                description: npmInfo.description || null,
                version: npmInfo["dist-tags"].latest,
                lastModified: (npmInfo.time && npmInfo.time.modified) ? npmInfo.time.modified : null,
                author: (npmInfo.author && npmInfo.author.name) ? npmInfo.author.name : null,
                keywords: npmInfo.keywords || [],
                github: githubData
              };

              async.parallel({
                npmStars: function (cb) {
                  getRegistryData(registry.uri + registry.byStarPackageView, {
                    group_level: 1,
                    startkey: [packageData.name],
                    endkey: [packageData.name, {}, {}]
                  }, function (err, data) {
                    if (err) {
                      cb(err);
                      return void 0;
                    }
                    if (data.rows.length > 0) {
                      cb(null, data.rows[0].value);
                    }
                    else {
                      cb(null, 0);
                    }
                  });
                },
                githubMetrics: function (cb) {

                  var githubEnv = environment.github;
                  if (!packageData.github) {
                    cb(null, null);
                    return void 0;
                  }
                  console.log(githubEnv.createRepoPath(packageData.github.account, packageData.github.project));
                  https.get(_.merge({
                    path: githubEnv.createRepoPath(packageData.github.account, packageData.github.project)
                  }, githubEnv.httpsBase), function (res) {
                    responseHandler(res, function (err, data) {
                      if (err) {
                        console.log(err);
                        cb(err);
                        return void 0;
                      }
                      if (200 !== res.statusCode) {
                        console.log(res.statusCode, data.message, res.req.path);
                        cb(null, {});
                        return void 0;
                      }
                      cb(null, {
                        githubForks: data.forks_count || 0,
                        githubStars: data.stargazers_count || 0,
                        githubWatches: data.subscribers_count || 0
                      });
                    });
                  }).on('error', function (e) {
                    console.log(e.message);
                    cb(null, {});
                  });

                }
              }, function (err, results) {
                if (err) {
                  cb(err);
                  return void 0;
                }
                packageData.npmStars = results.npmStars;
                if (results.githubMetrics) {
                  _.merge(packageData, results.githubMetrics);
                }
                packageData._lastUpdate = Date.now();
                NodePackage.findOneAndUpdate({name: packageData.name}, packageData, {upsert: true},
                  function (err, doc) {
                    if (err) {
                      console.log(err);
                      cb(err);
                      return void 0;
                    }
                    cb(err, doc);
                  });
              });
            });
          };
        }

        function checkIfPackageShouldBeUpatedAndAddToAsyncCalls(name) {
          return function asyncCbFn(cb) {
            NodePackage.findOne({name: name}, function (err, pkg) {
              if (err) {
                cb(err);
                return void 0;
              }
              if (!pkg || moment().subtract(1, 'day').isAfter(pkg._lastUpdate)) {
                asyncUpdateCalls.push(createCall(name));
              }
              cb();
            });
          };
        }

        _.forEach(data.rows, function (obj) {
          asyncDBSearchCalls.push(checkIfPackageShouldBeUpatedAndAddToAsyncCalls(obj.key[1]));
        });
        async.parallel(asyncDBSearchCalls, function () {
          async.parallel(asyncUpdateCalls, function () {
            setDoneAndLastFinsh(scheduler, function () {/*noop*/
            });
          });
        });
      });
    });
  }
  return toRefresh;
}

SchedulerSchema.methods.run = function run() {
  var me = this;
  if (me.type === 'keywords') {
    return refreshKeywordsScheduler(me);
  }
  else if (me.type === 'package') {
    return refreshPackageScheduler(me);
  }
  return false;
};

SchedulerSchema.static('updateKeywords', function (cb) {
  Scheduler.findOne({type: 'keywords'}, function (err, scheduler) {
    if (!scheduler) {
      scheduler = new Scheduler({type: 'keywords'});
    }
    if (cb && 'function' === typeof cb) {
      cb(scheduler.run());
    }
    else {
      scheduler.run();
    }
  })
});

SchedulerSchema.static('updatePackages', function (keyword, cb) {
  console.log('update nodePackage', keyword);
  Scheduler.findOne({type: 'package', keyword: keyword}, function (err, scheduler) {
    var hasCb = cb && 'function' === typeof cb;
    if (!scheduler) {
      if (hasCb) {
        cb(null, false);
      }
      return void 0;
    }
    if (hasCb) {
      cb(scheduler.run());
    }
    else {
      scheduler.run();
    }
  })
});

module.exports = Scheduler = mongoose.model('Scheduler', SchedulerSchema);
<commit_msg>Fixed to much open files/http requests. Limit currently: 500 calls parallel<commit_after>/**
 * This file is part of Node Package Comparator.
 *
 * Node Package Comparator is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * Node Package Comparator is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *   along with Node Package Comparator.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

var mongoose = require('mongoose'),
  moment = require('moment'),
  _ = require('lodash'),
  async = require('async'),
  https = require('https'),
  http = require('http'),
  Schema = mongoose.Schema,
  environment = require('../../config/environment'),
  NodePackage = require('../nodePackage/nodepackage.model');

/**
 * @type {Schema}
 * @name SchedulerSchema
 */
var SchedulerSchema = new Schema({
  type: {type: String, enum: ['keywords', 'package'], required: true, index: true},
  keyword: {type: String, index: {unique: true, sparse: true}},
  amount: {type: Number, index: true},
  active: Boolean,
  lastRun: Date,
  lastFinish: Date
});

/**
 * Defined at the end of the file while module.export
 * @typedef Scheduler
 * @type {Model}
 * @property {string} type - Either keywords or nodePackage
 * @property {string} keyword - The keyword for nodePackage type
 * @property {boolean} active - Is update active
 * @property {Date} lastRun - When last update started
 * @property {Date} lastFinish - When last update finished.
 * @property {function) run - Refreshing the data.
 */
var Scheduler;

/**
 * Generic response handler.
 * @param response The http(s) response.
 * @param {function} callback The callback to be called with err and data.
 */
function responseHandler(response, callback) {
  var data = '';
  response.setEncoding('utf8');
  response.on('data', function (chunk) {
    data += chunk;
  });
  response.on('end', function () {
    try {
      data = JSON.parse(data);
      callback(null, data);
    }
    catch (err) {
      callback(err);
    }
  });
}
/**
 * Helper to define if the url is either https or http.
 * @param {string} baseUrl Base url. Has to start with http:// or https://
 * @returns exports request client lib, that means either https or http.
 */
function getRequestClientLib(baseUrl) {
  var match = baseUrl.match(/^https:\/\//i);
  if (match && match.length === 1) {
    return https;
  }
  return http;
}

/**
 * Check if the last run is older than one day or not yet finished.
 * @param {Scheduler} instance of Scheduler
 * @returns {boolean} true if not active and lastRun is older than one day.
 */
function checkIfAllowedToRun(instance) {
  return (!instance.active && (!instance.lastRun || moment().subtract(1, 'day').isAfter(instance.lastRun))) || moment().subtract(2, 'day').isAfter(instance.lastRun);
}

/**
 * Updates the instance to active=true and sets the lastRun to Date.now().
 * Stores the instance directly to the db. Calls the callback with the results of the save function.
 * @param {Scheduler} instance of Scheduler
 * @param {function} callback
 */
function setActiveAndLastRun(instance, callback) {
  instance.active = true;
  instance.lastRun = Date.now();
  instance.save(callback);
}

/**
 * Updates the instance to active=true and sets the lastFinish to Date.now().
 * Stores the instance directly to the db. Calls the callback with the results of the save function.
 * @param {Scheduler} instance of Scheduler
 * @param {function} callback
 */
function setDoneAndLastFinsh(instance, callback) {
  instance.active = false;
  instance.lastFinish = Date.now();
  instance.save(callback);
}

/**
 *
 * @param {String} url the url without query parameters. As example: https://registry.npmjs.org/-/_view/byKeyword
 * @param {Object} queryParams The query params. Each value will be stringified by @{JSON#stringify}
 * @param {function} callback
 */
function getRegistryData(url, queryParams, callback) {
  var clientLib = getRequestClientLib(url);
  var queryParamsAsString = '';

  _.forIn(queryParams, function stringifyForCouch(item, key) {
    queryParamsAsString += queryParamsAsString.length === 0 ? '?' : '&';
    queryParamsAsString += key + '=' + JSON.stringify(item);
  });

  clientLib.get(url + queryParamsAsString, function (res) {
    responseHandler(res, callback);
  }).on('error', function(e) {
    console.error(e);
    if (callback && 'function' === typeof callback) {
      callback(e);
    }
  });
}

/**
 * Check if the url is a github url. If so, then extract account and project.
 * If not, return null.
 * @param url Found url.
 * @returns {object | null} Returns infos about github if there are some.
 */
function checkUrlForGithubInfos(url) {
  var tmp, projectName;
  if (!url) {
    return null;
  }
  tmp = url.match(/github.com[\/:]([\w\d-]+)\/([a-zA-Z0-9\-_.]+)$/i) || [];
  if (tmp.length === 3) {
    projectName = tmp[2].match(/(.+)\.git$/i) || [];
    return {
      account: tmp[1],
      project: projectName.length === 2 ? projectName[1] : tmp[2]
    };
  }

}

/**
 * Refreshing the keyword schedulers.
 * @param {Scheduler} instance - {Scheduler~type} must be keywords.
 * @returns {boolean} true if refresh occurred, false if not.
 */
function refreshKeywordsScheduler(instance) {
  var toRefresh = checkIfAllowedToRun(instance);
  if (toRefresh) {
    setActiveAndLastRun(instance, function refreshKeywords(err, scheduler) {
      var registry = environment.registry;

      if (err) {
        console.log(err);
        return void 0;
      }

      getRegistryData(registry.uri + registry.byKeywordView, {
        group_level: 1
      }, function updateAllPackageSchedulers(err, data) {
        var keywords = [];
        var amountMap = {};
        _.forEach(data.rows, function selectKeyword(elem) {
          if (elem.key.length > 0) {
            keywords.push(elem.key[0]);
            amountMap[elem.key[0]] = elem.value;
          }
        });

        async.parallel([
          function (cb) {
            Scheduler.remove({keyword: {$nin: keywords}, type: 'package'}, cb);
          },
          function (cb) {
            Scheduler.find({type: 'package', keyword: {$in: keywords}}, 'keyword', function (err, keywordSchedulers) {
              var schedulerKeywords = [], toCreateKeywords, toUpdateKeywordsFnList = [], keywordIdMap = {};

              function splitCreate(schedulerKeywordDocs, cb) {
                var i = 0, maxEach = 10000, total = schedulerKeywordDocs.length, splicedSchedulerDocs = [], functionList = [];
                for (i; i < total; i += maxEach) {
                  splicedSchedulerDocs.push(schedulerKeywordDocs.slice(i, i + maxEach));
                }
                _.forEach(splicedSchedulerDocs, function (splicedDocs) {
                  functionList.push(function createDocs(cb) {
                    Scheduler.create(splicedDocs, cb);
                  });
                });
                async.parallel(functionList, cb);
              }

              function findSchedulerByIdAndUpdateFnCreator(id, update) {
                return function findSchedulerByIdAndUpdateFn(cb) {
                  Scheduler.findByIdAndUpdate(id, update, cb)
                };
              }

              _.forEach(keywordSchedulers, function (extractKeywords) {
                schedulerKeywords.push(extractKeywords.keyword);
                keywordIdMap[extractKeywords.keyword] = extractKeywords._id;
              });

              toCreateKeywords = _.difference(keywords, schedulerKeywords);

              _.forEach(keywords, function (keyword) {
                if (amountMap.hasOwnProperty(keyword)) {
                  toUpdateKeywordsFnList.push(findSchedulerByIdAndUpdateFnCreator(keywordIdMap[keyword],
                    {amount: amountMap[keyword]}));
                }
              });

              schedulerKeywords = [];
              _.forEach(toCreateKeywords, function createDoc(keyword) {
                schedulerKeywords.push({type: 'package', keyword: keyword, amount: amountMap[keyword]});
              });

              async.parallel({
                create: function (cb) {
                  if (schedulerKeywords.length > 1000) {
                    splitCreate(schedulerKeywords, cb);
                  }
                  else {
                    Scheduler.create(schedulerKeywords, cb);
                  }
                },
                update: function (cb) {
                  if (toUpdateKeywordsFnList.length > 0) {
                    async.parallel(toUpdateKeywordsFnList, cb);
                  }
                  else {
                    cb();
                  }
                }

              }, function (err) {
                cb(err);
              });

            });
          }
        ], function finish() {
          setDoneAndLastFinsh(scheduler, function () {
          });
        });

      });
    });
  }
  return toRefresh;
}

function refreshPackageScheduler(instance) {
  console.log('called ' + instance.keyword);
  var toRefresh = checkIfAllowedToRun(instance);
  if (toRefresh) {
    setActiveAndLastRun(instance, function updatePackages(err, scheduler) {
      var registry = environment.registry;
      getRegistryData(registry.uri +
      registry.byKeywordView, {
        group_level: 2,
        startkey: [scheduler.keyword],
        endkey: [scheduler.keyword, {}, {}]
      }, function (err, data) {
        console.log(err);
        var asyncUpdateCalls = [], asyncDBSearchCalls = [], asyncDBSearchCallsSplited = [];

        /**
         * Creates a callback function for asking the registry and githup.
         * @param {string} name Name of the node package.
         * @returns {Function} callback function for async.
         */
        function createCall(name) {
          return function (cb) {
            getRegistryData(registry.uri + name, {}, function (err, npmInfo) {

              var packageData, githubData;
              if (err) {
                console.log(err);
                cb(err);
                return void 0;
              }
              githubData = checkUrlForGithubInfos(npmInfo.repository ? npmInfo.repository.url : null);
              if (!githubData && npmInfo.homepage) {
                githubData = checkUrlForGithubInfos(npmInfo.homepage);
              }
              packageData = {
                name: npmInfo.name,
                description: npmInfo.description || null,
                version: npmInfo["dist-tags"].latest,
                lastModified: (npmInfo.time && npmInfo.time.modified) ? npmInfo.time.modified : null,
                author: (npmInfo.author && npmInfo.author.name) ? npmInfo.author.name : null,
                keywords: npmInfo.keywords || [],
                github: githubData
              };

              async.parallel({
                npmStars: function (cb) {
                  getRegistryData(registry.uri + registry.byStarPackageView, {
                    group_level: 1,
                    startkey: [packageData.name],
                    endkey: [packageData.name, {}, {}]
                  }, function (err, data) {
                    if (err) {
                      cb(err);
                      return void 0;
                    }
                    if (data.rows.length > 0) {
                      cb(null, data.rows[0].value);
                    }
                    else {
                      cb(null, 0);
                    }
                  });
                },
                githubMetrics: function (cb) {

                  var githubEnv = environment.github;
                  if (!packageData.github) {
                    cb(null, null);
                    return void 0;
                  }
                  console.log(githubEnv.createRepoPath(packageData.github.account, packageData.github.project));
                  https.get(_.merge({
                    path: githubEnv.createRepoPath(packageData.github.account, packageData.github.project)
                  }, githubEnv.httpsBase), function (res) {
                    responseHandler(res, function (err, data) {
                      if (err) {
                        console.log(err);
                        cb(err);
                        return void 0;
                      }
                      if (200 !== res.statusCode) {
                        console.log(res.statusCode, data.message, res.req.path);
                        cb(null, {});
                        return void 0;
                      }
                      cb(null, {
                        githubForks: data.forks_count || 0,
                        githubStars: data.stargazers_count || 0,
                        githubWatches: data.subscribers_count || 0
                      });
                    });
                  }).on('error', function (e) {
                    console.log(e.message);
                    cb(null, {});
                  });

                }
              }, function (err, results) {
                if (err) {
                  cb(err);
                  return void 0;
                }
                packageData.npmStars = results.npmStars;
                if (results.githubMetrics) {
                  _.merge(packageData, results.githubMetrics);
                }
                packageData._lastUpdate = Date.now();
                NodePackage.findOneAndUpdate({name: packageData.name}, packageData, {upsert: true},
                  function (err, doc) {
                    if (err) {
                      console.log(err);
                      cb(err);
                      return void 0;
                    }
                    cb(err, doc);
                  });
              });
            });
          };
        }
        function noop() {}
        function checkIfPackageShouldBeUpatedAndAddToAsyncCalls(name) {
          return function asyncCbFn(cb) {
            NodePackage.findOne({name: name}, function (err, pkg) {
              if (err) {
                cb(err);
                return void 0;
              }
              if (!pkg || moment().subtract(1, 'day').isAfter(pkg._lastUpdate)) {
                cb(null, createCall(name));
              } else {
                cb (null, noop());
              }
            });
          };
        }

        _.forEach(data.rows, function (obj) {
          asyncDBSearchCalls.push(checkIfPackageShouldBeUpatedAndAddToAsyncCalls(obj.key[1]));
        });

        async.parallelLimit(asyncDBSearchCalls, 500, function (err, calls) {
          async.parallelLimit(calls, 500, function () {
            setDoneAndLastFinsh(scheduler, noop);
          });
        });
      });
    });
  }
  return toRefresh;
}

SchedulerSchema.methods.run = function run() {
  var me = this;
  if (me.type === 'keywords') {
    return refreshKeywordsScheduler(me);
  }
  else if (me.type === 'package') {
    return refreshPackageScheduler(me);
  }
  return false;
};

SchedulerSchema.static('updateKeywords', function (cb) {
  Scheduler.findOne({type: 'keywords'}, function (err, scheduler) {
    if (!scheduler) {
      scheduler = new Scheduler({type: 'keywords'});
    }
    if (cb && 'function' === typeof cb) {
      cb(scheduler.run());
    }
    else {
      scheduler.run();
    }
  })
});

SchedulerSchema.static('updatePackages', function (keyword, cb) {
  console.log('update nodePackage', keyword);
  Scheduler.findOne({type: 'package', keyword: keyword}, function (err, scheduler) {
    var hasCb = cb && 'function' === typeof cb;
    if (!scheduler) {
      if (hasCb) {
        cb(null, false);
      }
      return void 0;
    }
    if (hasCb) {
      cb(scheduler.run());
    }
    else {
      scheduler.run();
    }
  })
});

module.exports = Scheduler = mongoose.model('Scheduler', SchedulerSchema);
<|endoftext|>


<commit_before>import Course from '../models/course';
import SessionUtils from '../util/sessionUtils';

var async_f = require('asyncawait/async');
var await_f = require('asyncawait/await');

function  checkRequestSanity(req, res) {
  return new Promise (function (fulfill, reject){
    // make sure that the session is valid
    SessionUtils.isValidSession(req.cookies.sessionID).then((isValid) => {
      if (isValid !== true) {
          res.status(401).end();
          fulfill(false);
      } else {
        // make sure that this sessionID belongs to an Admin
        //TODO: we need to check that this admin is the owner of the course
        SessionUtils.isAdmin(req.cookies.sessionID).then((isAdmin) => {
          if (isAdmin !== true) {
              res.status(401).send("This API endpoint requires Admin capability").end();
              fulfill(false);
          } else {
            // make sure that the request contains a course title
            if(!req.params.courseTitle) {
              res.status(403).send("Invalid course title").end();
              fulfill(false);
            }
            // make sure the courseTitle is valid
            // TODO Important :: we need to make sure that the course title is alphanumeric and doesn't contain special characters

            // make sure that the request format is correct
            if(!req.body.students) {
              res.status(403).send("Invalid request").end();
              fulfill(false);
            }

            // make sure that the request size > 0
            if (!Array.isArray(req.body.students) || req.body.students.length <= 0) {
              res.status(403).send("Invalid request (Array of students is expected)").end();
              fulfill(false);
            }

            fulfill(true);
          }
        })
      }
    })
  })
}

/**
* This function add a list of students to a specific Course
* It requires admin access
* @author Gehad
* @param HTTP req
* @param HTTP res
* @returns void
*/
export function addStudents(req, res) {
  checkRequestSanity(req, res).then((accept) => {
    if (accept) {
      const numberOfStudents = req.body.students.length;
      var DBSuccesses = 0;
      var DBfails = 0;
      function checkAndSend () {
        if (DBSuccesses + DBfails === numberOfStudents) {
          res.status(200).send({
            Inserted: DBSuccesses,
            Failed: DBfails
          }).end();
        }
      }

      // add students to the database
      Course.
      findOne({ 'title': req.params.courseTitle}, 'title', (err, course) => {
        if (course === null) {
          res.status(403).send("Invalid request (Course not found)").end();
        } else {
          req.body.students.map((student)=>{
            return student.email
          }).filter((student_email) => {
            // TODO: We need to check if this user exists in the User collection before registering them to the course (Integrity)
            // TODO: We need to check if this user is added to this course before??
            // Will just return true for all now; need to be fixed
            return true;
          }).forEach(function(student_email) {
            if (typeof student_email == 'undefined') {
              DBfails = DBfails + 1;
              checkAndSend();
            } else {
              Course.updateOne(
              { _id: course._id },
              { $push: { usernames: student_email } },
              (err, raw) => {
                if (err !== null) {
                  console.log(err);
                  DBfails = DBfails + 1;
                } else {
                  DBSuccesses = DBSuccesses + 1;
                }
                checkAndSend();
              });
            }
          });
        }
      });
    }
  })
}

/**
* This function remove a list of students from a specific Course
* It requires admin access
* @author Gehad
* @param HTTP req
* @param HTTP res
* @returns void
*/
export function dropStudents(req, res) {
  checkRequestSanity(req, res).then((accept) => {
    if (accept) {
      const numberOfStudents = req.body.students.length;
      var DBSuccesses = 0;
      var DBfails = 0;
      function checkAndSend () {
        if (DBSuccesses + DBfails === numberOfStudents) {
          res.status(200).send({
            Deleted: DBSuccesses,
            Failed: DBfails
          }).end();
        }
      }

      // add students to the database
      Course.
      findOne({ 'title': req.params.courseTitle}, 'title', (err, course) => {
        if (course === null) {
          res.status(403).send("Invalid request (Course not found)").end();
        } else {
          req.body.students.map((student)=>{
            return student.email
          }).filter((student_email) => {
            // TODO: We need to check if these users exist before registering them to the course
            return true;
          }).forEach(function(student_email) {
            if (typeof student_email == 'undefined') {
              DBfails = DBfails + 1;
              checkAndSend();
            } else {
              Course.updateOne(
              { _id: course._id },
              // TODO: optimization :: we can remove a list in one query; we will need to filter the list beforehand
              { $pull: { usernames: { $in: [student_email] } } },
              (err, raw) => {
                if (err !== null || raw.nModified === 0) {
                  DBfails = DBfails + 1;
                } else {
                  DBSuccesses = DBSuccesses + 1;
                }
                checkAndSend();
              });
            }
          });
        }
      });
    }
  })
}

export var courseList = async_f( function (req,res) {
  var list = [];
  var results;

  results = await_f(
    Course.find(
      {},
      'title',
      function (err, course) {
        if (err){
          console.error(err);
          res.status(400).end();
        }
      }
    )
  )

  if (results) {

    results.forEach( function(course){

      if (course.title) {
        list.push(course.title);

      } else {
        res.status(400).end();
      }
    })

  } else {
    res.status(400).end();
  }

  res.send({
    courseList: list
  })

  res.status(200).end();
})<commit_msg>FIX - Add iproved error handling to new API call<commit_after>import Course from '../models/course';
import SessionUtils from '../util/sessionUtils';

var async_f = require('asyncawait/async');
var await_f = require('asyncawait/await');

function  checkRequestSanity(req, res) {
  return new Promise (function (fulfill, reject){
    // make sure that the session is valid
    SessionUtils.isValidSession(req.cookies.sessionID).then((isValid) => {
      if (isValid !== true) {
          res.status(401).end();
          fulfill(false);
      } else {
        // make sure that this sessionID belongs to an Admin
        //TODO: we need to check that this admin is the owner of the course
        SessionUtils.isAdmin(req.cookies.sessionID).then((isAdmin) => {
          if (isAdmin !== true) {
              res.status(401).send("This API endpoint requires Admin capability").end();
              fulfill(false);
          } else {
            // make sure that the request contains a course title
            if(!req.params.courseTitle) {
              res.status(403).send("Invalid course title").end();
              fulfill(false);
            }
            // make sure the courseTitle is valid
            // TODO Important :: we need to make sure that the course title is alphanumeric and doesn't contain special characters

            // make sure that the request format is correct
            if(!req.body.students) {
              res.status(403).send("Invalid request").end();
              fulfill(false);
            }

            // make sure that the request size > 0
            if (!Array.isArray(req.body.students) || req.body.students.length <= 0) {
              res.status(403).send("Invalid request (Array of students is expected)").end();
              fulfill(false);
            }

            fulfill(true);
          }
        })
      }
    })
  })
}

/**
* This function add a list of students to a specific Course
* It requires admin access
* @author Gehad
* @param HTTP req
* @param HTTP res
* @returns void
*/
export function addStudents(req, res) {
  checkRequestSanity(req, res).then((accept) => {
    if (accept) {
      const numberOfStudents = req.body.students.length;
      var DBSuccesses = 0;
      var DBfails = 0;
      function checkAndSend () {
        if (DBSuccesses + DBfails === numberOfStudents) {
          res.status(200).send({
            Inserted: DBSuccesses,
            Failed: DBfails
          }).end();
        }
      }

      // add students to the database
      Course.
      findOne({ 'title': req.params.courseTitle}, 'title', (err, course) => {
        if (course === null) {
          res.status(403).send("Invalid request (Course not found)").end();
        } else {
          req.body.students.map((student)=>{
            return student.email
          }).filter((student_email) => {
            // TODO: We need to check if this user exists in the User collection before registering them to the course (Integrity)
            // TODO: We need to check if this user is added to this course before??
            // Will just return true for all now; need to be fixed
            return true;
          }).forEach(function(student_email) {
            if (typeof student_email == 'undefined') {
              DBfails = DBfails + 1;
              checkAndSend();
            } else {
              Course.updateOne(
              { _id: course._id },
              { $push: { usernames: student_email } },
              (err, raw) => {
                if (err !== null) {
                  console.log(err);
                  DBfails = DBfails + 1;
                } else {
                  DBSuccesses = DBSuccesses + 1;
                }
                checkAndSend();
              });
            }
          });
        }
      });
    }
  })
}

/**
* This function remove a list of students from a specific Course
* It requires admin access
* @author Gehad
* @param HTTP req
* @param HTTP res
* @returns void
*/
export function dropStudents(req, res) {
  checkRequestSanity(req, res).then((accept) => {
    if (accept) {
      const numberOfStudents = req.body.students.length;
      var DBSuccesses = 0;
      var DBfails = 0;
      function checkAndSend () {
        if (DBSuccesses + DBfails === numberOfStudents) {
          res.status(200).send({
            Deleted: DBSuccesses,
            Failed: DBfails
          }).end();
        }
      }

      // add students to the database
      Course.
      findOne({ 'title': req.params.courseTitle}, 'title', (err, course) => {
        if (course === null) {
          res.status(403).send("Invalid request (Course not found)").end();
        } else {
          req.body.students.map((student)=>{
            return student.email
          }).filter((student_email) => {
            // TODO: We need to check if these users exist before registering them to the course
            return true;
          }).forEach(function(student_email) {
            if (typeof student_email == 'undefined') {
              DBfails = DBfails + 1;
              checkAndSend();
            } else {
              Course.updateOne(
              { _id: course._id },
              // TODO: optimization :: we can remove a list in one query; we will need to filter the list beforehand
              { $pull: { usernames: { $in: [student_email] } } },
              (err, raw) => {
                if (err !== null || raw.nModified === 0) {
                  DBfails = DBfails + 1;
                } else {
                  DBSuccesses = DBSuccesses + 1;
                }
                checkAndSend();
              });
            }
          });
        }
      });
    }
  })
}

export function courseList(req,res) {
  var list = [];
  var results;

  Course.find(
    {},
    'title',
    async_f(function (err, course) { // async

      await_f( function(){ // await
        if (err){
          console.error(err);
          res.status(400).end();

        } else if (course) {
          course.forEach( function(c){
            if (c.title) {
              list.push(c.title);

            } else {
              res.status(400).end();
            }
          })

          res.status(200).send({
            courseList: list
          })

        } else {
          res.status(400).end();
        }
      }) // end await
    }) // end async
  )
}
<|endoftext|>


<commit_before>import User from '../models/user.model';

/**
 *
 * @param {number} req.body.id - User id for whom we will increase currernt points.
 * @param {*} res
 * @param {*} next
 */
function addPoints(req, res, next) {
  User.addPoints(req.body.userId)
  .then((user) => {
    user.save();
    res.send('OK');
  })
  .catch(e => next(e));
}

/**
 *
 * @param {number} req.body.id - User id for whom we will obtain current points.
 * @param {*} res
 * @param {*} next
 */
function getPoints(req, res, next) {
  User.getPoints(req.query.userId)
  .then((user) => {
    return res.json(user.points);
  })
  .catch(e => next(e));
}

/**
 *
 * @param {*} request
 * @param {*} response: Json with all users
 * @param {*} next
 */
function getUsersPoints(req, res, next) {
  const filter = {
    skip: 0,
    limit: 500
  };

  User.list(filter)
  .then((users) => {
    return res.json(users);
  })
  .catch(e => next(e));
}

export default { addPoints, getPoints };
<commit_msg>add missing export<commit_after>import User from '../models/user.model';

/**
 *
 * @param {number} req.body.id - User id for whom we will increase currernt points.
 * @param {*} res
 * @param {*} next
 */
function addPoints(req, res, next) {
  User.addPoints(req.body.userId)
  .then((user) => {
    user.save();
    res.send('OK');
  })
  .catch(e => next(e));
}

/**
 *
 * @param {number} req.body.id - User id for whom we will obtain current points.
 * @param {*} res
 * @param {*} next
 */
function getPoints(req, res, next) {
  User.getPoints(req.query.userId)
  .then((user) => {
    return res.json(user.points);
  })
  .catch(e => next(e));
}

/**
 *
 * @param {*} request
 * @param {*} response: Json with all users
 * @param {*} next
 */
function getUsersPoints(req, res, next) {
  const filter = {
    skip: 0,
    limit: 500
  };

  User.list(filter)
  .then((users) => {
    return res.json(users);
  })
  .catch(e => next(e));
}

export default { addPoints, getPoints, getUsersPoints };
<|endoftext|>


<commit_before>
var context = new AudioContext();

var compressor = context.createDynamicsCompressor();
compressor.threshold.value = -50;
compressor.knee.value = 40;
compressor.ratio.value = 12;
compressor.reduction.value = -20;
compressor.attack.value = 20;
compressor.release.value = 0.25;
compressor.connect(context.destination)

var Audio = function(name, path){
  this.name = name
  this.path = path
  this.gain = context.createGain()
  this.gain.connect(compressor)
  this.pattern = {}
}

Audio.prototype.loadSound = function(){
  var sound = this
  var getSound = new XMLHttpRequest();
  getSound.open("GET", sound.path, true);
  getSound.responseType = "arraybuffer";
  getSound.onload = function(){
    context.decodeAudioData(getSound.response, function(audioBuffer){
      sound.buffer = audioBuffer;
    });
  }
  getSound.send();
}

var allSounds = {
  kick1: new Audio('kick1', 'sounds/kick-1.wav'),
  kick2: new Audio('kick2', 'sounds/kick-2.wav'),
  kick3: new Audio('kick3', 'sounds/kick-3.wav'),
  snare1: new Audio('snare1', 'sounds/snare-1.wav'),
  snare2: new Audio('snare2', 'sounds/snare-2.wav'),
  clap1: new Audio('clap1', 'sounds/clap-1.wav'),
  clap2: new Audio('clap2', 'sounds/clap-2.wav'),
  hihat1: new Audio('hihat1', 'sounds/hi-hat-1.wav'),
  hihat2: new Audio('hihat2', 'sounds/hi-hat-2.wav'),
  openhat1: new Audio('openhat1', 'sounds/open-hat-1.wav'),
  openhat2: new Audio('openhat2', 'sounds/open-hat-2.wav'),
}

function loadAllSounds(){
  for (sound in allSounds) {
    allSounds[sound].loadSound()
  }
}

function playSound(audio){
  var sound = context.createBufferSource();
  sound.buffer = audio.buffer;
  sound.connect(audio.gain);
  sound.start(0);
}

var tempo = 100;
var rhythmIndex = 1;
var playing = false;
var sixteenthNoteTime;
var timer;

function play(){
  if(!playing){
    loop();
    playing = true;
  }else{
    rhythmIndex = 1
  }
};

function loop(){
  sixteenthNoteTime = 60 / tempo / 4;
  timer = setTimeout(function(){
    playCurrentIndex()
    loop()
  }, sixteenthNoteTime*1000)
}

function playCurrentIndex(){
  for (audio in allSounds){
    var track = allSounds[audio];
    var pattern = track.pattern
    if (pattern[rhythmIndex]) {
      playSound(track)
    }
  }
  movePlayhead(rhythmIndex)
  progressRhythm()
};

function progressRhythm(){
  if(rhythmIndex < 16){
    rhythmIndex++
  }else{
    rhythmIndex = 1
  }
}

function stop(){
  clearTimeout(timer)
  playing = false
  rhythmIndex = 1
  $('.bar').css('background-color','');
};

function playButtonListener(){
  $('button.play').on('click', function(){
    play();
  });
}

function stopButtonListener(){
  $('button.stop').on('click', function(){
    stop();
  });
};

function padClickListener(){
  $('div.pad').on('click', function(){
    var sound = $(this).attr('id')
    var position = $(this).attr("value")

    if ($(this).hasClass('active')){
      $(this).removeClass('active')
      $(this).css('background-color', '')
      allSounds[sound].pattern[position] = false

    } else {
      $(this).addClass('active')
      $(this).css('background-color', 'red')
      allSounds[sound].pattern[position] = true
    }
  });
}

function tempoChangeListener(){
  $('input.bpm').on('change', function(){
    tempo = $(this).val()
  });
}

function gainChangeListener(){
  $('input.gain').on('change', function(e){
    e.preventDefault();
    var newGain = $(this).val()
    var instrument = $(this).parent().attr('id')
    allSounds[instrument].gain.gain.value = newGain
  });
};

function movePlayhead(index){
  $('.bar').css({
    'background-color':'',
  });
  $('#'+ index).css({
    'background-color':'red',
  });
}

function buildStepNumbers(){
  for (var i = 1; i < 17; i++) {
    var context = {number: i};
    var html = $('#step_number_template').html();
    var templatingFunction = Handlebars.compile(html);
    $('.step_number_container').append(templatingFunction(context));
  };
};

function buildTracks(){

    var context = {sounds: allSounds};
    var html = $('#track_template').html();
    var templatingFunction = Handlebars.compile(html);
    $('#instrument_container').append(templatingFunction(context));
};

function buildPads(){
  for(track in allSounds){
    var instrument = allSounds[track].name
    console.log(instrument)
    for (var i = 1; i < 17; i++) {
      var pads = $('span.' + instrument +'_pads')
      pads.append("

")
    };
  }
}

function buildBoard(){
  buildStepNumbers();
  buildTracks()
  buildPads()
};

function initializeControls(){
  playButtonListener();
  stopButtonListener()
  padClickListener();
  tempoChangeListener();
  gainChangeListener();
};

$(document).ready(function() {
  buildBoard()
  loadAllSounds();
  initializeControls();
});

<commit_msg>window.AudioContext = window.AudioContext || window.webkitAudioContext;<commit_after>
window.AudioContext = window.AudioContext || window.webkitAudioContext;

var context = new AudioContext();

var compressor = context.createDynamicsCompressor();
compressor.threshold.value = -50;
compressor.knee.value = 40;
compressor.ratio.value = 12;
compressor.reduction.value = -20;
compressor.attack.value = 20;
compressor.release.value = 0.25;
compressor.connect(context.destination)

var Audio = function(name, path){
  this.name = name
  this.path = path
  this.gain = context.createGain()
  this.gain.connect(compressor)
  this.pattern = {}
}

Audio.prototype.loadSound = function(){
  var sound = this
  var getSound = new XMLHttpRequest();
  getSound.open("GET", sound.path, true);
  getSound.responseType = "arraybuffer";
  getSound.onload = function(){
    context.decodeAudioData(getSound.response, function(audioBuffer){
      sound.buffer = audioBuffer;
    });
  }
  getSound.send();
}

var allSounds = {
  kick1: new Audio('kick1', 'sounds/kick-1.wav'),
  kick2: new Audio('kick2', 'sounds/kick-2.wav'),
  kick3: new Audio('kick3', 'sounds/kick-3.wav'),
  snare1: new Audio('snare1', 'sounds/snare-1.wav'),
  snare2: new Audio('snare2', 'sounds/snare-2.wav'),
  clap1: new Audio('clap1', 'sounds/clap-1.wav'),
  clap2: new Audio('clap2', 'sounds/clap-2.wav'),
  hihat1: new Audio('hihat1', 'sounds/hi-hat-1.wav'),
  hihat2: new Audio('hihat2', 'sounds/hi-hat-2.wav'),
  openhat1: new Audio('openhat1', 'sounds/open-hat-1.wav'),
  openhat2: new Audio('openhat2', 'sounds/open-hat-2.wav'),
}

function loadAllSounds(){
  for (sound in allSounds) {
    allSounds[sound].loadSound()
  }
}

function playSound(audio){
  var sound = context.createBufferSource();
  sound.buffer = audio.buffer;
  sound.connect(audio.gain);
  sound.start(0);
}

var tempo = 100;
var rhythmIndex = 1;
var playing = false;
var sixteenthNoteTime;
var timer;

function play(){
  if(!playing){
    loop();
    playing = true;
  }else{
    rhythmIndex = 1
  }
};

function loop(){
  sixteenthNoteTime = 60 / tempo / 4;
  timer = setTimeout(function(){
    playCurrentIndex()
    loop()
  }, sixteenthNoteTime*1000)
}

function playCurrentIndex(){
  for (audio in allSounds){
    var track = allSounds[audio];
    var pattern = track.pattern
    if (pattern[rhythmIndex]) {
      playSound(track)
    }
  }
  movePlayhead(rhythmIndex)
  progressRhythm()
};

function progressRhythm(){
  if(rhythmIndex < 16){
    rhythmIndex++
  }else{
    rhythmIndex = 1
  }
}

function stop(){
  clearTimeout(timer)
  playing = false
  rhythmIndex = 1
  $('.bar').css('background-color','');
};

function playButtonListener(){
  $('button.play').on('click', function(){
    play();
  });
}

function stopButtonListener(){
  $('button.stop').on('click', function(){
    stop();
  });
};

function padClickListener(){
  $('div.pad').on('click', function(){
    var sound = $(this).attr('id')
    var position = $(this).attr("value")

    if ($(this).hasClass('active')){
      $(this).removeClass('active')
      $(this).css('background-color', '')
      allSounds[sound].pattern[position] = false

    } else {
      $(this).addClass('active')
      $(this).css('background-color', 'red')
      allSounds[sound].pattern[position] = true
    }
  });
}

function tempoChangeListener(){
  $('input.bpm').on('change', function(){
    tempo = $(this).val()
  });
}

function gainChangeListener(){
  $('input.gain').on('change', function(e){
    e.preventDefault();
    var newGain = $(this).val()
    var instrument = $(this).parent().attr('id')
    allSounds[instrument].gain.gain.value = newGain
  });
};

function movePlayhead(index){
  $('.bar').css({
    'background-color':'',
  });
  $('#'+ index).css({
    'background-color':'red',
  });
}

function buildStepNumbers(){
  for (var i = 1; i < 17; i++) {
    var context = {number: i};
    var html = $('#step_number_template').html();
    var templatingFunction = Handlebars.compile(html);
    $('.step_number_container').append(templatingFunction(context));
  };
};

function buildTracks(){

    var context = {sounds: allSounds};
    var html = $('#track_template').html();
    var templatingFunction = Handlebars.compile(html);
    $('#instrument_container').append(templatingFunction(context));
};

function buildPads(){
  for(track in allSounds){
    var instrument = allSounds[track].name
    console.log(instrument)
    for (var i = 1; i < 17; i++) {
      var pads = $('span.' + instrument +'_pads')
      pads.append("

")
    };
  }
}

function buildBoard(){
  buildStepNumbers();
  buildTracks()
  buildPads()
};

function initializeControls(){
  playButtonListener();
  stopButtonListener()
  padClickListener();
  tempoChangeListener();
  gainChangeListener();
};

$(document).ready(function() {
  buildBoard()
  loadAllSounds();
  initializeControls();
});

<|endoftext|>


<commit_before>const DrawCard = require('../../drawcard.js');

class QueensMen extends DrawCard {
    setupCardAbilities() {
        this.reaction({
            when: {
                onCardEntersPlay: event => event.card === this && event.playingType === 'marshal'
            },
            chooseOpponent: opponent => opponent.hand.size() > 1,
            handler: context => {
                let opponent = context.opponent;

                this.chosenOpponent = opponent;

                let buttons = opponent.hand.map(card => {
                    return { card: card, method: 'cardSelected' };
                });

                buttons.push({ text: 'Done', method: 'doneSelected' });

                this.game.promptWithMenu(this.controller, this, {
                    activePrompt: {
                        menuTitle: 'Choose whether to discard a non-character card, or click done',
                        buttons: buttons
                    },
                    source: this
                });
            }
        });
    }

    cardSelected(player, cardId) {
        let toDiscard = this.game.findAnyCardInAnyList(cardId);
        let opponent = toDiscard.controller;
        this.game.addMessage('{0} uses {1} to look at {2}\'s hand', this.controller, this, opponent);

        if(toDiscard && toDiscard.getType() !== 'character' && this.controller.anyCardsInPlay(card => !card.isFaction('baratheon') && card.getType() === 'character' && !card.kneeled)) {
            this.game.promptForSelect(this.controller, {
                activePromptTitle: 'Select a character',
                source: this,
                gameAction: 'kneel',
                cardCondition: card => card.location === 'play area' && card.controller === this.controller &&
                                       !card.isFaction('baratheon') && card.getType() === 'character',
                onSelect: (player, toKneel) => this.kneelToDiscard(player, toKneel, toDiscard)
            });
        }

        return true;
    }

    doneSelected() {
        this.game.addMessage('{0} uses {1} to look at {2}\'s hand', this.controller, this, this.chosenOpponent);
        return true;
    }

    kneelToDiscard(p, toKneel, toDiscard) {
        toKneel.controller.kneelCard(toKneel);
        toDiscard.owner.discardCard(toDiscard);
        this.game.addMessage('{0} then kneels {1} to discard {2} from {3}\'s hand',
            this.controller, toKneel, toDiscard, toDiscard.owner);

        return true;
    }
}

QueensMen.code = '08008';

module.exports = QueensMen;
<commit_msg>Allow Queen's Men to trigger on opponents holding a single card (#1630)<commit_after>const DrawCard = require('../../drawcard.js');

class QueensMen extends DrawCard {
    setupCardAbilities() {
        this.reaction({
            when: {
                onCardEntersPlay: event => event.card === this && event.playingType === 'marshal'
            },
            chooseOpponent: opponent => opponent.hand.size() > 0,
            handler: context => {
                let opponent = context.opponent;

                this.chosenOpponent = opponent;

                let buttons = opponent.hand.map(card => {
                    return { card: card, method: 'cardSelected' };
                });

                buttons.push({ text: 'Done', method: 'doneSelected' });

                this.game.promptWithMenu(this.controller, this, {
                    activePrompt: {
                        menuTitle: 'Choose whether to discard a non-character card, or click done',
                        buttons: buttons
                    },
                    source: this
                });
            }
        });
    }

    cardSelected(player, cardId) {
        let toDiscard = this.game.findAnyCardInAnyList(cardId);
        let opponent = toDiscard.controller;
        this.game.addMessage('{0} uses {1} to look at {2}\'s hand', this.controller, this, opponent);

        if(toDiscard && toDiscard.getType() !== 'character' && this.controller.anyCardsInPlay(card => !card.isFaction('baratheon') && card.getType() === 'character' && !card.kneeled)) {
            this.game.promptForSelect(this.controller, {
                activePromptTitle: 'Select a character',
                source: this,
                gameAction: 'kneel',
                cardCondition: card => card.location === 'play area' && card.controller === this.controller &&
                                       !card.isFaction('baratheon') && card.getType() === 'character',
                onSelect: (player, toKneel) => this.kneelToDiscard(player, toKneel, toDiscard)
            });
        }

        return true;
    }

    doneSelected() {
        this.game.addMessage('{0} uses {1} to look at {2}\'s hand', this.controller, this, this.chosenOpponent);
        return true;
    }

    kneelToDiscard(p, toKneel, toDiscard) {
        toKneel.controller.kneelCard(toKneel);
        toDiscard.owner.discardCard(toDiscard);
        this.game.addMessage('{0} then kneels {1} to discard {2} from {3}\'s hand',
            this.controller, toKneel, toDiscard, toDiscard.owner);

        return true;
    }
}

QueensMen.code = '08008';

module.exports = QueensMen;
<|endoftext|>


<commit_before>L.Control.EasyButtons = L.Control.extend({
    options: {
        position: 'topleft',
        title: 'Locate Me',
        intentedIcon: 'fa-circle-o'
    },

    onAdd: function () {
        var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');

        this.link = L.DomUtil.create('a', 'leaflet-bar-part', container);
        L.DomUtil.create('i', 'fa fa-lg ' + this.options.intentedIcon , this.link);
        this.link.href = '#';

        L.DomEvent.on(this.link, 'click', this._click, this);
        this.link.title = this.options.title

        return container;
    },

    intendedFunction: function(){ alert('no function selected')},

    _click: function (e) {
        L.DomEvent.stopPropagation(e);
        L.DomEvent.preventDefault(e);
        this.intendedFunction();
    },
});

function easyButton( btnIcon , btnTitle , btnFunction , btnMap ) {
  var newControl = new L.Control.EasyButtons
  newControl.options.intentedIcon = btnIcon
  if ( typeof btnFunction === 'function'){
    newControl.intendedFunction = btnFunction
  }
  newControl.options.title = btnTitle
  if ( btnMap ){
    newControl.addTo(btnMap)
  } else {
    newControl.addTo(btnMap)
  }
}

easyButton( 'fa-exclamation-triangle', 'here' , function(){alert('batham')} , map )<commit_msg>included function inside L<commit_after>L.Control.EasyButtons = L.Control.extend({
    options: {
        position: 'topleft',
        title: 'Locate Me',
        intentedIcon: 'fa-circle-o'
    },

    onAdd: function () {
        var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');

        this.link = L.DomUtil.create('a', 'leaflet-bar-part', container);
        L.DomUtil.create('i', 'fa fa-lg ' + this.options.intentedIcon , this.link);
        this.link.href = '#';

        L.DomEvent.on(this.link, 'click', this._click, this);
        this.link.title = this.options.title

        return container;
    },

    intendedFunction: function(){ alert('no function selected')},

    _click: function (e) {
        L.DomEvent.stopPropagation(e);
        L.DomEvent.preventDefault(e);
        this.intendedFunction();
    },
});

L.easyButton = {}

L.easyButton = function( btnIcon , btnFunction , btnTitle , btnMap ) {
  var newControl = new L.Control.EasyButtons
  newControl.options.intentedIcon = btnIcon

  if ( typeof btnFunction === 'function'){
    newControl.intendedFunction = btnFunction
  }

  if (btnTitle) newControl.options.title = btnTitle

  if ( btnMap ){
    newControl.addTo(btnMap)
  } else {
    newControl.addTo(map)
  }
}

L.easyButton( 'fa-exclamation-triangle', function(){alert('batham')} , 'here' , map )<|endoftext|>


<commit_before>'use strict';

// == Application Menu ==
const file_menuitems = [
  { label: _('&New Project'),		id: 'new-project',		accelerator: 'Ctrl+N', },
  { label: _('&Open...'),		id: 'open-file',		accelerator: 'Ctrl+O', },
  { label: _('&Save'),			id: 'save-same',		accelerator: 'Ctrl+S', },
  { label: _('&Save As...'),		id: 'save-as',			accelerator: 'Shift+Ctrl+S', },
  { type:  'separator' },
  { label: _('&Preferences...'),	id: 'preferences-dialog',	accelerator: 'Ctrl+,', },
  { type:  'separator' },
  { label: _('&Quit'),			id: 'quit-app',			accelerator: 'Shift+Ctrl+Q', },
];
const view_menuitems = [
  { label: _('Toggle &Fullscreen'),	id: 'toggle-fulscreen', 	accelerator: 'F11', },
];
const help_menuitems = [
  { label: _('Beast &Manual...'),	id: 'manual-dialog-html',	},
  { type:  'separator' },
  { label: _('&About...'),		id: 'about-dialog',		},
];
const menubar_menuitems = [
  { label: _('&File'), 			submenu: file_menuitems, },
  { label: _('&View'), 			submenu: view_menuitems, },
  { label: _('&Help'), 			submenu: help_menuitems, },
];

let save_same_filename = undefined;
let open_dialog_lastdir = undefined;
let save_dialog_lastdir = undefined;

// assign global app menu
export async function setup_app_menu()
{
  open_dialog_lastdir = await Bse.server.get_demo_path();
  function complete_menu_items (item)
  {
    if (Array.isArray (item))
      for (let i = 0; i < item.length; i++)
	complete_menu_items (item[i]);
    else if (item.submenu !== undefined)
      complete_menu_items (item.submenu);
    else
      {
	if (item.role === undefined && item.id !== undefined)
	  item.role = item.id;
	if (item.click === undefined && item.role !== undefined)
	  item.click = (menuitem, _focusedBrowserWindow, _event) => menu_command (menuitem);
      }
  }
  complete_menu_items (menubar_menuitems);
  const menubar_menu = Electron.Menu.buildFromTemplate (menubar_menuitems);
  Electron.Menu.setApplicationMenu (menubar_menu);
  check_all_menu_items();
}

export function check_all_menu_items()
{
  function check_menu_item (item) {
    if (item.items)
      for (let i = 0; i < item.items.length; i++)
	check_menu_item (item.items[i]);
    else if (item.submenu)
      check_menu_item (item.submenu);
    else
      menu_sentinel (item);
  }
  const app_menu = Electron.Menu.getApplicationMenu();
  if (app_menu)
    check_menu_item (app_menu);
}

function menu_sentinel (menuitem)
{
  switch (menuitem.role) {
    case 'save-same':
      menuitem.enabled = save_same_filename ? true : false;
      break;
  }
}

// handle menu activations
function menu_command (menuitem)
{
  const BrowserWindow = Electron.getCurrentWindow(); // http://electron.atom.io/docs/api/browser-window/
  menu_sentinel (menuitem);
  if (!menuitem.enabled) return;
  switch (menuitem.role) {
    case 'about-dialog':
      Shell.show_about_dialog = !Shell.show_about_dialog;
      break;
    case 'preferences-dialog':
      Shell.show_preferences_dialog = !Shell.show_preferences_dialog;
      break;
    case 'manual-dialog-html': {
      const win = new Electron.BrowserWindow ({
	backgroundColor: '#fefdfc',
	webPreferences: { contextIsolation: true,
			  nodeIntegration: false,
			  plugins: true, // needed for pdf_viewer
			  sandbox: true } });
      let u = new URL (window.location);
      u = u.origin + '/doc/beast-manual.html';
      win.loadURL (u);
      win.setMenu (null);
      win.webContents.on ('before-input-event', (event, input) => {
	if (input.type == 'keyUp' || input.type == 'keyDown')
	  {
	    if (input.alt && input.code == "ArrowLeft" &&
		win.webContents.canGoBack())
	      {
		event.preventDefault();
		if (input.type == 'keyDown')
		  win.webContents.goBack();
	      }
	    if (input.alt && input.code == "ArrowRight" &&
		win.webContents.canGoForward())
	      {
		event.preventDefault();
		if (input.type == 'keyDown')
		  win.webContents.goForward();
	      }
	  }
      });
      break; }
    case 'toggle-fulscreen':
      BrowserWindow.setFullScreen (!BrowserWindow.isFullScreen());
      break;
    case 'quit-app':
      Electron.app.exit (0);
      return false;
    case 'new-project':
      save_same_filename = undefined;
      Shell.load_project();
      break;
    case 'open-file':
      Electron.dialog.showOpenDialog (Electron.getCurrentWindow(),
				      {
					title: Util.format_title ('Beast', 'Select File To Open'),
					buttonLabel: 'Open File',
					defaultPath: open_dialog_lastdir,
					properties: ['openFile', ], // 'multiSelections' 'openDirectory' 'showHiddenFiles'
					filters: [ { name: 'BSE Projects', extensions: ['bse'] },
						   { name: 'Audio Files', extensions: [ 'bse', 'mid', 'wav', 'mp3', 'ogg' ] },
						   { name: 'All Files', extensions: [ '*' ] }, ],
				      },
				      async (result) => {
					if (result && result.length == 1)
					  {
					    const Path = require ('path');
					    open_dialog_lastdir = Path.dirname (result[0]);
					    if (await Shell.load_project (result[0]) == Bse.Error.NONE)
					      {
						save_same_filename = result[0];
						check_all_menu_items();
					      }
					    else
					      console.error ('Failed to load:', result[0]);
					  }
				      });
      break;
    case 'save-as':
      if (!save_dialog_lastdir)
	{
	  const Path = require ('path');
	  save_dialog_lastdir = Electron.app.getPath ('music') || Path.resolve ('.');
	}
      Electron.dialog.showSaveDialog (Electron.getCurrentWindow(),
				      {
					title: Util.format_title ('Beast', 'Save Project'),
					buttonLabel: 'Save As',
					defaultPath: save_dialog_lastdir,
					filters: [ { name: 'BSE Projects', extensions: ['bse'] }, ],
				      },
				      (savepath) => {
					if (!savepath)
					  return;
					const Path = require ('path');
					save_dialog_lastdir = Path.dirname (savepath);
					if (!savepath.endsWith ('.bse'))
					  savepath += '.bse';
					const Fs = require ('fs');
					if (Fs.existsSync (savepath))
					  Fs.unlinkSync (savepath);
					let err = Shell.save_project (savepath);
					if (err == Bse.Error.NONE)
					  {
					    save_same_filename = savepath;
					    check_all_menu_items();
					  }
					else
					  console.log ('Save:', savepath, err);
				      });
      break;
    case 'save-same':
      if (save_same_filename)
	{
	  const Fs = require ('fs');
	  if (Fs.existsSync (save_same_filename))
	    Fs.unlinkSync (save_same_filename);
	  // TODO: instead of calling unlinkSync(), BSE should support atomically replacing bse files
	  Shell.save_project (save_same_filename);
	}
      break;
    default:
      console.log ('unhandled menu command: ' + menuitem.role);
      break;
  }
  menu_sentinel (menuitem);
}
<commit_msg>EBEAST: menus.js: allow BrowserWindow local menus<commit_after>'use strict';

// == Application Menu ==
const file_menuitems = [
  { label: _('&New Project'),		id: 'new-project',		accelerator: 'Ctrl+N', },
  { label: _('&Open...'),		id: 'open-file',		accelerator: 'Ctrl+O', },
  { label: _('&Save'),			id: 'save-same',		accelerator: 'Ctrl+S', },
  { label: _('&Save As...'),		id: 'save-as',			accelerator: 'Shift+Ctrl+S', },
  { type:  'separator' },
  { label: _('&Preferences...'),	id: 'preferences-dialog',	accelerator: 'Ctrl+,', },
  { type:  'separator' },
  { label: _('&Quit'),			id: 'quit-app',			accelerator: 'Shift+Ctrl+Q', },
];
const view_menuitems = [
  { label: _('Toggle &Fullscreen'),	id: 'toggle-fulscreen', 	accelerator: 'F11', },
];
const help_menuitems = [
  { label: _('Beast &Manual...'),	id: 'manual-dialog-html',	},
  { type:  'separator' },
  { label: _('&About...'),		id: 'about-dialog',		},
];
const menubar_menuitems = [
  { label: _('&File'), 			submenu: file_menuitems, },
  { label: _('&View'), 			submenu: view_menuitems, },
  { label: _('&Help'), 			submenu: help_menuitems, },
];

let save_same_filename = undefined;
let open_dialog_lastdir = undefined;
let save_dialog_lastdir = undefined;

// assign global app menu
export async function setup_app_menu()
{
  open_dialog_lastdir = await Bse.server.get_demo_path();
  function complete_menu_items (item)
  {
    if (Array.isArray (item))
      for (let i = 0; i < item.length; i++)
	complete_menu_items (item[i]);
    else if (item.submenu !== undefined)
      complete_menu_items (item.submenu);
    else
      {
	if (item.role === undefined && item.id !== undefined)
	  item.role = item.id;
	if (item.click === undefined && item.role !== undefined)
	  item.click = (menuitem, _focusedBrowserWindow, _event) => menu_command (menuitem);
      }
  }
  complete_menu_items (menubar_menuitems);
  const menubar_menu = Electron.Menu.buildFromTemplate (menubar_menuitems);
  Electron.getCurrentWindow().setMenu (menubar_menu);
  check_all_menu_items();
}

export function check_all_menu_items()
{
  function check_menu_item (item) {
    if (item.items)
      for (let i = 0; i < item.items.length; i++)
	check_menu_item (item.items[i]);
    else if (item.submenu)
      check_menu_item (item.submenu);
    else
      menu_sentinel (item);
  }
  const app_menu = Electron.Menu.getApplicationMenu();
  if (app_menu)
    check_menu_item (app_menu);
}

function menu_sentinel (menuitem)
{
  switch (menuitem.role) {
    case 'save-same':
      menuitem.enabled = save_same_filename ? true : false;
      break;
  }
}

// handle menu activations
function menu_command (menuitem)
{
  const BrowserWindow = Electron.getCurrentWindow(); // http://electron.atom.io/docs/api/browser-window/
  menu_sentinel (menuitem);
  if (!menuitem.enabled) return;
  switch (menuitem.role) {
    case 'about-dialog':
      Shell.show_about_dialog = !Shell.show_about_dialog;
      break;
    case 'preferences-dialog':
      Shell.show_preferences_dialog = !Shell.show_preferences_dialog;
      break;
    case 'manual-dialog-html': {
      const win = new Electron.BrowserWindow ({
	backgroundColor: '#fefdfc',
	webPreferences: { contextIsolation: true,
			  nodeIntegration: false,
			  plugins: true, // needed for pdf_viewer
			  sandbox: true } });
      let u = new URL (window.location);
      u = u.origin + '/doc/beast-manual.html';
      win.loadURL (u);
      win.setMenu (null);
      win.webContents.on ('before-input-event', (event, input) => {
	if (input.type == 'keyUp' || input.type == 'keyDown')
	  {
	    if (input.alt && input.code == "ArrowLeft" &&
		win.webContents.canGoBack())
	      {
		event.preventDefault();
		if (input.type == 'keyDown')
		  win.webContents.goBack();
	      }
	    if (input.alt && input.code == "ArrowRight" &&
		win.webContents.canGoForward())
	      {
		event.preventDefault();
		if (input.type == 'keyDown')
		  win.webContents.goForward();
	      }
	  }
      });
      break; }
    case 'toggle-fulscreen':
      BrowserWindow.setFullScreen (!BrowserWindow.isFullScreen());
      break;
    case 'quit-app':
      Electron.app.exit (0);
      return false;
    case 'new-project':
      save_same_filename = undefined;
      Shell.load_project();
      break;
    case 'open-file':
      Electron.dialog.showOpenDialog (Electron.getCurrentWindow(),
				      {
					title: Util.format_title ('Beast', 'Select File To Open'),
					buttonLabel: 'Open File',
					defaultPath: open_dialog_lastdir,
					properties: ['openFile', ], // 'multiSelections' 'openDirectory' 'showHiddenFiles'
					filters: [ { name: 'BSE Projects', extensions: ['bse'] },
						   { name: 'Audio Files', extensions: [ 'bse', 'mid', 'wav', 'mp3', 'ogg' ] },
						   { name: 'All Files', extensions: [ '*' ] }, ],
				      },
				      async (result) => {
					if (result && result.length == 1)
					  {
					    const Path = require ('path');
					    open_dialog_lastdir = Path.dirname (result[0]);
					    if (await Shell.load_project (result[0]) == Bse.Error.NONE)
					      {
						save_same_filename = result[0];
						check_all_menu_items();
					      }
					    else
					      console.error ('Failed to load:', result[0]);
					  }
				      });
      break;
    case 'save-as':
      if (!save_dialog_lastdir)
	{
	  const Path = require ('path');
	  save_dialog_lastdir = Electron.app.getPath ('music') || Path.resolve ('.');
	}
      Electron.dialog.showSaveDialog (Electron.getCurrentWindow(),
				      {
					title: Util.format_title ('Beast', 'Save Project'),
					buttonLabel: 'Save As',
					defaultPath: save_dialog_lastdir,
					filters: [ { name: 'BSE Projects', extensions: ['bse'] }, ],
				      },
				      (savepath) => {
					if (!savepath)
					  return;
					const Path = require ('path');
					save_dialog_lastdir = Path.dirname (savepath);
					if (!savepath.endsWith ('.bse'))
					  savepath += '.bse';
					const Fs = require ('fs');
					if (Fs.existsSync (savepath))
					  Fs.unlinkSync (savepath);
					let err = Shell.save_project (savepath);
					if (err == Bse.Error.NONE)
					  {
					    save_same_filename = savepath;
					    check_all_menu_items();
					  }
					else
					  console.log ('Save:', savepath, err);
				      });
      break;
    case 'save-same':
      if (save_same_filename)
	{
	  const Fs = require ('fs');
	  if (Fs.existsSync (save_same_filename))
	    Fs.unlinkSync (save_same_filename);
	  // TODO: instead of calling unlinkSync(), BSE should support atomically replacing bse files
	  Shell.save_project (save_same_filename);
	}
      break;
    default:
      console.log ('unhandled menu command: ' + menuitem.role);
      break;
  }
  menu_sentinel (menuitem);
}
<|endoftext|>


<commit_before>$( document ).ready(function() {

  $( document ).ajaxError(function(event, jqxhr, settings, exception) {
    popup( "Triggered ajaxError handler." );
    console.log(event);
    console.log(jqxhr);
    console.log(settings);
    console.log(exception);
  });

// Setup Ace editor
var editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/javascript");
editor.setFontSize(18);
editor.getSession().setTabSize(4);
editor.getSession().setUseSoftTabs(true);

var terminal = ace.edit("terminal");
terminal.setTheme("ace/theme/ambiance");
terminal.getSession().setMode("ace/mode/javascript");
terminal.setFontSize(18);
terminal.renderer.setShowGutter(false);
terminal.setHighlightActiveLine(false);
terminal.setReadOnly(true);

var docs = ace.edit("docs");
docs.setTheme("ace/theme/ambiance");
docs.getSession().setMode("ace/mode/javascript");
docs.setFontSize(18);
docs.renderer.setShowGutter(false);
docs.setHighlightActiveLine(false);
docs.setReadOnly(true);

var docScreen = document.getElementById("doc-screen");
docScreen.hidden = true;

var docInput = document.getElementById("doc-input");
docInput.onkeypress = function (e) {
  var query = docInput.value;
  search(query);
};

function printInput (s) { terminal.insert("\n" + s + "\n"); };
function printOutput (s) { terminal.insert("\n\t=> " + s + "\n"); };

popup = function (msg) {
  printOutput(msg);
};

// Augmenting JS
if(typeof(String.prototype.trim) === "undefined") {
    String.prototype.trim = function() {
        return String(this).replace(/^\s+|\s+$/g, '');
    };
}

// Restore code
editor.session.setValue(localStorage.sourceCode);

editor.session.on("tokenizerUpdate", function (e) {
  console.log(e);
});

editor.commands.addCommand({
    name: 'send-to-REPL',
    bindKey: {win: 'Ctrl-E',  mac: 'Command-E'},
    exec: function execute () {
      "use strict";
      var text = editor.session.getTextRange(editor.getSelectionRange());
      if (text == "") {
        var pos = editor.getCursorPosition();
        text = editor.session.getLine(pos.row);
      }
      printInput(text.trim());
      try {
        var result = window.eval(text);
        printOutput(result ? result.toString() : result);
      } catch(err) {
        if (err instanceof SyntaxError) {
          printOutput("[!] The syntax is incorrect.");
        } else {
          printOutput("[!] Error: " + err.message);
        }
      }
    },
    readOnly: true // false if this command should not apply in readOnly mode
});

editor.commands.addCommand({
    name: 'save',
    bindKey: {win: 'Ctrl-S',  mac: 'Command-S'},
    exec: function () {
      localStorage.sourceCode = editor.session.getValue();
      popup("[*] Saved");
    },
    readOnly: true // false if this command should not apply in readOnly mode
});

keypress.combo("ctrl space", function() {
    docScreen.hidden = !docScreen.hidden;
    docInput.focus();
});

/* Documentation search */
var SEARCH_API_URL = "http://localhost:5000/search";//?query=";

var search = function (query) {
  popup("Searching " + query);

  // $.getJSON( url, {
  //   query: "Boolean"
  // })
  // .done(function( data ) {
  //   console.log("Success!");
  //   console.log(data);
  // });

  (function() {
    var raiseAjaxError = function() {
        $.ajax({
            url: SEARCH_API_URL + query,
            type: 'GET',
            dataType: 'text',
            success: function(data) {
                console.log('OK: ' + data);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                console.log('Error: ' + errorThrown + ' ' + textStatus + ' ' + jqXHR);
            }
        });
    };

    raiseAjaxError(); // Error: NOT FOUND error [object Object]

  })();
};

});
<commit_msg>Remove 'illegal character' copied over from an example.<commit_after>$( document ).ready(function() {

  $( document ).ajaxError(function(event, jqxhr, settings, exception) {
    popup( "Triggered ajaxError handler." );
    console.log(event);
    console.log(jqxhr);
    console.log(settings);
    console.log(exception);
  });

// Setup Ace editor
var editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/javascript");
editor.setFontSize(18);
editor.getSession().setTabSize(4);
editor.getSession().setUseSoftTabs(true);

var terminal = ace.edit("terminal");
terminal.setTheme("ace/theme/ambiance");
terminal.getSession().setMode("ace/mode/javascript");
terminal.setFontSize(18);
terminal.renderer.setShowGutter(false);
terminal.setHighlightActiveLine(false);
terminal.setReadOnly(true);

var docs = ace.edit("docs");
docs.setTheme("ace/theme/ambiance");
docs.getSession().setMode("ace/mode/javascript");
docs.setFontSize(18);
docs.renderer.setShowGutter(false);
docs.setHighlightActiveLine(false);
docs.setReadOnly(true);

var docScreen = document.getElementById("doc-screen");
docScreen.hidden = true;

var docInput = document.getElementById("doc-input");
docInput.onkeypress = function (e) {
  var query = docInput.value;
  search(query);
};

function printInput (s) { terminal.insert("\n" + s + "\n"); };
function printOutput (s) { terminal.insert("\n\t=> " + s + "\n"); };

popup = function (msg) {
  printOutput(msg);
};

// Augmenting JS
if(typeof(String.prototype.trim) === "undefined") {
    String.prototype.trim = function() {
        return String(this).replace(/^\s+|\s+$/g, '');
    };
}

// Restore code
editor.session.setValue(localStorage.sourceCode);

editor.session.on("tokenizerUpdate", function (e) {
  console.log(e);
});

editor.commands.addCommand({
    name: 'send-to-REPL',
    bindKey: {win: 'Ctrl-E',  mac: 'Command-E'},
    exec: function execute () {
      "use strict";
      var text = editor.session.getTextRange(editor.getSelectionRange());
      if (text == "") {
        var pos = editor.getCursorPosition();
        text = editor.session.getLine(pos.row);
      }
      printInput(text.trim());
      try {
        var result = window.eval(text);
        printOutput(result ? result.toString() : result);
      } catch(err) {
        if (err instanceof SyntaxError) {
          printOutput("[!] The syntax is incorrect.");
        } else {
          printOutput("[!] Error: " + err.message);
        }
      }
    },
    readOnly: true // false if this command should not apply in readOnly mode
});

editor.commands.addCommand({
    name: 'save',
    bindKey: {win: 'Ctrl-S',  mac: 'Command-S'},
    exec: function () {
      localStorage.sourceCode = editor.session.getValue();
      popup("[*] Saved");
    },
    readOnly: true // false if this command should not apply in readOnly mode
});

keypress.combo("ctrl space", function() {
    docScreen.hidden = !docScreen.hidden;
    docInput.focus();
});

/* Documentation search */
var SEARCH_API_URL = "http://localhost:5000/search";//?query=";

var search = function (query) {
  popup("Searching " + query);

  $.ajax({
    url: SEARCH_API_URL + query,
    type: 'GET',
    dataType: 'text',
    success: function(data) {
      console.log('OK: ' + data);
    },
    error: function(jqXHR, textStatus, errorThrown) {
      console.log('Error: ' + errorThrown + ' ' + textStatus + ' ' + jqXHR);
      console.log(textStatus);
      console.log(jqXHR);
    }
  });
  };

});
<|endoftext|>


<commit_before>{
    init: function(elevators, floors) {
        var directions = {
            up: 'up',
            down: 'down',
            stopped: 'stopped'
        };

        var splitToQueue = function(floorNum) {
            var elevator = getNearestElevator();
            addToElevatorQueue(elevator, floorNum);
        };

        var addToElevatorQueue = function(elevator, floorNum) {
            if(elevator.destinationQueue.indexOf(floorNum) > -1) {
                return;
            }

            var dir = elevator.destinationDirection();
            var pos = elevator.currentFloor();
            var max = floors.length - 1;

            var nextFloor = getNextFloor(pos, dir, max);

            if(floorNum === nextFloor) {
                elevator.goToFloor(floorNum);
                elevator.checkDestinationQueue();
                return;
            }

            elevator.destinationQueue.push(floorNum);
            elevator.destinationQueue.sort();

            var aboveFloors = [];
            var belowFloors = [];

            elevator.destinationQueue.forEach(function(item) {
                if(item > pos) {
                    aboveFloors.push(item);
                } else if (item < pos) {
                    belowFloors.push(item);
                }
            });

            elevator.destinationQueue = [];
            belowFloors.reverse();

            if(dir === directions.up || dir === directions.stopped) {
                elevator.destinationQueue = elevator.destinationQueue.concat(aboveFloors.concat(belowFloors));
            } else {
                elevator.destinationQueue = elevator.destinationQueue.concat(belowFloors.concat(aboveFloors));
            }

            console.log(elevator.destinationQueue);
            elevator.checkDestinationQueue();
        };

        var getNextFloor = function(pos, dir, max) {
            var nextFloor = null;

            if(pos === 0) {
                nextFloor = pos + 1;
            } else if(pos === max - 1) {
                nextFloor = max;
            } else if(dir === directions.up) {
                nextFloor = Math.floor(pos) + 1;
                if(nextFloor > max) nextFloor = max;
            } else if(dir === directions.down) {
                nextFloor = Math.floor(pos) - 1;
                if(nextFloor < 0) nextFloor = 1;
            }

            return nextFloor;
        }

        var getNearestElevator = function(floorNum) {
            if(elevators.length === 1) return elevators[0];
        };

        var getElevatorDistances = function(floorNum) {
            var lap = floors.length;
            var factor = 0;
            var factors = [];

            elevators.forEach(function(e, i) {

            });

            return factors;
        };

        elevators.forEach(function(elevator) {
            elevator.on("floor_button_pressed", function(floorNum) {
                addToElevatorQueue(elevator, floorNum);
            });
        });

        floors.forEach(function(floor) {
            floor.on("up_button_pressed", function() {
                console.log('Floor ' + floor.floorNum() + ': Going Up');
                splitToQueue(floor.floorNum());
            });

            floor.on("down_button_pressed", function() {
                console.log('Floor ' + floor.floorNum() + ': Going Down');
                splitToQueue(floor.floorNum());
            });
        });
    },

    update: function(dt, elevators, floors) {
        // Do more stuff with the elevators and floors
        // dt is the number of game seconds that passed since the last time update was called
    }
}
    <commit_msg>Completed getNearestElevator() for multi-elevator puzzles.<commit_after>{
    init: function(elevators, floors) {
        var directions = {
            up: 'up',
            down: 'down',
            stopped: 'stopped'
        };

        var splitToQueue = function(floorNum) {
            var elevator = getNearestElevator(floorNum);
            addToElevatorQueue(elevator, floorNum);
        };

        var addToElevatorQueue = function(elevator, floorNum) {
            if(elevator.destinationQueue.indexOf(floorNum) > -1) {
                return;
            }

            var dir = elevator.destinationDirection();
            var pos = elevator.currentFloor();
            var max = floors.length - 1;

            var nextFloor = getNextFloor(pos, dir, max);

            if(floorNum === nextFloor) {
                elevator.goToFloor(floorNum);
                elevator.checkDestinationQueue();
                return;
            }

            elevator.destinationQueue.push(floorNum);
            elevator.destinationQueue.sort();

            var aboveFloors = [];
            var belowFloors = [];

            elevator.destinationQueue.forEach(function(item) {
                if(item > pos) {
                    aboveFloors.push(item);
                } else if (item < pos) {
                    belowFloors.push(item);
                }
            });

            elevator.destinationQueue = [];
            belowFloors.reverse();

            if(dir === directions.up || dir === directions.stopped) {
                elevator.destinationQueue = elevator.destinationQueue.concat(aboveFloors.concat(belowFloors));
            } else {
                elevator.destinationQueue = elevator.destinationQueue.concat(belowFloors.concat(aboveFloors));
            }

            elevator.checkDestinationQueue();
        };

        var getNextFloor = function(pos, dir, max) {
            var nextFloor = null;

            if(pos === 0) {
                nextFloor = pos + 1;
            } else if(pos === max - 1) {
                nextFloor = max;
            } else if(dir === directions.up) {
                nextFloor = Math.floor(pos) + 1;
                if(nextFloor > max) nextFloor = max;
            } else if(dir === directions.down) {
                nextFloor = Math.floor(pos) - 1;
                if(nextFloor < 0) nextFloor = 1;
            }

            return nextFloor;
        }

        var getNearestElevator = function(floorNum) {
            if(elevators.length === 1) return elevators[0];

            var numFloors = floors.length;
            var distanceFactors = [];

            elevators.forEach(function(elevator) {
                var dir = elevator.destinationDirection();
                var currentFloor = elevator.currentFloor();

                if(dir === directions.up) {
                    if(floorNum > currentFloor) {
                        distanceFactors.push(floorNum - currentFloor);
                    } else if(floorNum < currentFloor) {
                        distanceFactors.push(currentFloor - floorNum + numFloors);
                    } else {
                        distanceFactors.push(0);
                    }
                } else if(dir === directions.down) {
                    if(floorNum < currentFloor) {
                        distanceFactors.push(currentFloor - floorNum);
                    } else if(floorNum > currentFloor) {
                        distanceFactors.push(floorNum - currentFloor + numFloors);
                    } else {
                        distanceFactors.push(0);
                    }
                } else {
                    distanceFactors.push(0);
                }
            });

            var min = Math.min.apply(Math, distanceFactors);
            var i = distanceFactors.indexOf(min);

            console.log(distanceFactors);
            return i !== -1 ? elevators[i] : elevators[0];
        };

        elevators.forEach(function(elevator) {
            elevator.on("floor_button_pressed", function(floorNum) {
                addToElevatorQueue(elevator, floorNum);
            });
        });

        floors.forEach(function(floor) {
            floor.on("up_button_pressed", function() {
                splitToQueue(floor.floorNum());
            });

            floor.on("down_button_pressed", function() {
                splitToQueue(floor.floorNum());
            });
        });
    },

    update: function(dt, elevators, floors) {
        // Do more stuff with the elevators and floors
        // dt is the number of game seconds that passed since the last time update was called
    }
}
    <|endoftext|>