<commit_before>// Copyright 2014-2015 runtime.js project authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var browserify = require('browserify');
var initrdPack = require('./initrd-pack');
var fs = require('fs');
var path = require('path');
var builtins = require('browserify/lib/builtins');

module.exports = function (opts, cb) {
  var b = browserify(opts.file, opts);

  // TODO: include files separately
  // var rows = [];
  //
  // b.pipeline.get('deps').push(through.obj(
  //   function(row, enc, next) { rows.push(row); next() },
  //   function() {
  //     console.log(rows);
  //   }
  // ));

  var stream = b.bundle();
  var bundle = { stream: stream, name: '/bundle.js' };
  var out = fs.createWriteStream(path.resolve(opts.output));
  initrdPack(out, [bundle]);
  out.once('finish', cb);
  out.once('error', cb);
};

module.exports.builtins = builtins;
<commit_msg>Add runtime-node-net and http-node<commit_after>// Copyright 2014-2015 runtime.js project authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var browserify = require('browserify');
var initrdPack = require('./initrd-pack');
var fs = require('fs');
var path = require('path');
var builtins = require('browserify/lib/builtins');

builtins.http = require.resolve('http-node');
builtins.net = require.resolve('runtime-node-net');

module.exports = function (opts, cb) {
  var b = browserify(opts.file, opts);

  // TODO: include files separately
  // var rows = [];
  //
  // b.pipeline.get('deps').push(through.obj(
  //   function(row, enc, next) { rows.push(row); next() },
  //   function() {
  //     console.log(rows);
  //   }
  // ));

  var stream = b.bundle();
  var bundle = { stream: stream, name: '/bundle.js' };
  var out = fs.createWriteStream(path.resolve(opts.output));
  initrdPack(out, [bundle]);
  out.once('finish', cb);
  out.once('error', cb);
};

module.exports.builtins = builtins;
<|endoftext|>


<commit_before>
//module.exports = require('./lib/migrate');

/**
 * Module dependencies.
 */
var migrate = require('./lib/migrate'),
	path = require('path'),
	join = path.join,
	fs = require('fs');

/**
 * Arguments.
 */
var args = process.argv.slice(2);

/**
 * Option defaults.
 */
var options = { args: [] };

/**
 * Current working directory.
 */
var cwd;

var configFileName = 'default-config.json',
	dbProperty = 'mongoAppDb';

/**
 * Usage information.
 */
var usage = [
	''
	, '  Usage: migrate [options] [command]'
	, ''
	, '  Options:'
	, ''
	, '     -c, --chdir <path>    		change the working directory'
	, '     -cfg, --config <path> 		DB config file name'
	, '     -dbn, --dbPropName <string> Property name for database connection in config file'
	//, '     -run                        Run the migration from the command line' //TODO: Add this functionality so it can also be run as a node module through require
	, ''
	, '  Commands:'
	, ''
	, '     down   [name]    migrate down till given migration'
	, '     up     [name]    migrate up till given migration (the default command)'
	, '     create [title]   create a new migration file with optional [title]'
	, ''
].join('\n');

/**
 * Migration template.
 */

var template = [
	''
	, 'var mongodb = require(\'mongodb\');'
	, ''
	, 'exports.up = function(db, next){'
	, '    next();'
	, '};'
	, ''
	, 'exports.down = function(db, next){'
	, '    next();'
	, '};'
	, ''
].join('\n');

/**
 * require an argument
 * @returns {*}
 */
function required() {
	if (args.length) return args.shift();
	abort(arg + ' requires an argument');
}

/**
 * abort with a message
 * @param msg
 */
function abort(msg) {
	console.error('  %s', msg);
	process.exit(1);
}

// parse arguments
var arg;
while (args.length) {
	arg = args.shift();
	switch (arg) {
		case '-h':
		case '--help':
		case 'help':
			console.log(usage);
			process.exit();
			break;
		case '-c':
		case '--chdir':
			process.chdir(cwd = required());
			break;
		case '-cfg':
		case '-config':
			configFileName = required();
			break;
		case '-dbn':
		case '--dbPropName':
			dbProperty = required();
			break;
		default:
			if (options.command) {
				options.args.push(arg);
			} else {
				options.command = arg;
			}
	}
}

/**
 * Load migrations.
 * @param {String} direction
 * @param {Number} lastMigrationNum
 */
function migrations(direction, lastMigrationNum) {
	var isDirectionUp = direction === 'up';
	return fs.readdirSync('migrations').filter(function(file){
		var formatCorrect = file.match(/^\d+.*\.js$/),
			migrationNum = formatCorrect && parseInt(file.match(/^\d+/)[0], 10),
			isRunnable = formatCorrect && isDirectionUp ? migrationNum > lastMigrationNum : migrationNum <= lastMigrationNum;

		return formatCorrect && isRunnable;
	}).sort(function (a, b) {
		var aMigrationNum = parseInt(a.match(/^\d+/)[0], 10),
			bMigrationNum = parseInt(b.match(/^\d+/)[0], 10);

		if (aMigrationNum > bMigrationNum) {
			return isDirectionUp ? 1 : -1;
		}
		if (aMigrationNum < bMigrationNum) {
			return isDirectionUp ? -1 : 1;
		}

		return 0;
	}).map(function(file){
		return 'migrations/' + file;
	});
}

/**
 * Log a keyed message.
 */
function log(key, msg) {
	console.log('  \033[90m%s :\033[0m \033[36m%s\033[0m', key, msg);
}

/**
 * Slugify the given `str`.
 */
function slugify(str) {
	return str.replace(/\s+/g, '-');
}

// create ./migrations

try {
	fs.mkdirSync('migrations', 0774);
} catch (err) {
	// ignore
}

// commands

var commands = {
	/**
	 * up
	 */
	up: function(){
		performMigration('up');
	},

	/**
	 * down
	 */
	down: function(){
		performMigration('down');
	},

	/**
	 * create [title]
	 */
	create: function(){
		var migrations = fs.readdirSync('migrations').filter(function(file){
			return file.match(/^\d+/);
		}).map(function(file){
			return parseInt(file.match(/^(\d+)/)[1], 10);
		}).sort(function(a, b){
			return a - b;
		});

		var curr = pad((migrations.pop() || 0) + 1),
			title = slugify([].slice.call(arguments).join(' '));
		title = title ? curr + '-' + title : curr + '-';
		create(title);
	}
};

/**
 * Pad the given number.
 *
 * @param {Number} n
 * @return {String}
 */
function pad(n) {
	return Array(5 - n.toString().length).join('0') + n;
}

/**
 * Create a migration with the given `name`.
 *
 * @param {String} name
 */
function create(name) {
	var path = 'migrations/' + name + '.js';
	log('create', join(cwd, path));
	fs.writeFileSync(path, template);
}

/**
 * Perform a migration in the given `direction`.
 *
 * @param {String} direction
 */
function performMigration(direction) {
	var db = require('./lib/db');
	db.getConnection(require('./' + configFileName)[dbProperty], function (err, db) {
		var migrationCollection = db.migrationCollection,
			dbConnection = db.connection;
		if (err) {
			console.error('Error connecting to database');
			process.exit(1);
		}

		migrationCollection.find({}).sort({num: -11}).limit(1).toArray(function (err, migrationsRun) {
			if (err) {
				console.error('Error querying migration collection', err);
				process.exit(1);
			}

			var lastMigration = migrationsRun[0],
				lastMigrationNum = lastMigration ? lastMigration.num : 0;

			migrate({
				migrationTitle: 'migrations/.migrate',
				db: dbConnection,
				migrationCollection: migrationCollection
			});
			migrations(direction, lastMigrationNum).forEach(function(path){
				var mod = require(process.cwd() + '/' + path);
				migrate({
					num: parseInt(path.split('/')[1].match(/^(\d+)/)[0], 10),
					title: path,
					up: mod.up,
					down: mod.down});
			});

			var set = migrate();

			set.on('migration', function(migration, direction){
				log(direction, migration.title);
			});

			set.on('save', function(){
				log('migration', 'complete');
				process.exit();
			});

			set[direction](null, lastMigrationNum);
		});
	});
}

// invoke command

var command = options.command || 'up';
if (!(command in commands)) abort('unknown command "' + command + '"');
command = commands[command];
command.apply(this, options.args);
<commit_msg>Added ability to use mongo-migrate as a module and not just from command line<commit_after>/**
 * Arguments.
 */
var args = process.argv.slice(2);

/**
 * Module dependencies.
 */
var migrate = require('./lib/migrate'),
	path = require('path'),
	join = path.join,
	fs = require('fs');

/**
 * Option defaults.
 */
var options = { args: [] };

/**
 * Current working directory.
 */
var previousWorkingDirectory = process.cwd(),
	cwd;

var configFileName = 'default-config.json',
		dbProperty = 'mongoAppDb';

/**
 * Usage information.
 */
var usage = [
	''
	, '  Usage: migrate [options] [command]'
	, ''
	, '  Options:'
	, ''
	, '     -runmm, --runMongoMigrate   Run the migration from the command line',
	, '     -c, --chdir <path>    		change the working directory'
	, '     -cfg, --config <path> 		DB config file name'
	, '     -dbn, --dbPropName <string> Property name for database connection in config file'
	, ''
	, '  Commands:'
	, ''
	, '     down   [name]    migrate down till given migration'
	, '     up     [name]    migrate up till given migration (the default command)'
	, '     create [title]   create a new migration file with optional [title]'
	, ''
].join('\n');

/**
 * Migration template.
 */

var template = [
	''
	, 'var mongodb = require(\'mongodb\');'
	, ''
	, 'exports.up = function(db, next){'
	, '    next();'
	, '};'
	, ''
	, 'exports.down = function(db, next){'
	, '    next();'
	, '};'
	, ''
].join('\n');

/**
 * require an argument
 * @returns {*}
 */
function required() {
	if (args.length) return args.shift();
	abort(arg + ' requires an argument');
}

/**
 * abort with a message
 * @param msg
 */
function abort(msg) {
	console.error('  %s', msg);
	process.exit(1);
}

/**
 * Log a keyed message.
 */
function log(key, msg) {
	console.log('  \033[90m%s :\033[0m \033[36m%s\033[0m', key, msg);
}

/**
 * Slugify the given `str`.
 */
function slugify(str) {
	return str.replace(/\s+/g, '-');
}

