<commit_before>
/* Dasca, an idle game by @CylonicRaider (github.com/CylonicRaider/dasca)
 * Abstract engine for JS-based idle games */

'use strict';

/* *** Clock ***
 * An object allowing to sample "time" (as defined by an external source)
 * with a linear transform in place. */

/* Construct a new clock
 * source is a function that, when called, returns the current time; scale
 * and offset modify source's output in a linear fashion: the reading of
 * source is multiplied by scale and offset is added to that to obtain the
 * current time. If scale is undefined or null, it defaults to 1; if offset
 * is null, it is computed such that the clock starts counting from zero. */
function Clock(source, scale, offset) {
  if (scale == null) scale = 1.0;
  if (offset == null) offset = -source() * scale;
  this.source = source;
  this.scale = scale;
  this.offset = offset;
}

Clock.prototype = {
  /* Sample the current time */
  now: function() {
    return this.source() * this.scale + this.offset;
  },

  /* Construct a new clock from this one, applying the given transformation
   * The new clock derives its time from the same (!) source as this one,
   * but its scale and offset are modified to simulate a clock constructed
   * like
   *     new Clock(this, scale, offset);
   */
  derive: function(scale, offset) {
    if (scale == null) scale = 1.0;
    if (offset != null) offset = this.offset * scale + offset;
    var ret = new Clock(this.source, this.scale * scale, offset);
    if (this._realTime) ret._realTime = true;
    return ret;
  },

  /* Change the scale of the clock, ensuring that readings continue to be
   * continuous */
  setScale: function(scale) {
    var samp = this.source();
    this.offset = (this.scale - scale) * samp + this.offset;
    this.scale = scale;
  },

  /* Change the scale of the clock relatively to the current one */
  changeScale: function(factor) {
    this.setScale(this.scale * factor);
  },

  /* Jump to the given time */
  setTime: function(time) {
    this.offset = time - this.source() * this.scale;
  },

  /* Change the time reported by the clock by the given increment */
  changeTime: function(delta) {
    this.offset -= delta;
  },

  /* Serialization boilerplate */
  constructor: Clock
};

/* Construct a clock that returns (optionally scaled) real time
 * The clock counts from zero */
Clock.realTime = function(scale, start) {
  if (scale == null) scale = 1.0;
  var offset = (start == null) ? null : start - performance.now() * scale;
  var ret = new Clock(function() {
    return performance.now() / 1000.0;
  }, scale, offset);
  ret._realTime = true;
  return ret;
};

/* Seralize a Clock */
Clock.__save__ = function(clock) {
  if (! clock._realTime) throw new Error("Clock not serializable!");
  return {scale: clock.scale, time: clock.now()};
};

/* Deserialize a Clock */
Clock.__restore__ = function(data) {
  return Clock.realTime(data.scale, data.time);
};

/* *** Scheduler ***
 * Schedules callbacks to happen at certain times as defined by a Clock
 * instance. */

/* Construct a new scheduler
 * requeue is a funtion that somehow ("magically") ensures the function
 * to it is asynchronously called again some short time later; tasks is an
 * ordered array of objects whose "time" property is used to determine when
 * they are to run; contTasks is an array of tasks to be run continuously,
 * i.e. at every call of run() (the same semantics as for tasks apply); clock
 * is a Clock instance determining the time this instance works with. The
 * "running" property is set to true; it can be used to stop the Scheduler.
 * To start it, ensure the "running" property is true and call the run()
 * method. */
function Scheduler(requeue, tasks, contTasks, clock) {
  if (tasks == null) tasks = [];
  if (contTasks == null) contTasks = [];
  if (clock == null) clock = Clock.realTime();
  this.requeue = requeue;
  this.tasks = tasks;
  this.contTasks = contTasks;
  this.clock = clock;
  this.running = true;
  this._idle = true;
}

Scheduler.prototype = {
  /* Queue the next run of the scheduler, and perform any tasks whose time
   * has come
   * After obtaining a timestamp from the clock, all tasks whose time
   * property is not less than the timestamp are removed from the queue and
   * run using runTask(); after that, if the "running" property is true,
   * requeue is called with a bound version of this.run as only argument to
   * trigger another execution. */
  run: function() {
    /* Abort if not running */
    if (! this.running) return;
    /* Obtain current timestamp */
    var now = this.clock.now();
    /* For each task that is (over)due */
    while (this.tasks[0] && this.tasks[0].time <= now) {
      /* Run it */
      this.runTask(this.tasks.shift(), now);
    }
    /* Run the continuous tasks */
    for (var i = 0; i < this.contTasks.length; i++) {
      this.runTask(this.contTasks[i], now);
    }
    /* Schedule next iteration */
    if (this.tasks.length || this.contTasks.length) {
      this.requeue(this.run.bind(this));
    } else {
      this._idle = true;
    }
  },

  /* Run a singular task, providing the given timestamp
   * The default implementation calls the task's "cb" (which is assumed to be
   * a function) with the value of now and the Scheduler instance as
   * arguments, and consumes exceptions by logging them to the console. */
  runTask: function(task, now) {
    try {
      task.cb(now, this);
    } catch (e) {
      console.error(e);
    }
  },

  /* Schedule a task to be run
   * If time is not null, it is inserted into task as the "time" property;
   * after that, the task is inserted at an appropriate position. */
  addTask: function(task, time) {
    if (time != null) task.time = time;
    time = task.time;
    var i;
    for (i = 0; i < this.tasks.length; i++) {
      if (this.tasks[i].time > time) break;
    }
    this.tasks.splice(0, i, task);
    if (this.running && this._idle) {
      this._idle = false;
      this.requeue(this.run.bind(this));
    }
  },

  /* Cancel all tasks */
  clear: function() {
    this.tasks.splice(0, this.tasks.length);
  },

  /* OOP boilerplate */
  constructor: Scheduler
};

/* Create a Scheduler for animations */
Scheduler.makeAnimated = function(clock) {
  var ret = new Scheduler(requestAnimationFrame.bind(window), null, null,
                          clock);
  ret._type = "animated";
  return ret;
};

/* Create a Scheduler that polls tasks at regular intervals */
Scheduler.makeStrobe = function(fps, clock) {
  var delay = 1000.0 / fps;
  var ret = new Scheduler(function(cb) {
    setTimeout(cb, delay);
  }, null, null, clock);
  ret._type = "strobe";
  ret._fps = fps;
  return ret;
};

/* Prepare for serializing a Scheduler */
Scheduler.__save__ = function(sched) {
  var ret = {type: sched._type, tasks: sched.tasks,
             contTasks: sched.contTasks, clock: sched.clock,
             running: sched.running};
  if (sched._type == "strobe") {
    ret.fps = sched._fps;
  } else if (sched._type != "animated") {
    throw new Error("Scheduler not serializable!");
  }
  return ret;
};

/* Deserialize a Scheduler */
Scheduler.__restore__ = function(data) {
  var ret;
  if (data.type == "animated") {
    ret = Scheduler.makeAnimated(data.clock);
  } else if (data.type == "strobe") {
    ret = Scheduler.makeStrobe(data.fps, data.clock);
  }
  ret.tasks = data.tasks;
  ret.contTasks = data.contTasks;
  ret.running = data.running;
  return ret;
};

/* *** Serialization ***
 * Serializes object trees (!) into JSON strings, allowing reified objects to
 * be of the correct type, to hook their (de)serialization process.
 * Input containing enumerable function properties is rejected (since those
 * are silently swallowed by JSON); use hooks to meaningfully handle them. */

/* Serialize an object tree to JSON, storing type information */
function serialize(obj) {
  return JSON.stringify(obj, function(name, value) {
    /* Fail verbosely */
    if (typeof value == "function")
      throw new Error("Cannot serialize function");
    /* Only transform object values */
    if (typeof value != "object" || Array.isArray(value)) return value;
    /* Get a meaningful constructor name */
    var cons = value.constructor.__sername__;
    if (! cons) cons = value.constructor.name;
    if (! cons) cons = Object.prototype.toString(value).slice(8, -1);
    if (cons == "Object") cons = undefined;
    /* Copy properties into new object, or let object serialize itself */
    var ret;
    if (value.constructor.__save__) {
      ret = value.constructor.__save__(value);
    } else {
      ret = {};
      for (var prop in value) ret[prop] = value[prop];
    }
    /* Add __type__ */
    ret.__type__ = cons;
    /* Done */
    return ret;
  });
}

/* Deserialize a JSON string into an object structure */
function deserialize(obj, env) {
  if (env == null) env = window;
  return JSON.parse(obj, function(name, value) {
    /* Ignore non-objects */
    if (typeof value != "object" || Array.isArray(value)) return value;
    /* Check for a __type__ */
    if (value.__type__) {
      /* Obtain type object */
      var type = env[value.__type__];
      if (type && type.__restore__) {
        /* Use restorer function */
        value = type.__restore__(value, env);
      } else if (type) {
        /* Assume an object is deserializable as-is */
        var newVal = Object.create(type.prototype);
        for (var k in value) if (! /^__.+__$/.test(k)) newVal[k] = value[k];
        value = newVal;
      } else {
        /* Nope */
        throw new Error("Object not deserializable (cannot find type): " +
                        JSON.stringify(value));
      }
    }
    return value;
  });
}
<commit_msg>Fix Scheduler bug<commit_after>
/* Dasca, an idle game by @CylonicRaider (github.com/CylonicRaider/dasca)
 * Abstract engine for JS-based idle games */

'use strict';

/* *** Clock ***
 * An object allowing to sample "time" (as defined by an external source)
 * with a linear transform in place. */

/* Construct a new clock
 * source is a function that, when called, returns the current time; scale
 * and offset modify source's output in a linear fashion: the reading of
 * source is multiplied by scale and offset is added to that to obtain the
 * current time. If scale is undefined or null, it defaults to 1; if offset
 * is null, it is computed such that the clock starts counting from zero. */
function Clock(source, scale, offset) {
  if (scale == null) scale = 1.0;
  if (offset == null) offset = -source() * scale;
  this.source = source;
  this.scale = scale;
  this.offset = offset;
}

