<commit_before>module.exports.get = function(Faire) {

	login_validate = function() {
		var S = Faire.Joi.string;
		return {
			email: S().email().required().max(50),
			passwrd: S().required().min(8),
			view: S()
		}
	}

	register_validate = function() {
		var S = Faire.Joi.string;
		return {
			email: S().email().required().max(50),
			passwrd: S().required().min(8),
			passwrd0: S().required().min(8),
			view: S()
		}
	}

	return [
		//Faire Routes
		//Faire Preferences/Themes
		{
			method: 'GET', path: '/preferences/theme', config: {
				handler: function(request, reply) {
					var session = request.auth.credentials;
					if (session) {
						var theme = '';
						if (session.preference == null || session.preference.theme == null) {
							//Use the default since the user does not have it set.
							//We could decide to later create the field in the table here since it doesn't exist yet.
							theme = 'default';
						} else {
							theme = session.preference.theme;
						}
						request.reply({ theme: theme });
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'POST', path: '/preferences/theme', config: {
				handler: function(request, reply) {
					var theme = request.payload.theme;
					var session = request.auth.credentials;
					if (session) {
						if (theme && theme != '') {
							//check to see if a theme with that name even exists on the filesystem
							Faire.Preferences.getAvailableThemes(function(err, themes) {
								var exists = false;
								for (var i = 0; i < themes.length; i++) {
									if (themes[i] == theme) {
										exists = true;
										break;
									}
								}

								if (exists) {
									Faire.Preferences.setTheme({user: session.id, theme: theme}, function(err, preference) {
										session.preference = preference;
										request.auth.session.set(session);
										request.reply({ success: true });
									});
								} else {
									request.reply({error: 'Theme is invalid.'});
								}
							});
						} else {
							request.reply({error: 'Theme is invalid.'});
						}
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'GET', path: '/themes', config: {
				handler: function() {
					var request = this;
					var session = request.auth.credentials;
					if (session) {
						Faire.Preferences.getAvailableThemes(function(err, themes) {
							request.reply({ themes: themes });
						});
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				auth: 'session'
			}
		},
		//Faire Tasks
		{
			method: 'GET', path: '/tasks', config: {
				handler: function() {
					var request = this;
					var session = request.auth.credentials;
					if (session) {
						Faire.Tasks.getAll({ user: session.id }, function(err, getAllTasks) {
							if (err) throw err;
							request.reply(getAllTasks);
						})
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'POST', path: '/tasks/add', config: {
				handler: function() {
					var request = this;
					var taskname = request.payload.name;
					var session = request.auth.credentials;
					if (session) {
						if (taskname && taskname != '') {
							Faire.Tasks.add({ user: session.id, name: taskname }, function(err, addTask) {
								if (err) throw err;
								request.reply(addTask);
							})
						} else {
							request.reply({error: 'name is a required field.'});
						}
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				validate: {
					payload: {
						name: Faire.Joi.string().required()
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'GET', path: '/tasks/{id}', config: {
				handler: function() {
					var request = this;
					var taskid = request.params.id;
					var session = request.auth.credentials;
					if (session) {
						Faire.Tasks.get({ user: session.id, id: taskid }, function(err, getTask) {
							if (err) throw err;
							request.reply(getTask);
						})
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				validate: {
					params: {
						id: Faire.Joi.number().integer().required()
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'POST', path: '/tasks/{id}/update', config: {
				handler: function() {
					var request = this;
					var taskid = request.params.id;
					var taskname = request.payload.name;
					var taskstatus = request.payload.status;
					var session = request.auth.credentials;
					if (session) {
						var updateObj = {};
						updateObj.id = taskid;
						updateObj.user = session.id;
						if (taskname !== undefined) {
							updateObj.name = taskname;
						}
						if (taskstatus !== undefined) {
							updateObj.status = taskstatus;
						}
						Faire.Tasks.update(updateObj, function(err, updatedTask) {
							if (err) throw err;
							request.reply(updatedTask);
						})
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				validate: {
					params: {
						id: Faire.Joi.number().integer().required()
					},
					payload: {
						name: Faire.Joi.string().max(50),
						status: Faire.Joi.any().valid(['active','inactive','deleted'])
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'POST', path: '/tasks/{id}/delete', config: {
				handler: function() {
					var request = this;
					var taskid = request.params.id;
					var session = request.auth.credentials;
					if (session) {
						Faire.Tasks.delete({ user: session.id, id: taskid }, function(err, deletedTask) {
							if (err) throw err;
							request.reply(deletedTask);
						})
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				validate: {
					params: {
						id: Faire.Joi.number().integer().required()
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'POST', path: '/tasks/{id}/inactivate', config: {
				handler: function() {
					var request = this;
					var taskid = request.params.id;
					var session = request.auth.credentials;
					if (session) {
						Faire.Tasks.inactivate({ user: session.id, id: taskid }, function(err, inactivatedTask) {
							if (err) throw err;
							request.reply(inactivatedTask);
						})
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				validate: {
					params: {
						id: Faire.Joi.number().integer().required()
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'POST', path: '/tasks/{id}/activate', config: {
				handler: function() {
					var request = this;
					var taskid = request.params.id;
					var session = request.auth.credentials;
					if (session) {
						Faire.Tasks.activate({ user: session.id, id: taskid }, function(err, activatedTask) {
							if (err) throw err;
							request.reply(activatedTask);
						})
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				validate: {
					params: {
						id: Faire.Joi.number().integer().required()
					}
				},
				auth: 'session'
			}
		},
	  //Authentication Routes
	  { method: '*', path: '/confirm/{hashkey*}', config: { handler: Faire.Auth.confirm, auth: false  } },
	  { method: 'POST', path: '/register', config: { handler: Faire.Auth.register, validate: { payload: register_validate() }, auth: { mode: 'try', strategy: 'session' },
		plugins: {
			'hapi-auth-cookie': {
				redirectTo: false
			}
		}  } },
	  { method: 'POST', path: '/login', config: { handler: Faire.Auth.login, validate: { payload: login_validate() }, auth: { mode: 'try', strategy: 'session' },
		plugins: {
			'hapi-auth-cookie': {
				redirectTo: false
			}
		}  } },
	  { method: '*', path: '/logout', config: { handler: Faire.Auth.logout, auth: 'session' } },
	  //Views
	  { method: 'GET', path: '/', config: { handler: Faire.Home.home_view, auth: 'session'  } },
	  { method: 'GET', path: '/login', config: { handler: Faire.Auth.login_view, auth: { mode: 'try', strategy: 'session' },
		plugins: {
			'hapi-auth-cookie': {
				redirectTo: false
			}
		}  } },
	  { method: 'GET', path: '/register', config: { handler: Faire.Auth.register_view, auth: { mode: 'try', strategy: 'session' },
		plugins: {
			'hapi-auth-cookie': {
				redirectTo: false
			}
		}  } },
	  //All static content
	  { method: '*', path: '/{path*}', config: { handler: { directory: { path: './static/', listing: false, redirectToSlash: true } }, auth: { mode: 'try', strategy: 'session' } } }
	];
};<commit_msg>Removed auth requests from static content.<commit_after>module.exports.get = function(Faire) {

	login_validate = function() {
		var S = Faire.Joi.string;
		return {
			email: S().email().required().max(50),
			passwrd: S().required().min(8),
			view: S()
		}
	}

	register_validate = function() {
		var S = Faire.Joi.string;
		return {
			email: S().email().required().max(50),
			passwrd: S().required().min(8),
			passwrd0: S().required().min(8),
			view: S()
		}
	}

	return [
		//Faire Routes
		//Faire Preferences/Themes
		{
			method: 'GET', path: '/preferences/theme', config: {
				handler: function(request, reply) {
					var session = request.auth.credentials;
					if (session) {
						var theme = '';
						if (session.preference == null || session.preference.theme == null) {
							//Use the default since the user does not have it set.
							//We could decide to later create the field in the table here since it doesn't exist yet.
							theme = 'default';
						} else {
							theme = session.preference.theme;
						}
						request.reply({ theme: theme });
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'POST', path: '/preferences/theme', config: {
				handler: function(request, reply) {
					var theme = request.payload.theme;
					var session = request.auth.credentials;
					if (session) {
						if (theme && theme != '') {
							//check to see if a theme with that name even exists on the filesystem
							Faire.Preferences.getAvailableThemes(function(err, themes) {
								var exists = false;
								for (var i = 0; i < themes.length; i++) {
									if (themes[i] == theme) {
										exists = true;
										break;
									}
								}

								if (exists) {
									Faire.Preferences.setTheme({user: session.id, theme: theme}, function(err, preference) {
										session.preference = preference;
										request.auth.session.set(session);
										request.reply({ success: true });
									});
								} else {
									request.reply({error: 'Theme is invalid.'});
								}
							});
						} else {
							request.reply({error: 'Theme is invalid.'});
						}
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'GET', path: '/themes', config: {
				handler: function() {
					var request = this;
					var session = request.auth.credentials;
					if (session) {
						Faire.Preferences.getAvailableThemes(function(err, themes) {
							request.reply({ themes: themes });
						});
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				auth: 'session'
			}
		},
		//Faire Tasks
		{
			method: 'GET', path: '/tasks', config: {
				handler: function() {
					var request = this;
					var session = request.auth.credentials;
					if (session) {
						Faire.Tasks.getAll({ user: session.id }, function(err, getAllTasks) {
							if (err) throw err;
							request.reply(getAllTasks);
						})
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'POST', path: '/tasks/add', config: {
				handler: function() {
					var request = this;
					var taskname = request.payload.name;
					var session = request.auth.credentials;
					if (session) {
						if (taskname && taskname != '') {
							Faire.Tasks.add({ user: session.id, name: taskname }, function(err, addTask) {
								if (err) throw err;
								request.reply(addTask);
							})
						} else {
							request.reply({error: 'name is a required field.'});
						}
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				validate: {
					payload: {
						name: Faire.Joi.string().required()
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'GET', path: '/tasks/{id}', config: {
				handler: function() {
					var request = this;
					var taskid = request.params.id;
					var session = request.auth.credentials;
					if (session) {
						Faire.Tasks.get({ user: session.id, id: taskid }, function(err, getTask) {
							if (err) throw err;
							request.reply(getTask);
						})
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				validate: {
					params: {
						id: Faire.Joi.number().integer().required()
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'POST', path: '/tasks/{id}/update', config: {
				handler: function() {
					var request = this;
					var taskid = request.params.id;
					var taskname = request.payload.name;
					var taskstatus = request.payload.status;
					var session = request.auth.credentials;
					if (session) {
						var updateObj = {};
						updateObj.id = taskid;
						updateObj.user = session.id;
						if (taskname !== undefined) {
							updateObj.name = taskname;
						}
						if (taskstatus !== undefined) {
							updateObj.status = taskstatus;
						}
						Faire.Tasks.update(updateObj, function(err, updatedTask) {
							if (err) throw err;
							request.reply(updatedTask);
						})
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				validate: {
					params: {
						id: Faire.Joi.number().integer().required()
					},
					payload: {
						name: Faire.Joi.string().max(50),
						status: Faire.Joi.any().valid(['active','inactive','deleted'])
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'POST', path: '/tasks/{id}/delete', config: {
				handler: function() {
					var request = this;
					var taskid = request.params.id;
					var session = request.auth.credentials;
					if (session) {
						Faire.Tasks.delete({ user: session.id, id: taskid }, function(err, deletedTask) {
							if (err) throw err;
							request.reply(deletedTask);
						})
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				validate: {
					params: {
						id: Faire.Joi.number().integer().required()
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'POST', path: '/tasks/{id}/inactivate', config: {
				handler: function() {
					var request = this;
					var taskid = request.params.id;
					var session = request.auth.credentials;
					if (session) {
						Faire.Tasks.inactivate({ user: session.id, id: taskid }, function(err, inactivatedTask) {
							if (err) throw err;
							request.reply(inactivatedTask);
						})
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				validate: {
					params: {
						id: Faire.Joi.number().integer().required()
					}
				},
				auth: 'session'
			}
		},
		{
			method: 'POST', path: '/tasks/{id}/activate', config: {
				handler: function() {
					var request = this;
					var taskid = request.params.id;
					var session = request.auth.credentials;
					if (session) {
						Faire.Tasks.activate({ user: session.id, id: taskid }, function(err, activatedTask) {
							if (err) throw err;
							request.reply(activatedTask);
						})
					} else {
						request.reply({error: 'Must be logged in.'});
					}
				},
				validate: {
					params: {
						id: Faire.Joi.number().integer().required()
					}
				},
				auth: 'session'
			}
		},
	  //Authentication Routes
	  { method: '*', path: '/confirm/{hashkey*}', config: { handler: Faire.Auth.confirm, auth: false  } },
	  { method: 'POST', path: '/register', config: { handler: Faire.Auth.register, validate: { payload: register_validate() }, auth: { mode: 'try', strategy: 'session' },
		plugins: {
			'hapi-auth-cookie': {
				redirectTo: false
			}
		}  } },
	  { method: 'POST', path: '/login', config: { handler: Faire.Auth.login, validate: { payload: login_validate() }, auth: { mode: 'try', strategy: 'session' },
		plugins: {
			'hapi-auth-cookie': {
				redirectTo: false
			}
		}  } },
	  { method: '*', path: '/logout', config: { handler: Faire.Auth.logout, auth: 'session' } },
	  //Views
	  { method: 'GET', path: '/', config: { handler: Faire.Home.home_view, auth: 'session'  } },
	  { method: 'GET', path: '/login', config: { handler: Faire.Auth.login_view, auth: { mode: 'try', strategy: 'session' },
		plugins: {
			'hapi-auth-cookie': {
				redirectTo: false
			}
		}  } },
	  { method: 'GET', path: '/register', config: { handler: Faire.Auth.register_view, auth: { mode: 'try', strategy: 'session' },
		plugins: {
			'hapi-auth-cookie': {
				redirectTo: false
			}
		}  } },
	  //All static content
	  { method: '*', path: '/{path*}', config: { handler: { directory: { path: './static/', redirectToSlash: true } }, auth: { mode: 'try', strategy: 'session' },
		plugins: {
			'hapi-auth-cookie': {
				redirectTo: false
			}
		}  } }
	];
};<|endoftext|>


<commit_before>#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
// const spawn = require('child_process').spawn;
const fork = require('child_process').fork;
const os = require('os');
const redis = require('redis');

const queues = {};

const cfgfile = process.env['CONFIG_FILE'] || process.argv[2];
let scriptsPath = process.env['SCRIPT_PATH'] || './scripts';

if (!path.isAbsolute(scriptsPath)) {
  scriptsPath = path.join(process.cwd(), scriptsPath);
}

const defaultConfg = {
  redis: {
    server: "localhost",
    port: 6379,
    db: 2,
  },
  runners: {
    default: 5,
  },
}

let config = {};

if (cfgfile && fs.existsSync(cfgfile)) {
  config = Object.assign({}, defaultConfg, JSON.parse(fs.readFileSync(cfgfile)));
}

class QueueRunner {
  constructor(queue, worker_count) {
    this.queue = queue;
    this.clients = {};
    for (let i = 0; i < worker_count; i++) {
      this.clients[i] = new Worker(this.queue, i);
      this.clients[i].start();
    }
    this.quit = this.quit.bind(this);
  }

  quit() {
    Object.keys(this.clients).forEach((idx) => {
      this.clients[idx].stop();
    });
  }
}

class Worker {
  constructor(queue, id) {
    this.queue = queue;
    this.id = id;
    this.running = false;
    this.start = this.start.bind(this);
    this.stop = this.stop.bind(this);
    this.wait_and_process = this.wait_and_process.bind(this);
    this.worker_name = this.worker_name.bind(this);
    this.register = this.register.bind(this);
    this.unregister = this.unregister.bind(this);
    this.working_on = this.working_on.bind(this);
    this.done = this.done.bind(this);
    this.success = this.success.bind(this);
    this.fail = this.fail.bind(this);
    this.jobErrorTypes = ['exception', 'error', 'backtrace'];
    this.waitTimer = null;
  }

  start() {
    this.redis = createRedisClient();
    this.register();
    this.running = true;
    this.wait_and_process();
  }

  stop() {
    clearTimeout(this.waitTimer);
    this.unregister();
    this.running = false;
    this.redis.quit();
  }

  wait_and_process() {
    // this.redis.blpop(`resque:queue:${this.queue}`, 0, (err, res) => {
    this.redis.lpop(`resque:queue:${this.queue}`, (err, res) => {
      const bail = (reason) => {
        console.error(`Worker-Error: ${reason}`);
        // process.nextTick(this.wait_and_process);
        this.waitTimer = setTimeout(() => this.wait_and_process(), 5000);
      }

      if (err) {
        return bail(err);
      }

      if (!res) {
        this.waitTimer = setTimeout(() => this.wait_and_process(), 5000);
        return;
      }

      let job = {}
      try {
        job = JSON.parse(res);
      } catch (e) {
        return bail(`Parse Error: ${e}`);
      }

      this.working_on(job);

      let response = '';

      //Use a fork of a node proces
      const child = fork(path.join(__dirname, './helpers/runHelper'), [scriptsPath, res]);

      //TODO: Handle child error output in order to properly log it in resque

      // cat.stderr.on('data', (d) => {
      // this doesn't work since a forked child doesn't seem to have stderr prop
      //   response += d;
      // });

      child.on('exit', (code) => {
        if (code === 0) {
          this.success();
        } else {
          this.fail(job, response);
        }
        if (this.running) {
          // process.nextTick(this.wait_and_process);
          this.waitTimer = setTimeout(() => this.wait_and_process(), 5000);
        }
      });
    });
  }

  worker_name() {
    return `${os.hostname()}:${process.pid}-${this.id}:${this.queue}`;
  }

  register() {
    const name = this.worker_name();
    this.redis.sadd('resque:workers', name);
    this.redis.set(`resque:worker:${name}:started`, new Date().toISOString());
  }

  unregister() {
    const name = this.worker_name();
    this.redis.srem('resque:workers', name);
    this.redis.del(`resque:worker:${name}`);
    this.redis.del(`resque:worker:${name}:started`);
  }

  working_on(data) {
    const dataStr = JSON.stringify({
      queue: this.queue,
      run_at: new Date().toISOString(),
      payload: data,
    });
    const name = this.worker_name();
    this.redis.set(`resque:worker:${name}`, dataStr);
  }

  done(successful) {
    const name = this.worker_name();
    const key = successful ? 'processed' : 'failed';
    this.redis.incr("resque:stat:#{key}");
    this.redis.del("resque:worker:#{name}");
  }

  success() {
    this.done(true);
  }

  fail(job, response) {
    this.done(false);
    const name = this.worker_name();
    const error = {
      failed_at: new Date().toISOString(),
      payload: job,
      worker: name,
      queue: this.queue,
      backtrace: null,
    };
    const einfo = {};
    try {
      const data = JSON.parse(response);
      for (key in this.jobErrorTypes) {
        einfo[key] = data[key];
      }
    } catch (e) {
      einfo = {
        exception: 'UnclassifiedRunnerError',
        error: response,
      };
    }
    this.redis.lpush('resque:failed', JSON.stringify(Object.assign({}, error, einfo)));
  }
}

const createRedisClient = () => {
  const options = {};
  if (config && config.redis) {
    options.db = config.redis.db;
  }
  const client = redis.createClient(config.redis.port, config.redis.server, options);
  client.on('error', (err) => {
    console.error(`Error: ${err}`);
  });
  return client;
}

const clean_exit = () => {
  console.log('Unregistering workers');
  Object.keys(queues).forEach((queue) => {
    queues[queue].quit();
  });
  process.exit(0);
}

const run = () => {
  Object.keys(config.runners).forEach((queue) => {
    queues[queue] = new QueueRunner(queue, config.runners[queue]);
  });

  const sigArray = ['INT', 'HUP', 'TERM'];

  for (let sig in sigArray) {
    process.on(`SIG${sigArray[sig]}`, clean_exit);
  }

  process.on('uncaughtException', function (err) {
    console.error(err);
    clean_exit();
  });
}

exports.run = run;
<commit_msg>cleans up runner and makes it accept ENV configuration<commit_after>#!/usr/bin/env node
require('dotenv').config()

const path = require('path');
const fork = require('child_process').fork;
const os = require('os');
const redis = require('redis');

const RK_PREFIX = 'resque';

const RK_QUEUE = `${RK_PREFIX}:queue`;
const RK_WORKER = `${RK_PREFIX}:worker`;
const RK_WORKERS = `${RK_PREFIX}:workers`;
const RK_STAT = `${RK_PREFIX}:stat`;
const RK_FAILED = `${RK_PREFIX}:failed`;

const JOB_ERROR_TYPES = ['exception', 'error', 'backtrace'];

const QUEUE = process.env['QUEUE'];
const COUNT = parseInt(process.env['COUNT'], 10);
const INTERVAL = parseFloat(process.env['INTERVAL']);
const REDIS_URL = process.env['REDIS_URL'];
const SCRIPTS_PATH = process.env['SCRIPTS_PATH'];

let config = {};

class QueueRunner {
  constructor(queue, worker_count) {
    this.queue = queue;
    this.workers = {};
    for (let worker = 0; worker < worker_count; worker++) {
      this.workers[worker] = new Worker(this.queue, worker);
      this.workers[worker].start();
    }
    this.quit = this.quit.bind(this);
  }

  quit() {
    console.log(`Shutting down workers for ${this.queue} queue`);
    Object.keys(this.workers).forEach((worker) => {
      this.workers[worker].stop();
    });
  }
}

class Worker {
  constructor(queue, id) {
    this.queue = queue;
    this.id = id;
    this.name = `${os.hostname()}:${process.pid}-${this.id}:${this.queue}`;
    this.interval = parseFloat(config.interval) * 1000;
    this.running = false;
    this.pollTimer = null;

    this.start = this.start.bind(this);
    this.stop = this.stop.bind(this);
    this.waitAndProcess = this.waitAndProcess.bind(this);
    this.register = this.register.bind(this);
    this.unregister = this.unregister.bind(this);
    this.workingOn = this.workingOn.bind(this);
    this.done = this.done.bind(this);
    this.success = this.success.bind(this);
    this.fail = this.fail.bind(this);
  }

  start() {
    this.redis = createRedisClient();
    this.register();
    this.running = true;
    this.waitAndProcess();
  }

  stop() {
    console.log(`Shutting down worker: ${this.name}`);
    clearTimeout(this.pollTimer);
    this.unregister();
    this.running = false;
    this.redis.quit();
  }

  waitAndProcess() {
    this.redis.lpop(`resque:queue:${this.queue}`, (err, res) => {
      const bail = (reason) => {
        console.error(`Worker-Error: ${reason}`);
        this.pollTimer = setTimeout(() => this.waitAndProcess(), this.interval);
      }

      if (err) {
        return bail(err);
      }

      if (!res) {
        this.pollTimer = setTimeout(() => this.waitAndProcess(), this.interval);
        return;
      }

      let job = {}
      try {
        job = JSON.parse(res);
      } catch (e) {
        return bail(`Parse Error: ${e}`);
      }

      this.workingOn(job);

      let response = '';

      const child = fork(path.join(__dirname, './helpers/runHelper'), [config.scriptsPath, res], {silent: true});

      child.stderr.on('data', (errorData) => {
        response += errorData;
      });

      child.on('exit', (code) => {
        if (code === 0) {
          this.success();
        } else {
          this.fail(job, response);
        }
        if (this.running) {
          this.pollTimer = setTimeout(() => this.waitAndProcess(), this.interval);
        }
      });
    });
  }

  register() {
    this.redis.sadd(RK_WORKERS, this.name);
    this.redis.set(`${RK_WORKER}:${this.name}:started`, new Date().toISOString());
  }

  unregister() {
    this.redis.srem(RK_WORKERS, this.name);
    this.redis.del(`${RK_WORKER}:${this.name}`);
    this.redis.del(`${RK_WORKER}:${this.name}:started`);
  }

  workingOn(data) {
    const work = JSON.stringify({
      queue: this.queue,
      run_at: new Date().toISOString(),
      payload: data,
    });
    this.redis.set(`${RK_WORKER}:${this.name}`, work);
  }

  done(successful) {
    const key = successful ? 'processed' : 'failed';
    this.redis.incr(`${RK_STAT}:${key}`);
    this.redis.del(`${RK_WORKER}:${this.name}`);
  }

  success() {
    this.done(true);
  }

  fail(job, response) {
    this.done(false);
    const error = {
      failed_at: new Date().toISOString(),
      payload: job,
      worker: this.name,
      queue: this.queue,
      backtrace: null,
    };
    let info = {};
    try {
      const data = JSON.parse(response);
      JOB_ERROR_TYPES.forEach(errorKey => {
        info[errorKey] = data[errorKey];
      });
    } catch (e) {
      info = {
        exception: 'UnclassifiedRunnerError',
        error: response,
      };
    }
    this.redis.lpush(RK_FAILED, JSON.stringify(Object.assign({}, error, info)));
  }

}

const queues = {};

const createRedisClient = () => {
  const client = redis.createClient(config.redis);
  client.on('error', (err) => {
    console.error(`Error: ${err}`);
  });
  return client;
}

const cleanExit = () => {
  console.log('Unregistering workers');
  Object.keys(queues).forEach(queueName => {
    queues[queueName].quit();
  });
  process.exit(0);
}

const handleException = (error) => {
  console.error(error);
  handleExit({from: 'exception'});
}

const handleExit = ({from}) => {
  console.log(`Exiting from: ${from}`);
  cleanExit();
}

const constructConfigFromEnv = () => {
  const config = {};
  if (INTERVAL) {
    config.interval = INTERVAL;
  }
  if (REDIS_URL) {
    config.redis = REDIS_URL;
  }
  if (QUEUE) {
    const queues = QUEUE.split(',');
    config.queue = queues;
  }
  if (COUNT) {
    config.count = COUNT;
  }
  if (SCRIPTS_PATH) {
    config.scriptsPath = SCRIPTS_PATH;
  }
  return config;
}

const parseScriptsPath = () => {
  if (!path.isAbsolute(config.scriptsPath)) {
    config.scriptsPath = path.join(process.cwd(), config.scriptsPath);
  }
}

const defaultConfig = {
  interval: 5.0,
  redis: 'redis://localhost:6379/0',
  queue: ['default'],
  count: 1,
  scriptsPath: './scripts',
};

const run = (options = {}) => {
  const constructedConfig = constructConfigFromEnv();
  config = Object.assign({}, defaultConfig, constructedConfig, options);
  parseScriptsPath();
  console.log(config);
  config.queue.forEach(queue => {
    queues[queue] = new QueueRunner(queue, config.count);
  });
  process.on('uncaughtException', handleException);
  // process.on('exit', () => handleExit({from: 'exit'}));
  process.on('SIGINT', () => handleExit({from: 'SIGINT'}));
  process.on('SIGHUP', () => handleExit({from: 'SIGHUP'}));
  process.on('SIGTERM', () => handleExit({from: 'SIGTERM'}));
}

exports.run = run;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.cron',
  name: 'Cron',
  extends: 'foam.nanos.script.Script',

  imports: [ 'cronDAO as scriptDAO' ],

  javaImports: [
    'java.util.Date',
    'java.util.Calendar'
  ],

  documentation: 'FOAM class that models a Cron script',

  tableColumns: [
    'id', 'enabled', 'server', 'description', 'lastDuration', 'status', 'run'
  ],

  searchColumns: ['id', 'description'],

  properties: [
    {
      class: 'Int',
      name: 'minute',
      value: -1,
      documentation: `Minute to execute script.
          Ranges from 0 - 59. -1 for wildcard`
    },
    {
      class: 'Int',
      name: 'hour',
      value: -1,
      documentation: `Hour to execute script.
          Ranges from 0 - 23. -1 for wildcard`
    },
    {
      class: 'Int',
      name: 'dayOfMonth',
      value: -1,
      documentation: `Day of Month to execute script.
          Ranges from 1 - 31. -1 for wildcard`
    },
    {
      class: 'Int',
      name: 'month',
      value: -1,
      documentation: `Month to execute script.
          Ranges from 1 - 12. -1 for wildcard`
    },
    {
      class: 'Int',
      name: 'dayOfWeek',
      value: -1,
      documentation: `Day of week to execute script.
          Ranges from 0 - 6, where 0 is Sunday. -1 for wildcard`
    },
    {
      class: 'DateTime',
      name: 'scheduledTime',
      documentation: `Scheduled time to run Cron script.`,
      javaFactory: 'return getNextScheduledTime();'
    },
    {
      class: 'Boolean',
      name: 'enabled',
      value: false
    }
  ],

  methods: [
    {
      name: 'runScript',
      args: [
        {
          name: 'x', javaType: 'foam.core.X'
        }
      ],
      javaReturns: 'void',
      javaCode:
`super.runScript(x);
setScheduledTime(getNextScheduledTime());`
    },
    {
      name: 'getNextScheduledTime',
      javaReturns: 'Date',
      javaCode:
`Calendar next = Calendar.getInstance();
next.add(Calendar.MINUTE, 1);
next.set(Calendar.MILLISECOND, 0);
next.set(Calendar.SECOND, 0);

boolean dateFound = false;
while ( next.get(Calendar.YEAR) < 3000 ) {
  if ( getMonth() >= 0 && next.get(Calendar.MONTH) != getMonth() - 1 ) {
    next.add(Calendar.MONTH, 1);
    next.set(Calendar.DAY_OF_MONTH, 1);
    next.set(Calendar.HOUR_OF_DAY, 0);
    next.set(Calendar.MINUTE, 0);
    continue;
  }
  if ( ( getDayOfMonth() >= 0 && next.get(Calendar.DAY_OF_MONTH) != getDayOfMonth() ) ||
      ( getDayOfWeek() >= 0 && next.get(Calendar.DAY_OF_WEEK) != getDayOfWeek() + 1) ) {
    next.add(Calendar.DAY_OF_MONTH, 1);
    next.set(Calendar.HOUR_OF_DAY, 0);
    next.set(Calendar.MINUTE, 0);
    continue;
  }
  if ( getHour() >= 0 && next.get(Calendar.HOUR_OF_DAY) != getHour() ) {
    next.add(Calendar.HOUR_OF_DAY, 1);
    next.set(Calendar.MINUTE, 0);
    continue;
  }
  if ( getMinute() >= 0 && next.get(Calendar.MINUTE) != getMinute() ) {
    next.add(Calendar.MINUTE, 1);
    continue;
  }
  dateFound = true;
  break;
}
if ( !dateFound ) {
  throw new IllegalArgumentException("Unable to get next scheduled time");
}
return next.getTime();`
    }
  ]
});
<commit_msg>integrate cronjob start/end time in notifiaction<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.cron',
  name: 'Cron',
  extends: 'foam.nanos.script.Script',

  imports: [ 'cronDAO as scriptDAO' ],

  javaImports: [
    'foam.dao.DAO',
    'foam.nanos.notification.Notification',
    'java.util.Date',
    'java.util.Calendar'
  ],

  documentation: 'FOAM class that models a Cron script',

  tableColumns: [
    'id', 'enabled', 'server', 'description', 'lastDuration', 'status', 'run'
  ],

  searchColumns: ['id', 'description'],

  properties: [
    {
      class: 'Int',
      name: 'minute',
      value: -1,
      documentation: `Minute to execute script.
          Ranges from 0 - 59. -1 for wildcard`
    },
    {
      class: 'Int',
      name: 'hour',
      value: -1,
      documentation: `Hour to execute script.
          Ranges from 0 - 23. -1 for wildcard`
    },
    {
      class: 'Int',
      name: 'dayOfMonth',
      value: -1,
      documentation: `Day of Month to execute script.
          Ranges from 1 - 31. -1 for wildcard`
    },
    {
      class: 'Int',
      name: 'month',
      value: -1,
      documentation: `Month to execute script.
          Ranges from 1 - 12. -1 for wildcard`
    },
    {
      class: 'Int',
      name: 'dayOfWeek',
      value: -1,
      documentation: `Day of week to execute script.
          Ranges from 0 - 6, where 0 is Sunday. -1 for wildcard`
    },
    {
      class: 'DateTime',
      name: 'scheduledTime',
      documentation: `Scheduled time to run Cron script.`,
      javaFactory: 'return getNextScheduledTime();'
    },
    {
      class: 'Boolean',
      name: 'enabled',
      value: false
    }
  ],

  methods: [
    {
      name: 'runScript',
      args: [
        {
          name: 'x', javaType: 'foam.core.X'
        }
      ],
      javaReturns: 'void',
      javaCode:
`DAO notification = (DAO) x.get("notificationDAO");

Notification cronStartNotify = new Notification();
cronStartNotify.setBody(this.getId() + " started");
notification.put(cronStartNotify);

super.runScript(x);

Notification cronEndNotify = new Notification();
cronEndNotify.setBody(this.getId() + " ended");
notification.put(cronEndNotify);

setScheduledTime(getNextScheduledTime());`
    },
    {
      name: 'getNextScheduledTime',
      javaReturns: 'Date',
      javaCode:
`Calendar next = Calendar.getInstance();
next.add(Calendar.MINUTE, 1);
next.set(Calendar.MILLISECOND, 0);
next.set(Calendar.SECOND, 0);

boolean dateFound = false;
while ( next.get(Calendar.YEAR) < 3000 ) {
  if ( getMonth() >= 0 && next.get(Calendar.MONTH) != getMonth() - 1 ) {
    next.add(Calendar.MONTH, 1);
    next.set(Calendar.DAY_OF_MONTH, 1);
    next.set(Calendar.HOUR_OF_DAY, 0);
    next.set(Calendar.MINUTE, 0);
    continue;
  }
  if ( ( getDayOfMonth() >= 0 && next.get(Calendar.DAY_OF_MONTH) != getDayOfMonth() ) ||
      ( getDayOfWeek() >= 0 && next.get(Calendar.DAY_OF_WEEK) != getDayOfWeek() + 1) ) {
    next.add(Calendar.DAY_OF_MONTH, 1);
    next.set(Calendar.HOUR_OF_DAY, 0);
    next.set(Calendar.MINUTE, 0);
    continue;
  }
  if ( getHour() >= 0 && next.get(Calendar.HOUR_OF_DAY) != getHour() ) {
    next.add(Calendar.HOUR_OF_DAY, 1);
    next.set(Calendar.MINUTE, 0);
    continue;
  }
  if ( getMinute() >= 0 && next.get(Calendar.MINUTE) != getMinute() ) {
    next.add(Calendar.MINUTE, 1);
    continue;
  }
  dateFound = true;
  break;
}
if ( !dateFound ) {
  throw new IllegalArgumentException("Unable to get next scheduled time");
}
return next.getTime();`
    }
  ]
});
<|endoftext|>


<commit_before>var app = {
    // Application Constructor
    initialize: function() {
      this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
      document.addEventListener('deviceready', this.onDeviceReady, false);
      document.addEventListener("offline", checkConnection, false);
      document.addEventListener("pause", onPause, false);
      document.addEventListener("resume", onResume, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    onDeviceReady: function() {
      app.receivedEvent('deviceready');
      screen.lockOrientation('portrait');
      localStorage.setItem("saldo_actual",0);
      localStorage.setItem("wisi","false");
    },
    // Update DOM on a Received Event
    receivedEvent: function(id) {
      getDeviceProperty();
      detectGEO();
    }
  };

  function detectGEO(){
    var options = {
      enableHighAccuracy: true,
      timeout: 5000,
      maximumAge: 75000
    };
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(onSuccess, onError, options);
    } else {
      console.log('Geolocalización no soportada');
    }
  }

// onSuccess Geolocation    //
function onSuccess(position) {
  localStorage.removeItem("position");
  lat1= position.coords.latitude;
  lng1= position.coords.longitude;
  var pos= {lat:lat1,lng:lng1};
  localStorage.setItem("position",JSON.stringify(pos));
  console.log('Coordenadas detectadas');
}

function onError(error) {
  console.log('Error detectando coordenadas');
}

function getDeviceProperty()
{
     var deviceOS  = device.platform  ;  //fetch the device operating system
     var deviceOSVersion = device.version ;  //fetch the device OS version
     localStorage.setItem("OS",deviceOS);
   }

   function onPause() {
    localStorage.setItem("wisi","false");
    console.log("paused");
    if(!localStorage.scanear)searchWISI();
  }

  function searchWISI(){
    var statusWIFI=WifiWizard.isWifiEnabled(win, fail);

    window.setTimeout(function(){
      WifiWizard.getScanResults(listHandler, fail);
      console.log("Check available WIFIs");
    },60000);//1 minuto
  }
  function win(){
    console.log("Estado WIFI: ");
  }

  function onResume() {
    localStorage.setItem("wisi","true");
    localStorage.setItem("scanner","false");
    console.log("resume");
  }

  function checkConnection() {
    var state=true;
    var networkState = navigator.connection.type;
    var states = {};
    states[Connection.UNKNOWN]  = 'Unknown connection';
    states[Connection.ETHERNET] = 'Ethernet connection';
    states[Connection.WIFI]     = 'WiFi connection';
    states[Connection.CELL_2G]  = 'Cell 2G connection';
    states[Connection.CELL_3G]  = 'Cell 3G connection';
    states[Connection.CELL_4G]  = 'Cell 4G connection';
    states[Connection.CELL]     = 'Cell generic connection';
    states[Connection.NONE]     = 'No network connection';

    if(states[networkState]=='No network connection'){
        //navigator.vibrate(1000);
        //alert('Internet no disponible!');
        state=false;
      }
      return state;
    }

    function fail(e){
      console.log("Detect active "+e);
    }

  function listHandler(a){
  console.log("Flag listar "+localStorage.wisi);
  console.log("Array Wifis "+JSON.stringify(a));
  if(localStorage.wisi=="false"){
    var exists=0;
    for(var i=0; i<a.length; i++){
      alert("WIFI Detect "+a[i]["SSID"]);
/*      if((a[i].search("WISI TE CONECTA")>0||a[i].search("VALENCIA_V")>0)&&exists==0){
        console.log(a[i].search("WISI TE CONECTA")+" / "+a[i].search("VALENCIA_V"));
        if(localStorage.wisi=="false"){
            cordova.plugins.notification.local.schedule({ message:"Red WISI detectada",sound: "file://sounds/HTC Happy.mp3" });
            //myApp.alert("Red WISI detectada", "");
            localStorage.setItem("wisi","true");
            navigator.vibrate(1000);
            exists++;
          }
        }*/
      }
      searchWISI();
    }
  }<commit_msg>arrays<commit_after>var app = {
    // Application Constructor
    initialize: function() {
      this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
      document.addEventListener('deviceready', this.onDeviceReady, false);
      document.addEventListener("offline", checkConnection, false);
      document.addEventListener("pause", onPause, false);
      document.addEventListener("resume", onResume, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    onDeviceReady: function() {
      app.receivedEvent('deviceready');
      screen.lockOrientation('portrait');
      localStorage.setItem("saldo_actual",0);
      localStorage.setItem("wisi","false");
    },
    // Update DOM on a Received Event
    receivedEvent: function(id) {
      getDeviceProperty();
      detectGEO();
    }
  };

  function detectGEO(){
    var options = {
      enableHighAccuracy: true,
      timeout: 5000,
      maximumAge: 75000
    };
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(onSuccess, onError, options);
    } else {
      console.log('Geolocalización no soportada');
    }
  }

// onSuccess Geolocation    //
function onSuccess(position) {
  localStorage.removeItem("position");
  lat1= position.coords.latitude;
  lng1= position.coords.longitude;
  var pos= {lat:lat1,lng:lng1};
  localStorage.setItem("position",JSON.stringify(pos));
  console.log('Coordenadas detectadas');
}

function onError(error) {
  console.log('Error detectando coordenadas');
}

function getDeviceProperty()
{
     var deviceOS  = device.platform  ;  //fetch the device operating system
     var deviceOSVersion = device.version ;  //fetch the device OS version
     localStorage.setItem("OS",deviceOS);
   }

   function onPause() {
    localStorage.setItem("wisi","false");
    console.log("paused");
    if(!localStorage.scanear)searchWISI();
  }

  function searchWISI(){
    var statusWIFI=WifiWizard.isWifiEnabled(win, fail);

    window.setTimeout(function(){
      WifiWizard.getScanResults(listHandler, fail);
      console.log("Check available WIFIs");
    },60000);//1 minuto
  }
  function win(){
    console.log("Estado WIFI: ");
  }

  function onResume() {
    localStorage.setItem("wisi","true");
    localStorage.setItem("scanner","false");
    console.log("resume");
  }

  function checkConnection() {
    var state=true;
    var networkState = navigator.connection.type;
    var states = {};
    states[Connection.UNKNOWN]  = 'Unknown connection';
    states[Connection.ETHERNET] = 'Ethernet connection';
    states[Connection.WIFI]     = 'WiFi connection';
    states[Connection.CELL_2G]  = 'Cell 2G connection';
    states[Connection.CELL_3G]  = 'Cell 3G connection';
    states[Connection.CELL_4G]  = 'Cell 4G connection';
    states[Connection.CELL]     = 'Cell generic connection';
    states[Connection.NONE]     = 'No network connection';

    if(states[networkState]=='No network connection'){
        //navigator.vibrate(1000);
        //alert('Internet no disponible!');
        state=false;
      }
      return state;
    }

    function fail(e){
      console.log("Detect active "+e);
    }

  function listHandler(a){
  console.log("Flag listar "+localStorage.wisi);
  console.log("Array Wifis "+JSON.stringify(a));
  if(localStorage.wisi=="false"){
    var exists=0;
    alert(a[0].length);
    for(var i=0; i<a[0].length; i++){
      alert("WIFI Detect "+a[0][i]);
/*      if((a[i].search("WISI TE CONECTA")>0||a[i].search("VALENCIA_V")>0)&&exists==0){
        console.log(a[i].search("WISI TE CONECTA")+" / "+a[i].search("VALENCIA_V"));
        if(localStorage.wisi=="false"){
            cordova.plugins.notification.local.schedule({ message:"Red WISI detectada",sound: "file://sounds/HTC Happy.mp3" });
            //myApp.alert("Red WISI detectada", "");
            localStorage.setItem("wisi","true");
            navigator.vibrate(1000);
            exists++;
          }
        }*/
      }
      searchWISI();
    }
  }<|endoftext|>


<commit_before>$('#full-clndr').clndr({
  template: $('#full-clndr-template').html()
});<commit_msg>Add complete & functional front app.js<commit_after>function Agenda() {
  this.appointments = {};
}

Agenda.prototype = {
  fetchAppointments: function fetchAppointments(date) {
    var self = this;

    return $.ajax({
      type: 'GET',
      url: '/api/',
      data: {period: date}
    })
      .done(function(data) {
        _.each(data, function(appointment) {
          self.appointments[appointment.path.key] = {
            date: appointment.path.key,
            attendee: appointment.value.attendee
          };
        });

        return true;
      })
      .fail(function() {
        alert('There is a problem with the server');
        return false;
      });
  },

  getAppointments: function getAppointments() {
    return _.values(this.appointments);
  },

  getAppointment: function getAppointment(date) {
    var identifier = moment(date).format('YYYY-MM-DD');

    if(!this.appointments[identifier]) {
      return null;
    }

    return this.appointments[identifier];
  },

  createAppointment: function createAppointment(date, name) {
    return $.ajax({
      type: 'POST',
      url: '/api/',
      data: {id: moment(date).format('YYYY-MM-DD'), attendee: name}
    })
      .done(function(data) {
        return true;
      })
      .fail(function() {
        alert('There is a problem with the server');
        return false;
      });
  }
};

var agd = new Agenda();

var cal = $('#full-clndr').clndr({
  weekOffset: 1,
  template: $('#full-clndr-template').html(),
  clickEvents: {
    onMonthChange: function(date){
      var self = this;

      agd.fetchAppointments(date.toDate()).done(function() {
        self.setEvents(agd.getAppointments());
      });
    },
    click: function(target) {
      if(target.events[0]) {
        alert(target.events[0].attendee + ' made an appointment on that day');
        return;
      }

      if(confirm('Would you like to make an appointment ?')) {
        var name = '';

        while(!name) {
          name = prompt('Enter your name');
        }

        agd.createAppointment(target.date, name);
      }
    }
  }
});

agd.fetchAppointments(new Date()).done(function() {
  cal.setEvents(agd.getAppointments());
});<|endoftext|>


<commit_before>/*
 * grunt-checkwpversion
 * https://github.com/stephenharris/grunt-checkwpversion
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/MIT
 */
/* jshint -W099 */
/* jshint -W030 */
/* jshint -W084 */
'use strict';
var chalk = require('chalk');

module.exports = function(grunt) {

	// Task definition
	grunt.registerMultiTask('checkwpversion', 'Checking repository state.', function () {

		var pkg = grunt.file.readJSON('package.json');

		var options = this.options({
			plugin: pkg.name+".php",
			readme: "readme.txt",
		});

		var check = this.data;

		if( typeof check.version1 === undefined ){
			grunt.fail.fatal( 'Version 1 not found.');
		}

		if( typeof check.version2 === undefined ){
			grunt.fail.fatal( 'Version 2 not found.');
		}

		if( typeof check.compare === undefined ){
			check.compare = "==";
		}

		//First if version is 'readme' or 'plugin' parse version values
		var version1 = checkwpversion._parseVersion( check.version1, options );
		var version2 = checkwpversion._parseVersion( check.version2, options );

		//Compare the versions
		var comparison = checkwpversion.versionCompare( version1, version2 );

		//Determine result
		var result;
		switch( check.compare ){
			case '==':
			case '=':
			case 'eq':
				result = ( comparison === 0 );
				break;
			case '!=':
			case '<>':
			case 'neq':
				result = ( comparison !== 0 );
				break;
			case '>=':
			case 'ge':
				result = ( comparison >= 0 );
				break;
			case '<=':
			case 'le':
				result = ( comparison <= 0 );
				break;
			case '>':
			case 'gt':
				result = ( comparison === 1 );
				break;
			case '<':
			case 'lt':
				result = ( comparison === -1 ) ;
				break;
			default:
				result = false;
				grunt.fail.fatal( "Invalid comparison operator." );
		}

		if( !result ){

			console.log("\n" + chalk.bgRed("CHECK FAILED!\n "+this.target+" "+version1+" "+check.compare+" "+version2+"") + "\n");
			grunt.fail.warn();

		}

		return true;
	});

var checkwpversion = {

	versionCompare: function( version1, version2 ){

		version1 = this._sanitizeVersion( version1 );
		version2 = this._sanitizeVersion( version2 );

		grunt.verbose.write("=== versions === \n" );
		grunt.verbose.write(version1 + "\n" );
		grunt.verbose.write(version2 + "\n" );
		grunt.verbose.write("================ \n" );

		var version1Arr = version1.split( '.' );
		var version2Arr = version2.split( '.' );

		//Padd with zeroes to ensure arrays are the same length
		var size = Math.max( version1Arr.length, version2Arr.length );
		version1Arr = this._array_pad( version1Arr, size );
		version2Arr = this._array_pad( version2Arr, size );

		//Compare each component
		for( var i = 0; i < size; i++ ){

			if( this._toWeight( version1Arr[i] ) > this._toWeight( version2Arr[i] ) ){
				return 1;
			}else if( this._toWeight( version1Arr[i] ) < this._toWeight( version2Arr[i] ) ){
				return -1;
			}

			//continue;
		}
		return 0;

	},

	/**
	 * Sanitize a version string
	 *
	 * It does the following:
	 *
	 * * Casts the string to lower case
	 * * Empty spaces are removed
	 * * Converts accepted delimiters ('.', '_', '-', '+') to '.'
	 * * Adds a . between strings and non-strings: e.g. "1.4b1" becomes "1.4.b.1"
	 * * Empty components are removed, e.g. "1.4..1" because "1.4.1"
	 * * "alpha", "beta", "pl" are converted to their 1-character equivalents: "a", "b", "p"
	 *
	 * @param version
	 * @returns
	 */
	_sanitizeVersion: function( version ){

		//lowercase
		version = version.toLowerCase();

		//Remove spaces;
		version = version.replace( ' ', '' );

		//Make delimiters all the same
		version = version.replace( '-', '.' );
		version = version.replace( '_', '.' );
		version = version.replace( '+', '.' );

		var length = version.length;
		var newVersion = false;

		newVersion = version[0];

		for( var i=1; i < length; i++ ){

			var a = version[i-1];
			var b = version[i];

			if( '.' === a || '.' === b ){
				newVersion += b;
				continue;
			}

			var sameType = ( isNaN( a ) === isNaN( b ) );

			if( sameType ){
				newVersion += b;
			}else{
				newVersion += "."+b;
			}

		}

		//Split at delimiter
		var versionArray = newVersion.split( '.' );

		//Filter empty parts
		versionArray = versionArray.filter( function(e){ return ( e !== "" ); } );

		//Convert special text into character form.
		versionArray = versionArray.map( function( e ){

			switch( e ){
				case 'alpha':
					return 'a';
				case 'beta':
					return 'b';
				case 'pl':
					return 'p';
				default:
					return e;
			}
		} );

		return versionArray.join('.');
	},

	/**
	 * Allow for easy comparison of a version component:
	 *
	 * any string not found in this list < dev < alpha = a < beta = b < RC = rc < # < pl = p < [integers]
	 *
	 * Returns a weight (-7+) specifying its 'order'.
	 *
	 * * any string not found in this list -> -7,
	 * * dev -> -6,
	 * * p -> -1,
	 * * 0 -> 0
	 * * 1 -> 1
	 * * 123 -> 123,
	 */
	_toWeight: function ( str ){

		if( isNaN( str ) ){
			var list = [ 'dev', 'a', 'b', 'rc', '#', 'p' ];
			return list.indexOf( str ) - 6;

		}else{
			return parseInt( str, 10 );
		}

	},

	/**
	 * If version is 'readme' or 'plugin', attempts to extract the version stored in
	 * the corresponding files as specified by options.
	 */
	_parseVersion: function ( version, options ){

		var matches;

		//First if version is 'readme' or 'plugin' version values
		if( version === 'readme' ){
			var readme = grunt.file.read( options.readme );
			matches = readme.match( new RegExp("^Stable tag:\\s*(\\S+)","im") );

			if( matches.length <= 1 ){
				grunt.fail.fatal( 'Could not find version in "' + options.readme + '"' );
			}
			version = matches[1];

		}else if( version === 'plugin' ){
			var plugin = grunt.file.read( options.plugin );
			matches = plugin.match( new RegExp("^Version:\\s*(\\S+)","im") );
			if( matches.length <= 1 ){
				grunt.fail.fatal( 'Could not find version in "' + options.readme + '"' );
			}
			version = matches[1];
		}

		return version;
	},

	/**
	 * Pad an array with zeroes up to specified length
	 * @param Array arr Array to pad
	 * @param Int length Length to pad to
	 * @returns Array
	 */
	_array_pad: function ( arr, length ){

		while( arr.length  < length ){
			arr.push( 0 );
		}

		return arr;
	}
};
};
<commit_msg>Add warning message to grunt.fail.warn<commit_after>/*
 * grunt-checkwpversion
 * https://github.com/stephenharris/grunt-checkwpversion
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/MIT
 */
/* jshint -W099 */
/* jshint -W030 */
/* jshint -W084 */
'use strict';
var chalk = require('chalk');

module.exports = function(grunt) {

	// Task definition
	grunt.registerMultiTask('checkwpversion', 'Checking repository state.', function () {

		var pkg = grunt.file.readJSON('package.json');

		var options = this.options({
			plugin: pkg.name+".php",
			readme: "readme.txt",
		});

		var check = this.data;

		if( typeof check.version1 === undefined ){
			grunt.fail.fatal( 'Version 1 not found.');
		}

		if( typeof check.version2 === undefined ){
			grunt.fail.fatal( 'Version 2 not found.');
		}

		if( typeof check.compare === undefined ){
			check.compare = "==";
		}

		//First if version is 'readme' or 'plugin' parse version values
		var version1 = checkwpversion._parseVersion( check.version1, options );
		var version2 = checkwpversion._parseVersion( check.version2, options );

		//Compare the versions
		var comparison = checkwpversion.versionCompare( version1, version2 );

		//Determine result
		var result;
		switch( check.compare ){
			case '==':
			case '=':
			case 'eq':
				result = ( comparison === 0 );
				break;
			case '!=':
			case '<>':
			case 'neq':
				result = ( comparison !== 0 );
				break;
			case '>=':
			case 'ge':
				result = ( comparison >= 0 );
				break;
			case '<=':
			case 'le':
				result = ( comparison <= 0 );
				break;
			case '>':
			case 'gt':
				result = ( comparison === 1 );
				break;
			case '<':
			case 'lt':
				result = ( comparison === -1 ) ;
				break;
			default:
				result = false;
				grunt.fail.fatal( "Invalid comparison operator." );
		}

		if( !result ){

			console.log("\n" + chalk.bgRed("CHECK FAILED!\n "+this.target+" "+version1+" "+check.compare+" "+version2+"") + "\n");
			grunt.fail.warn( "Checks failed" );

		}

		return true;
	});

var checkwpversion = {

	versionCompare: function( version1, version2 ){

		version1 = this._sanitizeVersion( version1 );
		version2 = this._sanitizeVersion( version2 );

		grunt.verbose.write("=== versions === \n" );
		grunt.verbose.write(version1 + "\n" );
		grunt.verbose.write(version2 + "\n" );
		grunt.verbose.write("================ \n" );

		var version1Arr = version1.split( '.' );
		var version2Arr = version2.split( '.' );

		//Padd with zeroes to ensure arrays are the same length
		var size = Math.max( version1Arr.length, version2Arr.length );
		version1Arr = this._array_pad( version1Arr, size );
		version2Arr = this._array_pad( version2Arr, size );

		//Compare each component
		for( var i = 0; i < size; i++ ){

			if( this._toWeight( version1Arr[i] ) > this._toWeight( version2Arr[i] ) ){
				return 1;
			}else if( this._toWeight( version1Arr[i] ) < this._toWeight( version2Arr[i] ) ){
				return -1;
			}

			//continue;
		}
		return 0;

	},

	/**
	 * Sanitize a version string
	 *
	 * It does the following:
	 *
	 * * Casts the string to lower case
	 * * Empty spaces are removed
	 * * Converts accepted delimiters ('.', '_', '-', '+') to '.'
	 * * Adds a . between strings and non-strings: e.g. "1.4b1" becomes "1.4.b.1"
	 * * Empty components are removed, e.g. "1.4..1" because "1.4.1"
	 * * "alpha", "beta", "pl" are converted to their 1-character equivalents: "a", "b", "p"
	 *
	 * @param version
	 * @returns
	 */
	_sanitizeVersion: function( version ){

		//lowercase
		version = version.toLowerCase();

		//Remove spaces;
		version = version.replace( ' ', '' );

		//Make delimiters all the same
		version = version.replace( '-', '.' );
		version = version.replace( '_', '.' );
		version = version.replace( '+', '.' );

		var length = version.length;
		var newVersion = false;

		newVersion = version[0];

		for( var i=1; i < length; i++ ){

			var a = version[i-1];
			var b = version[i];

			if( '.' === a || '.' === b ){
				newVersion += b;
				continue;
			}

			var sameType = ( isNaN( a ) === isNaN( b ) );

			if( sameType ){
				newVersion += b;
			}else{
				newVersion += "."+b;
			}

		}

		//Split at delimiter
		var versionArray = newVersion.split( '.' );

		//Filter empty parts
		versionArray = versionArray.filter( function(e){ return ( e !== "" ); } );

		//Convert special text into character form.
		versionArray = versionArray.map( function( e ){

			switch( e ){
				case 'alpha':
					return 'a';
				case 'beta':
					return 'b';
				case 'pl':
					return 'p';
				default:
					return e;
			}
		} );

		return versionArray.join('.');
	},

	/**
	 * Allow for easy comparison of a version component:
	 *
	 * any string not found in this list < dev < alpha = a < beta = b < RC = rc < # < pl = p < [integers]
	 *
	 * Returns a weight (-7+) specifying its 'order'.
	 *
	 * * any string not found in this list -> -7,
	 * * dev -> -6,
	 * * p -> -1,
	 * * 0 -> 0
	 * * 1 -> 1
	 * * 123 -> 123,
	 */
	_toWeight: function ( str ){

		if( isNaN( str ) ){
			var list = [ 'dev', 'a', 'b', 'rc', '#', 'p' ];
			return list.indexOf( str ) - 6;

		}else{
			return parseInt( str, 10 );
		}

	},

	/**
	 * If version is 'readme' or 'plugin', attempts to extract the version stored in
	 * the corresponding files as specified by options.
	 */
	_parseVersion: function ( version, options ){

		var matches;

		//First if version is 'readme' or 'plugin' version values
		if( version === 'readme' ){
			var readme = grunt.file.read( options.readme );
			matches = readme.match( new RegExp("^Stable tag:\\s*(\\S+)","im") );

			if( matches.length <= 1 ){
				grunt.fail.fatal( 'Could not find version in "' + options.readme + '"' );
			}
			version = matches[1];

		}else if( version === 'plugin' ){
			var plugin = grunt.file.read( options.plugin );
			matches = plugin.match( new RegExp("^Version:\\s*(\\S+)","im") );
			if( matches.length <= 1 ){
				grunt.fail.fatal( 'Could not find version in "' + options.readme + '"' );
			}
			version = matches[1];
		}

		return version;
	},

	/**
	 * Pad an array with zeroes up to specified length
	 * @param Array arr Array to pad
	 * @param Int length Length to pad to
	 * @returns Array
	 */
	_array_pad: function ( arr, length ){

		while( arr.length  < length ){
			arr.push( 0 );
		}

		return arr;
	}
};
};
<|endoftext|>


<commit_before>
module.exports = exports = search

var npm = require("./npm.js")
  , registry = npm.registry

search.usage = "npm search [some search terms ...]"

search.completion = function (opts, cb) {
  var compl = {}
    , partial = opts.partialWord
    , ipartial = partial.toLowerCase()
    , plen = partial.length

  // get the batch of data that matches so far.
  // this is an example of using npm.commands.search programmatically
  // to fetch data that has been filtered by a set of arguments.
  search(opts.conf.argv.remain.slice(2), true, function (er, data) {
    if (er) return cb(er)
    Object.keys(data).forEach(function (name) {
      data[name].words.split(" ").forEach(function (w) {
        if (w.toLowerCase().indexOf(ipartial) === 0) {
          compl[partial + w.substr(plen)] = true
        }
      })
    })
    cb(null, Object.keys(compl))
  })
}

function search (args, silent, staleness, cb) {
  if (typeof cb !== "function") cb = staleness, staleness = 600
  if (typeof cb !== "function") cb = silent, silent = false

  var searchopts = npm.config.get("searchopts")
    , searchexclude = npm.config.get("searchexclude")
  if (typeof searchopts !== "string") searchopts = ""
  searchopts = searchopts.split(/\s+/)
  if (typeof searchexclude === "string") {
    searchexclude = searchexclude.split(/\s+/)
  } else searchexclude = []
  var opts = searchopts.concat(args).map(function (s) {
    return s.toLowerCase()
  }).filter(function (s) { return s })
  searchexclude = searchexclude.map(function (s) {
    return s.toLowerCase()
  })
  getFilteredData( staleness, opts, searchexclude, function (er, data) {
    // now data is the list of data that we want to show.
    // prettify and print it, and then provide the raw
    // data to the cb.
    if (er || silent) return cb(er, data)
    console.log(prettify(data, args))
    cb(null, data)
  })
}

function getFilteredData (staleness, args, notArgs, cb) {
  registry.get( "/-/all", staleness, false
              , true, function (er, data) {
    if (er) return cb(er)
    return cb(null, filter(data, args, notArgs))
  })
}

function filter (data, args, notArgs) {
  // data={<name>:{package data}}
  return Object.keys(data).map(function (d) {
    return data[d]
  }).filter(function (d) {
    return typeof d === "object"
  }).map(stripData).map(getWords).filter(function (data) {
    return filterWords(data, args, notArgs)
  }).reduce(function (l, r) {
    l[r.name] = r
    return l
  }, {})
}

function stripData (data) {
  return { name: data.name
         , description: npm.config.get("description") ? data.description : ""
         , maintainers: (data.maintainers || []).map(function (m) {
             return "=" + m.name
           })
         , url: !Object.keys(data.versions || {}).length ? data.url : null
         , keywords: data.keywords || []
         , version: Object.keys(data.versions)[0] || []
         , time: data.time
                 && data.time.modified
                 && (new Date(data.time.modified).toISOString()
                     .split("T").join(" ")
                     .replace(/:[0-9]{2}\.[0-9]{3}Z$/, ""))
                 || "(prehistoric)"
         }
}

function getWords (data) {
  data.words = [ data.name ]
               .concat(data.description)
               .concat(data.maintainers)
               .concat(data.url && ("<" + data.url + ">"))
               .concat(data.keywords)
               .map(function (f) { return f && f.trim && f.trim() })
               .filter(function (f) { return f })
               .join(" ")
               .toLowerCase()
  return data
}

function filterWords (data, args, notArgs) {
  var words = data.words
  for (var i = 0, l = args.length; i < l; i ++) {
    if (!match(words, args[i])) return false
  }
  for (var i = 0, l = notArgs.length; i < l; i ++) {
    if (match(words, notArgs[i])) return false
  }
  return true
}

function match (words, arg) {
  if (arg.charAt(0) === "/") {
    arg = arg.replace(/\/$/, "")
    arg = new RegExp(arg.substr(1, arg.length - 1))
    return words.match(arg)
  }
  return words.indexOf(arg) !== -1
}

function prettify (data, args) {
  try {
    var tty = require("tty")
      , stdout = process.stdout
      , cols = !tty.isatty(stdout.fd) ? Infinity
             : process.stdout.getWindowSize()[0]
      cols = (cols == 0) ? Infinity : cols
  } catch (ex) { cols = Infinity }

  // name, desc, author, keywords
  var longest = []
    , spaces
    , maxLen = npm.config.get("description")
             ? [20, 60, 20, 20, 10, Infinity]
             : [20, 20, 20, 10, Infinity]
    , headings = npm.config.get("description")
               ? ["NAME", "DESCRIPTION", "AUTHOR", "DATE", "VERSION", "KEYWORDS"]
               : ["NAME", "AUTHOR", "DATE", "VERSION", "KEYWORDS"]
    , lines
    , searchsort = (npm.config.get("searchsort") || "NAME").toLowerCase()
    , sortFields = { name: 0
                   , description: 1
                   , author: 2
                   , date: 3
                   , version: 4
                   , keywords: 5 }
    , searchRev = searchsort.charAt(0) === "-"
    , sortField = sortFields[searchsort.replace(/^\-+/, "")]

  lines = Object.keys(data).map(function (d) {
    return data[d]
  }).map(function (data) {
    // turn a pkg data into a string
    // [name,who,desc,targets,keywords] tuple
    // also set longest to the longest name
    if (typeof data.keywords === "string") {
      data.keywords = data.keywords.split(/[,\s]+/)
    }
    if (!Array.isArray(data.keywords)) data.keywords = []
    var l = [ data.name
            , data.description || ""
            , data.maintainers.join(" ")
            , data.time
            , data.version || ""
            , (data.keywords || []).join(" ")
            ]
    l.forEach(function (s, i) {
      var len = s.length
      longest[i] = Math.min(maxLen[i] || Infinity
                           ,Math.max(longest[i] || 0, len))
      if (len > longest[i]) {
        l._undent = l._undent || []
        l._undent[i] = len - longest[i]
      }
      l[i] = ('' + l[i]).replace(/\s+/g, " ")
    })
    return l
  }).sort(function (a, b) {
    // a and b are "line" objects of [name, desc, maint, time, kw]
    var aa = a[sortField].toLowerCase()
      , bb = b[sortField].toLowerCase()
    return aa === bb ? 0
         : aa < bb ? (searchRev ? 1 : -1)
         : (searchRev ? -1 : 1)
  }).map(function (line) {
    return line.map(function (s, i) {
      spaces = spaces || longest.map(function (n) {
        return new Array(n + 2).join(" ")
      })
      var len = s.length
      if (line._undent && line._undent[i - 1]) {
        len += line._undent[i - 1] - 1
      }
      return s + spaces[i].substr(len)
    }).join(" ").substr(0, cols).trim()
  }).map(function (line) {
    // colorize!
    args.forEach(function (arg, i) {
      line = addColorMarker(line, arg, i)
    })
    return colorize(line).trim()
  })

  if (lines.length === 0) {
    return "No match found for "+(args.map(JSON.stringify).join(" "))
  }

  // build the heading padded to the longest in each field
  return headings.map(function (h, i) {
    var space = Math.max(2, 3 + (longest[i] || 0) - h.length)
    return h + (new Array(space).join(" "))
  }).join("").substr(0, cols).trim() + "\n" + lines.join("\n")

}

var colors = [31, 33, 32, 36, 34, 35 ]
  , cl = colors.length
function addColorMarker (str, arg, i) {
  var m = i % cl + 1
    , markStart = String.fromCharCode(m)
    , markEnd = String.fromCharCode(0)

  if (arg.charAt(0) === "/") {
    //arg = arg.replace(/\/$/, "")
    return str.replace( new RegExp(arg.substr(1, arg.length - 1), "gi")
                      , function (bit) { return markStart + bit + markEnd } )

  }

  // just a normal string, do the split/map thing
  var pieces = str.toLowerCase().split(arg.toLowerCase())
    , p = 0

  return pieces.map(function (piece, i) {
    piece = str.substr(p, piece.length)
    var mark = markStart
             + str.substr(p+piece.length, arg.length)
             + markEnd
    p += piece.length + arg.length
    return piece + mark
  }).join("")
}

function colorize (line) {
  for (var i = 0; i < cl; i ++) {
    var m = i + 1
    var color = npm.color ? "\033["+colors[i]+"m" : ""
    line = line.split(String.fromCharCode(m)).join(color)
  }
  var uncolor = npm.color ? "\033[0m" : ""
  return line.split("\u0000").join(uncolor)
}
<commit_msg>Guard against falsey versions value.<commit_after>
module.exports = exports = search

var npm = require("./npm.js")
  , registry = npm.registry

search.usage = "npm search [some search terms ...]"

search.completion = function (opts, cb) {
  var compl = {}
    , partial = opts.partialWord
    , ipartial = partial.toLowerCase()
    , plen = partial.length

  // get the batch of data that matches so far.
  // this is an example of using npm.commands.search programmatically
  // to fetch data that has been filtered by a set of arguments.
  search(opts.conf.argv.remain.slice(2), true, function (er, data) {
    if (er) return cb(er)
    Object.keys(data).forEach(function (name) {
      data[name].words.split(" ").forEach(function (w) {
        if (w.toLowerCase().indexOf(ipartial) === 0) {
          compl[partial + w.substr(plen)] = true
        }
      })
    })
    cb(null, Object.keys(compl))
  })
}

function search (args, silent, staleness, cb) {
  if (typeof cb !== "function") cb = staleness, staleness = 600
  if (typeof cb !== "function") cb = silent, silent = false

  var searchopts = npm.config.get("searchopts")
    , searchexclude = npm.config.get("searchexclude")
  if (typeof searchopts !== "string") searchopts = ""
  searchopts = searchopts.split(/\s+/)
  if (typeof searchexclude === "string") {
    searchexclude = searchexclude.split(/\s+/)
  } else searchexclude = []
  var opts = searchopts.concat(args).map(function (s) {
    return s.toLowerCase()
  }).filter(function (s) { return s })
  searchexclude = searchexclude.map(function (s) {
    return s.toLowerCase()
  })
  getFilteredData( staleness, opts, searchexclude, function (er, data) {
    // now data is the list of data that we want to show.
    // prettify and print it, and then provide the raw
    // data to the cb.
    if (er || silent) return cb(er, data)
    console.log(prettify(data, args))
    cb(null, data)
  })
}

function getFilteredData (staleness, args, notArgs, cb) {
  registry.get( "/-/all", staleness, false
              , true, function (er, data) {
    if (er) return cb(er)
    return cb(null, filter(data, args, notArgs))
  })
}

function filter (data, args, notArgs) {
  // data={<name>:{package data}}
  return Object.keys(data).map(function (d) {
    return data[d]
  }).filter(function (d) {
    return typeof d === "object"
  }).map(stripData).map(getWords).filter(function (data) {
    return filterWords(data, args, notArgs)
  }).reduce(function (l, r) {
    l[r.name] = r
    return l
  }, {})
}

function stripData (data) {
  return { name: data.name
         , description: npm.config.get("description") ? data.description : ""
         , maintainers: (data.maintainers || []).map(function (m) {
             return "=" + m.name
           })
         , url: !Object.keys(data.versions || {}).length ? data.url : null
         , keywords: data.keywords || []
         , version: Object.keys(data.versions || {})[0] || []
         , time: data.time
                 && data.time.modified
                 && (new Date(data.time.modified).toISOString()
                     .split("T").join(" ")
                     .replace(/:[0-9]{2}\.[0-9]{3}Z$/, ""))
                 || "(prehistoric)"
         }
}

function getWords (data) {
  data.words = [ data.name ]
               .concat(data.description)
               .concat(data.maintainers)
               .concat(data.url && ("<" + data.url + ">"))
               .concat(data.keywords)
               .map(function (f) { return f && f.trim && f.trim() })
               .filter(function (f) { return f })
               .join(" ")
               .toLowerCase()
  return data
}

function filterWords (data, args, notArgs) {
  var words = data.words
  for (var i = 0, l = args.length; i < l; i ++) {
    if (!match(words, args[i])) return false
  }
  for (var i = 0, l = notArgs.length; i < l; i ++) {
    if (match(words, notArgs[i])) return false
  }
  return true
}

function match (words, arg) {
  if (arg.charAt(0) === "/") {
    arg = arg.replace(/\/$/, "")
    arg = new RegExp(arg.substr(1, arg.length - 1))
    return words.match(arg)
  }
  return words.indexOf(arg) !== -1
}

function prettify (data, args) {
  try {
    var tty = require("tty")
      , stdout = process.stdout
      , cols = !tty.isatty(stdout.fd) ? Infinity
             : process.stdout.getWindowSize()[0]
      cols = (cols == 0) ? Infinity : cols
  } catch (ex) { cols = Infinity }

  // name, desc, author, keywords
  var longest = []
    , spaces
    , maxLen = npm.config.get("description")
             ? [20, 60, 20, 20, 10, Infinity]
             : [20, 20, 20, 10, Infinity]
    , headings = npm.config.get("description")
               ? ["NAME", "DESCRIPTION", "AUTHOR", "DATE", "VERSION", "KEYWORDS"]
               : ["NAME", "AUTHOR", "DATE", "VERSION", "KEYWORDS"]
    , lines
    , searchsort = (npm.config.get("searchsort") || "NAME").toLowerCase()
    , sortFields = { name: 0
                   , description: 1
                   , author: 2
                   , date: 3
                   , version: 4
                   , keywords: 5 }
    , searchRev = searchsort.charAt(0) === "-"
    , sortField = sortFields[searchsort.replace(/^\-+/, "")]

  lines = Object.keys(data).map(function (d) {
    return data[d]
  }).map(function (data) {
    // turn a pkg data into a string
    // [name,who,desc,targets,keywords] tuple
    // also set longest to the longest name
    if (typeof data.keywords === "string") {
      data.keywords = data.keywords.split(/[,\s]+/)
    }
    if (!Array.isArray(data.keywords)) data.keywords = []
    var l = [ data.name
            , data.description || ""
            , data.maintainers.join(" ")
            , data.time
            , data.version || ""
            , (data.keywords || []).join(" ")
            ]
    l.forEach(function (s, i) {
      var len = s.length
      longest[i] = Math.min(maxLen[i] || Infinity
                           ,Math.max(longest[i] || 0, len))
      if (len > longest[i]) {
        l._undent = l._undent || []
        l._undent[i] = len - longest[i]
      }
      l[i] = ('' + l[i]).replace(/\s+/g, " ")
    })
    return l
  }).sort(function (a, b) {
    // a and b are "line" objects of [name, desc, maint, time, kw]
    var aa = a[sortField].toLowerCase()
      , bb = b[sortField].toLowerCase()
    return aa === bb ? 0
         : aa < bb ? (searchRev ? 1 : -1)
         : (searchRev ? -1 : 1)
  }).map(function (line) {
    return line.map(function (s, i) {
      spaces = spaces || longest.map(function (n) {
        return new Array(n + 2).join(" ")
      })
      var len = s.length
      if (line._undent && line._undent[i - 1]) {
        len += line._undent[i - 1] - 1
      }
      return s + spaces[i].substr(len)
    }).join(" ").substr(0, cols).trim()
  }).map(function (line) {
    // colorize!
    args.forEach(function (arg, i) {
      line = addColorMarker(line, arg, i)
    })
    return colorize(line).trim()
  })

  if (lines.length === 0) {
    return "No match found for "+(args.map(JSON.stringify).join(" "))
  }

  // build the heading padded to the longest in each field
  return headings.map(function (h, i) {
    var space = Math.max(2, 3 + (longest[i] || 0) - h.length)
    return h + (new Array(space).join(" "))
  }).join("").substr(0, cols).trim() + "\n" + lines.join("\n")

}

var colors = [31, 33, 32, 36, 34, 35 ]
  , cl = colors.length
function addColorMarker (str, arg, i) {
  var m = i % cl + 1
    , markStart = String.fromCharCode(m)
    , markEnd = String.fromCharCode(0)

  if (arg.charAt(0) === "/") {
    //arg = arg.replace(/\/$/, "")
    return str.replace( new RegExp(arg.substr(1, arg.length - 1), "gi")
                      , function (bit) { return markStart + bit + markEnd } )

  }

  // just a normal string, do the split/map thing
  var pieces = str.toLowerCase().split(arg.toLowerCase())
    , p = 0

  return pieces.map(function (piece, i) {
    piece = str.substr(p, piece.length)
    var mark = markStart
             + str.substr(p+piece.length, arg.length)
             + markEnd
    p += piece.length + arg.length
    return piece + mark
  }).join("")
}

function colorize (line) {
  for (var i = 0; i < cl; i ++) {
    var m = i + 1
    var color = npm.color ? "\033["+colors[i]+"m" : ""
    line = line.split(String.fromCharCode(m)).join(color)
  }
  var uncolor = npm.color ? "\033[0m" : ""
  return line.split("\u0000").join(uncolor)
}
<|endoftext|>


<commit_before>define(['charts/ChartBase'], function (cb) {
    return {
        type: "highcharts",
        callback: function (d) {
            cb.multivalueTimeDataConvertor(this.config, d);

            function daysBetween(first, second) {
                var one = new Date(first.getFullYear(), first.getMonth(), first.getDate());
                var two = new Date(second.getFullYear(), second.getMonth(), second.getDate());
                var millisecondsPerDay = 1000 * 60 * 60 * 24;
                var millisBetween = two.getTime() - one.getTime();
                var days = millisBetween / millisecondsPerDay;
                return Math.floor(days);
            }

            //determine max data range
            var minDate = +Infinity;
            var maxDate = -Infinity;
            for (var i = 0; i <  this.config.series.length; i++) {
                var minValue = this.config.series[i].data[0][0];
                var maxValue = this.config.series[i].data[this.config.series[i].data.length - 1][0];

                if (minValue < minDate) minDate = minValue;
                if (maxValue > maxDate) maxDate = maxValue;
            }

            var days = daysBetween(new Date(minValue), new Date(maxValue));

            if (days <= 2) {
                this.config.rangeSelector.buttons =
                    [{
                        type : 'minute',
                        count : 1,
                        text : '1m'
                    }, {
                        type : 'hour',
                        count : 1,
                        text : '1h'
                    }, {
                        type : 'all',
                        count : 1,
                        text : 'All'
                    }];
            } else if (days <= 30) {
                this.config.rangeSelector.buttons =
                    [{
                        type : 'hour',
                        count : 1,
                        text : '1h'
                    }, {
                        type : 'day',
                        count : 1,
                        text : '1D'
                    }, {
                        type : 'all',
                        count : 1,
                        text : 'All'
                    }];
            } else if (days <= 360) {
                this.config.rangeSelector.buttons =
                    [{
                        type : 'day',
                        count : 1,
                        text : '1D'
                    }, {
                        type : 'month',
                        count : 1,
                        text : '1M'
                    }, {
                        type : 'all',
                        count : 1,
                        text : 'All'
                    }];

            } else {
                this.config.rangeSelector.buttons =
                    [{
                        type : 'day',
                        count : 1,
                        text : '1D'
                    }, {
                        type : 'month',
                        count : 1,
                        text : '1M'
                    }, {
                        type : 'year',
                        count : 1,
                        text : '1Y'
                    }, {
                        type : 'all',
                        count : 1,
                        text : 'All'
                    }];
            }
        },
        config: {
            tooltip: {
                formatter: cb.defaultTimechartTooltipFormatter
            },
            timechart: 1,
            rangeSelector:{
                inputEnabled: false,
                buttons : [{
                    type : 'hour',
                    count : 1,
                    text : '1h'
                }, {
                    type : 'day',
                    count : 1,
                    text : '1D'
                }, {
                    type : 'all',
                    count : 1,
                    text : 'All'
                }],
                selected : 1
            },
            scrollbar : {
                enabled : false
            },
            title: {
                text: ''
            },
            xAxis: {
                type: 'datetime',
                title: {
                    text: ''
                }
            },
            yAxis: {
                min: 0,
                title: {
                    text: ''
                }
            },
            plotOptions: {
                series: {
                    lineWidth: 3,
                    marker: {
                        enabled: false
                    }
                }
            },
            legend: {
                enabled: false
            },
            series: []
        }
    }
});<commit_msg>bug fixes<commit_after>define(['charts/ChartBase'], function (cb) {
    return {
        type: "highcharts",
        callback: function (d) {

            cb.multivalueTimeDataConvertor(this.config, d);

            //determine max data range
            var minDate = +Infinity;
            var maxDate = -Infinity;
            for (var i = 0; i <  this.config.series.length; i++) {
                var minValue = this.config.series[i].data[0][0];
                var maxValue = this.config.series[i].data[this.config.series[i].data.length - 1][0];

                if (minValue < minDate) minDate = minValue;
                if (maxValue > maxDate) maxDate = maxValue;
            }

            function daysBetween(first, second) {
                var one = new Date(first.getFullYear(), first.getMonth(), first.getDate());
                var two = new Date(second.getFullYear(), second.getMonth(), second.getDate());
                var millisecondsPerDay = 1000 * 60 * 60 * 24;
                var millisBetween = two.getTime() - one.getTime();
                var days = millisBetween / millisecondsPerDay;
                return Math.floor(days);
            }

            var days = daysBetween(new Date(minValue), new Date(maxValue));

            if (days <= 2) {
                this.config.rangeSelector.buttons =
                    [{
                        type : 'minute',
                        count : 1,
                        text : '1m'
                    }, {
                        type : 'hour',
                        count : 1,
                        text : '1h'
                    }, {
                        type : 'all',
                        count : 1,
                        text : 'All'
                    }];
            } else if (days <= 30) {
                this.config.rangeSelector.buttons =
                    [{
                        type : 'hour',
                        count : 1,
                        text : '1h'
                    }, {
                        type : 'day',
                        count : 1,
                        text : '1D'
                    }, {
                        type : 'all',
                        count : 1,
                        text : 'All'
                    }];
            } else if (days <= 360) {
                this.config.rangeSelector.buttons =
                    [{
                        type : 'day',
                        count : 1,
                        text : '1D'
                    }, {
                        type : 'month',
                        count : 1,
                        text : '1M'
                    }, {
                        type : 'all',
                        count : 1,
                        text : 'All'
                    }];

            } else {
                this.config.rangeSelector.buttons =
                    [{
                        type : 'day',
                        count : 1,
                        text : '1D'
                    }, {
                        type : 'month',
                        count : 1,
                        text : '1M'
                    }, {
                        type : 'year',
                        count : 1,
                        text : '1Y'
                    }, {
                        type : 'all',
                        count : 1,
                        text : 'All'
                    }];
                this.config.rangeSelector.selected = 3;
            }
        },
        config: {
            tooltip: {
                formatter: cb.defaultTimechartTooltipFormatter
            },
            timechart: 1,
            rangeSelector:{
                inputEnabled: false,
                buttons : [{
                    type : 'hour',
                    count : 1,
                    text : '1h'
                }, {
                    type : 'day',
                    count : 1,
                    text : '1D'
                }, {
                    type : 'all',
                    count : 1,
                    text : 'All'
                }],
                selected : 1
            },
            scrollbar : {
                enabled : false
            },
            title: {
                text: ''
            },
            xAxis: {
                type: 'datetime',
                title: {
                    text: ''
                }
            },
            yAxis: {
                min: 0,
                title: {
                    text: ''
                }
            },
            plotOptions: {
                series: {
                    lineWidth: 3,
                    marker: {
                        enabled: false
                    }
                }
            },
            legend: {
                enabled: false
            },
            series: []
        }
    }
});<|endoftext|>


<commit_before>const async = require('async')

module.exports = (grunt) => {
  // register "ghostinspector" task
  grunt.registerMultiTask('ghostinspector', 'Execute your Ghost Inspector tests', function () {
    // reference to Grunt done() function
    const gruntDone = this.async()
    // get options
    const options = this.options()
    // get suites to execute
    let { suites } = this.data
    if (typeof suites === 'string') {
      suites = [suites]
    } else if (!(suites instanceof Array)) {
      suites = []
    }
    // get tests to execute
    let { tests } = this.data
    if (typeof tests === 'string') {
      tests = [tests]
    } else if (!(tests instanceof Array)) {
      tests = []
    }
    // create Ghost Inspector object
    const GhostInspector = require('ghost-inspector')(options.apiKey)
    // execute any specified suites
    if (suites.length) { grunt.log.writeln('Executing suites...') }
    async.eachSeries(suites, (suiteId, done) => {
      // execute suite
      GhostInspector.executeSuite(suiteId, options, (err, data, passing) => {
        // evaluate api response
        if (err) { return done(`Error executing suite "${suiteId}": ${err}`) }
        if (passing) {
          grunt.log.ok(`Suite "${suiteId}" passed.`)
          return done()
        } else {
          return done(`Suite "${suiteId}" failed.`)
        }
      })
    }, (err) => {
      // done with suites, bail if we hit an error/failure
      if (err) {
        grunt.log.error(err)
        return gruntDone(false)
      }
      // execute any specified tests
      if (tests.length) { grunt.log.writeln('Executing tests...') }
      async.eachSeries(tests, (testId, done) => {
        // execute test
        GhostInspector.executeTest(testId, options, (err, data, passing) => {
          // evaluate api response
          if (err) { return done(`Error executing test "${data.test.name}" (${testId}): ${err}`) }
          if (passing) {
            grunt.log.ok(`Test "${data.test.name}" (${testId}) passed.`)
            return done()
          } else {
            return done(`Test "${data.test.name}" (${testId}) failed`)
          }
        })
      }, (err) => {
        // done with tests, bail if we hit an error/failure
        if (err) {
          grunt.log.error(err)
          return gruntDone(false)
        }
        // done with suites and tests
        return gruntDone()
      })
    })
  })
}
<commit_msg>Consistent formatting<commit_after>const async = require('async')

module.exports = (grunt) => {
  // register "ghostinspector" task
  grunt.registerMultiTask('ghostinspector', 'Execute your Ghost Inspector tests', function () {
    // reference to Grunt done() function
    const gruntDone = this.async()
    // get options
    const options = this.options()
    // get suites to execute
    let { suites } = this.data
    if (typeof suites === 'string') {
      suites = [suites]
    } else if (!(suites instanceof Array)) {
      suites = []
    }
    // get tests to execute
    let { tests } = this.data
    if (typeof tests === 'string') {
      tests = [tests]
    } else if (!(tests instanceof Array)) {
      tests = []
    }
    // create Ghost Inspector object
    const GhostInspector = require('ghost-inspector')(options.apiKey)
    // execute any specified suites
    if (suites.length) {
      grunt.log.writeln('Executing suites...')
    }
    async.eachSeries(suites, (suiteId, done) => {
      // execute suite
      GhostInspector.executeSuite(suiteId, options, (err, data, passing) => {
        // evaluate api response
        if (err) {
          return done(`Error executing suite "${suiteId}": ${err}`)
        }
        if (passing) {
          grunt.log.ok(`Suite "${suiteId}" passed.`)
          return done()
        } else {
          return done(`Suite "${suiteId}" failed.`)
        }
      })
    }, (err) => {
      // done with suites, bail if we hit an error/failure
      if (err) {
        grunt.log.error(err)
        return gruntDone(false)
      }
      // execute any specified tests
      if (tests.length) {
        grunt.log.writeln('Executing tests...')
      }
      async.eachSeries(tests, (testId, done) => {
        // execute test
        GhostInspector.executeTest(testId, options, (err, data, passing) => {
          // evaluate api response
          if (err) {
            return done(`Error executing test "${data.test.name}" (${testId}): ${err}`)
          }
          if (passing) {
            grunt.log.ok(`Test "${data.test.name}" (${testId}) passed.`)
            return done()
          } else {
            return done(`Test "${data.test.name}" (${testId}) failed`)
          }
        })
      }, (err) => {
        // done with tests, bail if we hit an error/failure
        if (err) {
          grunt.log.error(err)
          return gruntDone(false)
        }
        // done with suites and tests
        return gruntDone()
      })
    })
  })
}
<|endoftext|>