/**
 * Pad the given number.
 *
 * @param {Number} n
 * @return {String}
 */
function pad(n) {
	return Array(5 - n.toString().length).join('0') + n;
}

function runMongoMigrate() {
	/**
	 * Load migrations.
	 * @param {String} direction
	 * @param {Number} lastMigrationNum
	 */
	function migrations(direction, lastMigrationNum) {
		var isDirectionUp = direction === 'up';
		return fs.readdirSync('migrations').filter(function(file){
			var formatCorrect = file.match(/^\d+.*\.js$/),
					migrationNum = formatCorrect && parseInt(file.match(/^\d+/)[0], 10),
					isRunnable = formatCorrect && isDirectionUp ? migrationNum > lastMigrationNum : migrationNum <= lastMigrationNum;

			return formatCorrect && isRunnable;
		}).sort(function (a, b) {
					var aMigrationNum = parseInt(a.match(/^\d+/)[0], 10),
							bMigrationNum = parseInt(b.match(/^\d+/)[0], 10);

					if (aMigrationNum > bMigrationNum) {
						return isDirectionUp ? 1 : -1;
					}
					if (aMigrationNum < bMigrationNum) {
						return isDirectionUp ? -1 : 1;
					}

					return 0;
				}).map(function(file){
					return 'migrations/' + file;
				});
	}

	// create ./migrations

	try {
		fs.mkdirSync('migrations', 0774);
	} catch (err) {
		// ignore
	}

	// commands

	var commands = {
		/**
		 * up
		 */
		up: function(){
			performMigration('up');
		},

		/**
		 * down
		 */
		down: function(){
			performMigration('down');
		},

		/**
		 * create [title]
		 */
		create: function(){
			var migrations = fs.readdirSync('migrations').filter(function(file){
				return file.match(/^\d+/);
			}).map(function(file){
						return parseInt(file.match(/^(\d+)/)[1], 10);
					}).sort(function(a, b){
						return a - b;
					});

			var curr = pad((migrations.pop() || 0) + 1),
					title = slugify([].slice.call(arguments).join(' '));
			title = title ? curr + '-' + title : curr + '-';
			create(title);
		}
	};

	/**
	 * Create a migration with the given `name`.
	 *
	 * @param {String} name
	 */
	function create(name) {
		var path = 'migrations/' + name + '.js';
		log('create', join(cwd, path));
		fs.writeFileSync(path, template);
	}

	/**
	 * Perform a migration in the given `direction`.
	 *
	 * @param {String} direction
	 */
	function performMigration(direction) {
		var db = require('./lib/db');
		db.getConnection(require('./' + configFileName)[dbProperty], function (err, db) {
			var migrationCollection = db.migrationCollection,
					dbConnection = db.connection;
			if (err) {
				console.error('Error connecting to database');
				process.exit(1);
			}

			migrationCollection.find({}).sort({num: -11}).limit(1).toArray(function (err, migrationsRun) {
				if (err) {
					console.error('Error querying migration collection', err);
					process.exit(1);
				}

				var lastMigration = migrationsRun[0],
					lastMigrationNum = lastMigration ? lastMigration.num : 0;

				migrate({
					migrationTitle: 'migrations/.migrate',
					db: dbConnection,
					migrationCollection: migrationCollection
				});
				migrations(direction, lastMigrationNum).forEach(function(path){
					var mod = require(process.cwd() + '/' + path);
					migrate({
						num: parseInt(path.split('/')[1].match(/^(\d+)/)[0], 10),
						title: path,
						up: mod.up,
						down: mod.down});
				});

				var set = migrate();

				set.on('migration', function(migration, direction){
					log(direction, migration.title);
				});

				set.on('save', function(){
					log('migration', 'complete');
					process.chdir(previousWorkingDirectory);
					process.exit();
				});

				set[direction](null, lastMigrationNum);
			});
		});
	}

	// invoke command

	var command = options.command || 'up';
	if (!(command in commands)) abort('unknown command "' + command + '"');
	command = commands[command];
	command.apply(this, options.args);
}

function chdir(dir) {
	cwd = dir;
	process.chdir(cwd = dir);
}

function setConfigFilename(filename) {
	configFileName = filename;
}

function setConfigFileProperty(propertyName) {
	dbProperty = propertyName;
}

var runmmIdx = args.indexOf('-runmm'),
	runMongoMigrateIdx = args.indexOf('--runMongoMigrate');
if (runmmIdx > -1 || runMongoMigrateIdx > -1) {
	args.splice(runmmIdx > -1 ? runmmIdx : runMongoMigrateIdx, 1);

	// parse arguments
	var arg;
	while (args.length) {
		arg = args.shift();
		switch (arg) {
			case '-h':
			case '--help':
			case 'help':
				console.log(usage);
				process.exit();
				break;
			case '-c':
			case '--chdir':
				chdir(required());
				break;
			case '-cfg':
			case '-config':
				setConfigFilename(required());
				break;
			case '-dbn':
			case '--dbPropName':
				setConfigFileProperty(require());
				break;
			default:
				if (options.command) {
					options.args.push(arg);
				} else {
					options.command = arg;
				}
		}
	}

	runMongoMigrate();
} else {
	module.exports = {
		run: runMongoMigrate,
		changeWorkingDirectory: chdir,
		setConfigFilename: setConfigFilename,
		setConfigFileProp: setConfigFileProperty
	};
}<|endoftext|>


<commit_before>/* eslint-env node */
'use strict';

module.exports = {
  name: 'ember-cli-chart',
  included: function(app, parentAddon) {
    this._super.included.apply(this, arguments);

    var target = (parentAddon || app);
    if (target.app) {
      target = target.app;
    }

    var bowerDir = target.bowerDirectory;
    app.import(bowerDir + '/chartjs/dist/Chart.js');
  }
};
<commit_msg>Use the find host method<commit_after>/* eslint-env node */
'use strict';

module.exports = {
  name: 'ember-cli-chart',
  included: function() {
    this._super.included.apply(this, arguments);

    var app;

    // If the addon has the _findHost() method (in ember-cli >= 2.7.0), we'll just
    // use that.
    if (typeof this._findHost === 'function') {
      app = this._findHost();
    } else {
      // Otherwise, we'll use this implementation borrowed from the _findHost()
      // method in ember-cli.
      var current = this;
      do {
        app = current.app || app;
      } while (current.parent.parent && (current = current.parent));
    }

    app.import(app.bowerDirectory + '/chartjs/dist/Chart.js');
  }
};
<|endoftext|>


<commit_before>/**
 * This module unifies handling of mouse whee event across different browsers
 *
 * See https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel?redirectlocale=en-US&redirectslug=DOM%2FMozilla_event_reference%2Fwheel
 * for more details
 *
 * Usage:
 *  var addWheelListener = require('wheel');
 *  addWheelListener(domElement, function (e) {
 *    // mouse wheel event
 *  });
 */
module.exports = addWheelListener;

var prefix = "", _addEventListener, onwheel, support;

// detect event model
if ( window.addEventListener ) {
    _addEventListener = "addEventListener";
} else {
    _addEventListener = "attachEvent";
    prefix = "on";
}

// detect available wheel event
support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
          document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
          "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox

function addWheelListener( elem, callback, useCapture ) {
    _addWheelListener( elem, support, callback, useCapture );

    // handle MozMousePixelScroll in older Firefox
    if( support == "DOMMouseScroll" ) {
        _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture );
    }
};

function _addWheelListener( elem, eventName, callback, useCapture ) {
  elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) {
    !originalEvent && ( originalEvent = window.event );

    // create a normalized event object
    var event = {
      // keep a ref to the original event object
      originalEvent: originalEvent,
      target: originalEvent.target || originalEvent.srcElement,
      type: "wheel",
      deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1,
      deltaX: 0,
      delatZ: 0,
      preventDefault: function() {
        originalEvent.preventDefault ?
            originalEvent.preventDefault() :
            originalEvent.returnValue = false;
      },
      stopPropagation: function() {
        if(originalEvent.stopPropagation)
          originalEvent.stopPropagation();
      }
    };

    // calculate deltaY (and deltaX) according to the event
    if ( support == "mousewheel" ) {
      event.deltaY = - 1/40 * originalEvent.wheelDelta;
      // Webkit also support wheelDeltaX
      originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX );
    } else {
      event.deltaY = originalEvent.detail;
    }

    // it's time to fire the callback
    return callback( event );

  }, useCapture || false );
}
<commit_msg>Forward stopImmediatePropagation in normalized event object.<commit_after>/**
 * This module unifies handling of mouse whee event across different browsers
 *
 * See https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel?redirectlocale=en-US&redirectslug=DOM%2FMozilla_event_reference%2Fwheel
 * for more details
 *
 * Usage:
 *  var addWheelListener = require('wheel');
 *  addWheelListener(domElement, function (e) {
 *    // mouse wheel event
 *  });
 */
module.exports = addWheelListener;

var prefix = "", _addEventListener, onwheel, support;

// detect event model
if ( window.addEventListener ) {
    _addEventListener = "addEventListener";
} else {
    _addEventListener = "attachEvent";
    prefix = "on";
}

// detect available wheel event
support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
          document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
          "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox

function addWheelListener( elem, callback, useCapture ) {
    _addWheelListener( elem, support, callback, useCapture );

    // handle MozMousePixelScroll in older Firefox
    if( support == "DOMMouseScroll" ) {
        _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture );
    }
};

function _addWheelListener( elem, eventName, callback, useCapture ) {
  elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) {
    !originalEvent && ( originalEvent = window.event );

    // create a normalized event object
    var event = {
      // keep a ref to the original event object
      originalEvent: originalEvent,
      target: originalEvent.target || originalEvent.srcElement,
      type: "wheel",
      deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1,
      deltaX: 0,
      delatZ: 0,
      preventDefault: function() {
        originalEvent.preventDefault ?
            originalEvent.preventDefault() :
            originalEvent.returnValue = false;
      },
      stopPropagation: function() {
        if(originalEvent.stopPropagation)
          originalEvent.stopPropagation();
      },
      stopImmediatePropagation: function() {
        if(originalEvent.stopImmediatePropagation)
          originalEvent.stopImmediatePropagation();
      }
    };

    // calculate deltaY (and deltaX) according to the event
    if ( support == "mousewheel" ) {
      event.deltaY = - 1/40 * originalEvent.wheelDelta;
      // Webkit also support wheelDeltaX
      originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX );
    } else {
      event.deltaY = originalEvent.detail;
    }

    // it's time to fire the callback
    return callback( event );

  }, useCapture || false );
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*jshint camelcase: false*/