Clock.prototype = {
  /* Sample the current time */
  now: function() {
    return this.source() * this.scale + this.offset;
  },

  /* Construct a new clock from this one, applying the given transformation
   * The new clock derives its time from the same (!) source as this one,
   * but its scale and offset are modified to simulate a clock constructed
   * like
   *     new Clock(this, scale, offset);
   */
  derive: function(scale, offset) {
    if (scale == null) scale = 1.0;
    if (offset != null) offset = this.offset * scale + offset;
    var ret = new Clock(this.source, this.scale * scale, offset);
    if (this._realTime) ret._realTime = true;
    return ret;
  },

  /* Change the scale of the clock, ensuring that readings continue to be
   * continuous */
  setScale: function(scale) {
    var samp = this.source();
    this.offset = (this.scale - scale) * samp + this.offset;
    this.scale = scale;
  },

  /* Change the scale of the clock relatively to the current one */
  changeScale: function(factor) {
    this.setScale(this.scale * factor);
  },

  /* Jump to the given time */
  setTime: function(time) {
    this.offset = time - this.source() * this.scale;
  },

  /* Change the time reported by the clock by the given increment */
  changeTime: function(delta) {
    this.offset -= delta;
  },

  /* Serialization boilerplate */
  constructor: Clock
};

/* Construct a clock that returns (optionally scaled) real time
 * The clock counts from zero */
Clock.realTime = function(scale, start) {
  if (scale == null) scale = 1.0;
  var offset = (start == null) ? null : start - performance.now() * scale;
  var ret = new Clock(function() {
    return performance.now() / 1000.0;
  }, scale, offset);
  ret._realTime = true;
  return ret;
};

/* Seralize a Clock */
Clock.__save__ = function(clock) {
  if (! clock._realTime) throw new Error("Clock not serializable!");
  return {scale: clock.scale, time: clock.now()};
};

/* Deserialize a Clock */
Clock.__restore__ = function(data) {
  return Clock.realTime(data.scale, data.time);
};

/* *** Scheduler ***
 * Schedules callbacks to happen at certain times as defined by a Clock
 * instance. */

/* Construct a new scheduler
 * requeue is a funtion that somehow ("magically") ensures the function
 * to it is asynchronously called again some short time later; tasks is an
 * ordered array of objects whose "time" property is used to determine when
 * they are to run; contTasks is an array of tasks to be run continuously,
 * i.e. at every call of run() (the same semantics as for tasks apply); clock
 * is a Clock instance determining the time this instance works with. The
 * "running" property is set to true; it can be used to stop the Scheduler.
 * To start it, ensure the "running" property is true and call the run()
 * method. */
function Scheduler(requeue, tasks, contTasks, clock) {
  if (tasks == null) tasks = [];
  if (contTasks == null) contTasks = [];
  if (clock == null) clock = Clock.realTime();
  this.requeue = requeue;
  this.tasks = tasks;
  this.contTasks = contTasks;
  this.clock = clock;
  this.running = true;
  this._idle = true;
}

Scheduler.prototype = {
  /* Queue the next run of the scheduler, and perform any tasks whose time
   * has come
   * After obtaining a timestamp from the clock, all tasks whose time
   * property is not less than the timestamp are removed from the queue and
   * run using runTask(); after that, if the "running" property is true,
   * requeue is called with a bound version of this.run as only argument to
   * trigger another execution. */
  run: function() {
    /* Abort if not running */
    if (! this.running) return;
    /* Obtain current timestamp */
    var now = this.clock.now();
    /* For each task that is (over)due */
    while (this.tasks[0] && this.tasks[0].time <= now) {
      /* Run it */
      this.runTask(this.tasks.shift(), now);
    }
    /* Run the continuous tasks */
    for (var i = 0; i < this.contTasks.length; i++) {
      this.runTask(this.contTasks[i], now);
    }
    /* Schedule next iteration */
    if (this.tasks.length || this.contTasks.length) {
      this.requeue(this.run.bind(this));
    } else {
      this._idle = true;
    }
  },

  /* Run a singular task, providing the given timestamp
   * The default implementation calls the task's "cb" (which is assumed to be
   * a function) with the value of now and the Scheduler instance as
   * arguments, and consumes exceptions by logging them to the console. */
  runTask: function(task, now) {
    try {
      task.cb(now, this);
    } catch (e) {
      console.error(e);
    }
  },

  /* Schedule a task to be run
   * If time is not null, it is inserted into task as the "time" property;
   * after that, the task is inserted at an appropriate position. */
  addTask: function(task, time) {
    if (time != null) task.time = time;
    time = task.time;
    var i;
    for (i = 0; i < this.tasks.length; i++) {
      if (this.tasks[i].time > time) break;
    }
    this.tasks.splice(i, 0, task);
    if (this.running && this._idle) {
      this._idle = false;
      this.requeue(this.run.bind(this));
    }
  },

  /* Cancel all tasks */
  clear: function() {
    this.tasks.splice(0, this.tasks.length);
  },

  /* OOP boilerplate */
  constructor: Scheduler
};

/* Create a Scheduler for animations */
Scheduler.makeAnimated = function(clock) {
  var ret = new Scheduler(requestAnimationFrame.bind(window), null, null,
                          clock);
  ret._type = "animated";
  return ret;
};

/* Create a Scheduler that polls tasks at regular intervals */
Scheduler.makeStrobe = function(fps, clock) {
  var delay = 1000.0 / fps;
  var ret = new Scheduler(function(cb) {
    setTimeout(cb, delay);
  }, null, null, clock);
  ret._type = "strobe";
  ret._fps = fps;
  return ret;
};

/* Prepare for serializing a Scheduler */
Scheduler.__save__ = function(sched) {
  var ret = {type: sched._type, tasks: sched.tasks,
             contTasks: sched.contTasks, clock: sched.clock,
             running: sched.running};
  if (sched._type == "strobe") {
    ret.fps = sched._fps;
  } else if (sched._type != "animated") {
    throw new Error("Scheduler not serializable!");
  }
  return ret;
};

/* Deserialize a Scheduler */
Scheduler.__restore__ = function(data) {
  var ret;
  if (data.type == "animated") {
    ret = Scheduler.makeAnimated(data.clock);
  } else if (data.type == "strobe") {
    ret = Scheduler.makeStrobe(data.fps, data.clock);
  }
  ret.tasks = data.tasks;
  ret.contTasks = data.contTasks;
  ret.running = data.running;
  return ret;
};

/* *** Serialization ***
 * Serializes object trees (!) into JSON strings, allowing reified objects to
 * be of the correct type, to hook their (de)serialization process.
 * Input containing enumerable function properties is rejected (since those
 * are silently swallowed by JSON); use hooks to meaningfully handle them. */

/* Serialize an object tree to JSON, storing type information */
function serialize(obj) {
  return JSON.stringify(obj, function(name, value) {
    /* Fail verbosely */
    if (typeof value == "function")
      throw new Error("Cannot serialize function");
    /* Only transform object values */
    if (typeof value != "object" || Array.isArray(value)) return value;
    /* Get a meaningful constructor name */
    var cons = value.constructor.__sername__;
    if (! cons) cons = value.constructor.name;
    if (! cons) cons = Object.prototype.toString(value).slice(8, -1);
    if (cons == "Object") cons = undefined;
    /* Copy properties into new object, or let object serialize itself */
    var ret;
    if (value.constructor.__save__) {
      ret = value.constructor.__save__(value);
    } else {
      ret = {};
      for (var prop in value) ret[prop] = value[prop];
    }
    /* Add __type__ */
    ret.__type__ = cons;
    /* Done */
    return ret;
  });
}

/* Deserialize a JSON string into an object structure */
function deserialize(obj, env) {
  if (env == null) env = window;
  return JSON.parse(obj, function(name, value) {
    /* Ignore non-objects */
    if (typeof value != "object" || Array.isArray(value)) return value;
    /* Check for a __type__ */
    if (value.__type__) {
      /* Obtain type object */
      var type = env[value.__type__];
      if (type && type.__restore__) {
        /* Use restorer function */
        value = type.__restore__(value, env);
      } else if (type) {
        /* Assume an object is deserializable as-is */
        var newVal = Object.create(type.prototype);
        for (var k in value) if (! /^__.+__$/.test(k)) newVal[k] = value[k];
        value = newVal;
      } else {
        /* Nope */
        throw new Error("Object not deserializable (cannot find type): " +
                        JSON.stringify(value));
      }
    }
    return value;
  });
}
<|endoftext|>


<commit_before>// Modules
var crypto = require('crypto'),
	OAuth2Error = require('./error');

var token = module.exports = {};

/**
 * Token endpoint
 *
 * @param  {Object}   req  Connect request
 * @param  {Object}   res  Connect response
 * @param  {Function} next Connect next
 * @return {Void}
 */
token.handle = function (req, res, next) {
	// Only POST via application/x-www-form-urlencoded is acceptable
	var contentType = (req.get('content-type') || ''),
		mime = contentType.split(';')[0];
	if (req.method !== 'POST' || mime !== 'application/x-www-form-urlencoded') {
		return next(new OAuth2Error('invalid_request',
			'Method must be POST with application/x-www-form-urlencoded encoding'));
	}

	// Grant type
	req.oauth.grantType = req.body && req.body.grant_type;
	if (!req.oauth.grantType || !req.oauth.grantType.match(this.regex.grantType)) {
		return next(new OAuth2Error('invalid_request', 'Invalid or missing grant_type parameter'));
	}

	// Extract credentials
	var creds = token.getClientCredentials(req);
	if (!creds.client_id || !creds.client_id.match(this.regex.clientId)) {
		return next(new OAuth2Error('invalid_client', 'Invalid or missing client_id parameter'));
	}

	// Check credentials against model
	var oauth = this;
	this.model.getClient(creds.client_id, creds.client_secret, function (err, client) {
		if (err) {
			return next(new OAuth2Error('server_error', false, err));
		}

		if (!client) {
			return next(new OAuth2Error('invalid_client', 'The client credentials are invalid'));
		}

		req.oauth.client = client;

		oauth.model.grantTypeAllowed(client.client_id, req.oauth.grantType, function (err, allowed) {
			if (!allowed) {
				return next(new OAuth2Error('invalid_client',
					'The grant type is unauthorised for this client_id'));
			}

			token.grant.call(oauth, req, res, next);
		});
	});
};

