<commit_before>
// require dependencies
const redis     = require ('redis-eventemitter');
const events    = require ('events');
const winston   = require ('winston');
const mongorito = require ('mongorito');

// require local dependencies
const log    = require ('lib/utilities/log');
const config = require ('config');

// require cached resources
const models  = cache ('models');
const daemons = cache ('daemons');

// build classes
let error  = require ('pretty-error');
error      = new error ();
let cached = require ('cacheman-redis');
cached     = new cached (config.get ('redis'));

// build error handler
process.on ('unhandledRejection', (e) => {
  // log error
  console.log (error.render (e));
});
process.on ('uncaughtException', (e) => {
  // log error
  console.log (error.render (e));
});

/**
 * build eden class
 */
class eden {
  /**
   * construct eden class
   */
  constructor () {
    // bind private variables
    this._ctrl   = {};
    this._hooks  = {};
    this._daemon = {};

    // bind methods
    this.start      = this.start.bind (this);
    this.error      = this.error.bind (this);
    this.require    = this.require.bind (this);
    this.controller = this.controller.bind (this);

    // bind event methods
    this.on   = this.on.bind (this);
    this.off  = this.off.bind (this);
    this.emit = this.emit.bind (this);

    // bind hook methods
    this.pre  = this.pre.bind (this);
    this.post = this.post.bind (this);
    this.hook = this.hook.bind (this);

    // bind cache methods
    this.get = this.get.bind (this);
    this.set = this.set.bind (this);
    this.del = this.del.bind (this);

    // bind private methods
    this._port     = this._port.bind (this);
    this._events   = this._events.bind (this);
    this._logger   = this._logger.bind (this);
    this._daemons  = this._daemons.bind (this);
    this._database = this._database.bind (this);
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  //  Main Methods
  //
  ////////////////////////////////////////////////////////////////////////////

  /**
   * start eden framework
   *
   * @param {Object} opts
   */
  start (opts) {
    // set variables
    this.id       = opts.id;
    this.port     = opts.port || this._port ();
    this.view     = false;
    this.email    = false;
    this.pubsub   = false;
    this.events   = false;
    this.logger   = opts.logger || this._logger ();
    this.express  = opts.express;
    this.compute  = !opts.express;
    this.database = false;

    // build eden database
    this._events ();
    this._database ();
    this._daemons ();

    // require router
    let router = require ('./eden/router');

    // bind eden classes
    this.router = opts.express ? new router () : false;
  }

  /**
   * require file
   *
   * @param  {String}   file
   * @param  {Boolean}  doNew
   *
   * @return {Promise}
   */
  async require (file) {
    // log which file to require
    this.logger.log ('debug', 'requiring ' + file, {
      'class' : 'eden'
    });

    // try catch
    try {
      // try require
      let req = require (file);

      // resolve
      return req;
    } catch (e) {
      // print error
      this.error (e);

      // exit process
      process.exit ();
    }
  }

  /**
   * run controller
   *
   * @param  {String} file
   *
   * @return {[type]}      [description]
   */
  async controller (file) {
    // try catch
    try {
      // check if controller registered
      if (!this._ctrl[file]) {
        // require controller
        let ctrl = await this.require (file);

        // register controller
        this._ctrl[file] = new ctrl ();
      }

      // resolve
      return this._ctrl[file];
    } catch (e) {
      // print error
      this.error (e);

      // exit process
      process.exit ();
    }
  }

  /**
   * pretty prints error
   *
   * @param  {Error} e
   */
  error (e) {
    // log error
    console.log (error.render (e));

    // emit error
    this.emit ('eden:error', e);
  }

  //////////////////////////////////////////////////////////////////////////////
  //
  //  Event Methods
  //
  //////////////////////////////////////////////////////////////////////////////

  /**
   * on function
   *
   * @param  {String}   str
   * @param  {Function} fn
   * @param  {Boolean}  all
   */
  on (str, fn, all) {
    // on str/fn
    if (all) {
      // pubsub on
      this.pubsub.on (str, (channel, data) => {
        // run function
        fn (data);
      });
    } else {
      // add event listener
      this.events.on (str, fn);
    }
  }

  /**
   * on function
   *
   * @param  {String}   str
   * @param  {Function} fn
   * @param  {Boolean}  all
   */
  off (str, fn, all) {
    // remove listener
    return this[all ? 'pubsub' : 'events'][(fn ? 'removeListener' : 'removeAllListeners')] (str, fn);
  }

  /**
   * on function
   *
   * @param  {String}  str
   * @param  {Object}  obj
   * @param  {Boolean} all
   */
  emit (str, obj, all) {
    // emit function
    return this[all ? 'pubsub' : 'events'].emit (str, obj);
  }

  /**
   * emit to main thread
   *
   * @param {String} str
   * @param {Object} obj
   */
  main (str, obj) {
    // emit function
    return this.pubsub.emit ('main', {
      'type' : str,
      'data' : obj
    });
  }

  //////////////////////////////////////////////////////////////////////////////
  //
  //  Cache Methods
  //
  //////////////////////////////////////////////////////////////////////////////

  /**
   * sets cache
   *
   * @param  {String} key
   *
   * @return {*}
   */
  get (key) {
    // return Promise
    return new Promise ((resolve, reject) => {
      // cache get
      cached.get (key, (err, value) => {
        // check err
        if (err) return reject (err);

        // check value
        if (value && typeof value === 'object' && value.id && value.model) {
          // load type
          value = model (value.model).findById (value.id);
        }

        // return value
        resolve (value);
      });
    });
  }

  /**
   * sets cache
   *
   * @param  {String} key
   * @param  {*} value
   *
   * @return {*}
   */
  set (key, value) {
    // return Promise
    return new Promise ((resolve, reject) => {
      // check value
      if (value instanceof require ('model') && value.get && value.get ('_id')) {
        // set value
        value = {
          'id'    : value.get ('_id').toString (),
          'model' : value.constructor.name
        };
      }

      // set cache value
      cached.set (key, value, (err, value) => {
        // check err
        if (err) return reject (err);

        // resolve
        resolve (value);
      });
    });
  }

  /**
   * delete cache by key
   *
   * @param {String} key
   */
  del (key) {
    // return Promise
    return new Promise ((resolve, reject) => {
      // delete from cache
      cached.del (key, (err) => {
        // check err
        if (err) return reject (err);

        // resolve
        resolve ();
      });
    });
  }

  //////////////////////////////////////////////////////////////////////////////
  //
  //  Hook Methods
  //
  //////////////////////////////////////////////////////////////////////////////

  /**
   * add post hook to act as pre
   *
   * @param {String} hook
   * @param {Function} fn
   */
  pre (hook, fn) {
    // check hook
    this.__hook (hook);

    // add to post
    this._hooks[hook].pre.push (fn);
  }

  /**
   * add post hook to act as pre
   *
   * @param {String} hook
   * @param {Function} fn
   */
  post (hook, fn) {
    // check hook
    this.__hook (hook);

    // add to post
    this._hooks[hook].post.push (fn);
  }

  /**
   * adds kareem hook
   *
   * @param {String} hook
   * @param {*} obj
   * @param {Function} fn
   */
  async hook (hook, obj, fn) {
    // check hook
    this.__hook (hook);

    // exec pres
    for (var a = 0; a < this._hooks[hook].pre.length; a++) {
      // exec pre
      await this._hooks[hook].pre[a].apply (obj, [obj]);
    }

    // exec actual function
    if (fn) await fn.apply (obj, [obj]);

    // exec post
    for (var b = 0; b < this._hooks[hook].post.length; b++) {
      // exec pre
      await this._hooks[hook].post[b].apply (obj, [obj]);
    }
  }

  /**
   * checks hook
   *
   * @param {String} hook
   *
   * @private
   */
  __hook (hook) {
    // check hook exists
    if (!this._hooks[hook]) this._hooks[hook] = {
      'pre'  : [],
      'post' : []
    };
  }

  //////////////////////////////////////////////////////////////////////////////
  //
  //  Private Methods
  //
  //////////////////////////////////////////////////////////////////////////////

  /**
   * registers port
   *
   * @private
   */
  _port () {
    // check if main should build
    if (!this.express) return;

    // return true port if clustered
    this.port = config.get ('port');

    // log port
    this.logger.log ('info', 'using express on port ' + this.port, {
      'class' : 'eden'
    });

    // resolve
    return this.port;
  }

  /**
   * registers logger
   *
   * @private
   * @return {winston} logger
   */
  _logger () {
    // set logger
    return new winston.Logger ({
      level      : config.get ('logLevel')  || 'info',
      transports : [
        new (winston.transports.Console) ({
          colorize  : true,
          formatter : log,
          timestamp : true
        })
      ]
    });
  }

  /**
   * registers eden event emitter
   *
   * @private
   */
  _events () {
    // get connection
    let conn = config.get ('redis');

    // set prefix
    conn.prefix = config.get ('domain') + '.event';

    // create events redis instance
    this.pubsub = redis (conn);

    // create new events class
    this.events = new events ();

    // on function
    if (this.main) {
      // only on this thread
      this.pubsub.on ('main', (channel, data) => {
        // emit to this thread
        this.emit (data.type, data.data);
      });
    }
  }

  /**
   * registers mongodb database
   *
   * @private
   */
  _database () {
    // connects to mongo database
    this.database = new mongorito.Database (config.get ('database')[config.get ('environment')].host + '/' + config.get ('database')[config.get ('environment')].db);

    // connect database
    this.database.connect ();

    // register models
    for (var key in models) {
      // register
      this.database.register (model (key));
    }
  }

  /**
   * builds daemons
   *
   * @private
   */
  async _daemons () {
    // loop daemons
    for (var file in daemons) {
      // check if should require
      if (!daemons[file].compute && this.compute) continue;
      if (!daemons[file].express && this.express) continue;

      // require daemon
      await this.controller (file);

      // run daemon
      try {
        // log daemon
        this.logger.log ('info', 'running daemon ' + daemons[file].name, {
          'class' : 'eden'
        });
      } catch (e) {
        // print error
        this.error (e);

        // log daemon
        this.logger.log ('error', 'daemon ' + daemons[file].name + ' failed!', {
          'class' : 'eden'
        });
      }
    }
  }
}

/**
 * export eden class
 *
 * @type {eden}
 */
exports = module.exports = new eden ();
<commit_msg>use mongorito-timestamps middleware<commit_after>
// require dependencies
const redis      = require ('redis-eventemitter');
const events     = require ('events');
const winston    = require ('winston');
const mongorito  = require ('mongorito');
const timestamps = require ('mongorito-timestamps');

// require local dependencies
const log    = require ('lib/utilities/log');
const config = require ('config');

// require cached resources
const models  = cache ('models');
const daemons = cache ('daemons');

// build classes
let error  = require ('pretty-error');
error      = new error ();
let cached = require ('cacheman-redis');
cached     = new cached (config.get ('redis'));

// build error handler
process.on ('unhandledRejection', (e) => {
  // log error
  console.log (error.render (e));
});
process.on ('uncaughtException', (e) => {
  // log error
  console.log (error.render (e));
});

/**
 * build eden class
 */
class eden {
  /**
   * construct eden class
   */
  constructor () {
    // bind private variables
    this._ctrl   = {};
    this._hooks  = {};
    this._daemon = {};

    // bind methods
    this.start      = this.start.bind (this);
    this.error      = this.error.bind (this);
    this.require    = this.require.bind (this);
    this.controller = this.controller.bind (this);

    // bind event methods
    this.on   = this.on.bind (this);
    this.off  = this.off.bind (this);
    this.emit = this.emit.bind (this);

    // bind hook methods
    this.pre  = this.pre.bind (this);
    this.post = this.post.bind (this);
    this.hook = this.hook.bind (this);

    // bind cache methods
    this.get = this.get.bind (this);
    this.set = this.set.bind (this);
    this.del = this.del.bind (this);

    // bind private methods
    this._port     = this._port.bind (this);
    this._events   = this._events.bind (this);
    this._logger   = this._logger.bind (this);
    this._daemons  = this._daemons.bind (this);
    this._database = this._database.bind (this);
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  //  Main Methods
  //
  ////////////////////////////////////////////////////////////////////////////

  /**
   * start eden framework
   *
   * @param {Object} opts
   */
  start (opts) {
    // set variables
    this.id       = opts.id;
    this.port     = opts.port || this._port ();
    this.view     = false;
    this.email    = false;
    this.pubsub   = false;
    this.events   = false;
    this.logger   = opts.logger || this._logger ();
    this.express  = opts.express;
    this.compute  = !opts.express;
    this.database = false;

    // build eden database
    this._events ();
    this._database ();
    this._daemons ();

    // require router
    let router = require ('./eden/router');

    // bind eden classes
    this.router = opts.express ? new router () : false;
  }

  /**
   * require file
   *
   * @param  {String}   file
   * @param  {Boolean}  doNew
   *
   * @return {Promise}
   */
  async require (file) {
    // log which file to require
    this.logger.log ('debug', 'requiring ' + file, {
      'class' : 'eden'
    });

    // try catch
    try {
      // try require
      let req = require (file);

      // resolve
      return req;
    } catch (e) {
      // print error
      this.error (e);

      // exit process
      process.exit ();
    }
  }

  /**
   * run controller
   *
   * @param  {String} file
   *
   * @return {[type]}      [description]
   */
  async controller (file) {
    // try catch
    try {
      // check if controller registered
      if (!this._ctrl[file]) {
        // require controller
        let ctrl = await this.require (file);

        // register controller
        this._ctrl[file] = new ctrl ();
      }

      // resolve
      return this._ctrl[file];
    } catch (e) {
      // print error
      this.error (e);

      // exit process
      process.exit ();
    }
  }

  /**
   * pretty prints error
   *
   * @param  {Error} e
   */
  error (e) {
    // log error
    console.log (error.render (e));

    // emit error
    this.emit ('eden:error', e);
  }

  //////////////////////////////////////////////////////////////////////////////
  //
  //  Event Methods
  //
  //////////////////////////////////////////////////////////////////////////////

  /**
   * on function
   *
   * @param  {String}   str
   * @param  {Function} fn
   * @param  {Boolean}  all
   */
  on (str, fn, all) {
    // on str/fn
    if (all) {
      // pubsub on
      this.pubsub.on (str, (channel, data) => {
        // run function
        fn (data);
      });
    } else {
      // add event listener
      this.events.on (str, fn);
    }
  }

  /**
   * on function
   *
   * @param  {String}   str
   * @param  {Function} fn
   * @param  {Boolean}  all
   */
  off (str, fn, all) {
    // remove listener
    return this[all ? 'pubsub' : 'events'][(fn ? 'removeListener' : 'removeAllListeners')] (str, fn);
  }

  /**
   * on function
   *
   * @param  {String}  str
   * @param  {Object}  obj
   * @param  {Boolean} all
   */
  emit (str, obj, all) {
    // emit function
    return this[all ? 'pubsub' : 'events'].emit (str, obj);
  }

  /**
   * emit to main thread
   *
   * @param {String} str
   * @param {Object} obj
   */
  main (str, obj) {
    // emit function
    return this.pubsub.emit ('main', {
      'type' : str,
      'data' : obj
    });
  }

  //////////////////////////////////////////////////////////////////////////////
  //
  //  Cache Methods
  //
  //////////////////////////////////////////////////////////////////////////////

  /**
   * sets cache
   *
   * @param  {String} key
   *
   * @return {*}
   */
  get (key) {
    // return Promise
    return new Promise ((resolve, reject) => {
      // cache get
      cached.get (key, (err, value) => {
        // check err
        if (err) return reject (err);

        // check value
        if (value && typeof value === 'object' && value.id && value.model) {
          // load type
          value = model (value.model).findById (value.id);
        }

        // return value
        resolve (value);
      });
    });
  }

  /**
   * sets cache
   *
   * @param  {String} key
   * @param  {*} value
   *
   * @return {*}
   */
  set (key, value) {
    // return Promise
    return new Promise ((resolve, reject) => {
      // check value
      if (value instanceof require ('model') && value.get && value.get ('_id')) {
        // set value
        value = {
          'id'    : value.get ('_id').toString (),
          'model' : value.constructor.name
        };
      }

      // set cache value
      cached.set (key, value, (err, value) => {
        // check err
        if (err) return reject (err);

        // resolve
        resolve (value);
      });
    });
  }

  /**
   * delete cache by key
   *
   * @param {String} key
   */
  del (key) {
    // return Promise
    return new Promise ((resolve, reject) => {
      // delete from cache
      cached.del (key, (err) => {
        // check err
        if (err) return reject (err);

        // resolve
        resolve ();
      });
    });
  }

  //////////////////////////////////////////////////////////////////////////////
  //
  //  Hook Methods
  //
  //////////////////////////////////////////////////////////////////////////////

  /**
   * add post hook to act as pre
   *
   * @param {String} hook
   * @param {Function} fn
   */
  pre (hook, fn) {
    // check hook
    this.__hook (hook);

    // add to post
    this._hooks[hook].pre.push (fn);
  }

  /**
   * add post hook to act as pre
   *
   * @param {String} hook
   * @param {Function} fn
   */
  post (hook, fn) {
    // check hook
    this.__hook (hook);

    // add to post
    this._hooks[hook].post.push (fn);
  }

  /**
   * adds kareem hook
   *
   * @param {String} hook
   * @param {*} obj
   * @param {Function} fn
   */
  async hook (hook, obj, fn) {
    // check hook
    this.__hook (hook);

    // exec pres
    for (var a = 0; a < this._hooks[hook].pre.length; a++) {
      // exec pre
      await this._hooks[hook].pre[a].apply (obj, [obj]);
    }

    // exec actual function
    if (fn) await fn.apply (obj, [obj]);

    // exec post
    for (var b = 0; b < this._hooks[hook].post.length; b++) {
      // exec pre
      await this._hooks[hook].post[b].apply (obj, [obj]);
    }
  }

  /**
   * checks hook
   *
   * @param {String} hook
   *
   * @private
   */
  __hook (hook) {
    // check hook exists
    if (!this._hooks[hook]) this._hooks[hook] = {
      'pre'  : [],
      'post' : []
    };
  }

  //////////////////////////////////////////////////////////////////////////////
  //
  //  Private Methods
  //
  //////////////////////////////////////////////////////////////////////////////

  /**
   * registers port
   *
   * @private
   */
  _port () {
    // check if main should build
    if (!this.express) return;

    // return true port if clustered
    this.port = config.get ('port');

    // log port
    this.logger.log ('info', 'using express on port ' + this.port, {
      'class' : 'eden'
    });

    // resolve
    return this.port;
  }

  /**
   * registers logger
   *
   * @private
   * @return {winston} logger
   */
  _logger () {
    // set logger
    return new winston.Logger ({
      level      : config.get ('logLevel')  || 'info',
      transports : [
        new (winston.transports.Console) ({
          colorize  : true,
          formatter : log,
          timestamp : true
        })
      ]
    });
  }

  /**
   * registers eden event emitter
   *
   * @private
   */
  _events () {
    // get connection
    let conn = config.get ('redis');

    // set prefix
    conn.prefix = config.get ('domain') + '.event';

    // create events redis instance
    this.pubsub = redis (conn);

    // create new events class
    this.events = new events ();

    // on function
    if (this.main) {
      // only on this thread
      this.pubsub.on ('main', (channel, data) => {
        // emit to this thread
        this.emit (data.type, data.data);
      });
    }
  }

  /**
   * registers mongodb database
   *
   * @private
   */
  _database () {
    // connects to mongo database
    this.database = new mongorito.Database (config.get ('database')[config.get ('environment')].host + '/' + config.get ('database')[config.get ('environment')].db);

    // connect database
    this.database.connect ();

    // use timestamps
    this.database.use (timestamps ());

    // register models
    for (var key in models) {
      // register
      this.database.register (model (key));
    }
  }

  /**
   * builds daemons
   *
   * @private
   */
  async _daemons () {
    // loop daemons
    for (var file in daemons) {
      // check if should require
      if (!daemons[file].compute && this.compute) continue;
      if (!daemons[file].express && this.express) continue;

      // require daemon
      await this.controller (file);

      // run daemon
      try {
        // log daemon
        this.logger.log ('info', 'running daemon ' + daemons[file].name, {
          'class' : 'eden'
        });
      } catch (e) {
        // print error
        this.error (e);

        // log daemon
        this.logger.log ('error', 'daemon ' + daemons[file].name + ' failed!', {
          'class' : 'eden'
        });
      }
    }
  }
}

/**
 * export eden class
 *
 * @type {eden}
 */
exports = module.exports = new eden ();
<|endoftext|>


<commit_before>'use strict';

var co = require('co');
var path = require('path');
var util = require('util');
var debug = require('debug');
var mkdirp = require('mkdirp');
var chokidar = require('chokidar');
var readSync = require('fs').readFileSync;
var Cat = require('concat-with-sourcemaps');
var assign = require('object-assign');
var flatten = require('flatten');
var expand = require('globby');
var arrify = require('arrify');
var rimraf = require('rimraf');
// locals
var utils = require('./utils');
var Emitter = require('./emitter');
// shorthands
var sep = path.sep;
var _ = debug('fly');

function Fly(options) {
	var self = this;

	if (!(self instanceof Fly)) {
		throw new TypeError('Fly cannot be invoked without \'new\'');
	}

	options = assign({
		file: '.',
		host: {},
		plugins: []
	}, options || {});

	// shorter aliases
	var host = options.host;
	var file = options.file;
	var plugins = options.plugins;

	// ~ `super()`
	Emitter.call(self);

	// attach files & funcs into the curr instance
	assign(self, {
		file: file,
		plugins: plugins,
		root: path.dirname(file),
		alert: utils.alert,
		error: utils.error,
		defer: utils.defer,
		log: utils.log,
		host: (typeof host === 'function') ? assign(host, {default: host}) : host,
		debug: _,
		_: {
			filters: []
		},
		tasks: Object.keys(host).reduce(function (_, key) {
			var obj = {};
			obj[key] = host[key].bind(self);
			return assign(_, obj);
		}, {})
	});

	plugins.forEach(function (el) {
		if (!el.plugin) {
			throw new Error('Did you forget to `npm i -D ' + el.name + '`?');
		}
		// if (plugin.default) plugin = plugin.default
		el.plugin.call(self, debug(el.name.replace('-', ':')), _('load %o', el.name));
	});

	_('chdir %o', self.root);
	process.chdir(self.root);
}

// `Fly extends Emitter`...
util.inherits(Fly, Emitter);
module.exports = Fly;

/**
 * Compose a new, yeildable sequence.
 * Resets instance's glob, filters, and writer.
 *
 * @param  {String|Array} globs The glob selection
 * @return 											The current Fly instance.
 */
Fly.prototype.source = function (globs) {
	globs = arrify(globs);

	assign(this, {
		_: {
			filters: [],
			globs: flatten(globs)
		}
	});

	this._.cat = undefined;
	_('source %o', this._.globs);
	return this;
};

/**
 * Add filter / transform function.
 * Create a closure bound to the current Fly instance.
 *
 * @param {String|Function} name 		The name of filter || the callback
 * @param {Function} 				cb 			the function: (cb, options) => {}
*/
Fly.prototype.filter = function (name, cb) {
	var type = typeof name;

	if (type === 'function') {
		this.filter({cb: name});
	} else if (type === 'object') {
		this._.filters.push(name);
	} else {
		if (typeof this[name] === 'function') {
			throw new RangeError(name + ' method is already defined!');
		}

		this[name] = function (options) {
			var rest = [].slice.call(arguments);
			rest.shift(); // remove `options` argument

			debug('fly')(name + ' %o, %o', options, rest);

			return this.filter({
				cb: cb,
				options: options,
				rest: rest
			});
		};
	}

	return this;
};

/**
 * Watch IO events within glob selections & run tasks
 * @param  {String} globs   The glob patterns to observe
 * @param  {String} tasks   The list of tasks to run on file changes
 * @param  {Object} options The options for `Fly.proto.start`
 * @return {void}
 */
Fly.prototype.watch = function (globs, tasks, options) {
	_('watch %o', globs);

	var self = this;
	return self.emit('fly+watch').start(tasks, options).then(function () {
		chokidar.watch(flatten(globs), {ignoreInitial: true}).on('all', function () {
			self.start(tasks, options);
		});
	});
};

/**
 * Unwrap/Expand source globs as single files, then run fn.
 * @param  {Function} onResolved  The callback to run on 'success'.
 * @param  {Function} onRejected  The callback to run on 'error'.
 * @return {Promise}
 */
Fly.prototype.unwrap = function (onResolved, onRejected) {
	var self = this;

	var p = new Promise(function (resolve, reject) {
		// unwrap all globs
		return Promise.all(self._.globs.map(function (glob) {
			return expand(glob);
		})).then(function (files) {
			// then attach 'files' array to instance
			return resolve.call(self, files.reduce(function (arr, item) {
				return arr.concat(item);
			}));
		}).catch(reject);
	});

	return p.then(onResolved).catch(onRejected);
};

/**
 * Execute a single task.
 * @param  {String} task     The name of the task
 * @param  {Mixed}  value    The initial value to pass into `task`
 * @param  {Object} instance The Fly instance `task` should be bound to
 * @return {Mixed}           The task's resulting value.
 */
Fly.prototype.exec = function * (task, value, instance) {
	instance = instance || this;

	_('run %o', task);

	try {
		var start = getTime();

		this.emit('task_start', {
			task: task
		});

		value = (yield this.host[task].call(instance, value)) || value;

		this.emit('task_complete', {
			task: task,
			duration: getTime() - start
		});
	} catch (e) {
		this.emit('task_error', {
			task: task,
			error: e
		});
	}

	return value;
};

/**
 * Run a task sequence of 1 or more.
 * Each task's return value is piped into the next task of sequence.
 *
 * @param  {Array}  tasks   The list of tasks to start
 * @param  {Object} options The options to begin.
 * @return {Promise}
 */
Fly.prototype.start = function (tasks, options) {
	tasks = arrify(tasks || 'default');
	options = assign({
		parallel: false,
		value: null
	}, options || {});

	_('start %o in ' + (options.parallel ? 'parallel' : 'sequence'), tasks);

	var self = this;
	var value = options.value;

	return co.call(self, function * (tasks) {
		if (options.parallel) {
			yield tasks.map(function (task) {
				self.exec(task, value, Object.create(self));
			});
		} else {
			for (var task of tasks) {
				value = yield self.exec(task, value);
			}
		}
		return value;
	}, [].concat(tasks).filter(function (task) {
		return Object.keys(self.host).indexOf(task) !== -1 || !self.emit('task_not_found', {task: task});
	}));
};

/**
 * Deferred `rimraf` wrapper
 * @param  {String|Array} paths The paths to delete
 * @return {void}
 */
Fly.prototype.clear = function (paths) {
	paths = arrify(paths);

	_('clear %o', paths);

	return flatten(paths).map(function (p) {
		utils.defer(rimraf)(p);
	});
};

/**
 * Concatenate active `files` to single file.
 * @param  {String} filename  The single output file
 * @return {Fly}      				The current Fly instance.
 */
Fly.prototype.concat = function (filename) {
	this._.cat = new Cat(false, filename, '\n');
	this._.cat.base = filename;
	return this;
};

/**
 * Resolve a yieldable sequence.
 * Reduces `source` with filters and invokes writer.
 *
 * @param  {Array}  dirs    The target/destination directories
 * @param  {Object} options Target options. Depth refers to path retention
 * @return {Promise}
 */
Fly.prototype.target = function (dirs, options) {
	dirs = arrify(dirs);
	options = assign({}, {depth: -1}, options || {});

	var self = this;
	var _cat = self._.concat;
	var _filters = self._.filters;

	// @todo: utilize `unwrap` here?
	return co.call(self, function * () {
		// run thru all globs
		yield self._.globs.map(function * (glob) {
			var files = yield expand(glob);

			// run thru all files of each glob
			yield files.map(function * (file) {
				// get data & stats
				var f = path.parse(file);
				var data = readSync(file);

				// pass files' data thru attached filters
				yield _filters.map(function * (filter) {
					// run the filter's closure fn w/data
					var res = yield Promise.resolve(filter.cb.apply(
						self, [data, assign({filename: f.base}, filter.options)].concat(filter.rest)
					));

					// once done, retrieve the final `data` & `ext` of output
					data = res.code || res.js || res.css || res.data || res || data;
					f.ext = res.ext || res.extension || f.ext;
				});

				if (_cat) {
					_cat.add(f.base, data);
				} else {
					// get shared dir between glob & current filepath
					var dir = path.join.apply(null, f.dir.split(sep).filter(function (filepath) {
						return glob.split(sep).indexOf(filepath) === -1;
					}));

					// complete the promise & write the output
					yield resolve(dirs, {
						data: data,
						base: path.join(dir, f.name + f.ext),
						depth: options.depth
					});
				}
			});
		});

		if (_cat) {
			yield resolve(dirs, {
				data: _cat.content,
				base: _cat.base,
				depth: options.depth
			});
		}
	});
};

/**
 * Write utility to help concat and target.
 * @param {String}   dirs  					parent directory
 * @param {String}   options.base  	directory/file
 * @param {Mixed}    options.data
 * @param {Integer}  options.depth 	number of parent directories to keep
 * @param {Function} options.write 	promisified writer function
 */
function * resolve(dirs, options) {
	var opts = options || {};
	var writer = opts.hasOwnProperty('writer') ? opts.writer : utils.write;

	if (opts.depth > -1) {
		opts.base = dirpaths(opts.base, opts.depth);
	}

	yield flatten(dirs).map(function * (dir) {
		var file = path.join(dir, opts.base);
		mkdirp.sync(path.dirname(file));
		yield writer(file, opts.data);
	});
}

/**
 * Shorten a directory string to # of parent dirs
 * @param  {String}  	full    The original full path
 * @param  {Integer}  depth   The number of levels to retain
 * @return {String}
 */
function dirpaths(full, depth) {
	var arr = full.split(sep);
	var len = arr.length;

	if (depth === 0) {
		return arr[len - 1];
	} else if (depth >= len) {
		return full;
	}

	return arr.slice(len - 1 - depth).join(sep);
}

/**
 * Get the current timestamp
 * @return {Integer}
 */
function getTime() {
	return (new Date()).getTime();
}
<commit_msg>add @todo for utils.read<commit_after>'use strict';

var co = require('co');
var path = require('path');
var util = require('util');
var debug = require('debug');
var mkdirp = require('mkdirp');
var chokidar = require('chokidar');
var readSync = require('fs').readFileSync;
var Cat = require('concat-with-sourcemaps');
var assign = require('object-assign');
var flatten = require('flatten');
var expand = require('globby');
var arrify = require('arrify');
var rimraf = require('rimraf');
// locals
var utils = require('./utils');
var Emitter = require('./emitter');
// shorthands
var sep = path.sep;
var _ = debug('fly');

function Fly(options) {
	var self = this;

	if (!(self instanceof Fly)) {
		throw new TypeError('Fly cannot be invoked without \'new\'');
	}

	options = assign({
		file: '.',
		host: {},
		plugins: []
	}, options || {});

	// shorter aliases
	var host = options.host;
	var file = options.file;
	var plugins = options.plugins;

	// ~ `super()`
	Emitter.call(self);

	// attach files & funcs into the curr instance
	assign(self, {
		file: file,
		plugins: plugins,
		root: path.dirname(file),
		alert: utils.alert,
		error: utils.error,
		defer: utils.defer,
		log: utils.log,
		host: (typeof host === 'function') ? assign(host, {default: host}) : host,
		debug: _,
		_: {
			filters: []
		},
		tasks: Object.keys(host).reduce(function (_, key) {
			var obj = {};
			obj[key] = host[key].bind(self);
			return assign(_, obj);
		}, {})
	});

	plugins.forEach(function (el) {
		if (!el.plugin) {
			throw new Error('Did you forget to `npm i -D ' + el.name + '`?');
		}
		// if (plugin.default) plugin = plugin.default
		el.plugin.call(self, debug(el.name.replace('-', ':')), _('load %o', el.name));
	});

	_('chdir %o', self.root);
	process.chdir(self.root);
}

// `Fly extends Emitter`...
util.inherits(Fly, Emitter);
module.exports = Fly;

/**
 * Compose a new, yeildable sequence.
 * Resets instance's glob, filters, and writer.
 *
 * @param  {String|Array} globs The glob selection
 * @return 											The current Fly instance.
 */
Fly.prototype.source = function (globs) {
	globs = arrify(globs);

	assign(this, {
		_: {
			filters: [],
			globs: flatten(globs)
		}
	});

	this._.cat = undefined;
	_('source %o', this._.globs);
	return this;
};

/**
 * Add filter / transform function.
 * Create a closure bound to the current Fly instance.
 *
 * @param {String|Function} name 		The name of filter || the callback
 * @param {Function} 				cb 			the function: (cb, options) => {}
*/
Fly.prototype.filter = function (name, cb) {
	var type = typeof name;

	if (type === 'function') {
		this.filter({cb: name});
	} else if (type === 'object') {
		this._.filters.push(name);
	} else {
		if (typeof this[name] === 'function') {
			throw new RangeError(name + ' method is already defined!');
		}

		this[name] = function (options) {
			var rest = [].slice.call(arguments);
			rest.shift(); // remove `options` argument

			debug('fly')(name + ' %o, %o', options, rest);

			return this.filter({
				cb: cb,
				options: options,
				rest: rest
			});
		};
	}

	return this;
};

/**
 * Watch IO events within glob selections & run tasks
 * @param  {String} globs   The glob patterns to observe
 * @param  {String} tasks   The list of tasks to run on file changes
 * @param  {Object} options The options for `Fly.proto.start`
 * @return {void}
 */
Fly.prototype.watch = function (globs, tasks, options) {
	_('watch %o', globs);

	var self = this;
	return self.emit('fly+watch').start(tasks, options).then(function () {
		chokidar.watch(flatten(globs), {ignoreInitial: true}).on('all', function () {
			self.start(tasks, options);
		});
	});
};

/**
 * Unwrap/Expand source globs as single files, then run fn.
 * @param  {Function} onResolved  The callback to run on 'success'.
 * @param  {Function} onRejected  The callback to run on 'error'.
 * @return {Promise}
 */
Fly.prototype.unwrap = function (onResolved, onRejected) {
	var self = this;

	var p = new Promise(function (resolve, reject) {
		// unwrap all globs
		return Promise.all(self._.globs.map(function (glob) {
			return expand(glob);
		})).then(function (files) {
			// then attach 'files' array to instance
			return resolve.call(self, files.reduce(function (arr, item) {
				return arr.concat(item);
			}));
		}).catch(reject);
	});

	return p.then(onResolved).catch(onRejected);
};

/**
 * Execute a single task.
 * @param  {String} task     The name of the task
 * @param  {Mixed}  value    The initial value to pass into `task`
 * @param  {Object} instance The Fly instance `task` should be bound to
 * @return {Mixed}           The task's resulting value.
 */
Fly.prototype.exec = function * (task, value, instance) {
	instance = instance || this;

	_('run %o', task);

	try {
		var start = getTime();

		this.emit('task_start', {
			task: task
		});

		value = (yield this.host[task].call(instance, value)) || value;

		this.emit('task_complete', {
			task: task,
			duration: getTime() - start
		});
	} catch (e) {
		this.emit('task_error', {
			task: task,
			error: e
		});
	}

	return value;
};

/**
 * Run a task sequence of 1 or more.
 * Each task's return value is piped into the next task of sequence.
 *
 * @param  {Array}  tasks   The list of tasks to start
 * @param  {Object} options The options to begin.
 * @return {Promise}
 */
Fly.prototype.start = function (tasks, options) {
	tasks = arrify(tasks || 'default');
	options = assign({
		parallel: false,
		value: null
	}, options || {});

	_('start %o in ' + (options.parallel ? 'parallel' : 'sequence'), tasks);

	var self = this;
	var value = options.value;

	return co.call(self, function * (tasks) {
		if (options.parallel) {
			yield tasks.map(function (task) {
				self.exec(task, value, Object.create(self));
			});
		} else {
			for (var task of tasks) {
				value = yield self.exec(task, value);
			}
		}
		return value;
	}, [].concat(tasks).filter(function (task) {
		return Object.keys(self.host).indexOf(task) !== -1 || !self.emit('task_not_found', {task: task});
	}));
};

/**
 * Deferred `rimraf` wrapper
 * @param  {String|Array} paths The paths to delete
 * @return {void}
 */
Fly.prototype.clear = function (paths) {
	paths = arrify(paths);

	_('clear %o', paths);

	return flatten(paths).map(function (p) {
		utils.defer(rimraf)(p);
	});
};

/**
 * Concatenate active `files` to single file.
 * @param  {String} filename  The single output file
 * @return {Fly}      				The current Fly instance.
 */
Fly.prototype.concat = function (filename) {
	this._.cat = new Cat(false, filename, '\n');
	this._.cat.base = filename;
	return this;
};

/**
 * Resolve a yieldable sequence.
 * Reduces `source` with filters and invokes writer.
 *
 * @param  {Array}  dirs    The target/destination directories
 * @param  {Object} options Target options. Depth refers to path retention
 * @return {Promise}
 */
Fly.prototype.target = function (dirs, options) {
	dirs = arrify(dirs);
	options = assign({}, {depth: -1}, options || {});

	var self = this;
	var _cat = self._.concat;
	var _filters = self._.filters;

	// @todo: utilize `unwrap` here?
	return co.call(self, function * () {
		// run thru all globs
		yield self._.globs.map(function * (glob) {
			var files = yield expand(glob);

			// run thru all files of each glob
			yield files.map(function * (file) {
				// get data & stats
				var f = path.parse(file);
				var data = readSync(file);
				// var data = yield utils.read(file); // @todo: pass in `normalize: false`

				// pass files' data thru attached filters
				yield _filters.map(function * (filter) {
					// run the filter's closure fn w/data
					var res = yield Promise.resolve(filter.cb.apply(
						self, [data, assign({filename: f.base}, filter.options)].concat(filter.rest)
					));

					// once done, retrieve the final `data` & `ext` of output
					data = res.code || res.js || res.css || res.data || res || data;
					f.ext = res.ext || res.extension || f.ext;
				});

				if (_cat) {
					_cat.add(f.base, data);
				} else {
					// get shared dir between glob & current filepath
					var dir = path.join.apply(null, f.dir.split(sep).filter(function (filepath) {
						return glob.split(sep).indexOf(filepath) === -1;
					}));

					// complete the promise & write the output
					yield resolve(dirs, {
						data: data,
						base: path.join(dir, f.name + f.ext),
						depth: options.depth
					});
				}
			});
		});

		if (_cat) {
			yield resolve(dirs, {
				data: _cat.content,
				base: _cat.base,
				depth: options.depth
			});
		}
	});
};

/**
 * Write utility to help concat and target.
 * @param {String}   dirs  					parent directory
 * @param {String}   options.base  	directory/file
 * @param {Mixed}    options.data
 * @param {Integer}  options.depth 	number of parent directories to keep
 * @param {Function} options.write 	promisified writer function
 */
function * resolve(dirs, options) {
	var opts = options || {};
	var writer = opts.hasOwnProperty('writer') ? opts.writer : utils.write;

	if (opts.depth > -1) {
		opts.base = dirpaths(opts.base, opts.depth);
	}

	yield flatten(dirs).map(function * (dir) {
		var file = path.join(dir, opts.base);
		mkdirp.sync(path.dirname(file));
		yield writer(file, opts.data);
	});
}

/**
 * Shorten a directory string to # of parent dirs
 * @param  {String}  	full    The original full path
 * @param  {Integer}  depth   The number of levels to retain
 * @return {String}
 */
function dirpaths(full, depth) {
	var arr = full.split(sep);
	var len = arr.length;

	if (depth === 0) {
		return arr[len - 1];
	} else if (depth >= len) {
		return full;
	}

	return arr.slice(len - 1 - depth).join(sep);
}

/**
 * Get the current timestamp
 * @return {Integer}
 */
function getTime() {
	return (new Date()).getTime();
}
<|endoftext|>


<commit_before>
const appName = '<%= Rails.application.class.parent_name.underscore %>';
const hauler = require('hauler');
const env = hauler.getEnvName();

const config = hauler.getCompilerConfig(env, __dirname, appName);
module.exports = config;
<commit_msg>chore(webpack): use getAppName in webpack.config.js<commit_after>
const hauler = require('hauler');

const env = hauler.getEnvName();
const railsRoot = __dirname;
const appName = hauler.getAppName(railsRoot);

module.exports = hauler.getCompilerConfig(env, railsRoot, appName);
<|endoftext|>


<commit_before>import expect from 'expect'
import mock from 'mock-fs'
import Traverser from '../src/traverser'

mock({
  'path/to/fake/dir': {
    'some-file.txt': 'file content here',
    'empty-dir': {/** empty directory */},
  },
  'path/to/some.png': new Buffer([8, 6, 7, 5, 3, 0, 9]),
  'some/other/path': {/** another empty directory */},
})

describe('Traverse', () => {

  it('should exist', () => {

    const excludes = new Set()

    excludes.add('node_modules')
    excludes.add('.git')

    const traverser = new Traverser(excludes, 'path/to/fake/dir')
    traverser.traverse()

    expect(traverser).toExist()
  })

  after(() => {
    mock.restore()
  })

})
<commit_msg>sample test<commit_after>import expect from 'expect'
import mock from 'mock-fs'
import Traverser from '../src/traverser'

mock({
  'path/to/fake/dir': {
    'some-file.txt': 'file content here',
    'empty-dir': {/** empty directory */},
  },
  'path/to/some.png': new Buffer([8, 6, 7, 5, 3, 0, 9]),
  'some/other/path': {/** another empty directory */},
})

describe('Traverse', () => {

  it('should exist', () => {

    const excludes = new Set()

    excludes.add('node_modules')
    excludes.add('.git')

    const traverser = new Traverser(excludes, 'path/to/fake/dir')
    traverser.traverse()

    expect(traverser).toExist()
  })

  after(() => {
    mock.restore()
  })

  it('should pass', () => {
    expect(true).toExist()
  })

})
<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var Segment = this.Segment = Base.extend({
	beans: true,

	initialize: function(arg0, arg1, arg2, arg3, arg4, arg5) {
		if (arguments.length == 0) {
			this._point = SegmentPoint.create(this, 0, 0);
		} else if (arguments.length == 1) {
			// TODO: If beans are not activated, this won't copy from
			// an existing segment. OK?
			if (arg0.point) {
				this._point = SegmentPoint.create(this, arg0.point);
				this._handleIn = SegmentPoint.create(this, arg0.handleIn);
				this._handleOut = SegmentPoint.create(this, arg0.handleOut);
			} else {
				this._point = SegmentPoint.create(this, arg0);
			}
		} else if (arguments.length < 6) {
			if (arguments.length == 2 && !arg1.x) {
				this._point = SegmentPoint.create(this, arg0, arg1);
			} else {
				this._point = SegmentPoint.create(this, arg0);
				// Doesn't matter if these arguments exist, it creates 0, 0
				// points otherwise
				this._handleIn = SegmentPoint.create(this, arg1);
				this._handleOut = SegmentPoint.create(this, arg2);
			}
		} else if (arguments.length == 6) {
			this._point = SegmentPoint.create(this, arg0, arg1);
			this._handleIn = SegmentPoint.create(this, arg2, arg3);
			this._handleOut = SegmentPoint.create(this, arg4, arg5);
		}
		if (!this._handleIn)
			this._handleIn = SegmentPoint.create(this, 0, 0);
		if (!this._handleOut)
			this._handleOut = SegmentPoint.create(this, 0, 0);
	},

	getPoint: function() {
		return this._point;
	},

	setPoint: function(point) {
		point = Point.read(arguments);
		// Do not replace the internal object but update it instead, so
		// references to it are kept alive.
		this._point.set(point.x, point.y);
	},

	getHandleIn: function() {
		return this._handleIn;
	},

	setHandleIn: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleIn.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isParallel(this._handleOut);
	},

	getHandleInIfSet: function() {
		return this._handleIn._x == 0 && this._handleIn._y == 0
			? null : this._handleIn;
	},

	getHandleOut: function() {
		return this._handleOut;
	},

	setHandleOut: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleOut.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isParallel(this._handleOut);
	},