const tty = require('tty').isatty(2);

const colors = {
  debug: 6,
  info: 2,
  warn: 3,
  error: 1
};

var dbug;

var isColored = (function envDebugColor(colorString) {
  try {
    return colorString ? !!JSON.parse(colorString) : tty;
  } catch (ex) {
    return false;
  }
})(process.env.DEBUG_COLORS);

function parseDEBUG(debugStr) {
  var names = [];
  var skips = [];
  debugStr
    .split(/[\s,]+/)
    .forEach(function(name){
      name = name.replace('*', '.*?');
      if (name[0] === '-') {
        skips.push(new RegExp('^' + name.substr(1) + '$'));
      } else {
        names.push(new RegExp('^' + name + '(:.+)?$'));
      }
    });

  return {
    names: names,
    skips: skips
  };
}

var env = process.env.DEBUG || '';
var parsedEnv = parseDEBUG(env);

function isEnabled(name) {
  var env = parsedEnv;
  var match = env.skips.some(function(re){
    return re.test(name);
  });
  if (match) {
    return false;
  }

  match = env.names.some(function(re){
    return re.test(name);
  });

  if (!match) {
    return false;
  }
  return true;
}

function colored(name, level, args) {
  var c = colors[level];
  args[0] = [
    '  ',
    name + ':',
    '\x1b[9' + c + 'm' + level.toUpperCase() + '\x1b[39m ',
    '\x1b[90m' + args[0]
  ].join('');
  args.push('\x1b[39m');
  console[level === 'debug' ? 'log' : level].apply(console, args);
}

function plain(name, level, args) {
  args[0] = new Date().toUTCString()
    + ' ' + name + ':' + level.toUpperCase() + ' ' + args[0];
  console[level === 'debug' ? 'log' : level].apply(console, args);
}

function disabled() {}

var log = isColored ? colored : plain;

colored.__dbug = plain.__dbug = disabled.__dbug = true;

var dbuggers = {};
function define(dbugger, name) {
  function logAt(level) {
    return function dbug() {
      var args = new Array(arguments.length);
      var i = args.length;
      while (i--) {
        args[i] = arguments[i];
      }
      log(name, level, args);
    };
  }
  Object.defineProperty(dbugger, 'enabled', {
    configurable: true,
    get: function getEnabled() {
      return dbugger.__enabled;
    },
    set: function setEnabled(val) {
      dbugger.__enabled = !!val;
      if (dbugger.__enabled) {
        dbugger.debug = logAt('debug');
        dbugger.info = logAt('info');
        dbugger.warn = logAt('warn');
        dbugger.error = logAt('error');
        dbugger.log = dbugger.debug;
      } else {
        dbugger.debug =
        dbugger.info =
        dbugger.warn =
        dbugger.error =
        dbugger.log =
          disabled;
      }
    }
  });

  Object.defineProperty(dbugger, 'colored', {
    configurable: true,
    get: function getColored() {
      return dbug.colored;
    },
    set: function setColored(val) {
      dbug.colored = val;
    }
  });

  dbugger.enabled = isEnabled(name);

  dbuggers[name] = dbugger;
}

function dbugger(name) {
  if (dbuggers[name]) {
    return dbuggers[name];
  }
  var dbugger = function dbug() {
    if (dbugger.__enabled) {
      dbugger.debug.apply(this, arguments);
    }
  };

  define(dbugger, name);

  return dbugger;
}

function enableDbuggers() {
  for (var name in dbuggers) {
    if (isEnabled(name)) {
      dbuggers[name].enabled = true;
    } else {
      dbuggers[name].enabled = false;
    }
  }
}

dbug = function dbug(name) {
  return dbugger(name);
};

Object.defineProperty(dbug, 'colored', {
  get: function getColored() {
    return isColored;
  },
  set: function setColored(val) {
    isColored = !!val;
    if (!log.__dbug) {
      return;
    }
    if (isColored) {
      log = colored;
    } else {
      log = plain;
    }
  }
});

Object.defineProperty(dbug, 'env', {
  get: function getEnv() {
    return env;
  },
  set: function setEnv(val) {
    env = String(val || '');
    parsedEnv = parseDEBUG(env);
    enableDbuggers();
  }
});

// woah. this is a private API. don't rely on it. i can blow it up
// any time. kablamo!
Object.defineProperty(dbug, '__log', {
  enumerable: false,
  get: function() {},
  set: function setLog(val) {
    log = val;
  }
});

dbug.version = require('./package.json').version;

function decimalVersion(verStr) {
  var parts = verStr.split('.');
  var v = 0;
  try {
    v = parseInt(parts[0], 10) * 10000
      + parseInt(parts[1], 10) * 100;
    if (parts[2].indexOf('-') !== -1) {
      parts = parts[2].split('-');
      v += parseInt(parts[0], 10);
      v += parseInt(parts[1], 16) / 1000; // a1, b1, b2, etc
    } else {
      v += parseInt(parts[2], 10);
    }
  } catch (e) {
    return -1;
  }
}

// old way, doesn't use properties, so i can't slurp that well
if (global.__dbug__1) {
  global.__dbug__1.__log = function __dbug() {
    log.apply(null, arguments);
  };
}
global.__dbug__1 = dbug;

// merge different versions to use the same dbug instance
if (global.__dbug__) {
  if (decimalVersion(dbug) > decimalVersion(global.__dbug__)) {
    dbuggers = global.__dbug__.__dbuggers;
    for (var k in dbuggers) {
      dbuggers[k] = define(dbuggers[k], k);
    }
    global.__dbug__ = dbug;
  }
} else {
  global.__dbug__ = dbug;
}
module.exports = global.__dbug__;
<commit_msg>allow getting __log function<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*jshint camelcase: false*/

const tty = require('tty').isatty(2);

const colors = {
  debug: 6,
  info: 2,
  warn: 3,
  error: 1
};

var dbug;

var isColored = (function envDebugColor(colorString) {
  try {
    return colorString ? !!JSON.parse(colorString) : tty;
  } catch (ex) {
    return false;
  }
})(process.env.DEBUG_COLORS);

function parseDEBUG(debugStr) {
  var names = [];
  var skips = [];
  debugStr
    .split(/[\s,]+/)
    .forEach(function(name){
      name = name.replace('*', '.*?');
      if (name[0] === '-') {
        skips.push(new RegExp('^' + name.substr(1) + '$'));
      } else {
        names.push(new RegExp('^' + name + '(:.+)?$'));
      }
    });

  return {
    names: names,
    skips: skips
  };
}

var env = process.env.DEBUG || '';
var parsedEnv = parseDEBUG(env);

function isEnabled(name) {
  var env = parsedEnv;
  var match = env.skips.some(function(re){
    return re.test(name);
  });
  if (match) {
    return false;
  }

  match = env.names.some(function(re){
    return re.test(name);
  });

  if (!match) {
    return false;
  }
  return true;
}

function colored(name, level, args) {
  var c = colors[level];
  args[0] = [
    '  ',
    name + ':',
    '\x1b[9' + c + 'm' + level.toUpperCase() + '\x1b[39m ',
    '\x1b[90m' + args[0]
  ].join('');
  args.push('\x1b[39m');
  console[level === 'debug' ? 'log' : level].apply(console, args);
}

function plain(name, level, args) {
  args[0] = new Date().toUTCString()
    + ' ' + name + ':' + level.toUpperCase() + ' ' + args[0];
  console[level === 'debug' ? 'log' : level].apply(console, args);
}

function disabled() {}

var log = isColored ? colored : plain;

colored.__dbug = plain.__dbug = disabled.__dbug = true;

var dbuggers = {};
function define(dbugger, name) {
  function logAt(level) {
    return function dbug() {
      var args = new Array(arguments.length);
      var i = args.length;
      while (i--) {
        args[i] = arguments[i];
      }
      log(name, level, args);
    };
  }
  Object.defineProperty(dbugger, 'enabled', {
    configurable: true,
    get: function getEnabled() {
      return dbugger.__enabled;
    },
    set: function setEnabled(val) {
      dbugger.__enabled = !!val;
      if (dbugger.__enabled) {
        dbugger.debug = logAt('debug');
        dbugger.info = logAt('info');
        dbugger.warn = logAt('warn');
        dbugger.error = logAt('error');
        dbugger.log = dbugger.debug;
      } else {
        dbugger.debug =
        dbugger.info =
        dbugger.warn =
        dbugger.error =
        dbugger.log =
          disabled;
      }
    }
  });

  Object.defineProperty(dbugger, 'colored', {
    configurable: true,
    get: function getColored() {
      return dbug.colored;
    },
    set: function setColored(val) {
      dbug.colored = val;
    }
  });

  dbugger.enabled = isEnabled(name);

  dbuggers[name] = dbugger;
}

function dbugger(name) {
  if (dbuggers[name]) {
    return dbuggers[name];
  }
  var dbugger = function dbug() {
    if (dbugger.__enabled) {
      dbugger.debug.apply(this, arguments);
    }
  };

  define(dbugger, name);

  return dbugger;
}

function enableDbuggers() {
  for (var name in dbuggers) {
    if (isEnabled(name)) {
      dbuggers[name].enabled = true;
    } else {
      dbuggers[name].enabled = false;
    }
  }
}

dbug = function dbug(name) {
  return dbugger(name);
};

Object.defineProperty(dbug, 'colored', {
  get: function getColored() {
    return isColored;
  },
  set: function setColored(val) {
    isColored = !!val;
    if (!log.__dbug) {
      return;
    }
    if (isColored) {
      log = colored;
    } else {
      log = plain;
    }
  }
});

Object.defineProperty(dbug, 'env', {
  get: function getEnv() {
    return env;
  },
  set: function setEnv(val) {
    env = String(val || '');
    parsedEnv = parseDEBUG(env);
    enableDbuggers();
  }
});

// woah. this is a private API. don't rely on it. i can blow it up
// any time. kablamo!
Object.defineProperty(dbug, '__log', {
  enumerable: false,
  get: function() {
    return log;
  },
  set: function setLog(val) {
    log = val;
  }
});