/**
 * Convinience function for extracting client credentials from request
 *
 * @see OAuth2Server#token
 *
 * @param  {Object} req Connect request
 * @return {Object}     Client id/secret from headers or body
 */
token.getClientCredentials = function (req) {

	// Return object
	var creds = function (clientId, clientSecret) {
		this.client_id = clientId;
		this.client_secret = clientSecret;
	};

	// Check for Basic Auth
	// Pulled from Connect:
	// https://github.com/senchalabs/connect/blob/master/lib/middleware/basicAuth.js#L65
	var fromBasicAuth = function () {
		var authorization = req.get('authorization');
		if (!authorization) return false;

		var parts = authorization.split(' ');

		if (parts.length !== 2) return false;

		var scheme = parts[0],
			credentials = new Buffer(parts[1], 'base64').toString(),
			index = credentials.indexOf(':');

		if (scheme !== 'Basic' || index < 0) return false;

		return new creds(credentials.slice(0, index), credentials.slice(index + 1));
	};

	return fromBasicAuth() || new creds(req.body.client_id, req.body.client_secret);
};

/**
 * Grant access token based on grant_type
 *
 * @see OAuth2Server#token
 *
 * @param  {Object}   req  Connect request
 * @param  {Object}   res  Connect response
 * @param  {Function} next Connect next
 * @return {Void}
 */