	getHandleOutIfSet: function() {
		return this._handleOut._x == 0 && this._handleOut._y == 0
			? null : this._handleOut;
	},

	getIndex: function() {
		// TODO: Cache and update indices instead of searching?
		// TODO: Return null instead of -1?
		return this._path ? this._path._segments.indexOf(this) : -1;
	},

	getPath: function() {
		return this._path;
	},

	getCurve: function() {
		if (this._path != null) {
			var index = this.getIndex();
			// The last segment of an open path belongs to the last curve
			// TODO: Port back to Scriptographer
			if (!this._path.closed && index == this._path._segments.length - 1)
				index--;
			return this._path.getCurves()[index];
		}
		return null;
	},

	getNext: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this.getIndex() + 1]
				|| this._path.closed && segments[0]) || null;
	},

	getPrevious: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this.getIndex() - 1]
				|| this._path.closed && segments[segments.length - 1]) || null;
	},

	_isSelected: function(point) {
		var state = this._selectionState;
		return point == this._point ? !!(state & SelectionState.POINT)
			: point == this._handleIn ? !!(state & SelectionState.HANDLE_IN)
			: point == this._handleOut ? !!(state & SelectionState.HANDLE_OUT)
			: false;
	},

	_setSelected: function(point, selected) {
		if (!this._path)
			return;
		var selected = !!selected, // convert to boolean
			state = this._selectionState,
			wasSelected = !!state,
			pointSelected = !!(state & SelectionState.POINT),
			handleInSelected = !!(state & SelectionState.HANDLE_IN),
			handleOutSelected = !!(state & SelectionState.HANDLE_OUT);
		if (point == this._point) {
			if (pointSelected != selected) {
				if (selected) {
					handleInSelected = handleOutSelected = false;
				} else {
					var previous = this.getPrevious(),
						next = this.getNext();
					// When deselecting a point, the handles get selected
					// instead depending on the selection state of their
					// neighbors.
					handleInSelected = previous
							&& (previous._point.isSelected()
							|| previous._handleOut.isSelected());
					handleOutSelected = next
							&& (next._point.isSelected()
							|| next._handleOut.isSelected());
				}
				pointSelected = selected;
			}
		} else if (point == this._handleIn) {
			if (handleInSelected != selected) {
				// When selecting handles, the point get deselected.
				if (selected)
					pointSelected = false;
				handleInSelected = selected;
			}
		} else if (point == this._handleOut) {
			if (handleOutSelected != selected) {
				// When selecting handles, the point get deselected.
				if (selected)
					pointSelected = false;
				handleOutSelected = selected;
			}
		}
		this._selectionState = (pointSelected ? SelectionState.POINT : 0)
				| (handleInSelected ? SelectionState.HANDLE_IN : 0)
				| (handleOutSelected ? SelectionState.HANDLE_OUT : 0);
		// If the selection state of the segment has changed, we need to let
		// it's path know and possibly add or remove it from
		// document._selectedItems
		if (wasSelected != !!this._selectionState) {
			var path = this._path,
				count = path._selectedSegmentCount
						+= this._selectionState ? 1 : -1;
			if (count <= 1)
				path._document._selectItem(path, count == 1);
		}
	},

	isSelected: function() {
		return this._isSelected(this._point);
	},

	setSelected: function(selected) {
		this._setSelected(this._point, selected);
	},

	reverse: function() {
		return new Segment(this._point, this._handleOut, this._handleIn);
	},

	clone: function() {
		return new Segment(this);
	},

	remove: function() {
		if (this._path) {
			this._path._segments.splice(this.getIndex(), 1);
			if (this.isSelected())
				this._path._selectedSegmentCount--;
			return true;
		}
		return false;
	},

	toString: function() {
		return '{ point: ' + this._point
				+ (!this._handleIn.isZero()
					? ', handleIn: ' + this._handleIn : '')
				+ (this._handleOut.isZero()
					? ', handleOut: ' + this._handleOut : '')
				+ ' }';
	},

	_transformCoordinates: function(matrix, coords, change) {
		// Use matrix.transform version() that takes arrays of multiple
		// points for largely improved performance, as no calls to
		// Point.read() and Point constructors are necessary.
		var point = this._point,
			// If a matrix is defined, only transform handles if they are set.
			// This saves some computation time. If no matrix is set, always
			// use the real handles, as we just want to receive a filled
			// coords array for getBounds().
			handleIn =  matrix && this.getHandleInIfSet() || this._handleIn,
			handleOut = matrix && this.getHandleOutIfSet() || this._handleOut,
			x = point._x,
			y = point._y;
		coords[0] = x;
		coords[1] = y;
		var index = 2;
		// We need to convert handles to absolute coordinates in order
		// to transform them.
		if (handleIn) {
			coords[index++] = handleIn._x + x;
			coords[index++] = handleIn._y + y;
		}
		if (handleOut) {
			coords[index++] = handleOut._x + x;
			coords[index++] = handleOut._y + y;
		}
		if (matrix) {
			matrix.transform(coords, 0, coords, 0, index / 2);
			x = coords[0];
			y = coords[1];
			if (change) {
				// If change is true, we need to set the new values back
				point._x = x;
				point._y = y;
				index  = 2;
				if (handleIn) {
					handleIn._x = coords[index++] - x;
					handleIn._y = coords[index++] - y;
				}
				if (handleOut) {
					handleOut._x = coords[index++] - x;
					handleOut._y = coords[index++] - y;
				}
			} else {
				// We want to receive the results in coords, so make sure
				// handleIn and out are defined too, even if they're 0
				if (!handleIn) {
					coords[index++] = x;
					coords[index++] = y;
				}
				if (!handleOut) {
					coords[index++] = x;
					coords[index++] = y;
				}
			}
		}
	}
});
<commit_msg>Further simplify and compress code.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var Segment = this.Segment = Base.extend({
	beans: true,

	initialize: function(arg0, arg1, arg2, arg3, arg4, arg5) {
		if (arguments.length == 0) {
			this._point = SegmentPoint.create(this, 0, 0);
		} else if (arguments.length == 1) {
			// TODO: If beans are not activated, this won't copy from
			// an existing segment. OK?
			if (arg0.point) {
				this._point = SegmentPoint.create(this, arg0.point);
				this._handleIn = SegmentPoint.create(this, arg0.handleIn);
				this._handleOut = SegmentPoint.create(this, arg0.handleOut);
			} else {
				this._point = SegmentPoint.create(this, arg0);
			}
		} else if (arguments.length < 6) {
			if (arguments.length == 2 && !arg1.x) {
				this._point = SegmentPoint.create(this, arg0, arg1);
			} else {
				this._point = SegmentPoint.create(this, arg0);
				// Doesn't matter if these arguments exist, it creates 0, 0
				// points otherwise
				this._handleIn = SegmentPoint.create(this, arg1);
				this._handleOut = SegmentPoint.create(this, arg2);
			}
		} else if (arguments.length == 6) {
			this._point = SegmentPoint.create(this, arg0, arg1);
			this._handleIn = SegmentPoint.create(this, arg2, arg3);
			this._handleOut = SegmentPoint.create(this, arg4, arg5);
		}
		if (!this._handleIn)
			this._handleIn = SegmentPoint.create(this, 0, 0);
		if (!this._handleOut)
			this._handleOut = SegmentPoint.create(this, 0, 0);
	},

	getPoint: function() {
		return this._point;
	},

	setPoint: function(point) {
		point = Point.read(arguments);
		// Do not replace the internal object but update it instead, so
		// references to it are kept alive.
		this._point.set(point.x, point.y);
	},

	getHandleIn: function() {
		return this._handleIn;
	},

	setHandleIn: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleIn.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isParallel(this._handleOut);
	},

	getHandleInIfSet: function() {
		return this._handleIn._x == 0 && this._handleIn._y == 0
			? null : this._handleIn;
	},

	getHandleOut: function() {
		return this._handleOut;
	},

	setHandleOut: function(point) {
		point = Point.read(arguments);
		// See #setPoint:
		this._handleOut.set(point.x, point.y);
		// Update corner accordingly
		// this.corner = !this._handleIn.isParallel(this._handleOut);
	},

	getHandleOutIfSet: function() {
		return this._handleOut._x == 0 && this._handleOut._y == 0
			? null : this._handleOut;
	},

	getIndex: function() {
		// TODO: Cache and update indices instead of searching?
		// TODO: Return null instead of -1?
		return this._path ? this._path._segments.indexOf(this) : -1;
	},

	getPath: function() {
		return this._path;
	},

	getCurve: function() {
		if (this._path != null) {
			var index = this.getIndex();
			// The last segment of an open path belongs to the last curve
			// TODO: Port back to Scriptographer
			if (!this._path.closed && index == this._path._segments.length - 1)
				index--;
			return this._path.getCurves()[index];
		}
		return null;
	},

	getNext: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this.getIndex() + 1]
				|| this._path.closed && segments[0]) || null;
	},

	getPrevious: function() {
		var segments = this._path && this._path._segments;
		return segments && (segments[this.getIndex() - 1]
				|| this._path.closed && segments[segments.length - 1]) || null;
	},

	_isSelected: function(point) {
		var state = this._selectionState;
		return point == this._point ? !!(state & SelectionState.POINT)
			: point == this._handleIn ? !!(state & SelectionState.HANDLE_IN)
			: point == this._handleOut ? !!(state & SelectionState.HANDLE_OUT)
			: false;
	},

	_setSelected: function(point, selected) {
		if (!this._path)
			return;
		var selected = !!selected, // convert to boolean
			state = this._selectionState,
			wasSelected = !!state,
			// For performance reasons use array indices to access the various
			// selection states: 0 = point, 1 = handleIn, 2 = handleOut
			selection = [
				!!(state & SelectionState.POINT),
				!!(state & SelectionState.HANDLE_IN),
				!!(state & SelectionState.HANDLE_OUT)
			];
		if (point == this._point) {
			if (selected) {
				// We're selecting point, deselect the handles
				selection[1] = selection[2] = false;
			} else {
				var previous = this.getPrevious(),
					next = this.getNext();
				// When deselecting a point, the handles get selected instead
				// depending on the selection state of their neighbors.
				selection[1] = previous && (previous._point.isSelected()
						|| previous._handleOut.isSelected());
				selection[2] = next && (next._point.isSelected()
						|| next._handleIn.isSelected());
			}
			selection[0] = selected;
		} else {
			var index = point == this._handleIn ? 1 : 2;
			if (selection[index] != selected) {
				// When selecting handles, the point get deselected.
				if (selected)
					selection[0] = false;
				selection[index] = selected;
			}
		}
		this._selectionState = (selection[0] ? SelectionState.POINT : 0)
				| (selection[1] ? SelectionState.HANDLE_IN : 0)
				| (selection[2] ? SelectionState.HANDLE_OUT : 0);
		// If the selection state of the segment has changed, we need to let
		// it's path know and possibly add or remove it from
		// document._selectedItems
		if (wasSelected != !!this._selectionState) {
			var path = this._path,
				count = path._selectedSegmentCount
						+= this._selectionState ? 1 : -1;
			if (count <= 1)
				path._document._selectItem(path, count == 1);
		}
	},

	isSelected: function() {
		return this._isSelected(this._point);
	},

	setSelected: function(selected) {
		this._setSelected(this._point, selected);
	},

	reverse: function() {
		return new Segment(this._point, this._handleOut, this._handleIn);
	},

	clone: function() {
		return new Segment(this);
	},

	remove: function() {
		if (this._path) {
			this._path._segments.splice(this.getIndex(), 1);
			if (this.isSelected())
				this._path._selectedSegmentCount--;
			return true;
		}
		return false;
	},

	toString: function() {
		return '{ point: ' + this._point
				+ (!this._handleIn.isZero()
					? ', handleIn: ' + this._handleIn : '')
				+ (this._handleOut.isZero()
					? ', handleOut: ' + this._handleOut : '')
				+ ' }';
	},

	_transformCoordinates: function(matrix, coords, change) {
		// Use matrix.transform version() that takes arrays of multiple
		// points for largely improved performance, as no calls to
		// Point.read() and Point constructors are necessary.
		var point = this._point,
			// If a matrix is defined, only transform handles if they are set.
			// This saves some computation time. If no matrix is set, always
			// use the real handles, as we just want to receive a filled
			// coords array for getBounds().
			handleIn =  matrix && this.getHandleInIfSet() || this._handleIn,
			handleOut = matrix && this.getHandleOutIfSet() || this._handleOut,
			x = point._x,
			y = point._y;
		coords[0] = x;
		coords[1] = y;
		var index = 2;
		// We need to convert handles to absolute coordinates in order
		// to transform them.
		if (handleIn) {
			coords[index++] = handleIn._x + x;
			coords[index++] = handleIn._y + y;
		}
		if (handleOut) {
			coords[index++] = handleOut._x + x;
			coords[index++] = handleOut._y + y;
		}
		if (matrix) {
			matrix.transform(coords, 0, coords, 0, index / 2);
			x = coords[0];
			y = coords[1];
			if (change) {
				// If change is true, we need to set the new values back
				point._x = x;
				point._y = y;
				index  = 2;
				if (handleIn) {
					handleIn._x = coords[index++] - x;
					handleIn._y = coords[index++] - y;
				}
				if (handleOut) {
					handleOut._x = coords[index++] - x;
					handleOut._y = coords[index++] - y;
				}
			} else {
				// We want to receive the results in coords, so make sure
				// handleIn and out are defined too, even if they're 0
				if (!handleIn) {
					coords[index++] = x;
					coords[index++] = y;
				}
				if (!handleOut) {
					coords[index++] = x;
					coords[index++] = y;
				}
			}
		}
	}
});
<|endoftext|>