dbug.version = require('./package.json').version;

function decimalVersion(verStr) {
  var parts = verStr.split('.');
  var v = 0;
  try {
    v = parseInt(parts[0], 10) * 10000
      + parseInt(parts[1], 10) * 100;
    if (parts[2].indexOf('-') !== -1) {
      parts = parts[2].split('-');
      v += parseInt(parts[0], 10);
      v += parseInt(parts[1], 16) / 1000; // a1, b1, b2, etc
    } else {
      v += parseInt(parts[2], 10);
    }
  } catch (e) {
    return -1;
  }
}

// old way, doesn't use properties, so i can't slurp that well
if (global.__dbug__1) {
  global.__dbug__1.__log = function __dbug() {
    log.apply(null, arguments);
  };
}
global.__dbug__1 = dbug;

// merge different versions to use the same dbug instance
if (global.__dbug__) {
  if (decimalVersion(dbug) > decimalVersion(global.__dbug__)) {
    dbuggers = global.__dbug__.__dbuggers;
    for (var k in dbuggers) {
      dbuggers[k] = define(dbuggers[k], k);
    }
    global.__dbug__ = dbug;
  }
} else {
  global.__dbug__ = dbug;
}
module.exports = global.__dbug__;
<|endoftext|>


<commit_before>var net = require('net')
var es  = require('event-stream')

exports.createServer = function (lookup, modify, errback) {
  return net.createServer(exports.handler(lookup, modify, errback))
}

exports.createStickyServer = function (lookup, errback) {
  return net.createServer(exports.handler(function (req) {
    var cookie, sticky, m
    if(cookie = req.headers.Cookie) {
      if(m = /sticky=([\w\d]+)[;$]/.exec(cookie))
       sticky = m[1]
    }
    return lookup(req, sticky)
  }, function (res, dest) {
    var setCookie
    if(setCookie = res.headers['Set-Cookie']) {
      if(!/sticky=[\w\d]+[;$]/.test(setCookie))
       res.headers['Set-Cookie'] = 'sticky='+dest.sticky+'; ' + setCookie
    }
  }, errback))
}

/*
  a sync through stream that matches and replaces only ONCE
  before a pattern.
  the intension is to match the empty line that marks the
  end of the first header.
*/

var replace =
exports.replace = function (match, replace) {
  var soFar = ''
  var matched = false
  return es.through(function (data) {
    if(!matched) {
      soFar += data
      matched = match.exec(soFar)
      if(matched) {
        this.emit('data', replace(soFar.substring(0, matched.index)))
        this.emit('data', soFar.substring(matched.index))
      }
    } else
      this.emit('data', data)
  }, function () {
    if(!matched && soFar)
      this.emit('data', soFar)
    this.emit('end')
  })
}

var parse =
exports.parse = function (header) {
  var lines = header.split('\r\n')
    , first = lines.shift(), headers = {}
  lines.forEach(function (l) {
    var i = l.indexOf(':')
    headers[l.substring(0, i).trim()] = l.substring(i + 1).trim()
  })
  return {first: first, headers: headers}
}

var stringify = exports.stringify = function (header) {
  var r = [header.first]
  for(var h in header.headers)
    r.push(h + ': ' + header.headers[h])
  return r.join('\r\n')
}

exports.handler = function (lookup, modify, errback) {
  return function (con) {
    var first
    con.pipe(first = replace(/\r\n\r\n/, function (header) {
      var req, dest = lookup(req = parse(header))
      var outs =
        first.pipe(
          net.connect(dest.port, dest.host)
          .on('error', function (err) { //will this be gc'd?
            if(errback) errback(err, dest)
            else console.error(err)
          })
        )
      if(modify)
        outs = outs.pipe(replace(/\r\n\r\n/, function (header) {
          var res = parse(header)
          modify(res, dest)
          return stringify(res)
        }))

        outs.pipe(con)
        return stringify(req)
    }))
  }
}
<commit_msg>implement support for unix sockets - 1 line!<commit_after>var net = require('net')
var es  = require('event-stream')

exports.createServer = function (lookup, modify, errback) {
  return net.createServer(exports.handler(lookup, modify, errback))
}

exports.createStickyServer = function (lookup, errback) {
  return net.createServer(exports.handler(function (req) {
    var cookie, sticky, m
    if(cookie = req.headers.Cookie) {
      if(m = /sticky=([\w\d]+)[;$]/.exec(cookie))
       sticky = m[1]
    }
    return lookup(req, sticky)
  }, function (res, dest) {
    var setCookie
    if(setCookie = res.headers['Set-Cookie']) {
      if(!/sticky=[\w\d]+[;$]/.test(setCookie))
       res.headers['Set-Cookie'] = 'sticky='+dest.sticky+'; ' + setCookie
    }
  }, errback))
}

/*
  a sync through stream that matches and replaces only ONCE
  before a pattern.
  the intension is to match the empty line that marks the
  end of the first header.
*/

var replace =
exports.replace = function (match, replace) {
  var soFar = ''
  var matched = false
  return es.through(function (data) {
    if(!matched) {
      soFar += data
      matched = match.exec(soFar)
      if(matched) {
        this.emit('data', replace(soFar.substring(0, matched.index)))
        this.emit('data', soFar.substring(matched.index))
      }
    } else
      this.emit('data', data)
  }, function () {
    if(!matched && soFar)
      this.emit('data', soFar)
    this.emit('end')
  })
}

var parse =
exports.parse = function (header) {
  var lines = header.split('\r\n')
    , first = lines.shift(), headers = {}
  lines.forEach(function (l) {
    var i = l.indexOf(':')
    headers[l.substring(0, i).trim()] = l.substring(i + 1).trim()
  })
  return {first: first, headers: headers}
}

var stringify = exports.stringify = function (header) {
  var r = [header.first]
  for(var h in header.headers)
    r.push(h + ': ' + header.headers[h])
  return r.join('\r\n')
}

exports.handler = function (lookup, modify, errback) {
  return function (con) {
    var first
    con.pipe(first = replace(/\r\n\r\n/, function (header) {
      var req, dest = lookup(req = parse(header))
      var outs =
        first.pipe(
          dest.path ? net.connect(dest.path) : net.connect(dest.port, dest.host)
          .on('error', function (err) { //will this be gc'd?
            if(errback) errback(err, dest)
            else console.error(err)
          })
        )
      if(modify)
        outs = outs.pipe(replace(/\r\n\r\n/, function (header) {
          var res = parse(header)
          modify(res, dest)
          return stringify(res)
        }))

        outs.pipe(con)
        return stringify(req)
    }))
  }
}
<|endoftext|>


<commit_before>'use strict'

var isDirectory = require('is-directory')

function pullFromQueue (options, callback) {
  if (!options) {
    return callback(new Error('Missing required input: options object'), null)
  }

  if (!options.key) {
    return callback(new Error('Missing required input: options.key'), null)
  }

  if (!options.payload) {
    return callback(new Error('Missing required input: options.payload'), null)
  }

  if (!options.jobFolderPath) {
    return callback(new Error('Missing required input: options.jobFolderPath'), null)
  }

  if (!isDirectory.sync(options.jobFolderPath)) {
    return callback(new Error('Invalid input: options.jobFolderPath is not a directory'), null)
  }

  if (!options.queueNextUrl) {
    return callback(new Error('Missing required input: options.queueNextUrl'), null)
  }

  if (!options.deleteFromQueueUrl) {
    return callback(new Error('Missing required input: options.deleteFromQueueUrl'), null)
  }

  if (!options.statusMessage) {
    return callback(new Error('Missing required input: options.statusMessage'), null)
  }

  var fs = require('fs')
  var Wreck = require('wreck')
  var generateToken = require('./lib/generate-token')
  var token = generateToken({key: options.key, payload: options.payload})
  var wreckOptions = {
    json: true,
    headers: {
      Authorization: token
    }
  }
  var job
  var CALLBACK_STATUS_URL

  function handleStatusUpdates (error, response, payload) {
    if (error) {
      return callback(error, null)
    } else {
      return callback(null, {message: 'Job ' + job._id + ' downloaded. Status updated.'})
    }
  }

  function handleDelete (error, response, payload) {
    if (error) {
      return callback(error, null)
    } else {
      if (CALLBACK_STATUS_URL) {
        wreckOptions.payload = JSON.stringify({status: options.statusMessage})
        Wreck.post(CALLBACK_STATUS_URL, wreckOptions, handleStatusUpdates)
      } else {
        return callback(null, {message: 'Job ' + job._id + ' downloaded.'})
      }
    }
  }

  function handleWrite (error) {
    if (error) {
      return callback(error, null)
    } else {
      Wreck.delete(options.deleteFromQueueUrl + '/' + job._id, wreckOptions, handleDelete)
    }
  }

  function handleNext (error, response, payload) {
    if (error) {
      return callback(error, null)
    }  else if (response.statusCode !== 200) {
      return callback(new Error('Unexpected statuscode: ' + response.statusCode), null)
    } else {
      if (payload && payload.length > 0) {
        job = payload[0]
        if (job.CALLBACK_STATUS_URL) {
          CALLBACK_STATUS_URL = job.CALLBACK_STATUS_URL + '/' + job._id
          job.CALLBACK_STATUS_URL = CALLBACK_STATUS_URL
        }
        fs.writeFile(options.jobFolderPath + '/' + job._id + '.json', JSON.stringify(job, null, 2), 'utf-8', handleWrite)
      } else {
        return callback(null, {message: 'No jobs in queue'})
      }
    }
  }

  Wreck.get(options.queueNextUrl, wreckOptions, handleNext)
}

module.exports = pullFromQueue
<commit_msg>Updates code style<commit_after>'use strict'

var isDirectory = require('is-directory')