token.grant = function (req, res, next) {

	var invalid = function () {
		next(new OAuth2Error('invalid_request',
			'Invalid grant_type parameter or parameter missing'));
	};

	var oauth = this;

	if (req.oauth.grantType.match(/^http(s|):\/\//) && this.model.extendedGrant) {
		return this.model.extendedGrant(req, function (err, supported, user) {
			if (err) return next(err);
			if (!supported) return invalid();

			if (!user || user.id === undefined) {
				return next(new OAuth2Error('invalid_request', 'Invalid request.'))
			}

			req.user = user;
			token.grantAccessToken.call(oauth, req, res, next);
		});
	}

	switch (req.oauth.grantType) {
		case 'password':
			// User credentials
			var uname = req.body.username,
				pword = req.body.password;
			if (!uname || !pword) {
				return next(new OAuth2Error('invalid_client',
					'Missing parameters. "username" and "password" are required'));
			}

			return this.model.getUser(uname, pword, function (err, user) {
				if (err) {
					return next(new OAuth2Error('server_error', false, err));
				}

				if (user) {
					req.user = user;
					token.grantAccessToken.call(oauth, req, res, next);
				} else {
					next(new OAuth2Error('invalid_grant', 'User credentials are invalid'));
				}
			});

		default:
			return invalid();
	}
};

/**
 * Save access token ready for issuing
 *
 * @see OAuth2Server#grant
 *
 * @param  {Object}   req  Connect request
 * @param  {Object}   res  Connect response
 * @param  {Function} next Connect next
 * @return {Void}
 */
token.grantAccessToken = function (req, res, next) {
	var oauth = this;

	var createRefreshToken = function (err, refreshToken) {
		if (err || !refreshToken) return next(err);

		// Object indicates a reissue
		if (typeof refreshToken === 'object' && refreshToken.refresh_token) {
			req.oauth.accessToken.refresh_token = { refresh_token: refreshToken.refresh_token };
			return token.issueToken.call(oauth, req, res, next);
		}

		req.oauth.accessToken.refresh_token = refreshToken;

		var expires = new Date(oauth.now);
		expires.setSeconds(expires.getSeconds() + oauth.refreshTokenLifeTime);

		oauth.model.saveRefreshToken(req.oauth.accessToken.refresh_token,
				req.oauth.client.client_id, req.user.id, expires, function (err) {
			if (err) return next(new OAuth2Error('server_error', false, err));

			token.issueToken.call(oauth, req, res, next);
		});
	};

	var issueRefreshToken = function () {
		// Are we issuing refresh tokens?
		if (oauth.grants.indexOf('refresh_token') >= 0) {
			token.generateToken.call(oauth, 'refreshToken', req, createRefreshToken);
		} else {
			token.issueToken.call(oauth, req, res, next);
		}
	};

	var createAccessToken = function (err, accessToken) {
		if (err || !accessToken) return next(err);

		// Object idicates a reissue
		if (typeof accessToken === 'object' && accessToken.access_token) {
			req.oauth.accessToken = { access_token: accessToken.access_token };
			return issueRefreshToken();
		}

		req.oauth.accessToken = { access_token: accessToken };

		var expires = new Date(oauth.now);
		expires.setSeconds(expires.getSeconds() + oauth.accessTokenLifetime);

		oauth.model.saveAccessToken(req.oauth.accessToken.access_token, req.oauth.client.client_id,
				req.user.id, expires, function (err) {
			if (err) return next(new OAuth2Error('server_error', false, err));

			issueRefreshToken();
		});
	};

	token.generateToken.call(oauth, 'accessToken', req, createAccessToken);
};

/**
 * Actually issue the token and send the response
 *
 * @see OAuth2Server#grantAccessToken
 *
 * @param  {Object}   req  Connect request
 * @param  {Object}   res  Connect response
 * @param  {Function} next Connect next
 * @return {Void}
 */
token.issueToken = function (req, res, next) {
	// Prepare for output
	req.oauth.accessToken.token_type = 'bearer';
	req.oauth.accessToken.expires_in = this.accessTokenLifetime;

	// That's it!
	res.set({ 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' });
	res.jsonp(req.oauth.accessToken);
};

/**
 * Convinience function for generating a token
 *
 * @param  {String}    req Connect req
 * @param  {Function}  next Connect next
 * @param  {Function}  callback
 * @return {Void}
 */
token.generateToken = function (type, req, callback) {
	if (this.model.generateToken) {
		this.model.generateToken(type, req, function (err, token) {
			if (err) return callback(new OAuth2Error('server_error'));
			if (!token) return token._generateToken(callback);
			callback(false, token);
		});
	} else {
		token._generateToken(callback);
	}
};

token._generateToken= function (callback) {
	crypto.randomBytes(256, function (ex, buffer) {
		if (ex) return callback(new OAuth2Error('server_error'));

		callback(false, crypto.createHash('sha1').update(buffer).digest('hex'));
	});
};<commit_msg>Allow extendedGrant to pass back a custom error message<commit_after>// Modules
var crypto = require('crypto'),
	OAuth2Error = require('./error');

var token = module.exports = {};

/**
 * Token endpoint
 *
 * @param  {Object}   req  Connect request
 * @param  {Object}   res  Connect response
 * @param  {Function} next Connect next
 * @return {Void}
 */
token.handle = function (req, res, next) {
	// Only POST via application/x-www-form-urlencoded is acceptable
	var contentType = (req.get('content-type') || ''),
		mime = contentType.split(';')[0];
	if (req.method !== 'POST' || mime !== 'application/x-www-form-urlencoded') {
		return next(new OAuth2Error('invalid_request',
			'Method must be POST with application/x-www-form-urlencoded encoding'));
	}

	// Grant type
	req.oauth.grantType = req.body && req.body.grant_type;
	if (!req.oauth.grantType || !req.oauth.grantType.match(this.regex.grantType)) {
		return next(new OAuth2Error('invalid_request', 'Invalid or missing grant_type parameter'));
	}

	// Extract credentials
	var creds = token.getClientCredentials(req);
	if (!creds.client_id || !creds.client_id.match(this.regex.clientId)) {
		return next(new OAuth2Error('invalid_client', 'Invalid or missing client_id parameter'));
	}

	// Check credentials against model
	var oauth = this;
	this.model.getClient(creds.client_id, creds.client_secret, function (err, client) {
		if (err) {
			return next(new OAuth2Error('server_error', false, err));
		}

		if (!client) {
			return next(new OAuth2Error('invalid_client', 'The client credentials are invalid'));
		}

		req.oauth.client = client;

		oauth.model.grantTypeAllowed(client.client_id, req.oauth.grantType, function (err, allowed) {
			if (!allowed) {
				return next(new OAuth2Error('invalid_client',
					'The grant type is unauthorised for this client_id'));
			}

			token.grant.call(oauth, req, res, next);
		});
	});
};

/**
 * Convinience function for extracting client credentials from request
 *
 * @see OAuth2Server#token
 *
 * @param  {Object} req Connect request
 * @return {Object}     Client id/secret from headers or body
 */
token.getClientCredentials = function (req) {

	// Return object
	var creds = function (clientId, clientSecret) {
		this.client_id = clientId;
		this.client_secret = clientSecret;
	};

	// Check for Basic Auth
	// Pulled from Connect:
	// https://github.com/senchalabs/connect/blob/master/lib/middleware/basicAuth.js#L65
	var fromBasicAuth = function () {
		var authorization = req.get('authorization');
		if (!authorization) return false;

		var parts = authorization.split(' ');

		if (parts.length !== 2) return false;

		var scheme = parts[0],
			credentials = new Buffer(parts[1], 'base64').toString(),
			index = credentials.indexOf(':');

		if (scheme !== 'Basic' || index < 0) return false;

		return new creds(credentials.slice(0, index), credentials.slice(index + 1));
	};

	return fromBasicAuth() || new creds(req.body.client_id, req.body.client_secret);
};

/**
 * Grant access token based on grant_type
 *
 * @see OAuth2Server#token
 *
 * @param  {Object}   req  Connect request
 * @param  {Object}   res  Connect response
 * @param  {Function} next Connect next
 * @return {Void}
 */
token.grant = function (req, res, next) {

	var invalid = function () {
		next(new OAuth2Error('invalid_request',
			'Invalid grant_type parameter or parameter missing'));
	};

	var oauth = this;

	if (req.oauth.grantType.match(/^http(s|):\/\//) && this.model.extendedGrant) {
		return this.model.extendedGrant(req, function (err, supported, user) {
			if (err && err.error && err.description) {
				return next(new OAuth2Error(err.error, err.description));
			}
			if (err) return next(err);
			if (!supported) return invalid();

			if (!user || user.id === undefined) {
				return next(new OAuth2Error('invalid_request', 'Invalid request.'))
			}

			req.user = user;
			token.grantAccessToken.call(oauth, req, res, next);
		});
	}

	switch (req.oauth.grantType) {
		case 'password':
			// User credentials
			var uname = req.body.username,
				pword = req.body.password;
			if (!uname || !pword) {
				return next(new OAuth2Error('invalid_client',
					'Missing parameters. "username" and "password" are required'));
			}

			return this.model.getUser(uname, pword, function (err, user) {
				if (err) {
					return next(new OAuth2Error('server_error', false, err));
				}

				if (user) {
					req.user = user;
					token.grantAccessToken.call(oauth, req, res, next);
				} else {
					next(new OAuth2Error('invalid_grant', 'User credentials are invalid'));
				}
			});

		default:
			return invalid();
	}
};

/**
 * Save access token ready for issuing
 *
 * @see OAuth2Server#grant
 *
 * @param  {Object}   req  Connect request
 * @param  {Object}   res  Connect response
 * @param  {Function} next Connect next
 * @return {Void}
 */
token.grantAccessToken = function (req, res, next) {
	var oauth = this;

	var createRefreshToken = function (err, refreshToken) {
		if (err || !refreshToken) return next(err);

		// Object indicates a reissue
		if (typeof refreshToken === 'object' && refreshToken.refresh_token) {
			req.oauth.accessToken.refresh_token = { refresh_token: refreshToken.refresh_token };
			return token.issueToken.call(oauth, req, res, next);
		}

		req.oauth.accessToken.refresh_token = refreshToken;

		var expires = new Date(oauth.now);
		expires.setSeconds(expires.getSeconds() + oauth.refreshTokenLifeTime);

		oauth.model.saveRefreshToken(req.oauth.accessToken.refresh_token,
				req.oauth.client.client_id, req.user.id, expires, function (err) {
			if (err) return next(new OAuth2Error('server_error', false, err));

			token.issueToken.call(oauth, req, res, next);
		});
	};

	var issueRefreshToken = function () {
		// Are we issuing refresh tokens?
		if (oauth.grants.indexOf('refresh_token') >= 0) {
			token.generateToken.call(oauth, 'refreshToken', req, createRefreshToken);
		} else {
			token.issueToken.call(oauth, req, res, next);
		}
	};

	var createAccessToken = function (err, accessToken) {
		if (err || !accessToken) return next(err);

		// Object idicates a reissue
		if (typeof accessToken === 'object' && accessToken.access_token) {
			req.oauth.accessToken = { access_token: accessToken.access_token };
			return issueRefreshToken();
		}

		req.oauth.accessToken = { access_token: accessToken };

		var expires = new Date(oauth.now);
		expires.setSeconds(expires.getSeconds() + oauth.accessTokenLifetime);

		oauth.model.saveAccessToken(req.oauth.accessToken.access_token, req.oauth.client.client_id,
				req.user.id, expires, function (err) {
			if (err) return next(new OAuth2Error('server_error', false, err));

			issueRefreshToken();
		});
	};

	token.generateToken.call(oauth, 'accessToken', req, createAccessToken);
};

/**
 * Actually issue the token and send the response
 *
 * @see OAuth2Server#grantAccessToken
 *
 * @param  {Object}   req  Connect request
 * @param  {Object}   res  Connect response
 * @param  {Function} next Connect next
 * @return {Void}
 */
token.issueToken = function (req, res, next) {
	// Prepare for output
	req.oauth.accessToken.token_type = 'bearer';
	req.oauth.accessToken.expires_in = this.accessTokenLifetime;

	// That's it!
	res.set({ 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' });
	res.jsonp(req.oauth.accessToken);
};

/**
 * Convinience function for generating a token
 *
 * @param  {String}    req Connect req
 * @param  {Function}  next Connect next
 * @param  {Function}  callback
 * @return {Void}
 */
token.generateToken = function (type, req, callback) {
	if (this.model.generateToken) {
		this.model.generateToken(type, req, function (err, token) {
			if (err) return callback(new OAuth2Error('server_error'));
			if (!token) return token._generateToken(callback);
			callback(false, token);
		});
	} else {
		token._generateToken(callback);
	}
};

token._generateToken= function (callback) {
	crypto.randomBytes(256, function (ex, buffer) {
		if (ex) return callback(new OAuth2Error('server_error'));

		callback(false, crypto.createHash('sha1').update(buffer).digest('hex'));
	});
};<|endoftext|>


<commit_before>define(function (require) {

  'use strict';

  require('bower_components/hammerjs/dist/jquery.hammer');

  return withTouchEvents;

  function withTouchEvents() {

    this.defaultAttrs({
      eventPrefix: "UI"
    });

    this.handleEvent = function (evnt) {
      this.trigger(this.attr.eventPrefix + this._capitalize(evnt.type));
    };

    this.after('initialize', function () {
      $(document).hammer().on("tap", this.handleEvent.bind(this));
      $(document).hammer().on("drag", this.handleEvent.bind(this));
      $(document).hammer().on("dragstart", this.handleEvent.bind(this));
      $(document).hammer().on("dragend", this.handleEvent.bind(this));
    });

    this._capitalize = function(string) {
      return string.charAt(0).toUpperCase() + string.slice(1);
    };
  }
});
<commit_msg>Changes hammer event listening to a more generic way of doing it<commit_after>define(function (require) {

  'use strict';

  require('bower_components/hammerjs/dist/jquery.hammer');

  return withTouchEvents;

  function withTouchEvents() {

    this.defaultAttrs({
      eventPrefix: "UI",
      hammerEvents: ['tap', 'drag', 'dragstart', 'dragend']
    });

    this.handleHammerEvent = function (evnt) {
      this.trigger(this.attr.eventPrefix + this._capitalize(evnt.type));
    };

    this.lisentToHammerEvent = function (hammerEvent) {
      $(document).hammer().on(hammerEvent, this.handleHammerEvent.bind(this));
    };

    this.after('initialize', function () {
      for(var i = 0; i < this.attr.hammerEvents.length; i++) {
        this.lisentToHammerEvent(this.attr.hammerEvents[i]);
      }
    });

    this._capitalize = function(string) {
      return string.charAt(0).toUpperCase() + string.slice(1);
    };
  }
});
<|endoftext|>


<commit_before>/*global Uint8Array, Uint16Array*/

var utils = require('./utils');
var isRegExp = utils.isRegExp;
var leftPad = utils.leftPad;
var shim = require('./shim');
var json = shim.JSON;
var every = shim.every;
var some = shim.some;
var forEach = shim.forEach;
var map = shim.map;
var getKeys = shim.getKeys;
var reduce = shim.reduce;
var extend = utils.extend;

module.exports = function (expect) {
    expect.addType({
        name: 'object',
        identify: function (arr) {
            return typeof arr === 'object';
        },
        equal: function (a, b, equal) {
            if (a === b) {
                return true;
            }

            // an identical "prototype" property.
            if (a.prototype !== b.prototype) {
                return false;
            }
            //~~~I've managed to break Object.keys through screwy arguments passing.
            //   Converting to array solves the problem.
            if (utils.isArguments(a)) {
                if (!utils.isArguments(b)) {
                    return false;
                }
                return equal(Array.prototype.slice.call(a), Array.prototype.slice.call(b));
            }
            var actualKeys = utils.getKeysOfDefinedProperties(a),
                expectedKeys = utils.getKeysOfDefinedProperties(b),
                key,
                i;

            // having the same number of owned properties (keys incorporates hasOwnProperty)
            if (actualKeys.length !== expectedKeys.length) {
                return false;
            }
            //the same set of keys (although not necessarily the same order),
            actualKeys.sort();
            expectedKeys.sort();
            //~~~cheap key test
            for (i = actualKeys.length - 1; i >= 0; i -= 1) {
                if (actualKeys[i] !== expectedKeys[i]) {
                    return false;
                }
            }

            //equivalent values for every corresponding key, and
            //~~~possibly expensive deep test
            for (i = actualKeys.length - 1; i >= 0; i -= 1) {
                key = actualKeys[i];
                if (!equal(a[key], b[key])) {
                    return false;
                }
            }
            return true;
        },
        inspect: function (output, obj, inspect) {
            var keys = getKeys(obj);
            if (keys.length === 0) {
                return output.text('{}');
            }

            var inspectedItems = map(keys, function (key) {
                var propertyOutput = output.clone();
                if (key.match(/["' ]/)) {
                    propertyOutput.strings(expect.inspect(key));
                } else {
                    propertyOutput.key(key);
                }
                propertyOutput.text(':');

                var hasGetter = obj.__lookupGetter__ && obj.__lookupGetter__(key);
                var hasSetter = obj.__lookupGetter__ && obj.__lookupSetter__(key);

                if (hasGetter || !hasSetter) {
                    propertyOutput.sp().append(inspect(propertyOutput.clone(), obj[key]));
                }

                if (hasGetter && hasSetter) {
                    propertyOutput.sp().text('[Getter/Setter]');
                } else if (hasGetter) {
                    propertyOutput.sp().text('[Getter]');
                } else if (hasSetter) {
                    propertyOutput.sp().text('[Setter]');
                }

                return propertyOutput;
            });

            var width = 0;
            var multipleLines = some(inspectedItems, function (o) {
                var size = o.size();
                width += size.width;
                return width > 50 || size.height > 1;
            });

            forEach(inspectedItems, function (inspectedItem, index) {
                var lastIndex = index === inspectedItems.length - 1;

                if (!lastIndex) {
                    inspectedItem.text(',');
                }
            });

            if (multipleLines) {
                output.text('{').nl().indentLines();

                forEach(inspectedItems, function (inspectedItem, index) {
                    output.i().block(inspectedItem).nl();
                });

                output.outdentLines().text('}');
            } else {
                output.text('{ ');
                forEach(inspectedItems, function (inspectedItem, index) {
                    output.append(inspectedItem);
                    var lastIndex = index === inspectedItems.length - 1;
                    if (!lastIndex) {
                        output.sp();
                    }
                });
                output.text(' }');
            }
            return output;
        },
        toJSON: function (obj, toJSON) {
            return reduce(getKeys(obj), function (result, key) {
                result[key] = toJSON(obj[key]);
                return result;
            }, {});
        }
    });

    expect.addType({
        name: 'array',
        identify: function (arr) {
            return utils.isArray(arr) || utils.isArguments(arr);
        },
        equal: function (a, b, equal) {
            return a === b || (a.length === b.length && every(a, function (v, index) {
                return equal(v, b[index]);
            }));
        },
        inspect: function (output, arr, inspect, depth) {
            if (arr.length === 0) {
                return output.text('[]');
            }

            if (depth === 1) {
                return output.text('[...]');
            }

            var inspectedItems = map(arr, function (v) {
                return inspect(output.clone(), v);
            });

            var width = 0;
            var multipleLines = some(inspectedItems, function (o) {
                var size = o.size();
                width += size.width;
                return width > 50 || o.height > 1;
            });

            forEach(inspectedItems, function (inspectedItem, index) {
                var lastIndex = index === inspectedItems.length - 1;
                if (!lastIndex) {
                    inspectedItem.text(',');
                }
            });

            if (multipleLines) {
                output.text('[').nl().indentLines();

                forEach(inspectedItems, function (inspectedItem, index) {
                    output.i().block(inspectedItem).nl();
                });

                output.outdentLines().text(']');
            } else {
                output.text('[ ');
                forEach(inspectedItems, function (inspectedItem, index) {
                    output.append(inspectedItem);
                    var lastIndex = index === inspectedItems.length - 1;
                    if (!lastIndex) {
                        output.sp();
                    }
                });
                output.text(' ]');
            }
            return output;
        },
        toJSON: function (arr, toJSON) {
            return map(arr, toJSON);
        }
    });

    expect.addType({
        base: 'object',
        name: 'Error',
        identify: function (value) {
            return utils.isError(value);
        },
        equal: function (a, b, equal) {
            return a === b ||
                (equal(a.message, b.message) && this.baseType.equal(a, b, equal));
        },
        inspect: function (output, value, inspect) {
            var errorObject = extend({
                message: value.message
            }, value);
            return output.text('[Error: ').append(inspect(output.clone(), errorObject)).text(']');
        }
    });

    expect.addType({
        name: 'date',
        identify: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Date]';
        },
        equal: function (a, b) {
            return a.getTime() === b.getTime();
        },
        inspect: function (output, date) {
            return output.text('[Date ').text(date.toUTCString()).text(']');
        },
        toJSON: function (date) {
            return {
                $Date: expect.inspect(date)
            };
        }
    });

    expect.addType({
        name: 'function',
        identify: function (f) {
            return typeof f === 'function';
        },
        equal: function (a, b) {
            return a === b || a.toString() === b.toString();
        },
        inspect: function (output, f) {
            output.text('[Function');
            if (f.name) {
                output.text(': ').text(f.name);
            }
            output.text(']');
            return output;
        },
        toJSON: function (f) {
            return {
                $Function: expect.inspect(f)
            };
        }
    });

    expect.addType({
        name: 'regexp',
        identify: isRegExp,
        equal: function (a, b) {
            return a === b || (
                a.source === b.source &&
                    a.global === b.global &&
                    a.ignoreCase === b.ignoreCase &&
                    a.multiline === b.multiline
            );
        },
        inspect: function (output, regExp) {
            return output.text(regExp);
        },
        toJSON: function (regExp) {
            return {
                $RegExp: expect.inspect(regExp)
            };
        }
    });

    expect.addType({
        name: 'DomElement',
        identify: function (value) {
            return utils.isDOMElement(value);
        },
        inspect: function (output, value) {
            return output.text(utils.getOuterHTML(value));
        }
    });

    function getHexDumpLinesForBufferLikeObject(obj, width, digitWidth, maxLength) {
        digitWidth = digitWidth || 2;
        var hexDumpLines = [];
        if (typeof maxLength === 'undefined') {
            maxLength = obj.length;
        }
        if (typeof width !== 'number') {
            width = 16;
        } else if (width === 0) {
            width = maxLength;
        }
        for (var i = 0 ; i < maxLength ; i += width) {
            var hexChars = '',
                asciiChars = ' |';

            for (var j = 0 ; j < width ; j += 1) {
                if (i + j < maxLength) {
                    var octet = obj[i + j];
                    hexChars += leftPad(octet.toString(16).toUpperCase(), digitWidth, '0') + ' ';
                    asciiChars += (octet >= 32 && octet < 127) ? String.fromCharCode(octet) : '.';
                } else if (digitWidth === 2) {
                    hexChars += '   ';
                }
            }

            if (digitWidth === 2) {
                asciiChars += '|';
                hexDumpLines.push(hexChars + asciiChars);
            } else {
                hexDumpLines.push(hexChars.replace(/\s+$/, ''));
            }
        }
        return hexDumpLines;
    }

    function inspectBufferLikeObject(output, buffer, digitWidth, maxLength) {
        if (typeof maxLength !== 'number') {
            maxLength = 20;
        }
        if (buffer.length > maxLength) {
            output.text(getHexDumpLinesForBufferLikeObject(buffer, 0, digitWidth, maxLength))
                .text(' (+').text(buffer.length - maxLength).text(')');

        } else {
            var hexDumpLines = getHexDumpLinesForBufferLikeObject(buffer, 0, digitWidth);
            forEach(hexDumpLines, function (line, i) {
                if (i > 0) {
                    output.nl();
                }
                output.text(line);
            });
        }
        return output;
    }

    function bufferLikeObjectsEqual(a, b) {
        if (a === b) {
            return true;
        }

        if (a.length !== b.length) return false;

        for (var i = 0; i < a.length; i += 1) {
            if (a[i] !== b[i]) return false;
        }

        return true;
    }

    if (typeof Buffer !== 'undefined') {
        expect.addType({
            name: 'Buffer',
            identify: Buffer.isBuffer,
            equal: bufferLikeObjectsEqual,
            inspect: function (output, buffer) {
                output.text('[Buffer ');
                inspectBufferLikeObject(output, buffer);
                return output.text(']');
            },
            toJSON: function (buffer) {
                return {
                    $Buffer: getHexDumpLinesForBufferLikeObject(buffer)
                };
            }
        });
    }

    if (typeof Uint8Array !== 'undefined') {
        expect.addType({
            name: 'Uint8Array',
            identify: function (obj) {
                return obj && obj instanceof Uint8Array;
            },
            equal: bufferLikeObjectsEqual,
            inspect: function (output, uint8Array) {
                output.text('[Uint8Array ');
                inspectBufferLikeObject(output, uint8Array);
                return output.text(']');
            },
            toJSON: function (uint8Array) {
                return {
                    $Uint8Array: getHexDumpLinesForBufferLikeObject(uint8Array)
                };
            }
        });
    }

    if (typeof Uint16Array !== 'undefined') {
        expect.addType({
            name: 'Uint16Array',
            identify: function (obj) {
                return obj && obj instanceof Uint16Array;
            },
            equal: bufferLikeObjectsEqual,
            inspect: function (output, uint16Array) {
                output.text('[Uint16Array ');
                inspectBufferLikeObject(output, uint16Array, 4);
                return output.text(']');
            },
            toJSON: function (uint16Array) {
                return {
                    $Uint16Array: getHexDumpLinesForBufferLikeObject(uint16Array, 8, 4)
                };
            }
        });
    }

    expect.addType({
        name: 'string',
        identify: function (value) {
            return typeof value === 'string';
        },
        inspect: function (output, value) {
            return output.strings('\'')
                .strings(json.stringify(value).replace(/^"|"$/g, '')
                      .replace(/'/g, "\\'")
                      .replace(/\\"/g, '"'))
                .strings('\'');
        }
    });

    expect.addType({
        name: 'number',
        identify: function (value) {
            return typeof value === 'number';
        }
    });

    expect.addType({
        name: 'boolean',
        identify: function (value) {
            return typeof value === 'boolean';
        }
    });

    expect.addType({
        name: 'undefined',
        identify: function (value) {
            return typeof value === 'undefined';
        }
    });

    expect.addType({
        name: 'null',
        identify: function (value) {
            return value === null;
        }
    });
};
<commit_msg>types: Don't consider null to be an object.<commit_after>/*global Uint8Array, Uint16Array*/

var utils = require('./utils');
var isRegExp = utils.isRegExp;
var leftPad = utils.leftPad;
var shim = require('./shim');
var json = shim.JSON;
var every = shim.every;
var some = shim.some;
var forEach = shim.forEach;
var map = shim.map;
var getKeys = shim.getKeys;
var reduce = shim.reduce;
var extend = utils.extend;

module.exports = function (expect) {
    expect.addType({
        name: 'object',
        identify: function (obj) {
            return obj && typeof obj === 'object';
        },
        equal: function (a, b, equal) {
            if (a === b) {
                return true;
            }

            // an identical "prototype" property.
            if (a.prototype !== b.prototype) {
                return false;
            }
            //~~~I've managed to break Object.keys through screwy arguments passing.
            //   Converting to array solves the problem.
            if (utils.isArguments(a)) {
                if (!utils.isArguments(b)) {
                    return false;
                }
                return equal(Array.prototype.slice.call(a), Array.prototype.slice.call(b));
            }
            var actualKeys = utils.getKeysOfDefinedProperties(a),
                expectedKeys = utils.getKeysOfDefinedProperties(b),
                key,
                i;

            // having the same number of owned properties (keys incorporates hasOwnProperty)
            if (actualKeys.length !== expectedKeys.length) {
                return false;
            }
            //the same set of keys (although not necessarily the same order),
            actualKeys.sort();
            expectedKeys.sort();
            //~~~cheap key test
            for (i = actualKeys.length - 1; i >= 0; i -= 1) {
                if (actualKeys[i] !== expectedKeys[i]) {
                    return false;
                }
            }

            //equivalent values for every corresponding key, and
            //~~~possibly expensive deep test
            for (i = actualKeys.length - 1; i >= 0; i -= 1) {
                key = actualKeys[i];
                if (!equal(a[key], b[key])) {
                    return false;
                }
            }
            return true;
        },
        inspect: function (output, obj, inspect) {
            var keys = getKeys(obj);
            if (keys.length === 0) {
                return output.text('{}');
            }

            var inspectedItems = map(keys, function (key) {
                var propertyOutput = output.clone();
                if (key.match(/["' ]/)) {
                    propertyOutput.strings(expect.inspect(key));
                } else {
                    propertyOutput.key(key);
                }
                propertyOutput.text(':');

                var hasGetter = obj.__lookupGetter__ && obj.__lookupGetter__(key);
                var hasSetter = obj.__lookupGetter__ && obj.__lookupSetter__(key);

                if (hasGetter || !hasSetter) {
                    propertyOutput.sp().append(inspect(propertyOutput.clone(), obj[key]));
                }

                if (hasGetter && hasSetter) {
                    propertyOutput.sp().text('[Getter/Setter]');
                } else if (hasGetter) {
                    propertyOutput.sp().text('[Getter]');
                } else if (hasSetter) {
                    propertyOutput.sp().text('[Setter]');
                }

                return propertyOutput;
            });

            var width = 0;
            var multipleLines = some(inspectedItems, function (o) {
                var size = o.size();
                width += size.width;
                return width > 50 || size.height > 1;
            });

            forEach(inspectedItems, function (inspectedItem, index) {
                var lastIndex = index === inspectedItems.length - 1;

                if (!lastIndex) {
                    inspectedItem.text(',');
                }
            });

            if (multipleLines) {
                output.text('{').nl().indentLines();

                forEach(inspectedItems, function (inspectedItem, index) {
                    output.i().block(inspectedItem).nl();
                });

                output.outdentLines().text('}');
            } else {
                output.text('{ ');
                forEach(inspectedItems, function (inspectedItem, index) {
                    output.append(inspectedItem);
                    var lastIndex = index === inspectedItems.length - 1;
                    if (!lastIndex) {
                        output.sp();
                    }
                });
                output.text(' }');
            }
            return output;
        },
        toJSON: function (obj, toJSON) {
            return reduce(getKeys(obj), function (result, key) {
                result[key] = toJSON(obj[key]);
                return result;
            }, {});
        }
    });

    expect.addType({
        name: 'array',
        identify: function (arr) {
            return utils.isArray(arr) || utils.isArguments(arr);
        },
        equal: function (a, b, equal) {
            return a === b || (a.length === b.length && every(a, function (v, index) {
                return equal(v, b[index]);
            }));
        },
        inspect: function (output, arr, inspect, depth) {
            if (arr.length === 0) {
                return output.text('[]');
            }

            if (depth === 1) {
                return output.text('[...]');
            }

            var inspectedItems = map(arr, function (v) {
                return inspect(output.clone(), v);
            });

            var width = 0;
            var multipleLines = some(inspectedItems, function (o) {
                var size = o.size();
                width += size.width;
                return width > 50 || o.height > 1;
            });

            forEach(inspectedItems, function (inspectedItem, index) {
                var lastIndex = index === inspectedItems.length - 1;
                if (!lastIndex) {
                    inspectedItem.text(',');
                }
            });

            if (multipleLines) {
                output.text('[').nl().indentLines();

                forEach(inspectedItems, function (inspectedItem, index) {
                    output.i().block(inspectedItem).nl();
                });

                output.outdentLines().text(']');
            } else {
                output.text('[ ');
                forEach(inspectedItems, function (inspectedItem, index) {
                    output.append(inspectedItem);
                    var lastIndex = index === inspectedItems.length - 1;
                    if (!lastIndex) {
                        output.sp();
                    }
                });
                output.text(' ]');
            }
            return output;
        },
        toJSON: function (arr, toJSON) {
            return map(arr, toJSON);
        }
    });

    expect.addType({
        base: 'object',
        name: 'Error',
        identify: function (value) {
            return utils.isError(value);
        },
        equal: function (a, b, equal) {
            return a === b ||
                (equal(a.message, b.message) && this.baseType.equal(a, b, equal));
        },
        inspect: function (output, value, inspect) {
            var errorObject = extend({
                message: value.message
            }, value);
            return output.text('[Error: ').append(inspect(output.clone(), errorObject)).text(']');
        }
    });

    expect.addType({
        name: 'date',
        identify: function (obj) {
            return Object.prototype.toString.call(obj) === '[object Date]';
        },
        equal: function (a, b) {
            return a.getTime() === b.getTime();
        },
        inspect: function (output, date) {
            return output.text('[Date ').text(date.toUTCString()).text(']');
        },
        toJSON: function (date) {
            return {
                $Date: expect.inspect(date)
            };
        }
    });

    expect.addType({
        name: 'function',
        identify: function (f) {
            return typeof f === 'function';
        },
        equal: function (a, b) {
            return a === b || a.toString() === b.toString();
        },
        inspect: function (output, f) {
            output.text('[Function');
            if (f.name) {
                output.text(': ').text(f.name);
            }
            output.text(']');
            return output;
        },
        toJSON: function (f) {
            return {
                $Function: expect.inspect(f)
            };
        }
    });

    expect.addType({
        name: 'regexp',
        identify: isRegExp,
        equal: function (a, b) {
            return a === b || (
                a.source === b.source &&
                    a.global === b.global &&
                    a.ignoreCase === b.ignoreCase &&
                    a.multiline === b.multiline
            );
        },
        inspect: function (output, regExp) {
            return output.text(regExp);
        },
        toJSON: function (regExp) {
            return {
                $RegExp: expect.inspect(regExp)
            };
        }
    });

    expect.addType({
        name: 'DomElement',
        identify: function (value) {
            return utils.isDOMElement(value);
        },
        inspect: function (output, value) {
            return output.text(utils.getOuterHTML(value));
        }
    });

    function getHexDumpLinesForBufferLikeObject(obj, width, digitWidth, maxLength) {
        digitWidth = digitWidth || 2;
        var hexDumpLines = [];
        if (typeof maxLength === 'undefined') {
            maxLength = obj.length;
        }
        if (typeof width !== 'number') {
            width = 16;
        } else if (width === 0) {
            width = maxLength;
        }
        for (var i = 0 ; i < maxLength ; i += width) {
            var hexChars = '',
                asciiChars = ' |';

            for (var j = 0 ; j < width ; j += 1) {
                if (i + j < maxLength) {
                    var octet = obj[i + j];
                    hexChars += leftPad(octet.toString(16).toUpperCase(), digitWidth, '0') + ' ';
                    asciiChars += (octet >= 32 && octet < 127) ? String.fromCharCode(octet) : '.';
                } else if (digitWidth === 2) {
                    hexChars += '   ';
                }
            }

            if (digitWidth === 2) {
                asciiChars += '|';
                hexDumpLines.push(hexChars + asciiChars);
            } else {
                hexDumpLines.push(hexChars.replace(/\s+$/, ''));
            }
        }
        return hexDumpLines;
    }

    function inspectBufferLikeObject(output, buffer, digitWidth, maxLength) {
        if (typeof maxLength !== 'number') {
            maxLength = 20;
        }
        if (buffer.length > maxLength) {
            output.text(getHexDumpLinesForBufferLikeObject(buffer, 0, digitWidth, maxLength))
                .text(' (+').text(buffer.length - maxLength).text(')');

        } else {
            var hexDumpLines = getHexDumpLinesForBufferLikeObject(buffer, 0, digitWidth);
            forEach(hexDumpLines, function (line, i) {
                if (i > 0) {
                    output.nl();
                }
                output.text(line);
            });
        }
        return output;
    }

    function bufferLikeObjectsEqual(a, b) {
        if (a === b) {
            return true;
        }

        if (a.length !== b.length) return false;

        for (var i = 0; i < a.length; i += 1) {
            if (a[i] !== b[i]) return false;
        }

        return true;
    }

    if (typeof Buffer !== 'undefined') {
        expect.addType({
            name: 'Buffer',
            identify: Buffer.isBuffer,
            equal: bufferLikeObjectsEqual,
            inspect: function (output, buffer) {
                output.text('[Buffer ');
                inspectBufferLikeObject(output, buffer);
                return output.text(']');
            },
            toJSON: function (buffer) {
                return {
                    $Buffer: getHexDumpLinesForBufferLikeObject(buffer)
                };
            }
        });
    }

    if (typeof Uint8Array !== 'undefined') {
        expect.addType({
            name: 'Uint8Array',
            identify: function (obj) {
                return obj && obj instanceof Uint8Array;
            },
            equal: bufferLikeObjectsEqual,
            inspect: function (output, uint8Array) {
                output.text('[Uint8Array ');
                inspectBufferLikeObject(output, uint8Array);
                return output.text(']');
            },
            toJSON: function (uint8Array) {
                return {
                    $Uint8Array: getHexDumpLinesForBufferLikeObject(uint8Array)
                };
            }
        });
    }

    if (typeof Uint16Array !== 'undefined') {
        expect.addType({
            name: 'Uint16Array',
            identify: function (obj) {
                return obj && obj instanceof Uint16Array;
            },
            equal: bufferLikeObjectsEqual,
            inspect: function (output, uint16Array) {
                output.text('[Uint16Array ');
                inspectBufferLikeObject(output, uint16Array, 4);
                return output.text(']');
            },
            toJSON: function (uint16Array) {
                return {
                    $Uint16Array: getHexDumpLinesForBufferLikeObject(uint16Array, 8, 4)
                };
            }
        });
    }

    expect.addType({
        name: 'string',
        identify: function (value) {
            return typeof value === 'string';
        },
        inspect: function (output, value) {
            return output.strings('\'')
                .strings(json.stringify(value).replace(/^"|"$/g, '')
                      .replace(/'/g, "\\'")
                      .replace(/\\"/g, '"'))
                .strings('\'');
        }
    });

    expect.addType({
        name: 'number',
        identify: function (value) {
            return typeof value === 'number';
        }
    });

    expect.addType({
        name: 'boolean',
        identify: function (value) {
            return typeof value === 'boolean';
        }
    });

    expect.addType({
        name: 'undefined',
        identify: function (value) {
            return typeof value === 'undefined';
        }
    });

    expect.addType({
        name: 'null',
        identify: function (value) {
            return value === null;
        }
    });
};
<|endoftext|>


<commit_before>/*!
 * Nodeunit
 * Copyright (c) 2010 Caolan McMahon
 * MIT Licensed
 *
 * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
 * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build.
 * Only code on that line will be removed, it's mostly to avoid requiring code
 * that is node specific
 */

/**
 * Module dependencies
 */

var assert = require('./assert'),     //@REMOVE_LINE_FOR_BROWSER
    async = require('../deps/async'); //@REMOVE_LINE_FOR_BROWSER

/**
 * Creates assertion objects representing the result of an assert call.
 * Accepts an object or AssertionError as its argument.
 *
 * @param {object} obj
 * @api public
 */

exports.assertion = function (obj) {
    return {
        method: obj.method || '',
        message: obj.message || (obj.error && obj.error.message) || '',
        error: obj.error,
        passed: function () {
            return !this.error;
        },
        failed: function () {
            return Boolean(this.error);
        }
    };
};

/**
 * Creates an assertion list object representing a group of assertions.
 * Accepts an array of assertion objects.
 *
 * @param {Array} arr
 * @param {Number} duration
 * @api public
 */

exports.assertionList = function (arr, duration) {
    var that = arr || [];
    that.failures = function () {
        var failures = 0;
        for (var i = 0; i < this.length; i += 1) {
            if (this[i].failed()) {
                failures += 1;
            }
        }
        return failures;
    };
    that.passes = function () {
        return that.length - that.failures();
    };
    that.duration = duration || 0;
    return that;
};

/**
 * Create a wrapper function for assert module methods. Executes a callback
 * after it's complete with an assertion object representing the result.
 *
 * @param {Function} callback
 * @api private
 */

var assertWrapper = function (callback) {
    return function (new_method, assert_method, arity) {
        return function () {
            var message = arguments[arity - 1];
            var a = exports.assertion({method: new_method, message: message});
            try {
                assert[assert_method].apply(null, arguments);
            }
            catch (e) {
                a.error = e;
            }
            callback(a);
        };
    };
};

/**
 * Creates the 'test' object that gets passed to every test function.
 * Accepts the name of the test function as its first argument, followed by
 * the start time in ms, the options object and a callback function.
 *
 * @param {String} name
 * @param {Number} start
 * @param {Object} options
 * @param {Function} callback
 * @api public
 */

exports.test = function (name, start, options, callback) {
    var expecting;
    var allExpectedCalls = [];
    var a_list = [];

    var wrapAssert = assertWrapper(function (a) {
        a_list.push(a);
        if (options.log) {
            async.nextTick(function () {
                options.log(a);
            });
        }
    });

    var test = {
        done: function (err) {
            if (expecting !== undefined && expecting !== a_list.length) {
                var e = new Error(
                    'Expected ' + expecting + ' assertions, ' +
                    a_list.length + ' ran'
                );
                var a1 = exports.assertion({method: 'expect', error: e});
                a_list.push(a1);
                if (options.log) {
                    async.nextTick(function () {
                        options.log(a1);
                    });
                }
            }
            for (var i = 0; i < allExpectedCalls.length; i++) {
                allExpectedCalls[i].assertEmpty();
            }
            if (err) {
                var a2 = exports.assertion({error: err});
                a_list.push(a2);
                if (options.log) {
                    async.nextTick(function () {
                        options.log(a2);
                    });
                }
            }
            var end = new Date().getTime();
            async.nextTick(function () {
                var assertion_list = exports.assertionList(a_list, end - start);
                options.testDone(name, assertion_list);
                callback(null, a_list);
            });
        },
        ok: wrapAssert('ok', 'ok', 2),
        same: wrapAssert('same', 'deepEqual', 3),
        equals: wrapAssert('equals', 'equal', 3),
        expect: function (num) {
            expecting = num;
        },
        mockFunction: function(name) {
            var callNumber = 0;
            var myExpectedCalls = Array.prototype.slice.call(arguments, 1);
            allExpectedCalls.push(myExpectedCalls);

            myExpectedCalls.assertEmpty = function() {

                if (this.length > 0) {
                    var e2 = new Error(
                        'Expected ' + this.length + ' more "' + name +
                        '" mock method calls, than actually ran'
                    );
                    var a3 = exports.assertion({method: 'mockMethod', error: e2});
                    a_list.push(a3);
                    if (options.log) {
                        async.nextTick(function (a3) {
                            options.log(a3);
                        }.bind(a3));
                    }
                }
            };

            var mock = function() {

                // Detect call
                callNumber++;
                if (myExpectedCalls.length == 0) {
                    test.fail('Calling "' + name + '" function more times, than expected. Call #' + callNumber);
                    return;
                }

                var expectedCall = myExpectedCalls.shift();

                // Trigger custom logic, if any
                if (typeof expectedCall === 'function') {
                    return expectedCall.apply(null, arguments);
                }

                // Expand short form
                if (Array.isArray(expectedCall)) {
                    expectedCall = { arguments: expectedCall };
                }

                // Check arguments
                var expectedArguments = expectedCall.arguments || [];

                if (arguments.length != expectedArguments.length) {
                    test.fail(
                        'Wrong "' + name + '" function argument count. Expected ' + expectedArguments.length + ', got ' +
                        arguments.length + ', call #' + callNumber
                    );
                }
                else {
                    for (var i = 0; i < arguments.length; i++) {
                        test.deepEqual(arguments[i], expectedArguments[i],
                            'Wrong "' + name + '" function argument #' + (i+1) + ' value. Expected "' + expectedArguments[i] + '" got "' +
                            arguments[i] + '", call #' + callNumber
                        );
                    }
                }

                // Construct
                if (expectedCall.construct) {
                    // _.assign(this, expectedCall.construct):
                    for (var prop in expectedCall.construct) {
                        if (expectedCall.construct.hasOwnProperty(prop)) {
                            this[prop] = expectedCall.construct[prop];
                        }
                    }
                }

                // Apply side effects
                if (expectedCall.sideEffect) {
                    expectedCall.sideEffect(); // Don't pass arguments. Use constants inside
                }

                // Throw
                if (expectedCall.throw) {
                    throw expectedCall.throw;
                }

                // Return
                return expectedCall.return;
            };

            mock.mockStep = function() {

                myExpectedCalls.assertEmpty();

                // Note: do not override myExpectedCalls. It has custom methods
                for (var i = 0; i < arguments.length; i++) {
                    myExpectedCalls[i] = arguments[i];
                }
            };

            return mock;
        },
        _assertion_list: a_list
    };
    // add all functions from the assert module
    for (var k in assert) {
        if (assert.hasOwnProperty(k)) {
            test[k] = wrapAssert(k, k, assert[k].length);
        }
    }
    return test;
};

/**
 * Ensures an options object has all callbacks, adding empty callback functions
 * if any are missing.
 *
 * @param {Object} opt
 * @return {Object}
 * @api public
 */

exports.options = function (opt) {
    var optionalCallback = function (name) {
        opt[name] = opt[name] || function () {};
    };

    optionalCallback('moduleStart');
    optionalCallback('moduleDone');
    optionalCallback('testStart');
    optionalCallback('testReady');
    optionalCallback('testDone');
    //optionalCallback('log');

    // 'done' callback is not optional.

    return opt;
};
<commit_msg>Make test.fail() expose message<commit_after>/*!
 * Nodeunit
 * Copyright (c) 2010 Caolan McMahon
 * MIT Licensed
 *
 * THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
 * You can use @REMOVE_LINE_FOR_BROWSER to remove code from the browser build.
 * Only code on that line will be removed, it's mostly to avoid requiring code
 * that is node specific
 */

/**
 * Module dependencies
 */

var assert = require('./assert'),     //@REMOVE_LINE_FOR_BROWSER
    async = require('../deps/async'); //@REMOVE_LINE_FOR_BROWSER

/**
 * Creates assertion objects representing the result of an assert call.
 * Accepts an object or AssertionError as its argument.
 *
 * @param {object} obj
 * @api public
 */

exports.assertion = function (obj) {
    return {
        method: obj.method || '',
        message: obj.message || (obj.error && obj.error.message) || '',
        error: obj.error,
        passed: function () {
            return !this.error;
        },
        failed: function () {
            return Boolean(this.error);
        }
    };
};

/**
 * Creates an assertion list object representing a group of assertions.
 * Accepts an array of assertion objects.
 *
 * @param {Array} arr
 * @param {Number} duration
 * @api public
 */

exports.assertionList = function (arr, duration) {
    var that = arr || [];
    that.failures = function () {
        var failures = 0;
        for (var i = 0; i < this.length; i += 1) {
            if (this[i].failed()) {
                failures += 1;
            }
        }
        return failures;
    };
    that.passes = function () {
        return that.length - that.failures();
    };
    that.duration = duration || 0;
    return that;
};

/**
 * Create a wrapper function for assert module methods. Executes a callback
 * after it's complete with an assertion object representing the result.
 *
 * @param {Function} callback
 * @api private
 */

var assertWrapper = function (callback) {
    return function (new_method, assert_method, arity) {
        return function () {
            var message = arguments[arity - 1];
            var a = exports.assertion({method: new_method, message: message});
            try {
                assert[assert_method].apply(null, arguments);
            }
            catch (e) {
                a.error = e;
            }
            callback(a);
        };
    };
};

/**
 * Creates the 'test' object that gets passed to every test function.
 * Accepts the name of the test function as its first argument, followed by
 * the start time in ms, the options object and a callback function.
 *
 * @param {String} name
 * @param {Number} start
 * @param {Object} options
 * @param {Function} callback
 * @api public
 */

exports.test = function (name, start, options, callback) {
    var expecting;
    var allExpectedCalls = [];
    var a_list = [];

    var wrapAssert = assertWrapper(function (a) {
        a_list.push(a);
        if (options.log) {
            async.nextTick(function () {
                options.log(a);
            });
        }
    });

    var test = {
        done: function (err) {
            if (expecting !== undefined && expecting !== a_list.length) {
                var e = new Error(
                    'Expected ' + expecting + ' assertions, ' +
                    a_list.length + ' ran'
                );
                var a1 = exports.assertion({method: 'expect', error: e});
                a_list.push(a1);
                if (options.log) {
                    async.nextTick(function () {
                        options.log(a1);
                    });
                }
            }
            for (var i = 0; i < allExpectedCalls.length; i++) {
                allExpectedCalls[i].assertEmpty();
            }
            if (err) {
                var a2 = exports.assertion({error: err});
                a_list.push(a2);
                if (options.log) {
                    async.nextTick(function () {
                        options.log(a2);
                    });
                }
            }
            var end = new Date().getTime();
            async.nextTick(function () {
                var assertion_list = exports.assertionList(a_list, end - start);
                options.testDone(name, assertion_list);
                callback(null, a_list);
            });
        },
        ok: wrapAssert('ok', 'ok', 2),
        same: wrapAssert('same', 'deepEqual', 3),
        equals: wrapAssert('equals', 'equal', 3),
        expect: function (num) {
            expecting = num;
        },
        mockFunction: function(name) {
            var callNumber = 0;
            var myExpectedCalls = Array.prototype.slice.call(arguments, 1);
            allExpectedCalls.push(myExpectedCalls);

            myExpectedCalls.assertEmpty = function() {

                if (this.length > 0) {
                    var e2 = new Error(
                        'Expected ' + this.length + ' more "' + name +
                        '" mock method calls, than actually ran'
                    );
                    var a3 = exports.assertion({method: 'mockMethod', error: e2});
                    a_list.push(a3);
                    if (options.log) {
                        async.nextTick(function (a3) {
                            options.log(a3);
                        }.bind(a3));
                    }
                }
            };

            var mock = function() {

                // Detect call
                callNumber++;
                if (myExpectedCalls.length == 0) {
                    test.fail(null, null, 'Calling "' + name + '" function more times, than expected. Call #' + callNumber);
                    return;
                }

                var expectedCall = myExpectedCalls.shift();

                // Trigger custom logic, if any
                if (typeof expectedCall === 'function') {
                    return expectedCall.apply(null, arguments);
                }

                // Expand short form
                if (Array.isArray(expectedCall)) {
                    expectedCall = { arguments: expectedCall };
                }

                // Check arguments
                var expectedArguments = expectedCall.arguments || [];

                if (arguments.length != expectedArguments.length) {
                    test.fail(null, null,
                        'Wrong "' + name + '" function argument count. Expected ' + expectedArguments.length + ', got ' +
                        arguments.length + ', call #' + callNumber
                    );
                }
                else {
                    for (var i = 0; i < arguments.length; i++) {
                        test.deepEqual(arguments[i], expectedArguments[i],
                            'Wrong "' + name + '" function argument #' + (i+1) + ' value. Expected "' + expectedArguments[i] + '" got "' +
                            arguments[i] + '", call #' + callNumber
                        );
                    }
                }

                // Construct
                if (expectedCall.construct) {
                    // _.assign(this, expectedCall.construct):
                    for (var prop in expectedCall.construct) {
                        if (expectedCall.construct.hasOwnProperty(prop)) {
                            this[prop] = expectedCall.construct[prop];
                        }
                    }
                }

                // Apply side effects
                if (expectedCall.sideEffect) {
                    expectedCall.sideEffect(); // Don't pass arguments. Use constants inside
                }

                // Throw
                if (expectedCall.throw) {
                    throw expectedCall.throw;
                }

                // Return
                return expectedCall.return;
            };

            mock.mockStep = function() {

                myExpectedCalls.assertEmpty();

                // Note: do not override myExpectedCalls. It has custom methods
                for (var i = 0; i < arguments.length; i++) {
                    myExpectedCalls[i] = arguments[i];
                }
            };

            return mock;
        },
        _assertion_list: a_list
    };
    // add all functions from the assert module
    for (var k in assert) {
        if (assert.hasOwnProperty(k)) {
            test[k] = wrapAssert(k, k, assert[k].length);
        }
    }
    return test;
};

/**
 * Ensures an options object has all callbacks, adding empty callback functions
 * if any are missing.
 *
 * @param {Object} opt
 * @return {Object}
 * @api public
 */

exports.options = function (opt) {
    var optionalCallback = function (name) {
        opt[name] = opt[name] || function () {};
    };

    optionalCallback('moduleStart');
    optionalCallback('moduleDone');
    optionalCallback('testStart');
    optionalCallback('testReady');
    optionalCallback('testDone');
    //optionalCallback('log');

    // 'done' callback is not optional.

    return opt;
};
<|endoftext|>


<commit_before>/* jslint node: true, esnext: true */
"use strict";

const log4js = require('log4js');
const logger = log4js.getLogger('kronos-step-archive-tar');
//logger.setLevel(log4js.levels.ERROR);

const tar = require('tar-stream');

module.exports = {
	"description": "decomposes tar archive into individual output requests",
	"endpoints": {
		"in": {
			"direction": "in(active,passive)",
			"uti": "public.tar-archive",
			"contentInfo": {
				"name": {
					"description": "The file name of the original tar file",
					"mandatory": false
				}
			}
		},
		"out": {
			"direction": "out(active)",
			"contentInfo": {
				"archiveName": {
					"description": "The file name of the original tar file"
				},
				"name": {
					"description": "The file name of the extratcted entry"
				},
				"mtime": {
					"description": "modification time of the entry"
				}
			}
		}
	},
	initialize(manager, step) {
		let archiveName;

		// Each genartor must be initialized before the first use
		let isInitialized = false;

		// --------------------------------------
		// Initialize the OUT endpoint
		// --------------------------------------
		const out = step.endpoints.out.initialize(manager);
		logger.debug(`Initialized the OUT endpoint`);

		// --------------------------------------
		// get the untar function
		// --------------------------------------
		const extract = tar.extract();

		// This will be called from the untar function for each element in the TAR archive
		extract.on('entry', function (header, stream, callback) {
			step.debug(`Extract new entry '${header.name}'`);

			//console.log(`got tar entry: ${JSON.stringify(header)}`);

			stream.on('end', function () {
				callback(); // ready for next entry
			});

			header.archiveName = archiveName;

			// if (!isInitialized) {
			// 	out.next();
			// 	isInitialized = true;
			// }

			try {
				out.next({
					info: header,
					stream: stream
				});
			} catch (e) {
				step.error(e);
				stream.resume();
			}
		});

		// --------------------------------------
		// initialize the IN endpoint
		// --------------------------------------
		if (step.endpoints.in.isActive) {
			const iterator = step.endpoints.in.initialize(manager);
			for (let request of iterator) {
				// set the archive name
				archiveName = request.info.name;

				request.stream.pipe(extract);
			}
		} else {
			step.endpoints.in.initialize(manager, function* () {
				const request = yield;

				//console.log(`Passive got tar stream: ${JSON.stringify(request.info)}`);

				// set the archive name
				archiveName = request.info.name;
				step.debug(`New archive ${archiveName}`);

				request.stream.pipe(extract);
			});
		}
	}
};
<commit_msg>add log messages<commit_after>/* jslint node: true, esnext: true */
"use strict";

const log4js = require('log4js');
const logger = log4js.getLogger('kronos-step-archive-tar');
//logger.setLevel(log4js.levels.ERROR);

const tar = require('tar-stream');

module.exports = {
	"description": "decomposes tar archive into individual output requests",
	"endpoints": {
		"in": {
			"direction": "in(active,passive)",
			"uti": "public.tar-archive",
			"contentInfo": {
				"name": {
					"description": "The file name of the original tar file",
					"mandatory": false
				}
			}
		},
		"out": {
			"direction": "out(active)",
			"contentInfo": {
				"archiveName": {
					"description": "The file name of the original tar file"
				},
				"name": {
					"description": "The file name of the extratcted entry"
				},
				"mtime": {
					"description": "modification time of the entry"
				}
			}
		}
	},
	initialize(manager, step) {
		let archiveName;

		// Each genartor must be initialized before the first use
		let isInitialized = false;

		// --------------------------------------
		// Initialize the OUT endpoint
		// --------------------------------------
		const out = step.endpoints.out.initialize(manager);
		logger.debug(`Initialized the OUT endpoint`);

		// --------------------------------------
		// get the untar function
		// --------------------------------------
		const extract = tar.extract();

		// This will be called from the untar function for each element in the TAR archive
		extract.on('entry', function (header, stream, callback) {
			step.debug(`Extract new entry '${header.name}'`);

			//console.log(`got tar entry: ${JSON.stringify(header)}`);

			stream.on('end', function () {
				callback(); // ready for next entry
			});

			header.archiveName = archiveName;

			// if (!isInitialized) {
			// 	out.next();
			// 	isInitialized = true;
			// }

			try {
				out.next({
					info: header,
					stream: stream
				});
			} catch (e) {
				step.error(e);
				stream.resume();
			}
		});

		// --------------------------------------
		// initialize the IN endpoint
		// --------------------------------------
		if (step.endpoints.in.isActive) {
			const iterator = step.endpoints.in.initialize(manager);
			logger.debug(`Initialized the IN endpoint Active`);
			for (let request of iterator) {
				// set the archive name
				archiveName = request.info.name;

				request.stream.pipe(extract);
			}
		} else {
			step.endpoints.in.initialize(manager, function* () {
				const request = yield;

				//console.log(`Passive got tar stream: ${JSON.stringify(request.info)}`);

				// set the archive name
				archiveName = request.info.name;
				step.debug(`New archive ${archiveName}`);

				request.stream.pipe(extract);
			});
			logger.debug(`Initialized the IN endpoint Passive`);
		}
	}
};
<|endoftext|>