<commit_before>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class luno extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'luno',
            'name': 'luno',
            'countries': [ 'GB', 'SG', 'ZA' ],
            'rateLimit': 1000,
            'version': '1',
            'has': {
                'CORS': false,
                'fetchTickers': true,
                'fetchOrder': true,
                'fetchOrders': true,
                'fetchOpenOrders': true,
                'fetchClosedOrders': true,
                'fetchMyTrades': true,
                'fetchTradingFee': true,
                'fetchTradingFees': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27766607-8c1a69d8-5ede-11e7-930c-540b5eb9be24.jpg',
                'api': 'https://api.mybitx.com/api',
                'www': 'https://www.luno.com',
                'doc': [
                    'https://www.luno.com/en/api',
                    'https://npmjs.org/package/bitx',
                    'https://github.com/bausmeier/node-bitx',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        'orderbook',
                        'orderbook_top',
                        'ticker',
                        'tickers',
                        'trades',
                    ],
                },
                'private': {
                    'get': [
                        'accounts/{id}/pending',
                        'accounts/{id}/transactions',
                        'balance',
                        'fee_info',
                        'funding_address',
                        'listorders',
                        'listtrades',
                        'orders/{id}',
                        'quotes/{id}',
                        'withdrawals',
                        'withdrawals/{id}',
                    ],
                    'post': [
                        'accounts',
                        'postorder',
                        'marketorder',
                        'stoporder',
                        'funding_address',
                        'withdrawals',
                        'send',
                        'quotes',
                        'oauth2/grant',
                    ],
                    'put': [
                        'quotes/{id}',
                    ],
                    'delete': [
                        'quotes/{id}',
                        'withdrawals/{id}',
                    ],
                },
            },
        });
    }

    async fetchMarkets (params = {}) {
        let markets = await this.publicGetTickers ();
        let result = [];
        for (let p = 0; p < markets['tickers'].length; p++) {
            let market = markets['tickers'][p];
            let id = market['pair'];
            let base = id.slice (0, 3);
            let quote = id.slice (3, 6);
            base = this.commonCurrencyCode (base);
            quote = this.commonCurrencyCode (quote);
            let symbol = base + '/' + quote;
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'info': market,
            });
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetBalance ();
        let wallets = response['balance'];
        let result = { 'info': response };
        for (let b = 0; b < wallets.length; b++) {
            let wallet = wallets[b];
            let currency = this.commonCurrencyCode (wallet['asset']);
            let reserved = parseFloat (wallet['reserved']);
            let unconfirmed = parseFloat (wallet['unconfirmed']);
            let balance = parseFloat (wallet['balance']);
            let account = {
                'free': 0.0,
                'used': this.sum (reserved, unconfirmed),
                'total': this.sum (balance, unconfirmed),
            };
            account['free'] = account['total'] - account['used'];
            result[currency] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let method = 'publicGetOrderbook';
        if (limit !== undefined) {
            if (limit <= 100) {
                method += 'Top'; // get just the top of the orderbook when limit is low
            }
        }
        let orderbook = await this[method] (this.extend ({
            'pair': this.marketId (symbol),
        }, params));
        let timestamp = orderbook['timestamp'];
        return this.parseOrderBook (orderbook, timestamp, 'bids', 'asks', 'price', 'volume');
    }

    parseOrder (order, market = undefined) {
        let timestamp = order['creation_timestamp'];
        let status = (order['state'] === 'PENDING') ? 'open' : 'closed';
        let side = (order['type'] === 'ASK') ? 'sell' : 'buy';
        if (market === undefined)
            market = this.findMarket (order['pair']);
        let symbol = market['symbol'];
        let price = this.safeFloat (order, 'limit_price');
        let amount = this.safeFloat (order, 'limit_volume');
        let quoteFee = this.safeFloat (order, 'fee_counter');
        let baseFee = this.safeFloat (order, 'fee_base');
        let filled = this.safeFloat (order, 'base');
        let cost = this.safeFloat (order, 'counter');
        let remaining = undefined;
        if (amount !== undefined) {
            if (filled !== undefined) {
                remaining = Math.max (0, amount - filled);
            }
        }
        let fee = { 'currency': undefined };
        if (quoteFee) {
            fee['side'] = 'quote';
            fee['cost'] = quoteFee;
        } else {
            fee['side'] = 'base';
            fee['cost'] = baseFee;
        }
        return {
            'id': order['order_id'],
            'datetime': this.iso8601 (timestamp),
            'timestamp': timestamp,
            'lastTradeTimestamp': undefined,
            'status': status,
            'symbol': symbol,
            'type': undefined,
            'side': side,
            'price': price,
            'amount': amount,
            'filled': filled,
            'cost': cost,
            'remaining': remaining,
            'trades': undefined,
            'fee': fee,
            'info': order,
        };
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetOrdersId (this.extend ({
            'id': id,
        }, params));
        return this.parseOrder (response);
    }

    async fetchOrdersByState (state = undefined, symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let request = {};
        let market = undefined;
        if (state !== undefined) {
            request['state'] = state;
        }
        if (symbol !== undefined) {
            market = this.market (symbol);
            request['pair'] = market['id'];
        }
        let response = await this.privateGetListorders (this.extend (request, params));
        let orders = this.safeValue (response, 'orders', []);
        return this.parseOrders (orders, market, since, limit);
    }

    async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return await this.fetchOrdersByState (undefined, symbol, since, limit, params);
    }

    async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return await this.fetchOrdersByState ('PENDING', symbol, since, limit, params);
    }

    async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return await this.fetchOrdersByState ('COMPLETE', symbol, since, limit, params);
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = ticker['timestamp'];
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        let last = this.safeFloat (ticker, 'last_trade');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': undefined,
            'low': undefined,
            'bid': this.safeFloat (ticker, 'bid'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'ask'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'rolling_24_hour_volume'),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        let response = await this.publicGetTickers (params);
        let tickers = this.indexBy (response['tickers'], 'pair');
        let ids = Object.keys (tickers);
        let result = {};
        for (let i = 0; i < ids.length; i++) {
            let id = ids[i];
            let market = this.markets_by_id[id];
            let symbol = market['symbol'];
            let ticker = tickers[id];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let ticker = await this.publicGetTicker (this.extend ({
            'pair': market['id'],
        }, params));
        return this.parseTicker (ticker, market);
    }

    parseTrade (trade, market) {
        // For public trade data (is_buy === True) indicates 'buy' side but for private trade data
        // is_buy indicates maker or taker. The value of "type" (ASK/BID) indicate sell/buy side.
        // Private trade data includes ID field which public trade data does not.
        let order = this.safeString (trade, 'order_id');
        let takerOrMaker = undefined;
        let side = undefined;
        if (order !== undefined) {
            side = (trade['type'] === 'ASK') ? 'sell' : 'buy';
            if (side === 'sell' && trade['is_buy']) {
                takerOrMaker = 'maker';
            } else if (side === 'buy' && !trade['is_buy']) {
                takerOrMaker = 'maker';
            } else {
                takerOrMaker = 'taker';
            }
        } else {
            side = (trade['is_buy']) ? 'buy' : 'sell';
        }
        let feeBase = this.safeFloat (trade, 'fee_base');
        let feeCounter = this.safeFloat (trade, 'fee_counter');
        let feeCurrency = undefined;
        let feeCost = undefined;
        if (feeBase !== undefined) {
            if (feeBase !== 0.0) {
                feeCurrency = market['base'];
                feeCost = feeBase;
            }
        } else if (feeCounter !== undefined) {
            if (feeCounter !== 0.0) {
                feeCurrency = market['quote'];
                feeCost = feeCounter;
            }
        }
        return {
            'info': trade,
            'id': undefined,
            'timestamp': trade['timestamp'],
            'datetime': this.iso8601 (trade['timestamp']),
            'symbol': market['symbol'],
            'order': order,
            'type': undefined,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': this.safeFloat (trade, 'price'),
            'amount': this.safeFloat (trade, 'volume'),
            // Does not include potential fee costs
            'cost': this.safeFloat (trade, 'counter'),
            'fee': {
                'cost': feeCost,
                'currency': feeCurrency,
            },
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let request = {
            'pair': market['id'],
        };
        if (since !== undefined)
            request['since'] = since;
        let response = await this.publicGetTrades (this.extend (request, params));
        let trades = this.safeValue (response, 'trades', []);
        return this.parseTrades (trades, market, since, limit);
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        if (symbol === undefined)
            throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
        await this.loadMarkets ();
        let market = this.market (symbol);
        let request = {
            'pair': market['id'],
        };
        if (since !== undefined)
            request['since'] = since;
        if (limit !== undefined)
            request['limit'] = limit;
        let response = await this.privateGetListtrades (this.extend (request, params));
        let trades = this.safeValue (response, 'trades', []);
        return this.parseTrades (trades, market, since, limit);
    }

    async fetchTradingFees (params = {}) {
        await this.loadMarkets ();
        let response = await this.privateGetFeeInfo (params);
        return {
            'info': response,
            'maker': this.safeFloat (response, 'maker_fee'),
            'taker': this.safeFloat (response, 'taker_fee'),
        };
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let method = 'privatePost';
        let order = { 'pair': this.marketId (symbol) };
        if (type === 'market') {
            method += 'Marketorder';
            order['type'] = side.toUpperCase ();
            if (side === 'buy')
                order['counter_volume'] = amount;
            else
                order['base_volume'] = amount;
        } else {
            method += 'Postorder';
            order['volume'] = amount;
            order['price'] = price;
            if (side === 'buy')
                order['type'] = 'BID';
            else
                order['type'] = 'ASK';
        }
        let response = await this[method] (this.extend (order, params));
        return {
            'info': response,
            'id': response['order_id'],
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        return await this.privatePostStoporder ({ 'order_id': id });
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
        let query = this.omit (params, this.extractParams (path));
        if (Object.keys (query).length)
            url += '?' + this.urlencode (query);
        if (api === 'private') {
            this.checkRequiredCredentials ();
            let auth = this.encode (this.apiKey + ':' + this.secret);
            auth = this.stringToBase64 (auth);
            headers = { 'Authorization': 'Basic ' + this.decode (auth) };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        if ('error' in response)
            throw new ExchangeError (this.id + ' ' + this.json (response));
        return response;
    }
};
<commit_msg>luno cleanup for eslint curly<commit_after>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');
const { ExchangeError, ArgumentsRequired } = require ('./base/errors');

//  ---------------------------------------------------------------------------

module.exports = class luno extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'luno',
            'name': 'luno',
            'countries': [ 'GB', 'SG', 'ZA' ],
            'rateLimit': 1000,
            'version': '1',
            'has': {
                'CORS': false,
                'fetchTickers': true,
                'fetchOrder': true,
                'fetchOrders': true,
                'fetchOpenOrders': true,
                'fetchClosedOrders': true,
                'fetchMyTrades': true,
                'fetchTradingFee': true,
                'fetchTradingFees': true,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/27766607-8c1a69d8-5ede-11e7-930c-540b5eb9be24.jpg',
                'api': 'https://api.mybitx.com/api',
                'www': 'https://www.luno.com',
                'doc': [
                    'https://www.luno.com/en/api',
                    'https://npmjs.org/package/bitx',
                    'https://github.com/bausmeier/node-bitx',
                ],
            },
            'api': {
                'public': {
                    'get': [
                        'orderbook',
                        'orderbook_top',
                        'ticker',
                        'tickers',
                        'trades',
                    ],
                },
                'private': {
                    'get': [
                        'accounts/{id}/pending',
                        'accounts/{id}/transactions',
                        'balance',
                        'fee_info',
                        'funding_address',
                        'listorders',
                        'listtrades',
                        'orders/{id}',
                        'quotes/{id}',
                        'withdrawals',
                        'withdrawals/{id}',
                    ],
                    'post': [
                        'accounts',
                        'postorder',
                        'marketorder',
                        'stoporder',
                        'funding_address',
                        'withdrawals',
                        'send',
                        'quotes',
                        'oauth2/grant',
                    ],
                    'put': [
                        'quotes/{id}',
                    ],
                    'delete': [
                        'quotes/{id}',
                        'withdrawals/{id}',
                    ],
                },
            },
        });
    }

    async fetchMarkets (params = {}) {
        const response = await this.publicGetTickers (params);
        const result = [];
        for (let i = 0; i < response['tickers'].length; i++) {
            const market = response['tickers'][i];
            const id = market['pair'];
            const baseId = id.slice (0, 3);
            const quoteId = id.slice (3, 6);
            const base = this.commonCurrencyCode (baseId);
            const quote = this.commonCurrencyCode (quoteId);
            let symbol = base + '/' + quote;
            result.push ({
                'id': id,
                'symbol': symbol,
                'base': base,
                'quote': quote,
                'baseId': baseId,
                'quoteId': quoteId,
                'info': market,
            });
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        const response = await this.privateGetBalance (params);
        const wallets = this.safeValue (response, 'balance', []);
        const result = { 'info': response };
        for (let i = 0; i < wallets.length; i++) {
            const wallet = wallets[i];
            const currencyId = this.safeString (wallet, 'asset');
            const code = this.commonCurrencyCode (currencyId);
            const reserved = this.safeFloat (wallet, 'reserved');
            const unconfirmed = this.safeFloat (wallet, 'unconfirmed');
            const balance = this.safeFloat (wallet, 'balance');
            const account = {
                'free': 0.0,
                'used': this.sum (reserved, unconfirmed),
                'total': this.sum (balance, unconfirmed),
            };
            account['free'] = account['total'] - account['used'];
            result[code] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let method = 'publicGetOrderbook';
        if (limit !== undefined) {
            if (limit <= 100) {
                method += 'Top'; // get just the top of the orderbook when limit is low
            }
        }
        const request = {
            'pair': this.marketId (symbol),
        };
        const response = await this[method] (this.extend (request, params));
        const timestamp = this.safeInteger (response, 'timestamp');
        return this.parseOrderBook (response, timestamp, 'bids', 'asks', 'price', 'volume');
    }

    parseOrder (order, market = undefined) {
        const timestamp = this.safeInteger (order, 'creation_timestamp');
        const status = (order['state'] === 'PENDING') ? 'open' : 'closed';
        const side = (order['type'] === 'ASK') ? 'sell' : 'buy';
        if (market === undefined) {
            market = this.findMarket (order['pair']);
        }
        let symbol = undefined;
        if (market !== undefined) {
            symbol = market['symbol'];
        }
        const price = this.safeFloat (order, 'limit_price');
        const amount = this.safeFloat (order, 'limit_volume');
        const quoteFee = this.safeFloat (order, 'fee_counter');
        const baseFee = this.safeFloat (order, 'fee_base');
        const filled = this.safeFloat (order, 'base');
        const cost = this.safeFloat (order, 'counter');
        let remaining = undefined;
        if (amount !== undefined) {
            if (filled !== undefined) {
                remaining = Math.max (0, amount - filled);
            }
        }
        const fee = { 'currency': undefined };
        if (quoteFee) {
            fee['side'] = 'quote';
            fee['cost'] = quoteFee;
        } else {
            fee['side'] = 'base';
            fee['cost'] = baseFee;
        }
        const id = this.safeString (order, 'order_id');
        return {
            'id': id,
            'datetime': this.iso8601 (timestamp),
            'timestamp': timestamp,
            'lastTradeTimestamp': undefined,
            'status': status,
            'symbol': symbol,
            'type': undefined,
            'side': side,
            'price': price,
            'amount': amount,
            'filled': filled,
            'cost': cost,
            'remaining': remaining,
            'trades': undefined,
            'fee': fee,
            'info': order,
        };
    }

    async fetchOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {
            'id': id,
        };
        const response = await this.privateGetOrdersId (this.extend (request, params));
        return this.parseOrder (response);
    }

    async fetchOrdersByState (state = undefined, symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {};
        let market = undefined;
        if (state !== undefined) {
            request['state'] = state;
        }
        if (symbol !== undefined) {
            market = this.market (symbol);
            request['pair'] = market['id'];
        }
        const response = await this.privateGetListorders (this.extend (request, params));
        const orders = this.safeValue (response, 'orders', []);
        return this.parseOrders (orders, market, since, limit);
    }

    async fetchOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return await this.fetchOrdersByState (undefined, symbol, since, limit, params);
    }

    async fetchOpenOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return await this.fetchOrdersByState ('PENDING', symbol, since, limit, params);
    }

    async fetchClosedOrders (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        return await this.fetchOrdersByState ('COMPLETE', symbol, since, limit, params);
    }

    parseTicker (ticker, market = undefined) {
        const timestamp = this.safeInteger (ticker, 'timestamp');
        let symbol = undefined;
        if (market) {
            symbol = market['symbol'];
        }
        const last = this.safeFloat (ticker, 'last_trade');
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': undefined,
            'low': undefined,
            'bid': this.safeFloat (ticker, 'bid'),
            'bidVolume': undefined,
            'ask': this.safeFloat (ticker, 'ask'),
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': undefined,
            'baseVolume': this.safeFloat (ticker, 'rolling_24_hour_volume'),
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    async fetchTickers (symbols = undefined, params = {}) {
        await this.loadMarkets ();
        const response = await this.publicGetTickers (params);
        const tickers = this.indexBy (response['tickers'], 'pair');
        const ids = Object.keys (tickers);
        const result = {};
        for (let i = 0; i < ids.length; i++) {
            const id = ids[i];
            const market = this.markets_by_id[id];
            const symbol = market['symbol'];
            const ticker = tickers[id];
            result[symbol] = this.parseTicker (ticker, market);
        }
        return result;
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'pair': market['id'],
        };
        const response = await this.publicGetTicker (this.extend (request, params));
        return this.parseTicker (response, market);
    }

    parseTrade (trade, market) {
        // For public trade data (is_buy === True) indicates 'buy' side but for private trade data
        // is_buy indicates maker or taker. The value of "type" (ASK/BID) indicate sell/buy side.
        // Private trade data includes ID field which public trade data does not.
        const orderId = this.safeString (trade, 'order_id');
        let takerOrMaker = undefined;
        let side = undefined;
        if (orderId !== undefined) {
            side = (trade['type'] === 'ASK') ? 'sell' : 'buy';
            if (side === 'sell' && trade['is_buy']) {
                takerOrMaker = 'maker';
            } else if (side === 'buy' && !trade['is_buy']) {
                takerOrMaker = 'maker';
            } else {
                takerOrMaker = 'taker';
            }
        } else {
            side = (trade['is_buy']) ? 'buy' : 'sell';
        }
        const feeBase = this.safeFloat (trade, 'fee_base');
        const feeCounter = this.safeFloat (trade, 'fee_counter');
        let feeCurrency = undefined;
        let feeCost = undefined;
        if (feeBase !== undefined) {
            if (feeBase !== 0.0) {
                feeCurrency = market['base'];
                feeCost = feeBase;
            }
        } else if (feeCounter !== undefined) {
            if (feeCounter !== 0.0) {
                feeCurrency = market['quote'];
                feeCost = feeCounter;
            }
        }
        const timestamp = this.safeInteger (trade, 'timestamp');
        return {
            'info': trade,
            'id': undefined,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'order': orderId,
            'type': undefined,
            'side': side,
            'takerOrMaker': takerOrMaker,
            'price': this.safeFloat (trade, 'price'),
            'amount': this.safeFloat (trade, 'volume'),
            // Does not include potential fee costs
            'cost': this.safeFloat (trade, 'counter'),
            'fee': {
                'cost': feeCost,
                'currency': feeCurrency,
            },
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'pair': market['id'],
        };
        if (since !== undefined) {
            request['since'] = since;
        }
        const response = await this.publicGetTrades (this.extend (request, params));
        const trades = this.safeValue (response, 'trades', []);
        return this.parseTrades (trades, market, since, limit);
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        if (symbol === undefined) {
            throw new ArgumentsRequired (this.id + ' fetchMyTrades requires a symbol argument');
        }
        await this.loadMarkets ();
        const market = this.market (symbol);
        const request = {
            'pair': market['id'],
        };
        if (since !== undefined) {
            request['since'] = since;
        }
        if (limit !== undefined) {
            request['limit'] = limit;
        }
        const response = await this.privateGetListtrades (this.extend (request, params));
        const trades = this.safeValue (response, 'trades', []);
        return this.parseTrades (trades, market, since, limit);
    }

    async fetchTradingFees (params = {}) {
        await this.loadMarkets ();
        const response = await this.privateGetFeeInfo (params);
        return {
            'info': response,
            'maker': this.safeFloat (response, 'maker_fee'),
            'taker': this.safeFloat (response, 'taker_fee'),
        };
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let method = 'privatePost';
        const request = {
            'pair': this.marketId (symbol),
        };
        if (type === 'market') {
            method += 'Marketorder';
            request['type'] = side.toUpperCase ();
            if (side === 'buy') {
                request['counter_volume'] = amount;
            } else {
                request['base_volume'] = amount;
            }
        } else {
            method += 'Postorder';
            request['volume'] = amount;
            request['price'] = price;
            request['type'] = (side === 'buy') ? 'BID' : 'ASK';
        }
        const response = await this[method] (this.extend (request, params));
        return {
            'info': response,
            'id': response['order_id'],
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {
            'order_id': id,
        };
        return await this.privatePostStoporder (this.extend (request, params));
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'] + '/' + this.version + '/' + this.implodeParams (path, params);
        const query = this.omit (params, this.extractParams (path));
        if (Object.keys (query).length) {
            url += '?' + this.urlencode (query);
        }
        if (api === 'private') {
            this.checkRequiredCredentials ();
            let auth = this.encode (this.apiKey + ':' + this.secret);
            auth = this.stringToBase64 (auth);
            headers = { 'Authorization': 'Basic ' + this.decode (auth) };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        const response = await this.fetch2 (path, api, method, params, headers, body);
        if ('error' in response) {
            throw new ExchangeError (this.id + ' ' + this.json (response));
        }
        return response;
    }
};
<|endoftext|>


<commit_before>/**
 * Alternative to fs module with methods that return Promises and limit the amount of File Descriptors that are used.
 * Limiting the amount of File Descriptors is especially useful on OS X.
 * Note: The alternative graceful-fs also queues FD functions, but uses as many as possible which can be a problem.
 */

'use strict';

var FD_LIMIT = 100;

var _ = require('lodash');
var fs = require('fs');
var Promise = require('./promise');

var fdCount = 0;
var fdWaiting = [];

function getFDSlot() {
	return new Promise(function(resolve) {
		if (fdCount++ < FD_LIMIT) {
			resolve();
		} else {
			fdWaiting.push(resolve);
		}
	});
}

function releaseFDSlot() {
	fdCount--;
	if (fdWaiting.length > 0) {
		fdWaiting.shift()();
	}
}

function fdSlotify(asyncFn, thisArg) {
	return function() {
		var args = _.toArray(arguments);
		return getFDSlot().then(function() {
			return new Promise(function(resolve, reject) {
				args.push(function(err, val) {
					if (err !== null) {
						reject(err);
					} else {
						resolve(val);
					}
					releaseFDSlot();
				});
				asyncFn.apply(thisArg, args);
			});
		});
	};
}

module.exports = {
	lstat: fdSlotify(fs.lstat, fs),
	readFile: fdSlotify(fs.readFile, fs),
	writeFile: fdSlotify(fs.writeFile, fs)
};
<commit_msg>Added some preliminary commented code for debugging fs2 IO's<commit_after>/**
 * Alternative to fs module with methods that return Promises and limit the amount of File Descriptors that are used.
 * Limiting the amount of File Descriptors is especially useful on OS X.
 * Note: The alternative graceful-fs also queues FD functions, but uses as many as possible which can be a problem.
 */

'use strict';

var FD_LIMIT = 100;

var _ = require('lodash');
var fs = require('fs');
var Promise = require('./promise');
// TODO: Look at common use cases and see if IO's can be optimized
// var log = require('./logger').create('fs2');

var fdCount = 0;
var fdWaiting = [];

function getFDSlot() {
	return new Promise(function(resolve) {
		if (fdCount++ < FD_LIMIT) {
			resolve();
		} else {
			fdWaiting.push(resolve);
		}
	});
}

function releaseFDSlot() {
	fdCount--;
	if (fdWaiting.length > 0) {
		fdWaiting.shift()();
	}
}

function fdSlotify(asyncFn, thisArg/*, name*/) {
	return function() {
		var args = _.toArray(arguments);
		// log.spam('%s %s', name, args[0]);
		return getFDSlot().then(function() {
			return new Promise(function(resolve, reject) {
				args.push(function(err, val) {
					if (err !== null) {
						reject(err);
					} else {
						resolve(val);
					}
					releaseFDSlot();
				});
				asyncFn.apply(thisArg, args);
			});
		});
	};
}

module.exports = {
	lstat: fdSlotify(fs.lstat, fs, 'lstat'),
	readFile: fdSlotify(fs.readFile, fs, 'readFile'),
	writeFile: fdSlotify(fs.writeFile, fs, 'writeFile')
};
<|endoftext|>