function pullFromQueue (options, callback) {
  if (!options) {
    return callback(new Error('Missing required input: options object'), null)
  }

  if (!options.key) {
    return callback(new Error('Missing required input: options.key'), null)
  }

  if (!options.payload) {
    return callback(new Error('Missing required input: options.payload'), null)
  }

  if (!options.jobFolderPath) {
    return callback(new Error('Missing required input: options.jobFolderPath'), null)
  }

  if (!isDirectory.sync(options.jobFolderPath)) {
    return callback(new Error('Invalid input: options.jobFolderPath is not a directory'), null)
  }

  if (!options.queueNextUrl) {
    return callback(new Error('Missing required input: options.queueNextUrl'), null)
  }

  if (!options.deleteFromQueueUrl) {
    return callback(new Error('Missing required input: options.deleteFromQueueUrl'), null)
  }

  if (!options.statusMessage) {
    return callback(new Error('Missing required input: options.statusMessage'), null)
  }

  var fs = require('fs')
  var Wreck = require('wreck')
  var generateToken = require('./lib/generate-token')
  var token = generateToken({key: options.key, payload: options.payload})
  var wreckOptions = {
    json: true,
    headers: {
      Authorization: token
    }
  }
  var job
  var CALLBACK_STATUS_URL

  function handleStatusUpdates (error, response, payload) {
    if (error) {
      return callback(error, null)
    } else {
      return callback(null, {message: 'Job ' + job._id + ' downloaded. Status updated.'})
    }
  }

  function handleDelete (error, response, payload) {
    if (error) {
      return callback(error, null)
    } else {
      if (CALLBACK_STATUS_URL) {
        wreckOptions.payload = JSON.stringify({status: options.statusMessage})
        Wreck.post(CALLBACK_STATUS_URL, wreckOptions, handleStatusUpdates)
      } else {
        return callback(null, {message: 'Job ' + job._id + ' downloaded.'})
      }
    }
  }

  function handleWrite (error) {
    if (error) {
      return callback(error, null)
    } else {
      Wreck.delete(options.deleteFromQueueUrl + '/' + job._id, wreckOptions, handleDelete)
    }
  }

  function handleNext (error, response, payload) {
    if (error) {
      return callback(error, null)
    } else if (response.statusCode !== 200) {
      return callback(new Error('Unexpected statuscode: ' + response.statusCode), null)
    } else {
      if (payload && payload.length > 0) {
        job = payload[0]
        if (job.CALLBACK_STATUS_URL) {
          CALLBACK_STATUS_URL = job.CALLBACK_STATUS_URL + '/' + job._id
          job.CALLBACK_STATUS_URL = CALLBACK_STATUS_URL
        }
        fs.writeFile(options.jobFolderPath + '/' + job._id + '.json', JSON.stringify(job, null, 2), 'utf-8', handleWrite)
      } else {
        return callback(null, {message: 'No jobs in queue'})
      }
    }
  }

  Wreck.get(options.queueNextUrl, wreckOptions, handleNext)
}

module.exports = pullFromQueue
<|endoftext|>


<commit_before>/*
 * Iris
 * (c) 2016+ Buenos Aires City Government
 * License: https://opensource.org/licenses/MIT
 *
 */

'use strict';

const Flow = require('./lib/Flow');
const BaseDock = require('./lib/bases/Dock');
const BaseHandler = require('./lib/bases/Handler');
const BaseHook = require('./lib/bases/Hook');
const dispatcher = require('./lib/Dispatcher');
const validator = require('propchecker');
const shortid = require('shortid');
const ora = require('ora');
const chalk = require('chalk');

class Iris {
    constructor() {
        this._config = {};
        this._flows = [];
        this._modules = [];
    }

    get flows() {
        return this._flows;
    }

    get config() {
        return this._config;
    }

    get modules() {
        return this._modules;
    }

    get Dock() {
        return BaseDock;
    }

    get Handler() {
        return BaseHandler;
    }

    get Hook() {
        return BaseHook;
    }

    set config(options) {
        const config = options;

        this._checkConfig(config);
        config.logLevel = config.logLevel || 'info';

        if (config.events) {
            config.events.dispatcher = config.events.dispatcher && true;
            config.events.docks = config.events.docks && true;
            config.events.handlers = config.events.handlers && true;
            config.events.hooks = config.events.hooks && true;
        }

        if (config.vantage) {
            config.vantage.enabled = config.vantage.enabled || false;
        }

        Object.assign(this._config, options);
    }

    set(key, value) {
        this._config[key] = value;
    }

    flow(name, options) {
        const config = options;

        this._spinner = ora(`Validating '${name}'`).start();
        this._checkFlowOptions(config);
        config.inputHooks = config.inputHooks || [];
        config.outputHooks = config.outputHooks || [];

        const flow = new Flow(name, config);

        this._validateDocks(flow);
        this._validateHooks(flow);
        this._validateHandler(flow);
        this._spinner.succeed();
        this._flows.push(flow);
    }

    _checkConfig(config) {
        const schema = {
            threads: validator.isNumber,
            logLevel: validator.isString,
            events: {
                dispatcher: validator.isBoolean,
                docks: validator.isBoolean,
                handlers: validator.isBoolean,
                hooks: validator.isBoolean
            },
            vantage: {
                enabled: validator.isBoolean,
                port: validator.isNumber
            }
        };

        validator.validate(config, schema, this._handleErrors);
    }

    _checkFlowOptions(options) {
        const flowSchema = {
            tag: [validator.isRequired, validator.isString],
            docks: [validator.isRequired, validator.isArray],
            handler: validator.isRequired,
            inputHooks: validator.isArray,
            outputHooks: validator.isArray
        };

        validator.validate(options, flowSchema, this._handleErrors);
    }

    _validateDocks(flow) {
        const dockSchema = {
            name: [validator.isRequired, validator.isString],
            protocol: [validator.isRequired, validator.isString],
            path: [validator.isRequired, validator.isString],
            validate: [validator.isRequired, validator.isFunction],
            parse: [validator.isRequired, validator.isFunction],
            process: [validator.isRequired, validator.isFunction],
            reply: [validator.isRequired, validator.isFunction],
            encode: [validator.isRequired, validator.isFunction],
            listen: [validator.isRequired, validator.isFunction],
            port: [validator.isRequired, validator.isNumber],
            send: [validator.isFunction]
        };

        flow.docks.forEach((dock) => {
            if (!dock.id) {
                validator.validate(dock, dockSchema, this._handleErrors);
                dock.id = shortid.generate();
                dock.config = Object.assign(dock.config, {
                    events: this._config.events.docks
                });
                this._modules.push(dock.path);
                this._startDock(dock);
            }
        }, this);
    }

    _validateHooks(flow) {
        const hookSchema = {
            name: [validator.isRequired, validator.isString],
            path: [validator.isRequired, validator.isString],
            process: [validator.isRequired, validator.isFunction],
            run: [validator.isRequired, validator.isFunction]
        };

        flow.inputHooks.forEach(function (hook) {
            if (!hook.validated) {
                validator.validate(hook, hookSchema, this._handleErrors);
                this._configureComponent(hook, this.config.events.hooks);
            }
        }, this);

        flow.outputHooks.forEach(function (hook) {
            if (!hook.validated) {
                validator.validate(hook, hookSchema, this._handleErrors);
                this._configureComponent(hook, this.config.events.hooks);
            }
        }, this);
    }

    _validateHandler(flow) {
        const handlerSchema = {
            name: [validator.isRequired, validator.isString],
            path: [validator.isRequired, validator.isString],
            process: [validator.isRequired, validator.isFunction],
            handle: [validator.isRequired, validator.isFunction]
        };

        if (!flow.handler.validated) {
            validator.validate(flow.handler, handlerSchema, this._handleErrors);
            this._configureComponent(flow.handler, this.config.events.handlers);
        }
    }

    _configureComponent(component, eventConfig) {
        component.validated = true;
        component.config = Object.assign(component.config, {
            events: eventConfig
        });
        this._modules.push(component.path);
    }

    _handleErrors(errors) {
        this._spinner.fail();

        errors.forEach(function (error) {
            console.error(chalk.red(error));
        }, this);

        process.exit(1);
    }

    _startDock(dock) {
        let id = shortid.generate();

        dock.id = id;
        dock.dispatcher = dispatcher;
        dock.listen(dock.config.port);
    }
}

module.exports = new Iris();<commit_msg>Revert "Cleanup"<commit_after>/*
 * Iris
 * (c) 2016+ Buenos Aires City Government
 * License: https://opensource.org/licenses/MIT
 *
 */

'use strict';

const Flow = require('./lib/Flow');
const BaseDock = require('./lib/bases/Dock');
const BaseHandler = require('./lib/bases/Handler');
const BaseHook = require('./lib/bases/Hook');
const dispatcher = require('./lib/Dispatcher');
const validator = require('propchecker');
const shortid = require('shortid');
const ora = require('ora');
const chalk = require('chalk');

class Iris {
    constructor() {
        this._config = {};
        this._flows = [];
        this._modules = [];
    }

    get flows() {
        return this._flows;
    }

    get config() {
        return this._config;
    }

    get modules() {
        return this._modules;
    }

    get Dock() {
        return BaseDock;
    }

    get Handler() {
        return BaseHandler;
    }

    get Hook() {
        return BaseHook;
    }

    set config(options) {
        const config = options;

        this._checkConfig(config);
        config.logLevel = config.logLevel || 'info';

        if (config.events) {
            config.events.dispatcher = config.events.dispatcher && true;
            config.events.docks = config.events.docks && true;
            config.events.handlers = config.events.handlers && true;
            config.events.hooks = config.events.hooks && true;
        }

        if (config.vantage) {
            config.vantage.enabled = config.vantage.enabled || false;
        }

        Object.assign(this._config, options);
    }

    set(key, value) {
        this._config[key] = value;
    }

    flow(name, options) {
        const config = options;

        this._spinner = ora(`Validating '${name}'`).start();
        this._checkFlowOptions(config);
        config.inputHooks = config.inputHooks || [];
        config.outputHooks = config.outputHooks || [];

        const flow = new Flow(name, config);

        this._validateDocks(flow, this._spinner);
        this._validateHooks(flow, this._spinner);
        this._validateHandler(flow, this._spinner);
        this._spinner.succeed();
        this._flows.push(flow);
    }

    _checkConfig(config) {
        const schema = {
            threads: validator.isNumber,
            logLevel: validator.isString,
            events: {
                dispatcher: validator.isBoolean,
                docks: validator.isBoolean,
                handlers: validator.isBoolean,
                hooks: validator.isBoolean
            },
            vantage: {
                enabled: validator.isBoolean,
                port: validator.isNumber
            }
        };

        validator.validate(config, schema, this._handleErrors);
    }

    _checkFlowOptions(options) {
        const flowSchema = {
            tag: [validator.isRequired, validator.isString],
            docks: [validator.isRequired, validator.isArray],
            handler: validator.isRequired,
            inputHooks: validator.isArray,
            outputHooks: validator.isArray
        };

        validator.validate(options, flowSchema, this._handleErrors);
    }

    _validateDocks(flow) {
        const dockSchema = {
            name: [validator.isRequired, validator.isString],
            protocol: [validator.isRequired, validator.isString],
            path: [validator.isRequired, validator.isString],
            validate: [validator.isRequired, validator.isFunction],
            parse: [validator.isRequired, validator.isFunction],
            process: [validator.isRequired, validator.isFunction],
            reply: [validator.isRequired, validator.isFunction],
            encode: [validator.isRequired, validator.isFunction],
            listen: [validator.isRequired, validator.isFunction],
            port: [validator.isRequired, validator.isNumber],
            send: [validator.isFunction]
        };

        flow.docks.forEach((dock) => {
            if (!dock.id) {
                validator.validate(dock, dockSchema, this._handleErrors);
                dock.id = shortid.generate();
                dock.config = Object.assign(dock.config, {
                    events: this._config.events.docks
                });
                this._modules.push(dock.path);
                this._startDock(dock);
            }
        }, this);
    }

    _validateHooks(flow) {
        const hookSchema = {
            name: [validator.isRequired, validator.isString],
            path: [validator.isRequired, validator.isString],
            process: [validator.isRequired, validator.isFunction],
            run: [validator.isRequired, validator.isFunction]
        };

        flow.inputHooks.forEach(function (hook) {
            if (!hook.validated) {
                validator.validate(hook, hookSchema, this._handleErrors);
                this._configureComponent(hook, this.config.events.hooks);
            }
        }, this);

        flow.outputHooks.forEach(function (hook) {
            if (!hook.validated) {
                validator.validate(hook, hookSchema, this._handleErrors);
                this._configureComponent(hook, this.config.events.hooks);
            }
        }, this);
    }

    _validateHandler(flow) {
        const handlerSchema = {
            name: [validator.isRequired, validator.isString],
            path: [validator.isRequired, validator.isString],
            process: [validator.isRequired, validator.isFunction],
            handle: [validator.isRequired, validator.isFunction]
        };

        if (!flow.handler.validated) {
            validator.validate(flow.handler, handlerSchema, this._handleErrors);
            this._configureComponent(flow.handler, this.config.events.handlers);
        }
    }

    _configureComponent(component, eventConfig) {
        component.validated = true;
        component.config = Object.assign(component.config, {
            events: eventConfig
        });
        this._modules.push(component.path);
    }

    _handleErrors(errors) {
        this._spinner.fail();

        errors.forEach(function (error) {
            console.error(chalk.red(error));
        }, this);

        process.exit(1);
    }

    _startDock(dock) {
        let id = shortid.generate();

        dock.id = id;
        dock.dispatcher = dispatcher;
        dock.listen(dock.config.port);
    }
}

module.exports = new Iris();<|endoftext|>


<commit_before>var request = require('request'),
    util = require("util"),
    EventEmitter = require("events").EventEmitter;

function BarryDonations(options) {
    EventEmitter.call(this);

    this.options = options;

    this.validate();
}

util.inherits(BarryDonations, EventEmitter);

BarryDonations.prototype.validate = function() {
    var self = this;

    var url = 'http://don.barrycarlyon.co.uk/api.php?method=validate' +
        '&username=' + this.options.username +
        '&password=' + this.options.password +
        '&version=' + this.options.version;

    request(url, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            var bodyJSON = JSON.parse(body);

            if (bodyJSON.status !== "ok") {
                console.error("[BARRY-DONATIONS] Failed to validate, API returned status: " + bodyJSON.status);
                return;
            }

            self.settings = bodyJSON.settings;

            self.init();
        } else {
            console.error("[BARRY-DONATIONS] Failed to validate: " + error);
        }
    });
};

BarryDonations.prototype.init = function() {
    var self = this;

    var url = 'http://don.barrycarlyon.co.uk/api.php?method=initial' +
        '&username=' + this.options.username +
        '&password=' + this.options.password +
        '&version=' + this.options.version;

    request(url, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            var bodyJSON = JSON.parse(body);

                        if (data.data.hasOwnProperty(key)) {

            var latest = 0;
            // process lasttos from all transaction types to minimize data packet size
            for (var key in bodyJSON.data) {
                bodyJSON.data[key].forEach(function(donation) {
                    if (donation.utos > latest) {
                        latest = donation.utos;
                    }
                });
            }

            self.options.lasttos = latest;

            self.emitInit(bodyJSON.data, latest);

            //kill any existing fetch timers
            self.kill();

            //fetch new data (delta) from the api every 30 seconds
            self._fetchtimer = setInterval(self.fetch, 30000, self);
        } else {
            console.error("[BARRY-DONATIONS] Failed to get initial data: " + error);
        }
    });
};

BarryDonations.prototype.fetch = function(scope) {
    var url = 'http://don.barrycarlyon.co.uk/api.php?method=update' +
        '&username=' + scope.options.username +
        '&password=' + scope.options.password +
        '&version=' + scope.options.version +
        '&lasttos=' + scope.options.lasttos; //make sure we fetch this freshly from

    request(url, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            var bodyJSON = JSON.parse(body);

            // this will be true if any donations have a newer timestamp than "lasttos"
            if (bodyJSON.flag === false) {
                return;
            }

            var latest = 0;
            // process lasttos from all transaction types to minimize data packet size
            for (var key in bodyJSON.data) {
                bodyJSON.data[key].forEach(function(donation) {
                    if (donation.utos > latest) {
                        latest = donation.utos;
                    }
                });
            }
            scope.options.lasttos = latest;

            scope.emitNewDonations(bodyJSON.data, latest);
        } else {
            console.error("[BARRY-DONATIONS] Failed to fetch update: " + error);
        }
    });
};

BarryDonations.prototype.kill = function() {
    if(this._fetchtimer !== null) {
        clearInterval(this.fetchtimer);
        this._fetchtimer = null;
    }
};

BarryDonations.prototype.emitInit = function(data, lasttos) {
    this.emit("initialized", data, lasttos);
};

BarryDonations.prototype.emitNewDonations = function(data, lasttos) {
    this.emit("newdonations", data, lasttos);
};

module.exports = BarryDonations;
<commit_msg>crap copypasta code<commit_after>var request = require('request'),
    util = require("util"),
    EventEmitter = require("events").EventEmitter;

function BarryDonations(options) {
    EventEmitter.call(this);

    this.options = options;

    this.validate();
}

util.inherits(BarryDonations, EventEmitter);

BarryDonations.prototype.validate = function() {
    var self = this;

    var url = 'http://don.barrycarlyon.co.uk/api.php?method=validate' +
        '&username=' + this.options.username +
        '&password=' + this.options.password +
        '&version=' + this.options.version;

    request(url, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            var bodyJSON = JSON.parse(body);

            if (bodyJSON.status !== "ok") {
                console.error("[BARRY-DONATIONS] Failed to validate, API returned status: " + bodyJSON.status);
                return;
            }

            self.settings = bodyJSON.settings;

            self.init();
        } else {
            console.error("[BARRY-DONATIONS] Failed to validate: " + error);
        }
    });
};

BarryDonations.prototype.init = function() {
    var self = this;

    var url = 'http://don.barrycarlyon.co.uk/api.php?method=initial' +
        '&username=' + this.options.username +
        '&password=' + this.options.password +
        '&version=' + this.options.version;

    request(url, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            var bodyJSON = JSON.parse(body);

            var latest = 0;
            // process lasttos from all transaction types to minimize data packet size
            for (var key in bodyJSON.data) {
                bodyJSON.data[key].forEach(function(donation) {
                    if (donation.utos > latest) {
                        latest = donation.utos;
                    }
                });
            }

            self.options.lasttos = latest;

            self.emitInit(bodyJSON.data, latest);

            //kill any existing fetch timers
            self.kill();

            //fetch new data (delta) from the api every 30 seconds
            self._fetchtimer = setInterval(self.fetch, 30000, self);
        } else {
            console.error("[BARRY-DONATIONS] Failed to get initial data: " + error);
        }
    });
};

BarryDonations.prototype.fetch = function(scope) {
    var url = 'http://don.barrycarlyon.co.uk/api.php?method=update' +
        '&username=' + scope.options.username +
        '&password=' + scope.options.password +
        '&version=' + scope.options.version +
        '&lasttos=' + scope.options.lasttos; //make sure we fetch this freshly from

    request(url, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            var bodyJSON = JSON.parse(body);

            // this will be true if any donations have a newer timestamp than "lasttos"
            if (bodyJSON.flag === false) {
                return;
            }

            var latest = 0;
            // process lasttos from all transaction types to minimize data packet size
            for (var key in bodyJSON.data) {
                bodyJSON.data[key].forEach(function(donation) {
                    if (donation.utos > latest) {
                        latest = donation.utos;
                    }
                });
            }
            scope.options.lasttos = latest;

            scope.emitNewDonations(bodyJSON.data, latest);
        } else {
            console.error("[BARRY-DONATIONS] Failed to fetch update: " + error);
        }
    });
};

BarryDonations.prototype.kill = function() {
    if(this._fetchtimer !== null) {
        clearInterval(this.fetchtimer);
        this._fetchtimer = null;
    }
};

BarryDonations.prototype.emitInit = function(data, lasttos) {
    this.emit("initialized", data, lasttos);
};

BarryDonations.prototype.emitNewDonations = function(data, lasttos) {
    this.emit("newdonations", data, lasttos);
};

module.exports = BarryDonations;
<|endoftext|>


<commit_before>var vector = require("vektor").vector,
  rotate = require("vektor").rotate;

// Wrap our chains into a single object we can control
//
// @param {Object} opts Options: {chains, offset, orientation}
//  - opts.chains {Array} An array of chains that makeup this robot
//  - opts.robotType {String} One of the predefined robot types.
//  - opts.offset {Array} A three element array describing an offset
//    for the robot's origin point. This can be used to shift the
//    robot's position relative to its origin point (picture the
//    robot shifting it's weight along the x/z plane or stretching
//    to be taller without moving the end effectors)
//  - opts.orientation {Array} A three element array of Numbers
//    giving the rotation of the robot's chassis around its
//    origin point (make it dance! )
function Robot(opts) {

  if (!(this instanceof Robot)) {
    return new Robot(opts);
  }

  this.chains = opts.chains;

  this.offset = opts.offset || [0, 0, 0];

  if (!opts.orientation) {
    opts.orientation = {};
  }

  this.orientation = {
    pitch: opts.orientation.pitch || 0,
    yaw: opts.orientation.yaw || 0,
    roll: opts.orientation.roll || 0
  };

}

// Call the @@normalize function on each of the chains
Robot.prototype["@@normalize"] = function(keyFrameSet) {
  keyFrameSet.forEach(function( keyFrames, index ) {
    keyFrames = this.chains[index].devices["@@normalize"]([keyFrames])[0];
  }, this);

  return keyFrameSet;
};

// Find the solution for each chain. Make sure that all requied chains
// have a valid solution before rendering the robot movement
Robot.prototype["@@render"] = function(opts) {

  var passed = true;

  opts = opts || {};

  this.chains.forEach( function(chain, index) {
    if (opts.length < index - 1) {
      opts[index] = chain.position;
    }
    chain.solve({position: opts[index], orientation: this.orientation, offset: this.offset });

  }, this);

  if (passed) {
    this.chains.forEach( function(chain) {
      chain.devices["@@render"](chain.angles);
    });
  }

};

// Wrap our servos object so that we have all the info and
// methods we need to define and solve our the kinematic system
//
// @param {Object} opts Options: {actuators, systemType, origin, bones }
//  - opts.actuators {Servos}: The Servos() object that contains the chain's actuators
//  - opts.chainType {String}: One of the pre-defined chain types. Don't see your
//    system type? Open an issue, or better yet a Pull Request!
//  - opts.origin {Array}: A three-tuple representing the x, y and z offset of the
//    chain's origin point from the robot's origin point.
//  - opts.segments {Object}: An object with the segment names and lengths for our system
//    The names vary with the systemType
//
// returns this chain
function Chain(opts) {

  if (!(this instanceof Chain)) {
    return new Chain(opts);
  }

  if (opts.constructor) {
    this.devices = new opts.constructor(opts.actuators);
  } else {
    this.devices = opts.actuators;
  }

  this.chainType = opts.chainType;

  this.segments = opts.segments;

  this.origin = opts.origin || [0, 0, 0];
  this.position = opts.position || [0, 0, 0];

  this.require = opts.require || true;

}

// // Move all the servos so our end effector is at the
// // target position.
// //
// // returns the Servos() object this was called upon
// Chain.prototype.render = function( opts ) {
//   this.solve();
//   this.forEach(function(device, index) {
//     // This will call the device's render method
//     device["@@render"](this.angles[index]);
//   });
// };

// Find the angles needed to position the end effector
// at a desired point
//
// @param {Object} opts Options: {chain, position, orientation, type}
//  - opts.chain {Servos*}: Typically a Johnny-Five Servos() instance
//    that contains the servos used in the chain. Prior to using this
//    method the Servos must be run through tharp.makeChain().
//  - opts.origin {Array}: Three tuple of kinematic chain origin relative
//    to the robots origin point
//  - opts.orientation {Object}: {[pitch][, roll][, yaw]}
//    pitch, roll and yaw are given in radians
//  - opts.type {String}: The type of chain (see the readme for
//    a list of types)
//  - opts.immediate {Boolean}: Will move the servos immediately this
//    is usually a bad idea since other legs (chains) of your robot
//    may not have a valid solution and will not move.
//
// returns a three tuple [x, y, z]
Chain.prototype.solve = function( opts ) {

  opts = opts || {};

  if (opts) {
    this.position = opts.position || this.position;
    this.orientation = opts.orientation || this.orientation;
    this.offset = opts.offset || this.offset;
  }

  // Find the end effector position relative to the chain origin
  var offsetPosition = this.eePosition({
    position: this.position,
    origin: this.origin,
    offset: this.offset,
    orientation: this.orientation
  });

  this.angles = ikSolvers[this.chainType](this, offsetPosition);

  if (opts.immediate) {
    this.devices["@@render"](this.angles);
  }

  // If one of the joints could not be solved, return false
  return (this.angles.indexOf(false) === -1);

};

// Find an end effector's position relative to the kinematic chain origin
//
// @param {Object} opts Options: {position[, origin][, orientation] }
//  - opts.position {Array}: Three tuple of end effector position
//    relative to the robots origin point
//  - opts.origin {Array}: Three tuple of kinematic chain origin relative
//    to the robots origin point
//  - opts.orientation {Object}: {[pitch][, roll][, yaw]}
//    pitch, roll and yaw are given in radians
//
// returns a three tuple [x, y, z]
Chain.prototype.eePosition = function(opts) {

  var pos = opts.position || [0, 0, 0];
  var oPos = opts.origin || [0, 0, 0];
  var orientation = opts.orientation || {};
  var roll = orientation.roll || 0;
  var pitch = orientation.pitch || 0;
  var yaw = orientation.yaw || 0;

  var xOffset = opts.offset[0] || 0;
  var yOffset = opts.offset[1] || 0;
  var zOffset = opts.offset[2] || 0;

  pos = [pos[0] - xOffset, pos[1] - yOffset, pos[2] - zOffset];

  // End effector position
  var posVector = new vector(pos);

  // Chain origin position
  var oPosVector = new vector(oPos);

  var rotationMatrix = new rotate.RotZ(roll);
  posVector = rotationMatrix.dot(posVector);
  oPosVector = rotationMatrix.dot(oPosVector);

  rotationMatrix = new rotate.RotX(pitch);
  posVector = rotationMatrix.dot(posVector);
  oPosVector = rotationMatrix.dot(oPosVector);

  rotationMatrix = new rotate.RotY(yaw);
  posVector = rotationMatrix.dot(posVector);
  oPosVector = rotationMatrix.dot(oPosVector);

  // We need to subtract the chain origin from the desired position
  // but there is no vector.subtract() so let's invert the origin
  // and then add the vectors together
  oPosVector = oPosVector.scale(-1);
  posVector = posVector.add(oPosVector);

  return posVector.v;

};

// Convert radians to degrees
var radToDeg = function(x) {
  return x / Math.PI * 180;
};

// Convert degrees to radians
var degToRad = function(x) {
  return x / 180 * Math.PI;
};

// Given three sides this will solve a triangle using law of cosines
//
// @param {Number} a: An adjacent side's length
// @param {Number} b: THe other adjacent side's length
// @param {Number} c: The opposite side's length
//
// Returns the angle in radians
var solveAngle = function(a, b, c) {
  return Math.acos((a * a + b * b - c * c) / (2 * a * b));
};

// Given an angle in radian and a range in degrees will find the correct
// quadrant for the servo to hit that angle.
//
// @param {Number} a: An adjacent side's length
// @param {Number} b: THe other adjacent side's length
// @param {Number} c: The opposite side's length
//
// Returns the angle in degrees unless no solution can be found. In which
// case false is returned.
var findValidAngle = function(angle, range) {
  var degrees = radToDeg(angle);

  if (degrees > range[1] || degrees < range[0]) {

    // Try adding 180
    if (degrees + 180 >= range[0] && degrees + 180 <= range[1]) {
      degrees = degrees + 180;
      return degrees;
    }

    // Try subtracting 180
    if (degrees - 180 >= range[0] && degrees - 180 <= range[1]) {
      degrees = degrees - 180;
      return degrees;
    }

    // No solution was found
    return false;
  }

  // The angle passed in was valid so just return that in degrees
  return degrees;

};

var ikSolvers = {
  "CoxaY-FemurZ-TibiaZ": function(chain, offsetPosition) {

    // Put the coordinates into seperate variables for readability
    var xd = offsetPosition[0];
    var yd = offsetPosition[1];
    var zd = offsetPosition[2];

    // We use the squares of these a lot so let's store the result
    var xd_sq = xd * xd;
    var yd_sq = yd * yd;
    var zd_sq = zd * zd;

    // This is the 3D hypoteneuse from the origin point to the end effector
    var hypot = Math.sqrt(xd_sq + yd_sq + zd_sq);

    // This is the 2D hypoteneuse on the x/z plane.
    var hypot2d = Math.sqrt(xd_sq + zd_sq);

    // Calculate the coxa angle in radians.
    // This is a simple 2D right triangle solve (yay)
    var coxaAngle = Math.atan(offsetPosition[2]/offsetPosition[0]);

    // This is a slightly tougher triangle solve
    var tibiaAngle = solveAngle(chain.segments.femur, chain.segments.tibia, hypot);

    // Our last triangle solve
    var femurAngle = solveAngle(chain.segments.femur, hypot, chain.segments.tibia);

    // But wait! The femur angle returned represents the angle between the
    // femur and the 2D hypoteneuse but we actually want the angle between
    // the femur servo's axis of rotation and the femur so we need to
    // subtract the angle described by the end effector, the femur origin
    // and the femur's axis of rotation.
    // It really is easier than I make it sound.
    femurAngle += Math.sin(yd/hypot2d);

    // If the chain is on the left side we need to modify our solutions
    if (offsetPosition[0] < 0) {
      femurAngle = Math.PI - femurAngle;
      tibiaAngle = Math.PI - tibiaAngle;
    }

    // Just two things left: The angles are in radians (we need degrees
    // for our servo) and the angles may or may not be within our servo's
    // range. Fix that with findValidAngle().
    var coxaDegrees = findValidAngle(coxaAngle, chain.devices[0].range);
    var femurDegrees = findValidAngle(femurAngle, chain.devices[1].range);
    var tibiaDegrees = findValidAngle(tibiaAngle, chain.devices[2].range);

    // Our solution Array
    var angles = [coxaDegrees, femurDegrees, tibiaDegrees];

    return angles;
  }
};

module.exports = {
  Chain: Chain,
  Robot: Robot,
  ikSolvers: ikSolvers,
  radToDeg: radToDeg,
  degToRad: degToRad,
  solveAngle: solveAngle,
  findValidAngle: findValidAngle
};
<commit_msg>Allow for missing opts.offset<commit_after>var vector = require("vektor").vector,
  rotate = require("vektor").rotate;

// Wrap our chains into a single object we can control
//
// @param {Object} opts Options: {chains, offset, orientation}
//  - opts.chains {Array} An array of chains that makeup this robot
//  - opts.robotType {String} One of the predefined robot types.
//  - opts.offset {Array} A three element array describing an offset
//    for the robot's origin point. This can be used to shift the
//    robot's position relative to its origin point (picture the
//    robot shifting it's weight along the x/z plane or stretching
//    to be taller without moving the end effectors)
//  - opts.orientation {Array} A three element array of Numbers
//    giving the rotation of the robot's chassis around its
//    origin point (make it dance! )
function Robot(opts) {

  if (!(this instanceof Robot)) {
    return new Robot(opts);
  }

  this.chains = opts.chains;

  this.offset = opts.offset || [0, 0, 0];

  if (!opts.orientation) {
    opts.orientation = {};
  }

  this.orientation = {
    pitch: opts.orientation.pitch || 0,
    yaw: opts.orientation.yaw || 0,
    roll: opts.orientation.roll || 0
  };

}

// Call the @@normalize function on each of the chains
Robot.prototype["@@normalize"] = function(keyFrameSet) {
  keyFrameSet.forEach(function( keyFrames, index ) {
    keyFrames = this.chains[index].devices["@@normalize"]([keyFrames])[0];
  }, this);

  return keyFrameSet;
};

// Find the solution for each chain. Make sure that all requied chains
// have a valid solution before rendering the robot movement
Robot.prototype["@@render"] = function(opts) {

  var passed = true;

  opts = opts || {};

  this.chains.forEach( function(chain, index) {
    if (opts.length < index - 1) {
      opts[index] = chain.position;
    }
    chain.solve({position: opts[index], orientation: this.orientation, offset: this.offset });

  }, this);

  if (passed) {
    this.chains.forEach( function(chain) {
      chain.devices["@@render"](chain.angles);
    });
  }

};

// Wrap our servos object so that we have all the info and
// methods we need to define and solve our the kinematic system
//
// @param {Object} opts Options: {actuators, systemType, origin, bones }
//  - opts.actuators {Servos}: The Servos() object that contains the chain's actuators
//  - opts.chainType {String}: One of the pre-defined chain types. Don't see your
//    system type? Open an issue, or better yet a Pull Request!
//  - opts.origin {Array}: A three-tuple representing the x, y and z offset of the
//    chain's origin point from the robot's origin point.
//  - opts.segments {Object}: An object with the segment names and lengths for our system
//    The names vary with the systemType
//
// returns this chain
function Chain(opts) {

  if (!(this instanceof Chain)) {
    return new Chain(opts);
  }

  if (opts.constructor) {
    this.devices = new opts.constructor(opts.actuators);
  } else {
    this.devices = opts.actuators;
  }

  this.chainType = opts.chainType;

  this.segments = opts.segments;

  this.origin = opts.origin || [0, 0, 0];
  this.position = opts.position || [0, 0, 0];

  this.require = opts.require || true;

}

// // Move all the servos so our end effector is at the
// // target position.
// //
// // returns the Servos() object this was called upon
// Chain.prototype.render = function( opts ) {
//   this.solve();
//   this.forEach(function(device, index) {
//     // This will call the device's render method
//     device["@@render"](this.angles[index]);
//   });
// };

// Find the angles needed to position the end effector
// at a desired point
//
// @param {Object} opts Options: {chain, position, orientation, type}
//  - opts.chain {Servos*}: Typically a Johnny-Five Servos() instance
//    that contains the servos used in the chain. Prior to using this
//    method the Servos must be run through tharp.makeChain().
//  - opts.origin {Array}: Three tuple of kinematic chain origin relative
//    to the robots origin point
//  - opts.orientation {Object}: {[pitch][, roll][, yaw]}
//    pitch, roll and yaw are given in radians
//  - opts.type {String}: The type of chain (see the readme for
//    a list of types)
//  - opts.immediate {Boolean}: Will move the servos immediately this
//    is usually a bad idea since other legs (chains) of your robot
//    may not have a valid solution and will not move.
//
// returns a three tuple [x, y, z]
Chain.prototype.solve = function( opts ) {

  opts = opts || {};

  if (opts) {
    this.position = opts.position || this.position;
    this.orientation = opts.orientation || this.orientation;
    this.offset = opts.offset || this.offset;
  }

  // Find the end effector position relative to the chain origin
  var offsetPosition = this.eePosition({
    position: this.position,
    origin: this.origin,
    offset: this.offset,
    orientation: this.orientation
  });

  this.angles = ikSolvers[this.chainType](this, offsetPosition);

  if (opts.immediate) {
    this.devices["@@render"](this.angles);
  }

  // If one of the joints could not be solved, return false
  return (this.angles.indexOf(false) === -1);

};

// Find an end effector's position relative to the kinematic chain origin
//
// @param {Object} opts Options: {position[, origin][, orientation] }
//  - opts.position {Array}: Three tuple of end effector position
//    relative to the robots origin point
//  - opts.origin {Array}: Three tuple of kinematic chain origin relative
//    to the robots origin point
//  - opts.orientation {Object}: {[pitch][, roll][, yaw]}
//    pitch, roll and yaw are given in radians
//
// returns a three tuple [x, y, z]
Chain.prototype.eePosition = function(opts) {

  var pos = opts.position || [0, 0, 0];
  var oPos = opts.origin || [0, 0, 0];
  var orientation = opts.orientation || {};
  var roll = orientation.roll || 0;
  var pitch = orientation.pitch || 0;
  var yaw = orientation.yaw || 0;
  var offset = opts.offset || [0, 0, 0];

  var xOffset = offset[0] || 0;
  var yOffset = offset[1] || 0;
  var zOffset = offset[2] || 0;

  pos = [pos[0] - xOffset, pos[1] - yOffset, pos[2] - zOffset];

  // End effector position
  var posVector = new vector(pos);

  // Chain origin position
  var oPosVector = new vector(oPos);

  var rotationMatrix = new rotate.RotZ(roll);
  posVector = rotationMatrix.dot(posVector);
  oPosVector = rotationMatrix.dot(oPosVector);

  rotationMatrix = new rotate.RotX(pitch);
  posVector = rotationMatrix.dot(posVector);
  oPosVector = rotationMatrix.dot(oPosVector);

  rotationMatrix = new rotate.RotY(yaw);
  posVector = rotationMatrix.dot(posVector);
  oPosVector = rotationMatrix.dot(oPosVector);

  // We need to subtract the chain origin from the desired position
  // but there is no vector.subtract() so let's invert the origin
  // and then add the vectors together
  oPosVector = oPosVector.scale(-1);
  posVector = posVector.add(oPosVector);

  return posVector.v;

};

// Convert radians to degrees
var radToDeg = function(x) {
  return x / Math.PI * 180;
};

// Convert degrees to radians
var degToRad = function(x) {
  return x / 180 * Math.PI;
};

// Given three sides this will solve a triangle using law of cosines
//
// @param {Number} a: An adjacent side's length
// @param {Number} b: THe other adjacent side's length
// @param {Number} c: The opposite side's length
//
// Returns the angle in radians
var solveAngle = function(a, b, c) {
  return Math.acos((a * a + b * b - c * c) / (2 * a * b));
};

// Given an angle in radian and a range in degrees will find the correct
// quadrant for the servo to hit that angle.
//
// @param {Number} a: An adjacent side's length
// @param {Number} b: THe other adjacent side's length
// @param {Number} c: The opposite side's length
//
// Returns the angle in degrees unless no solution can be found. In which
// case false is returned.
var findValidAngle = function(angle, range) {
  var degrees = radToDeg(angle);

  if (degrees > range[1] || degrees < range[0]) {

    // Try adding 180
    if (degrees + 180 >= range[0] && degrees + 180 <= range[1]) {
      degrees = degrees + 180;
      return degrees;
    }

    // Try subtracting 180
    if (degrees - 180 >= range[0] && degrees - 180 <= range[1]) {
      degrees = degrees - 180;
      return degrees;
    }

    // No solution was found
    return false;
  }

  // The angle passed in was valid so just return that in degrees
  return degrees;

};

var ikSolvers = {
  "CoxaY-FemurZ-TibiaZ": function(chain, offsetPosition) {

    // Put the coordinates into seperate variables for readability
    var xd = offsetPosition[0];
    var yd = offsetPosition[1];
    var zd = offsetPosition[2];

    // We use the squares of these a lot so let's store the result
    var xd_sq = xd * xd;
    var yd_sq = yd * yd;
    var zd_sq = zd * zd;

    // This is the 3D hypoteneuse from the origin point to the end effector
    var hypot = Math.sqrt(xd_sq + yd_sq + zd_sq);

    // This is the 2D hypoteneuse on the x/z plane.
    var hypot2d = Math.sqrt(xd_sq + zd_sq);

    // Calculate the coxa angle in radians.
    // This is a simple 2D right triangle solve (yay)
    var coxaAngle = Math.atan(offsetPosition[2]/offsetPosition[0]);

    // This is a slightly tougher triangle solve
    var tibiaAngle = solveAngle(chain.segments.femur, chain.segments.tibia, hypot);

    // Our last triangle solve
    var femurAngle = solveAngle(chain.segments.femur, hypot, chain.segments.tibia);

    // But wait! The femur angle returned represents the angle between the
    // femur and the 2D hypoteneuse but we actually want the angle between
    // the femur servo's axis of rotation and the femur so we need to
    // subtract the angle described by the end effector, the femur origin
    // and the femur's axis of rotation.
    // It really is easier than I make it sound.
    femurAngle += Math.sin(yd/hypot2d);

    // If the chain is on the left side we need to modify our solutions
    if (offsetPosition[0] < 0) {
      femurAngle = Math.PI - femurAngle;
      tibiaAngle = Math.PI - tibiaAngle;
    }

    // Just two things left: The angles are in radians (we need degrees
    // for our servo) and the angles may or may not be within our servo's
    // range. Fix that with findValidAngle().
    var coxaDegrees = findValidAngle(coxaAngle, chain.devices[0].range);
    var femurDegrees = findValidAngle(femurAngle, chain.devices[1].range);
    var tibiaDegrees = findValidAngle(tibiaAngle, chain.devices[2].range);

    // Our solution Array
    var angles = [coxaDegrees, femurDegrees, tibiaDegrees];

    return angles;
  }
};

module.exports = {
  Chain: Chain,
  Robot: Robot,
  ikSolvers: ikSolvers,
  radToDeg: radToDeg,
  degToRad: degToRad,
  solveAngle: solveAngle,
  findValidAngle: findValidAngle
};
<|endoftext|>