<commit_before>// Current version turns on office light when there's motion
// this code has an adaptive algorithm that extends the time the
// light is on the more often it is turned on.

const hue = require("node-hue-api")
const moment = require("moment")
const constants = require('./constants')
const api = new hue.HueApi(constants.ip, constants.username)
const particle = require('./lib/Particle')
const _ = require('underscore')
const lightState = hue.lightState
const huelib = require('./lib/Hue')
const lightName = require('./lib/LightName')

const debug = function(m) { console.log(`${moment().format('MMM DD hh:mm:ss')}   ${m}`) }

const lightIntervals = [10, 60, 5*60, 5*60, 10*60] // seconds
let lightIntervalIndex = 0

let senseTimerHandle = null
let lightTimerHandle = null
let motionSticky = false

// called whenever motion is seen
const onMotion = function() {
	debug(`saw motion`)
	debug(`setting light timer to ${getLightInterval()} seconds`)
	motionSticky = true
	turnLightsOn()
	clearTimeout(lightTimerHandle)
	lightTimerHandle = setTimeout(turnLightsOff, getLightInterval() * 1000)

	// set a timer that determines next time to see if there's been motion
	// set twice as long as light is on, so that user can wave to turn light on
	// when it goes off, and process is still sensing
	if (!senseTimerHandle) {
		debug(`setting sense timer to ${getLightInterval()} seconds`)
		senseTimerHandle = setTimeout(senseTimerExpired, getLightInterval() * 1000)
	}
}

const onHighMotion = function() {
	debug(`saw lots of motion, setting interval to max`)
	lightIntervalIndex = lightIntervals.length - 1
}

const senseTimerExpired = function() {
	debug(`sense timer expired`)
	senseTimerHandle = null
	if (motionSticky) {
		debug(`there was motion during sense period`)
		motionSticky = false
		increaseLightInterval()
		// set timer again and wait another sense interval
		debug(`setting sense timer for ${getLightInterval()} seconds`)
		senseTimerHandle = setTimeout(senseTimerExpired, getLightInterval() * 1000)
	} else {
		// no motion in last sense interval
		// set sense interval back to default and turn lights off
		resetLightInterval()
		debug(`no motion in sense interval, light interval is now ${getLightInterval()} seconds`)
	}
}

const getLightInterval = function() {
	return lightIntervals[lightIntervalIndex]
}

const increaseLightInterval = function() {
	// don't increase past length of array
	lightIntervalIndex = Math.min(lightIntervalIndex + 1, lightIntervals.length - 1)
}

const resetLightInterval = function() {
	lightIntervalIndex = 0
}

// turn light to low on then fade to on. Turn light to low off, then fade to off
var lights = null // set by main from argv
const lowOn = lightState.create().on(true).hsb(100, 30, 0)
const on = lightState.create().on(true).white(325, 100	).transition(3000)
const lowOff = lightState.create().on(true).hsb(250, 100, 0).transition(10000)
const off = lightState.create().on(false)

// used to transition between low on to on and low off to off states.
let lightTimer = null
let _lightState = 'off' // off, lowOff, on, lowOn

const turnLightsOn = function() {
	if (isDaytime()) { return }
	if (_lightState === 'off') {
		// set to lowOn, wait, On
		clearTimeout(lightTimer)
		setLights(lights, lowOn)
		_lightState = 'lowOn'
		lightTimer = setTimeout(function() {
			setLights(lights, on)
			_lightState = 'on'
		}, 1000)
	} else if (_lightState === 'lowOff') {
		// straight to on
		clearTimeout(lightTimer)
		setLights(lights, on)
		_lightState = 'on'
	}
	// if _lightState is lowOn or on, do nothing
}

const turnLightsOff = function() {
	if (_lightState === 'on' || _lightState === 'lowOn') {
		debug(`turning lights off`)
		clearTimeout(lightTimer)
		setLights(lights, lowOff)
		_lightState = 'lowOff'
		lightTimer = setTimeout(function() {
			setLights(lights, off)
			_lightState = 'off'
		}, 10000)
	}
}

const setLights = function(lights, lightState) {
	lights.forEach(function(light) {
		api.setLightState(light.id, lightState)
	})
}

const isDaytime = function() {
	let h = moment().hour()
	return (h > 7 && h < 17)
}

const loop = function() {
	particle.getMotion()
	.then(function(reply) {
		if (reply.return_value === 1) {
			onMotion()
		} else if (reply.return_value === 2) {
			onHighMotion()
			onMotion()
		}
		setTimeout(loop, 1000)
	}).catch(function(err) {
		debug(`Caught error: ${err}`)
		setTimeout(loop, 10000)
	})
}

const main = function() {
	if (process.argv.length < 3) {
		usage()
		process.exit(0)
	}

	debug(`starting`)
	lights = lightName.lightsFromNamesOrExit(process.argv.slice(2))
	setLights(lights, off)
	loop()
}

const usage = function() {
	debug("usage: node " + __filename + " LIGHTNAME LIGHTNAME ...")
	debug("Each LIGHTNAME is the name of a light defined in constants.js:")
	var lightNames = _.pluck(constants.lights, 'name').join(", ")
	debug('"' + lightNames + '"')
}

main()

<commit_msg>more purple<commit_after>// Current version turns on office light when there's motion
// this code has an adaptive algorithm that extends the time the
// light is on the more often it is turned on.

const hue = require("node-hue-api")
const moment = require("moment")
const constants = require('./constants')
const api = new hue.HueApi(constants.ip, constants.username)
const particle = require('./lib/Particle')
const _ = require('underscore')
const lightState = hue.lightState
const huelib = require('./lib/Hue')
const lightName = require('./lib/LightName')

const debug = function(m) { console.log(`${moment().format('MMM DD hh:mm:ss')}   ${m}`) }

const lightIntervals = [10, 60, 5*60, 5*60, 10*60] // seconds
let lightIntervalIndex = 0

let senseTimerHandle = null
let lightTimerHandle = null
let motionSticky = false

// called whenever motion is seen
const onMotion = function() {
	debug(`saw motion`)
	debug(`setting light timer to ${getLightInterval()} seconds`)
	motionSticky = true
	turnLightsOn()
	clearTimeout(lightTimerHandle)
	lightTimerHandle = setTimeout(turnLightsOff, getLightInterval() * 1000)

	// set a timer that determines next time to see if there's been motion
	// set twice as long as light is on, so that user can wave to turn light on
	// when it goes off, and process is still sensing
	if (!senseTimerHandle) {
		debug(`setting sense timer to ${getLightInterval()} seconds`)
		senseTimerHandle = setTimeout(senseTimerExpired, getLightInterval() * 1000)
	}
}

const onHighMotion = function() {
	debug(`saw lots of motion, setting interval to max`)
	lightIntervalIndex = lightIntervals.length - 1
}

const senseTimerExpired = function() {
	debug(`sense timer expired`)
	senseTimerHandle = null
	if (motionSticky) {
		debug(`there was motion during sense period`)
		motionSticky = false
		increaseLightInterval()
		// set timer again and wait another sense interval
		debug(`setting sense timer for ${getLightInterval()} seconds`)
		senseTimerHandle = setTimeout(senseTimerExpired, getLightInterval() * 1000)
	} else {
		// no motion in last sense interval
		// set sense interval back to default and turn lights off
		resetLightInterval()
		debug(`no motion in sense interval, light interval is now ${getLightInterval()} seconds`)
	}
}

const getLightInterval = function() {
	return lightIntervals[lightIntervalIndex]
}

const increaseLightInterval = function() {
	// don't increase past length of array
	lightIntervalIndex = Math.min(lightIntervalIndex + 1, lightIntervals.length - 1)
}

const resetLightInterval = function() {
	lightIntervalIndex = 0
}

// turn light to low on then fade to on. Turn light to low off, then fade to off
var lights = null // set by main from argv
const lowOn = lightState.create().on(true).hsb(250, 100, 0)
const on = lightState.create().on(true).white(325, 100	).transition(3000)
const lowOff = lightState.create().on(true).hsb(250, 100, 0).transition(10000)
const off = lightState.create().on(false)

// used to transition between low on to on and low off to off states.
let lightTimer = null
let _lightState = 'off' // off, lowOff, on, lowOn

const turnLightsOn = function() {
	if (isDaytime()) { return }
	if (_lightState === 'off') {
		// set to lowOn, wait, On
		clearTimeout(lightTimer)
		setLights(lights, lowOn)
		_lightState = 'lowOn'
		lightTimer = setTimeout(function() {
			setLights(lights, on)
			_lightState = 'on'
		}, 1000)
	} else if (_lightState === 'lowOff') {
		// straight to on
		clearTimeout(lightTimer)
		setLights(lights, on)
		_lightState = 'on'
	}
	// if _lightState is lowOn or on, do nothing
}

const turnLightsOff = function() {
	if (_lightState === 'on' || _lightState === 'lowOn') {
		debug(`turning lights off`)
		clearTimeout(lightTimer)
		setLights(lights, lowOff)
		_lightState = 'lowOff'
		lightTimer = setTimeout(function() {
			setLights(lights, off)
			_lightState = 'off'
		}, 10000)
	}
}

const setLights = function(lights, lightState) {
	lights.forEach(function(light) {
		api.setLightState(light.id, lightState)
	})
}

const isDaytime = function() {
	let h = moment().hour()
	return (h > 7 && h < 17)
}

const loop = function() {
	particle.getMotion()
	.then(function(reply) {
		if (reply.return_value === 1) {
			onMotion()
		} else if (reply.return_value === 2) {
			onHighMotion()
			onMotion()
		}
		setTimeout(loop, 1000)
	}).catch(function(err) {
		debug(`Caught error: ${err}`)
		setTimeout(loop, 10000)
	})
}

const main = function() {
	if (process.argv.length < 3) {
		usage()
		process.exit(0)
	}

	debug(`starting`)
	lights = lightName.lightsFromNamesOrExit(process.argv.slice(2))
	setLights(lights, off)
	loop()
}

const usage = function() {
	debug("usage: node " + __filename + " LIGHTNAME LIGHTNAME ...")
	debug("Each LIGHTNAME is the name of a light defined in constants.js:")
	var lightNames = _.pluck(constants.lights, 'name').join(", ")
	debug('"' + lightNames + '"')
}

main()

<|endoftext|>


<commit_before>"use strict";

module.exports = Scope;

var contains = require("lodash/collection/contains");
var traverse = require("./index");
var defaults = require("lodash/object/defaults");
var globals  = require("globals");
var flatten  = require("lodash/array/flatten");
var extend   = require("lodash/object/extend");
var object   = require("../helpers/object");
var each     = require("lodash/collection/each");
var has      = require("lodash/object/has");
var t        = require("../types");

/**
 * This searches the current "scope" and collects all references/bindings
 * within.
 *
 * @param {Node} block
 * @param {Node} parentBlock
 * @param {Scope} [parent]
 * @param {File} [file]
 */

function Scope(block, parentBlock, parent, file) {
  this.parent = parent;
  this.file   = parent ? parent.file : file;

  this.parentBlock = parentBlock;
  this.block       = block;

  this.init();
}

Scope.defaultDeclarations = flatten([globals.builtin, globals.browser, globals.node].map(Object.keys));

/**
 * Description
 *
 * @param {File} file
 * @param {String} [name="temp"]
 */

Scope.prototype.generateTemp = function (file, name) {
  var id = file.generateUidIdentifier(name || "temp", this);
  this.push({
    key: id.name,
    id: id
  });
  return id;
};

/**
 * Description
 *
 * @param {String} name
 */

Scope.prototype.generateUidIdentifier = function (name) {
  return this.file.generateUidIdentifier(name, this);
};

/*
 * Description
 *
 * @param {Object} parent
 * @returns {Object}
 */

Scope.prototype.generateUidBasedOnNode = function (parent) {
  var node = parent;

  if (t.isAssignmentExpression(parent)) {
    node = parent.left;
  } else if (t.isVariableDeclarator(parent)) {
    node = parent.id;
  } else if (t.isProperty(node)) {
    node = node.key;
  }

  var parts = [];

  var add = function (node) {
    if (t.isMemberExpression(node)) {
      add(node.object);
      add(node.property);
    } else if (t.isIdentifier(node)) {
      parts.push(node.name);
    } else if (t.isLiteral(node)) {
      parts.push(node.value);
    } else if (t.isCallExpression(node)) {
      add(node.callee);
    }
  };

  add(node);

  var id = parts.join("$");
  id = id.replace(/^_/, "") || "ref";

  return this.file.generateUidIdentifier(id, this);
};

/**
 * Description
 *
 * @param {Object} node
 * @returns {Object}
 */

Scope.prototype.generateTempBasedOnNode = function (node) {
  if (t.isIdentifier(node) && this.hasBinding(node.name)) {
    return null;
  }

  var id = this.generateUidBasedOnNode(node);
  this.push({
    key: id.name,
    id: id
  });
  return id;
};

Scope.prototype.checkBlockScopedCollisions = function (key, id) {
  if (this.declarationKinds["let"][key] || this.declarationKinds["const"][key]) {
    throw this.file.errorWithNode(id, "Duplicate declaration " + key, TypeError);
  }
};

Scope.prototype.inferType = function (node) {
  var target;

  if (t.isVariableDeclarator(node)) {
    target = node.init;
  }

  if (t.isArrayExpression(target) || t.isObjectExpression(target)) {
    // todo: possibly call some helper that will resolve these to a type annotation
  }

  if (t.isCallExpression(target)) {
    target = target.callee;
  }

  if (t.isMemberExpression(target)) {
    // todo: crawl this and find the correct type, bail on anything that we cannot
    // possibly be 100% confident on
  }

  if (t.isIdentifier(target)) {
    return this.getType(target.name);
  }
};

Scope.prototype.registerType = function (key, id, node) {
  var type;

  if (id.typeAnnotation) {
    type = id.typeAnnotation;
  }

  if (!type) {
    type = this.inferType(node);
  }

  if (type) {
    if (t.isTypeAnnotation(type)) type = type.typeAnnotation;
    this.types[key] = type;
  }
};

Scope.prototype.register = function (node, reference, kind) {
  if (t.isVariableDeclaration(node)) {
    return this.registerVariableDeclaration(node);
  }

  var ids = t.getBindingIdentifiers(node);

  extend(this.references, ids);

  if (reference) return;

  for (var key in ids) {
    var id = ids[key];

    this.checkBlockScopedCollisions(key, id);

    this.registerType(key, id, node);
    this.bindings[key] = id;
  }

  var kinds = this.declarationKinds[kind];
  if (kinds) extend(kinds, ids);
};

Scope.prototype.registerVariableDeclaration = function (declar) {
  var declars = declar.declarations;
  for (var i = 0; i < declars.length; i++) {
    this.register(declars[i], false, declar.kind);
  }
};

var functionVariableVisitor = {
  enter: function (node, parent, scope, context, state) {
    if (t.isFor(node)) {
      each(t.FOR_INIT_KEYS, function (key) {
        var declar = node[key];
        if (t.isVar(declar)) state.scope.register(declar);
      });
    }

    // this block is a function so we'll stop since none of the variables
    // declared within are accessible
    if (t.isFunction(node)) return context.skip();

    // function identifier doesn't belong to this scope
    if (state.blockId && node === state.blockId) return;

    // delegate block scope handling to the `blockVariableVisitor`
    if (t.isBlockScoped(node)) return;

    // this will be hit again once we traverse into it after this iteration
    if (t.isExportDeclaration(node) && t.isDeclaration(node.declaration)) return;

    // we've ran into a declaration!
    if (t.isDeclaration(node)) state.scope.register(node);
  }
};

var programReferenceVisitor = {
  enter: function (node, parent, scope, context, state) {
    if (t.isReferencedIdentifier(node, parent) && !scope.hasReference(node.name)) {
      state.register(node, true);
    }
  }
};

var blockVariableVisitor = {
  enter: function (node, parent, scope, context, state) {
    if (t.isBlockScoped(node)) {
      state.register(node);
    } else if (t.isScope(node)) {
      context.skip();
    }
  }
};

Scope.prototype.init = function () {
  var parent = this.parent;
  var block  = this.block;
  var i;

  //

  var info = block._scopeInfo;
  if (info) {
    extend(this, info);
    return;
  }

  info = block._scopeInfo = {
    declarationKinds: {
      "const": object(),
      "var":   object(),
      "let":   object()
    },

    references: object(),
    bindings:   object(),
    types:      object(),
  };

  extend(this, info);

  //

  if (parent && t.isBlockStatement(block) && t.isLoop(parent.block, { body: block })) {
    // delegate let bindings to the parent loop
    return;
  }

  // ForStatement - left, init

  if (t.isLoop(block)) {
    for (i = 0; i < t.FOR_INIT_KEYS.length; i++) {
      var node = block[t.FOR_INIT_KEYS[i]];
      if (t.isBlockScoped(node)) this.register(node, false, true);
    }

    if (t.isBlockStatement(block.body)) {
      block = block.body;
    }
  }

  // Program, BlockStatement - let variables

  if (t.isBlockStatement(block) || t.isProgram(block)) {
    traverse(block, blockVariableVisitor, this, this);
  }

  // CatchClause - param

  if (t.isCatchClause(block)) {
    this.register(block.param);
  }

  // ComprehensionExpression - blocks

  if (t.isComprehensionExpression(block)) {
    this.register(block);
  }

  // Function - params, rest

  if (t.isFunction(block)) {
    for (i = 0; i < block.params.length; i++) {
      this.register(block.params[i]);
    }
  }

  // Program, Function - var variables

  if (t.isProgram(block) || t.isFunction(block)) {
    traverse(block, functionVariableVisitor, this, {
      blockId: block.id,
      scope:   this
    });
  }

  if (t.isFunctionExpression(block) && block.id) {
    if (!t.isProperty(this.parentBlock, { method: true })) {
      // SpiderMonkey AST doesn't use MethodDefinition here when it probably
      // should since they should be semantically the same?
      this.register(block.id);
    }
  }

  // Program

  if (t.isProgram(block)) {
    traverse(block, programReferenceVisitor, this, this);
  }
};

/**
 * Description
 *
 * @param {Object} opts
 */

Scope.prototype.push = function (opts) {
  var block = this.block;

  if (t.isFor(block) || t.isCatchClause(block) || t.isFunction(block)) {
    t.ensureBlock(block);
    block = block.body;
  }

  if (t.isBlockStatement(block) || t.isProgram(block)) {
    block._declarations = block._declarations || {};
    block._declarations[opts.key] = {
      kind: opts.kind,
      id: opts.id,
      init: opts.init
    };
  } else {
    throw new TypeError("cannot add a declaration here in node type " + block.type);
  }
};

/**
 * Walk up the scope tree until we hit a `Function` and then
 * push our `node` to it's references.
 *
 * @param {String} kind
 * @param {Object} node
 */

Scope.prototype.addDeclarationToFunctionScope = function (kind, node) {
  var scope = this.getFunctionParent();
  var ids   = t.getBindingIdentifiers(node);

  extend(scope.bindings, ids);
  extend(scope.references, ids);

  // this ignores the duplicate declaration logic specified in `getInfo`
  // but it doesn't really matter
  extend(scope.declarationKinds[kind], ids);
};

/**
 * Walk up the scope tree until we hit either a Function or reach the
 * very top at hit Program.
 */

Scope.prototype.getFunctionParent = function () {
  var scope = this;
  while (scope.parent && !t.isFunction(scope.block)) {
    scope = scope.parent;
  }
  return scope;
};

/**
 * Walks the scope tree and gathers **all** bindings.
 *
 * @returns {Object}
 */

Scope.prototype.getAllBindings = function () {
  var ids = object();

  var scope = this;
  do {
    defaults(ids, scope.bindings);
    scope = scope.parent;
  } while (scope);

  return ids;
};

/**
 * Walks the scope tree and gathers all declarations of `kind`.
 *
 * @param {String} kind
 * @returns {Object}
 */

Scope.prototype.getAllDeclarationsOfKind = function (kind) {
  var ids = object();

  var scope = this;
  do {
    defaults(ids, scope.declarationKinds[kind]);
    scope = scope.parent;
  } while (scope);

  return ids;
};

//

Scope.prototype.get = function (id, type) {
  return id && (this.getOwn(id, type) || this.parentGet(id, type));
};

Scope.prototype.getOwn = function (id, type) {
  var refs = {
    reference: this.references,
    binding:   this.bindings,
    type:      this.types
  }[type];
  return refs && has(refs, id) && refs[id];
};

Scope.prototype.parentGet = function (id, type) {
  return this.parent && this.parent.get(id, type);
};

Scope.prototype.has = function (id, type) {
  if (!id) return false;
  if (this.hasOwn(id, type)) return true;
  if (this.parentHas(id, type)) return true;
  if (contains(Scope.defaultDeclarations, id)) return true;
  return false;
};

Scope.prototype.hasOwn = function (id, type) {
  return !!this.getOwn(id, type);
};

Scope.prototype.parentHas = function (id, type) {
  return this.parent && this.parent.has(id, type);
};

each({
  reference: "Reference",
  binding: "Binding",
  type: "Type"
}, function (title, type) {
  each([
    "get",
    "has",
    "getOwn",
    "hasOwn",
    "parentGet",
    "parentHas",
  ], function (methodName) {
    Scope.prototype[methodName + title] = function (id) {
      return this[methodName](id, type);
    };
  });
});
<commit_msg>rename Scope.init to Scope.crawl<commit_after>"use strict";

module.exports = Scope;

var contains = require("lodash/collection/contains");
var traverse = require("./index");
var defaults = require("lodash/object/defaults");
var globals  = require("globals");
var flatten  = require("lodash/array/flatten");
var extend   = require("lodash/object/extend");
var object   = require("../helpers/object");
var each     = require("lodash/collection/each");
var has      = require("lodash/object/has");
var t        = require("../types");

/**
 * This searches the current "scope" and collects all references/bindings
 * within.
 *
 * @param {Node} block
 * @param {Node} parentBlock
 * @param {Scope} [parent]
 * @param {File} [file]
 */

function Scope(block, parentBlock, parent, file) {
  this.parent = parent;
  this.file   = parent ? parent.file : file;

  this.parentBlock = parentBlock;
  this.block       = block;

  this.crawl();
}

Scope.defaultDeclarations = flatten([globals.builtin, globals.browser, globals.node].map(Object.keys));

/**
 * Description
 *
 * @param {File} file
 * @param {String} [name="temp"]
 */

Scope.prototype.generateTemp = function (file, name) {
  var id = file.generateUidIdentifier(name || "temp", this);
  this.push({
    key: id.name,
    id: id
  });
  return id;
};

/**
 * Description
 *
 * @param {String} name
 */

Scope.prototype.generateUidIdentifier = function (name) {
  return this.file.generateUidIdentifier(name, this);
};

/*
 * Description
 *
 * @param {Object} parent
 * @returns {Object}
 */

Scope.prototype.generateUidBasedOnNode = function (parent) {
  var node = parent;

  if (t.isAssignmentExpression(parent)) {
    node = parent.left;
  } else if (t.isVariableDeclarator(parent)) {
    node = parent.id;
  } else if (t.isProperty(node)) {
    node = node.key;
  }

  var parts = [];

  var add = function (node) {
    if (t.isMemberExpression(node)) {
      add(node.object);
      add(node.property);
    } else if (t.isIdentifier(node)) {
      parts.push(node.name);
    } else if (t.isLiteral(node)) {
      parts.push(node.value);
    } else if (t.isCallExpression(node)) {
      add(node.callee);
    }
  };

  add(node);

  var id = parts.join("$");
  id = id.replace(/^_/, "") || "ref";

  return this.file.generateUidIdentifier(id, this);
};

/**
 * Description
 *
 * @param {Object} node
 * @returns {Object}
 */

Scope.prototype.generateTempBasedOnNode = function (node) {
  if (t.isIdentifier(node) && this.hasBinding(node.name)) {
    return null;
  }

  var id = this.generateUidBasedOnNode(node);
  this.push({
    key: id.name,
    id: id
  });
  return id;
};

Scope.prototype.checkBlockScopedCollisions = function (key, id) {
  if (this.declarationKinds["let"][key] || this.declarationKinds["const"][key]) {
    throw this.file.errorWithNode(id, "Duplicate declaration " + key, TypeError);
  }
};

Scope.prototype.inferType = function (node) {
  var target;

  if (t.isVariableDeclarator(node)) {
    target = node.init;
  }

  if (t.isArrayExpression(target) || t.isObjectExpression(target)) {
    // todo: possibly call some helper that will resolve these to a type annotation
  }

  if (t.isCallExpression(target)) {
    target = target.callee;
  }

  if (t.isMemberExpression(target)) {
    // todo: crawl this and find the correct type, bail on anything that we cannot
    // possibly be 100% confident on
  }

  if (t.isIdentifier(target)) {
    return this.getType(target.name);
  }
};

Scope.prototype.registerType = function (key, id, node) {
  var type;

  if (id.typeAnnotation) {
    type = id.typeAnnotation;
  }

  if (!type) {
    type = this.inferType(node);
  }

  if (type) {
    if (t.isTypeAnnotation(type)) type = type.typeAnnotation;
    this.types[key] = type;
  }
};

Scope.prototype.register = function (node, reference, kind) {
  if (t.isVariableDeclaration(node)) {
    return this.registerVariableDeclaration(node);
  }

  var ids = t.getBindingIdentifiers(node);

  extend(this.references, ids);

  if (reference) return;

  for (var key in ids) {
    var id = ids[key];

    this.checkBlockScopedCollisions(key, id);

    this.registerType(key, id, node);
    this.bindings[key] = id;
  }

  var kinds = this.declarationKinds[kind];
  if (kinds) extend(kinds, ids);
};

Scope.prototype.registerVariableDeclaration = function (declar) {
  var declars = declar.declarations;
  for (var i = 0; i < declars.length; i++) {
    this.register(declars[i], false, declar.kind);
  }
};

var functionVariableVisitor = {
  enter: function (node, parent, scope, context, state) {
    if (t.isFor(node)) {
      each(t.FOR_INIT_KEYS, function (key) {
        var declar = node[key];
        if (t.isVar(declar)) state.scope.register(declar);
      });
    }

    // this block is a function so we'll stop since none of the variables
    // declared within are accessible
    if (t.isFunction(node)) return context.skip();

    // function identifier doesn't belong to this scope
    if (state.blockId && node === state.blockId) return;

    // delegate block scope handling to the `blockVariableVisitor`
    if (t.isBlockScoped(node)) return;

    // this will be hit again once we traverse into it after this iteration
    if (t.isExportDeclaration(node) && t.isDeclaration(node.declaration)) return;

    // we've ran into a declaration!
    if (t.isDeclaration(node)) state.scope.register(node);
  }
};

var programReferenceVisitor = {
  enter: function (node, parent, scope, context, state) {
    if (t.isReferencedIdentifier(node, parent) && !scope.hasReference(node.name)) {
      state.register(node, true);
    }
  }
};

var blockVariableVisitor = {
  enter: function (node, parent, scope, context, state) {
    if (t.isBlockScoped(node)) {
      state.register(node);
    } else if (t.isScope(node)) {
      context.skip();
    }
  }
};

Scope.prototype.crawl = function () {
  var parent = this.parent;
  var block  = this.block;
  var i;

  //

  var info = block._scopeInfo;
  if (info) {
    extend(this, info);
    return;
  }

  info = block._scopeInfo = {
    declarationKinds: {
      "const": object(),
      "var":   object(),
      "let":   object()
    },

    references: object(),
    bindings:   object(),
    types:      object(),
  };

  extend(this, info);

  //

  if (parent && t.isBlockStatement(block) && t.isLoop(parent.block, { body: block })) {
    // delegate let bindings to the parent loop
    return;
  }

  // ForStatement - left, init

  if (t.isLoop(block)) {
    for (i = 0; i < t.FOR_INIT_KEYS.length; i++) {
      var node = block[t.FOR_INIT_KEYS[i]];
      if (t.isBlockScoped(node)) this.register(node, false, true);
    }

    if (t.isBlockStatement(block.body)) {
      block = block.body;
    }
  }

  // Program, BlockStatement - let variables

  if (t.isBlockStatement(block) || t.isProgram(block)) {
    traverse(block, blockVariableVisitor, this, this);
  }

  // CatchClause - param

  if (t.isCatchClause(block)) {
    this.register(block.param);
  }

  // ComprehensionExpression - blocks

  if (t.isComprehensionExpression(block)) {
    this.register(block);
  }

  // Function - params, rest

  if (t.isFunction(block)) {
    for (i = 0; i < block.params.length; i++) {
      this.register(block.params[i]);
    }
  }

  // Program, Function - var variables

  if (t.isProgram(block) || t.isFunction(block)) {
    traverse(block, functionVariableVisitor, this, {
      blockId: block.id,
      scope:   this
    });
  }

  if (t.isFunctionExpression(block) && block.id) {
    if (!t.isProperty(this.parentBlock, { method: true })) {
      // SpiderMonkey AST doesn't use MethodDefinition here when it probably
      // should since they should be semantically the same?
      this.register(block.id);
    }
  }

  // Program

  if (t.isProgram(block)) {
    traverse(block, programReferenceVisitor, this, this);
  }
};

/**
 * Description
 *
 * @param {Object} opts
 */

Scope.prototype.push = function (opts) {
  var block = this.block;

  if (t.isFor(block) || t.isCatchClause(block) || t.isFunction(block)) {
    t.ensureBlock(block);
    block = block.body;
  }

  if (t.isBlockStatement(block) || t.isProgram(block)) {
    block._declarations = block._declarations || {};
    block._declarations[opts.key] = {
      kind: opts.kind,
      id: opts.id,
      init: opts.init
    };
  } else {
    throw new TypeError("cannot add a declaration here in node type " + block.type);
  }
};

/**
 * Walk up the scope tree until we hit a `Function` and then
 * push our `node` to it's references.
 *
 * @param {String} kind
 * @param {Object} node
 */

Scope.prototype.addDeclarationToFunctionScope = function (kind, node) {
  var scope = this.getFunctionParent();
  var ids   = t.getBindingIdentifiers(node);

  extend(scope.bindings, ids);
  extend(scope.references, ids);

  // this ignores the duplicate declaration logic specified in `getInfo`
  // but it doesn't really matter
  extend(scope.declarationKinds[kind], ids);
};

/**
 * Walk up the scope tree until we hit either a Function or reach the
 * very top at hit Program.
 */

Scope.prototype.getFunctionParent = function () {
  var scope = this;
  while (scope.parent && !t.isFunction(scope.block)) {
    scope = scope.parent;
  }
  return scope;
};

/**
 * Walks the scope tree and gathers **all** bindings.
 *
 * @returns {Object}
 */

Scope.prototype.getAllBindings = function () {
  var ids = object();

  var scope = this;
  do {
    defaults(ids, scope.bindings);
    scope = scope.parent;
  } while (scope);

  return ids;
};

/**
 * Walks the scope tree and gathers all declarations of `kind`.
 *
 * @param {String} kind
 * @returns {Object}
 */

Scope.prototype.getAllDeclarationsOfKind = function (kind) {
  var ids = object();

  var scope = this;
  do {
    defaults(ids, scope.declarationKinds[kind]);
    scope = scope.parent;
  } while (scope);

  return ids;
};

//

Scope.prototype.get = function (id, type) {
  return id && (this.getOwn(id, type) || this.parentGet(id, type));
};

Scope.prototype.getOwn = function (id, type) {
  var refs = {
    reference: this.references,
    binding:   this.bindings,
    type:      this.types
  }[type];
  return refs && has(refs, id) && refs[id];
};

Scope.prototype.parentGet = function (id, type) {
  return this.parent && this.parent.get(id, type);
};

Scope.prototype.has = function (id, type) {
  if (!id) return false;
  if (this.hasOwn(id, type)) return true;
  if (this.parentHas(id, type)) return true;
  if (contains(Scope.defaultDeclarations, id)) return true;
  return false;
};

Scope.prototype.hasOwn = function (id, type) {
  return !!this.getOwn(id, type);
};

Scope.prototype.parentHas = function (id, type) {
  return this.parent && this.parent.has(id, type);
};

each({
  reference: "Reference",
  binding: "Binding",
  type: "Type"
}, function (title, type) {
  each([
    "get",
    "has",
    "getOwn",
    "hasOwn",
    "parentGet",
    "parentHas",
  ], function (methodName) {
    Scope.prototype[methodName + title] = function (id) {
      return this[methodName](id, type);
    };
  });
});
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2012, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint anon:true, sloppy:true, regexp:true, nomen:true, stupid:true*/

var fs = require('fs'),
    path = require('path'),
    existsSync = fs.existsSync || path.existsSync,
    utils = require(path.join(__dirname, '../../management/utils')),
    usage = 'mojito jslint [app | mojit] [<name>] {options}\n' +
            '\nOPTIONS: \n' +
            '\t  --print      :  print results to stdout \n' +
            '\t   -p          :  short for --print\n',
    options = [
        {
            shortName: 'p',
            longName: 'print',
            hasValue: false
        }
    ];

// ---------- Generic file system utilities ----------

/*
 * A version of fs.mkdirSync that ensures that all intermediate directories
 * are created.
 */
function mkdirsSync(dir, mode) {
    var modeDefault = parseInt('755', 8); // Avoid octal per JSLint

    if (existsSync(dir)) {
        return;
    }
    mode = mode || modeDefault;
    mkdirsSync(path.dirname(dir), mode);
    fs.mkdirSync(dir, mode);
}

/*
 * Deletes a directory including any contained files and directories. This is
 * roughly equivalent to the Unix "rm -rf" command.
 */
function rmSync(file) {
    var failures = [],
        files,
        stat;

    try {
        stat = fs.lstatSync(file);
    } catch (e1) {
        if (/^ENOENT/.test(e1.message)) {
            return null;
        }
        return [file];
    }

    if (stat.isFile() || stat.isSymbolicLink()) {
        try {
            fs.unlinkSync(file);
        } catch (e2) {
            failures.push(file);
        }
    } else if (stat.isDirectory()) {
        files = fs.readdirSync(file);
        files.forEach(function(f) {
            try {
                failures.concat(rmSync(path.join(file, f)));
            } catch (e3) {
                failures.push(path.join(file, f));
            }
        });
    }

    return failures;
}

/*
 * Recursively processes all of the files in a directory tree. Each file is
 * passed to a 'processor' function that can do whatever it needs to do. An
 * optional 'filter' function allows files to be skipped (i.e. not passed to
 * the processor) by returning false.
 */
function processDir(dir, processor, filter) {
    var files = fs.readdirSync(dir),
        file,
        stat;

    filter = filter || function() { return true; };

    files.forEach(function(f) {
        file = path.join(dir, f);
        stat = fs.statSync(file);
        if (filter(file, stat)) {
            if (stat.isDirectory()) {
                processDir(file, processor, filter);
            } else {
                processor(file);
            }
        }
    });
}

/*
 * A very simple class to allow writes to a file that is created only if it
 * needs to be written to, without the caller needing to check on every write.
 */
function OutputFile(filename) {
    var outstream = null;

    this.write = function(s) {
        if (!outstream) {
            mkdirsSync(path.dirname(filename));
            outstream = fs.createWriteStream(filename);
        }
        outstream.write(s);
    };
    this.done = function() {
        if (outstream) {
            outstream.end();
            outstream = null;
        }
    };
}

/*
 * A very simple class to allow writes to stdout.
 */
function OutputStdout(filename) {
    var printedName = false;

    this.write = function(s) {
        if (!printedName) {
            printedName = true;
            process.stdout.write(filename + '\n');
        }
        process.stdout.write(s);
    };
    this.done = function() {};
}

// ---------- JSLint processing ----------

/*
 * Run JSLint over a single file, writing any errors to the provided output,
 * and returning the number of errors encountered.
 */
function lintOneFile(infile, outfile) {
    var jslint = require('jslint'),
        OPTS = {
            'continue': true, // Tolerate continue
            node: true,
            predef: [
                // CommonJS
                'exports',
                // YUI
                'YUI', 'YUI_config', 'YAHOO', 'YAHOO_config', 'Y',
                // Node
                'global', 'process', 'require', '__filename', 'module',
                // Browser
                'document', 'navigator', 'console', 'self', 'window'
            ]
        },
        errors = 0,
        i = 0,
        e,
        input,
        len,
        success,
        pad,
        data;

    input = fs.readFileSync(infile);
    if (!input) {
        utils.error("Failed to open file '" + infile + "'.", usage);
        return 0;
    }
    input = input.toString('utf8');

    // remove shebang (lifted from node.js)
    input = input.replace(/^\#\!.*/, '');

    success = jslint(input, OPTS);
    if (!success) {
        errors = len = jslint.errors.length;
        for (i = 0; i < len; i += 1) {
            pad = String(i + 1);
            while (pad.length < 4) {
                pad = ' ' + pad;
            }
            e = jslint.errors[i];
            if (e) {
                outfile.write(pad + ' ' + e.line + ',' + e.character + ': ' +
                        e.reason + '\n');
                outfile.write('     ' +
                        (e.evidence || '').replace(/^\s+|\s+$/, '') + '\n');
            }
        }
    }

    data = jslint.data();
    data.functions.forEach(function(f) {
        if (f.name.charAt(0) === '_') {
            pad = String(i + 1);
            while (pad.length < 4) {
                pad = ' ' + pad;
            }
            outfile.write(pad + ' ' + f.line + ': ' +
                    "Unexpected leading '_' in '" + f.name + "'.\n");
            i += 1;
            errors += 1;
        }
        if (f['var']) {
            f['var'].forEach(function(v) {
                if (v.charAt(0) === '_') {
                    pad = String(i + 1);
                    while (pad.length < 4) {
                        pad = ' ' + pad;
                    }
                    outfile.write(pad + ' ' + f.line + ': ' +
                            "Unexpected leading '_' in '" + v + "'.\n");
                    outfile.write('     ' +
                            'function ' + f.name + '\n');
                    i += 1;
                    errors += 1;
                }
            });
        }
    });

    return errors;
}

// ---------- Mojito JSLint usage ----------

/*
 * Return the location of te Mojito framework. We're currently making an
 * assumption about relative location; we really need a way to validate that.
 */
function getFrameworkDir() {
    return path.normalize(__dirname + '/../../../lib');
}

/*
 * Return the directory for the application. If no application name is given,
 * we look to see if we are inside an application already. If a name is given,
 * we check for an app in the current directory, and then look to see if we
 * are already there.
 */
function getAppDir(appName) {
    var cwd = process.cwd(),
        dir,
        file,
        stat,
        parts;

    // If appName was not supplied, see if we're in an app.
    if (!appName) {
        file = path.join(cwd, 'server.js');
        return (existsSync(file) ? cwd : null);
    }

    // Look for the app where we are.
    dir = path.join(cwd, appName);
    if (existsSync(dir)) {
        stat = fs.statSync(dir);
        if (stat.isDirectory()) {
            return dir;
        }
    }

    // See if we are already in the app.
    parts = cwd.split('/');
    if (parts[parts.length - 1] === appName) {
        return cwd;
    }

    // App not found.
    return null;
}

/*
 * Return the directory for the mojit. Currently assumes we are in the app.
 */
function getMojitDir(mojitPath) {
    var cwd = process.cwd();

    return path.join(cwd, mojitPath);
}

/*
 * Return the name of the mojit, extracted from the path supplied.
 */
function getMojitName(mojitPath) {
    var parts = mojitPath.split('/');

    return parts[parts.length - 1];
}

/*
 * Write an HTML page that makes it easy to navigate through the results. The
 * implementation of this is very "lazy" but also very simple in terms of file
 * system interaction.
 */
function writePage(outDir, rowdata, totalErrors) {
    var outfile = path.join(outDir, 'jslint.html'),
        html = '' +
            '<title>JSLint Report</title>' +
            '
JSLint Report
' +
            '
Total of ' + totalErrors + ' errors in ' +
            rowdata.length + ' files.
' +
            '
\n';

    rowdata.forEach(function(row) {
        html += '
' +
            row.count +
            "	" +
            row.text +
            '
\n';
    });

    html += '
';

    fs.writeFileSync(outfile, html);
}

/*
 * Process all of the files in the input directory, writing any JSLint errors
 * encountered to the output directory. Return the total count of errors.
 */
function processFiles(inDir, outDir, excludeMatcher) {
    var totalErrors = 0,
        rowdata = [];

    inDir = path.normalize(inDir);
    if (outDir) {
        outDir = path.normalize(outDir);
    }

    processDir(inDir,
        function(f) {
            var relname = f.replace(inDir, '').replace(/^\//, ''),
                outname = relname.replace(/\.js$/, '.txt'),
                out,
                errors;

            if (!outDir) {
                out = new OutputStdout(relname);
            } else {
                out = new OutputFile(path.join(outDir, outname));
            }

            errors = lintOneFile(f, out);

            if (errors > 0) {
                rowdata.push({count: errors, text: relname, url: outname});
            }
            totalErrors += errors;
            out.done();
        },
        function(f, s) {
            var p;

            if (excludeMatcher(f, s.isDirectory() ? 'dir' : 'file')) {
                return false;
            }
            p = f.split('/');
            return (p[p.length - 1]).charAt(0) !== '.' &&
                (s.isDirectory() || /\.js$/.test(p));
        });

    if (outDir && totalErrors > 0) {
        writePage(outDir, rowdata, totalErrors);
    }

    return totalErrors;
}

/*
 * Run this command. The options (framework, application, or mojit check, and
 * an optional path) are processed, and JSLint is run on each file. If there
 * are errors, a non-zero process exit is made.
 */
function run(params, options) {
    var excludes,
        inDir,
        outDir,
        failures,
        errors,
        print = options && options.print;

    // Process params to determine input and output locations.
    if (!params || params.length === 0) {
        // framework
        inDir = getFrameworkDir();
        outDir = path.join(process.cwd(), 'artifacts/framework/jslint');
        excludes = [
            /\/artifacts$/,
            /\/tests$/,
            /\/libs$/,
            /\/app\/libs$/,
            /\/node_modules$/,
            /\/mojito$/ // for the CI environment
        ];
    } else if (params[0] === 'app') {
        // application
        inDir = getAppDir(params[1]);
        if (!inDir) {
            utils.error('Application not found.', usage);
            return;
        }
        outDir = path.join(inDir, 'artifacts/jslint');
        excludes = [
            /\/tests$/,
            /\/artifacts$/,
            /\/node_modules$/
        ];
    } else if (params[0] === 'mojit') {
        // mojit
        inDir = getMojitDir(params[1]);
        if (!inDir) {
            utils.error('Mojit not found.', usage);
            return;
        }
        outDir = path.join(inDir, 'artifacts/jslint/mojits',
            getMojitName(params[1]));
        excludes = [
            /\/tests$/,
            /\/artifacts$/,
            /\/node_modules$/
        ];
    } else {
        // TODO: [Issue 85] Maybe allow for a single file?
        utils.error('Unrecognized option: ' + params[0], usage);
        return;
    }

    // Delete any files currently in the output location.
    failures = rmSync(outDir);
    if (failures && failures.length) {
        console.log('Some files could not be deleted:\n');
        failures.forEach(function(f) {
            console.log('\t' + f);
        });
    }

    // Set outDir to null if we just plan on printing to stdout
    if (print) {
        outDir = null;
    }

    // Process the files with JSLint.
    errors = processFiles(inDir, outDir, utils.getExclusionMatcher(excludes));
    if (!print && errors) {
        console.log('Lint report: ' + path.normalize(outDir));
    }
    process.stdout.write(errors + ' errors found.\n', 'utf8',
        function() {
            if (errors) {
                // Make sure file streams have the opportunity to finish
                setTimeout(function () {
                    process.exit(1);
                });
            }
        });
}

/**
 * Standard usage string export.
 */
exports.usage = usage;

/**
 * Standard options export.
 */
exports.options = options;

/**
 * Standard run method hook export.
 */
exports.run = run;
<commit_msg>updating PR with the latest from develop after 2.5.2, solving conflits generated by the hook system.<commit_after>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

/*jslint anon:true, sloppy:true, regexp:true, nomen:true, stupid:true*/

(function() {

    'use strict';

/**
 * @fileoverview Command logic for the Mojito 'jslint' command.
 */

//  ----------------------------------------------------------------------------
//  Prerequisites
//  ----------------------------------------------------------------------------

    var libfs = require('fs'),
        libpath = require('path'),
        existsSync = libfs.existsSync || libpath.existsSync,
        utils = require(libpath.join(__dirname, '../../management/utils'));

//  ----------------------------------------------------------------------------
//  Command Root Object
//  ----------------------------------------------------------------------------

    /**
     * The Command Line Interpreter object. All testable methods and attributes
     * are properties of this object.
     */
    function Command() {
    }

    /**
     * The usage string output for command help.
     * @type {string}
     */
    Command.usage = 'mojito jslint [app | mojit] [<path>] {options}\n' +
        '\t- target: app or mojit\n' +
        '\t- path (required for a mojit target): ' +
        'the path to the mojit to run jslint on\n\n' +
        'Run jslint on the app in the current directory:\n' +
        '\tmojito jslint app .\n' +
        'Run jslint on mojits/Bar:\n' +
        '\tmojito jslint mojit ./mojits/Bar\n' +
        'Run jslint on the mojito framework itself!\n' +
        '\tmojito jslint\n' +
        '\nOPTIONS: \n' +
        '\t  --print      :  print results to stdout \n' +
        '\t   -p          :  short for --print\n';

    /**
     * An array of option objects containing shortName, longName, and whether
     * the option expects a value (hasValue).
     * @type {Array}
     */
    Command.options = [
        {
            shortName: 'p',
            longName: 'print',
            hasValue: false
        }
    ];

//  ----------------------------------------------------------------------------
//  Generic File System Utilities
//  ----------------------------------------------------------------------------

    /*
     * A version of fs.mkdirSync that ensures that intermediate directories
     * are created.
     * @param {string} dir The leaf-level directory name to create.
     * @param {Number} mode The directory mode as an octal number.
     */
    Command._mkdirsSync = function(dir, mode) {
        var modeDefault = parseInt('755', 8); // Avoid octal per JSLint

        if (existsSync(dir)) {
            return;
        }
        mode = mode || modeDefault;
        Command._mkdirsSync(libpath.dirname(dir), mode);
        libfs.mkdirSync(dir, mode);
    };

    /*
     * Deletes a directory including any contained files and directories. This
     * is roughly equivalent to the Unix "rm -rf" command.
     * @param {string} file The file to remove.
     */
    Command._rmSync = function(file) {
        var failures = [],
            files,
            stat;

        try {
            stat = libfs.lstatSync(file);
        } catch (e1) {
            if (/^ENOENT/.test(e1.message)) {
                return null;
            }
            return [file];
        }

        if (stat.isFile() || stat.isSymbolicLink()) {
            try {
                libfs.unlinkSync(file);
            } catch (e2) {
                failures.push(file);
            }
        } else if (stat.isDirectory()) {
            files = libfs.readdirSync(file);
            files.forEach(function(f) {
                try {
                    failures.concat(Command._rmSync(libpath.join(file, f)));
                } catch (e3) {
                    failures.push(libpath.join(file, f));
                }
            });
        }

        return failures;
    };

    /*
     * Recursively processes all of the files in a directory tree. Each file is
     * passed to a 'processor' function that can do whatever it needs to do. An
     * optional 'filter' function allows files to be skipped (i.e. not passed to
     * the processor) by returning false.
     */
    Command._processDir = function(dir, processor, filter) {
        var files = libfs.readdirSync(dir),
            file,
            stat;

        filter = filter || function() { return true; };

        files.forEach(function(f) {
            file = libpath.join(dir, f);
            stat = libfs.statSync(file);
            if (filter(file, stat)) {
                if (stat.isDirectory()) {
                    Command._processDir(file, processor, filter);
                } else {
                    processor(file);
                }
            }
        });
    };

//  ----------------------------------------------------------------------------
//  Helper Classes
//  ----------------------------------------------------------------------------

    /*
     * A very simple class to allow writes to a file that is created only if it
     * needs to be written to, without the caller needing to check every write.
     * @param {String} filename The name of the file to write to.
     * @constructor
     */
    Command._OutputFile = function(filename) {
        var outstream = null;

        /*
         * Writes a string to the output file.
         * @param {string} s The string to write.
         */
        this.write = function(s) {
            if (!outstream) {
                Command._mkdirsSync(libpath.dirname(filename));
                outstream = libfs.createWriteStream(filename);
            }
            outstream.write(s);
        };

        /*
         * Closes the output stream.
         */
        this.done = function() {
            if (outstream) {
                outstream.end();
                outstream = null;
            }
        };
    };

    /*
     * A very simple class to allow writes to stdout.
     * @param {String} filename The name of the file to print as a "header".
     * @constructor
     */
    Command._OutputStdout = function(filename) {
        var printedName = false;

        /*
         * Writes a string to standard output.
         * @param {string} s The string to write.
         */
        this.write = function(s) {
            if (!printedName) {
                printedName = true;
                process.stdout.write(filename + '\n');
            }
            process.stdout.write(s);
        };

        /*
         * Closes the output stream. (Note that for stdout this is a noop.)
         */
        this.done = function() {};
    };

//  ----------------------------------------------------------------------------
//  Internal Helper Functions
//  ----------------------------------------------------------------------------

    /*
     * Run JSLint over a single file, writing any errors to the provided output,
     * and returning the number of errors encountered.
     */
    Command._lintOneFile = function(infile, outfile) {
        var jslint = require('jslint'),
            OPTS = {
                'continue': true, // Tolerate continue
                node: true,
                predef: [
                    // CommonJS
                    'exports',
                    // YUI
                    'YUI', 'YUI_config', 'YAHOO', 'YAHOO_config', 'Y',
                    // Node
                    'global', 'process', 'require', '__filename', 'module',
                    // Browser
                    'document', 'navigator', 'console', 'self', 'window'
                ]
            },
            errors = 0,
            i = 0,
            e,
            input,
            len,
            success,
            pad,
            data;

        input = libfs.readFileSync(infile);
        if (!input) {
            utils.error("Failed to open file '" + infile + "'.", Command.usage);
            return 0;
        }
        input = input.toString('utf8');

        // remove shebang (lifted from node.js)
        input = input.replace(/^\#\!.*/, '');

        success = jslint(input, OPTS);
        if (!success) {
            errors = len = jslint.errors.length;
            for (i = 0; i < len; i += 1) {
                pad = String(i + 1);
                while (pad.length < 4) {
                    pad = ' ' + pad;
                }
                e = jslint.errors[i];
                if (e) {
                    outfile.write(pad + ' ' + e.line + ',' + e.character +
                        ': ' + e.reason + '\n');
                    outfile.write('     ' +
                        (e.evidence || '').replace(/^\s+|\s+$/, '') + '\n');
                }
            }
        }

        data = jslint.data();
        data.functions.forEach(function(f) {
            if (f.name.charAt(0) === '_') {
                pad = String(i + 1);
                while (pad.length < 4) {
                    pad = ' ' + pad;
                }
                outfile.write(pad + ' ' + f.line + ': ' +
                        "Unexpected leading '_' in '" + f.name + "'.\n");
                i += 1;
                errors += 1;
            }
            if (f['var']) {
                f['var'].forEach(function(v) {
                    if (v.charAt(0) === '_') {
                        pad = String(i + 1);
                        while (pad.length < 4) {
                            pad = ' ' + pad;
                        }
                        outfile.write(pad + ' ' + f.line + ': ' +
                                "Unexpected leading '_' in '" + v + "'.\n");
                        outfile.write('     ' +
                                'function ' + f.name + '\n');
                        i += 1;
                        errors += 1;
                    }
                });
            }
        });

        return errors;
    };

    /*
     * Return the location of te Mojito framework. We're currently making an
     * assumption about relative location; we really need a way to validate that.
     */
    Command._getFrameworkDir = function() {
        return libpath.normalize(__dirname + '/../../../lib');
    };

    /*
     * Return the directory for the application. If no application name is given,
     * we look to see if we are inside an application already. If a name is given,
     * we check for an app in the current directory, and then look to see if we
     * are already there.
     */
    Command._getAppDir = function(appName) {
        var cwd = process.cwd(),
            dir,
            file,
            stat,
            parts;

        // If appName was not supplied, see if we're in an app.
        if (!appName) {
            file = libpath.join(cwd, 'server.js');
            return (existsSync(file) ? cwd : null);
        }

        // Look for the app where we are.
        dir = libpath.join(cwd, appName);
        if (existsSync(dir)) {
            stat = libfs.statSync(dir);
            if (stat.isDirectory()) {
                return dir;
            }
        }

        // See if we are already in the app.
        parts = cwd.split('/');
        if (parts[parts.length - 1] === appName) {
            return cwd;
        }

        // App not found.
        return null;
    };

    /*
     * Return the directory for the mojit. Currently assumes we are in the app.
     */
    Command._getMojitDir = function(mojitPath) {
        var cwd = process.cwd();

        return libpath.join(cwd, mojitPath);
    };

    /*
     * Return the name of the mojit, extracted from the path supplied.
     */
    Command._getMojitName = function(mojitPath) {
        var parts = mojitPath.split('/');

        return parts[parts.length - 1];
    };

    /*
     * Write an HTML page that makes it easy to navigate through the results. The
     * implementation of this is very "lazy" but also very simple in terms of file
     * system interaction.
     */
    Command._writePage = function(outDir, rowdata, totalErrors) {
        var outfile = libpath.join(outDir, 'jslint.html'),
            html = '' +
                '<title>JSLint Report</title>' +
                '
JSLint Report
' +
                '
Total of ' + totalErrors + ' errors in ' +
                rowdata.length + ' files.
' +
                '
\n';

        rowdata.forEach(function(row) {
            html += '
' +
                row.count +
                "	" +
                row.text +
                '
\n';
        });

        html += '
';

        libfs.writeFileSync(outfile, html);
    };

    /*
     * Process all of the files in the input directory, writing any JSLint errors
     * encountered to the output directory. Return the total count of errors.
     */
    Command._processFiles = function(inDir, outDir, excludeMatcher) {
        var totalErrors = 0,
            rowdata = [];

        inDir = libpath.normalize(inDir);
        if (outDir) {
            outDir = libpath.normalize(outDir);
        }

        Command._processDir(inDir,
            function(f) {
                var relname = f.replace(inDir, '').replace(/^\//, ''),
                    outname = relname.replace(/\.js$/, '.txt'),
                    out,
                    errors;

                if (!outDir) {
                    out = new Command._OutputStdout(relname);
                } else {
                    out = new Command._OutputFile(
                        libpath.join(outDir, outname)
                    );
                }

                errors = Command._lintOneFile(f, out);

                if (errors > 0) {
                    rowdata.push({count: errors, text: relname, url: outname});
                }
                totalErrors += errors;
                out.done();
            },
            function(f, s) {
                var p;
                if (excludeMatcher &&
                        excludeMatcher(f, s.isDirectory() ? 'dir' : 'file')) {
                    return false;
                }
                p = f.split('/');
                return (p[p.length - 1]).charAt(0) !== '.' &&
                    (s.isDirectory() || /\.js$/.test(p));
            });

        if (outDir && totalErrors > 0) {
            Command._writePage(outDir, rowdata, totalErrors);
        }

        return totalErrors;
    };

//  ----------------------------------------------------------------------------
//  Public Members
//  ----------------------------------------------------------------------------

    /*
     * Run this command. The options (framework, application, or mojit check, and
     * an optional path) are processed, and JSLint is run on each file. If there
     * are errors, a non-zero process exit is made.
     */
    Command.run = function(params, options) {
        var excludes,
            inDir,
            outDir,
            failures,
            errors,
            mojit_path,
            print = options && options.print;

        // Process params to determine input and output locations.
        if (!params || params.length === 0) {
            // framework
            inDir = Command._getFrameworkDir();
            outDir = libpath.join(process.cwd(), 'artifacts/framework/jslint');
            excludes = [
                /\/artifacts$/,
                /\/tests$/,
                /\/libs$/,
                /\/app\/libs$/,
                /\/node_modules$/,
                /\/mojito$/ // for the CI environment
            ];
        } else if (params[0] === 'app') {
            // application
            inDir = Command._getAppDir(params[1]);
            if (!inDir) {
                utils.error('Application not found.', Command.usage);
                return;
            }
            outDir = libpath.join(inDir, 'artifacts/jslint');
            excludes = [
                /\/tests$/,
                /\/artifacts$/,
                /\/node_modules$/
            ];
        } else if (params[0] === 'mojit') {
            // mojit
            mojit_path = params[1];
            if (!mojit_path) {
                utils.error('Please specify the path to mojit', Command.usage);
                return;
            }
            inDir = Command._getMojitDir(mojit_path);
            if (!existsSync(inDir)) {
                utils.error('Mojit ' + mojit_path + ' not found.',
                    Command.usage);
                return;
            }
            if (!inDir) {
                utils.error('Mojit ' + mojit_path + ' not found.',
                    Command.usage);
                return;
            }
            outDir = libpath.join(inDir, 'artifacts/jslint/mojits',
                Command._getMojitName(mojit_path));
            excludes = [
                /\/tests$/,
                /\/artifacts$/,
                /\/node_modules$/
            ];
        } else {
            utils.error('Unrecognized option: ' + params[0], Command.usage);
            return;
        }

        // Delete any files currently in the output location.
        failures = Command._rmSync(outDir);
        if (failures && failures.length) {
            console.log('Some files could not be deleted:\n');
            failures.forEach(function(f) {
                console.log('\t' + f);
            });
        }

        // Set outDir to null if we just plan on printing to stdout
        if (print) {
            outDir = null;
        }

        // Process the files with JSLint.
        errors = Command._processFiles(inDir, outDir,
            utils.getExclusionMatcher(excludes));
        if (!print && errors) {
            console.log('Lint report: ' + libpath.normalize(outDir));
        }
        process.stdout.write(errors + ' errors found.\n', 'utf8',
            function() {
                if (errors) {
                    // Make sure file streams have the opportunity to finish
                    setTimeout(function () {
                        process.exit(1);
                    });
                }
            });
    };

//  ----------------------------------------------------------------------------
//  EXPORT(S)
//  ----------------------------------------------------------------------------

    /**
     * Export the Command object, which includes all testable functions.
     * @type {Function}
     */
    module.exports = Command;

}());
<|endoftext|>


<commit_before>/**
 * @license
 * The MIT License (MIT)
 * Copyright 2015 Government of Canada
 *
 * @author
 * Ian Boyes
 *
 * @exports UniqueNames
 */

import React from "react";
import { connect } from "react-redux";
import { Row, Col, Panel } from "react-bootstrap";

import { updateSettings } from "../../actions";
import { Checkbox, Button } from "virtool/js/components/Base";

/**
 * A component that allows the addition and removal of allowed source types. The use of restricted source types can also
 * be toggled.
 */
const UniqueNames = (props) => {
    return (

            <Row>
                <Col md={12}>

Unique Sample Names

                </Col>
                <Col xs={12} md={6} mdPush={6}>
                    <Panel>
                        Enable this feature to ensure that every created sample has a unique name. If a user
                        attempts to assign an existing name to a new sample an error will be displayed.
                    </Panel>
                </Col>
                <Col xs={12} md={6} mdPull={6}>
                    <Panel>
                        <Button onClick={() => {props.onToggle(!props.enabled)}} block>
                            <Checkbox checked={props.enabled} /> Enable
                        </Button>
                    </Panel>
                </Col>
            </Row>

    );
};

const mapStateToProps = (state) => {
    return {
        enabled: state.settings.data.sample_unique_names
    };
};

const mapDispatchToProps = (dispatch) => {
    return {
        onToggle: (enabled) => {
            dispatch(updateSettings({sample_unique_names: enabled}));
        }
    };
};

const Container = connect(
    mapStateToProps,
    mapDispatchToProps
)(UniqueNames);

export default Container;
<commit_msg>Fix UniqueNames component<commit_after>/**
 * @license
 * The MIT License (MIT)
 * Copyright 2015 Government of Canada
 *
 * @author
 * Ian Boyes
 *
 * @exports UniqueNames
 */

import React from "react";
import { connect } from "react-redux";
import { Row, Col, Panel } from "react-bootstrap";

import { updateSetting } from "../../actions";
import { Checkbox, Button } from "virtool/js/components/Base";

/**
 * A component that allows the addition and removal of allowed source types. The use of restricted source types can also
 * be toggled.
 */
const UniqueNames = (props) => {
    return (

            <Row>
                <Col md={12}>

Unique Sample Names

                </Col>
                <Col xs={12} md={6} mdPush={6}>
                    <Panel>
                        Enable this feature to ensure that every created sample has a unique name. If a user
                        attempts to assign an existing name to a new sample an error will be displayed.
                    </Panel>
                </Col>
                <Col xs={12} md={6} mdPull={6}>
                    <Panel>
                        <Button onClick={() => {props.onToggle(!props.enabled)}} block>
                            <Checkbox checked={props.enabled} /> Enable
                        </Button>
                    </Panel>
                </Col>
            </Row>

    );
};

const mapStateToProps = (state) => {
    return {
        enabled: state.settings.data.sample_unique_names
    };
};

const mapDispatchToProps = (dispatch) => {
    return {
        onToggle: (value) => {
            dispatch(updateSetting("sample_unique_names", value));
        }
    };
};

const Container = connect(mapStateToProps, mapDispatchToProps)(UniqueNames);

export default Container;
<|endoftext|>


<commit_before><commit_msg>Update namespace<commit_after><|endoftext|>


<commit_before>'enable aexpr';
'use strict';

import chai, {expect} from 'src/external/chai.js';
import sinon from 'src/external/sinon-3.2.1.js';
import sinonChai from 'src/external/sinon-chai.js';
chai.use(sinonChai);

import { wait } from 'utils';

function check() {}
function clearDefaultActiveExpressions() {}

describe('Time-based Triggers for Active Expressions', () => {
    it("runs a basic aexpr", () => {
        let obj = {a: 2, b: 3},
            spy = sinon.spy();

        aexpr(() => obj.a).onChange(spy);

        expect(spy.called).to.be.false;

        obj.a = 42;

        expect(spy.calledOnce).to.be.true;
    });

    it("recognize changes to local variables", () => {
        let val = 17,
            spy = sinon.spy();

        aexpr(() => val).onChange(spy);

        val = 42;

        expect(spy.calledOnce).to.be.true;
    });

    it("provide the new value of the expression to the callback", () => {
        let val = 17,
            spy = sinon.spy();

        aexpr(() => val).onChange(spy);

        val = 42;

        expect(spy.withArgs(42).calledOnce).to.be.true;
    });

    it("provide additional information (e.g. the last value of the expression) to the callback", () => {
        let val = 17,
            spy = sinon.spy();

        aexpr(() => val).onChange(spy);

        val = 42;

        expect(spy).to.have.been.calledOnce;
        expect(spy.getCall(0).args[0]).to.equal(42);
        expect(spy.getCall(0).args[1]).to.have.property('lastValue', 17);
    });

    describe('check', () => {
        it("run multiple checks only invokes the callback once", () => {
            let val = 17,
                spy = sinon.spy();

            aexpr(() => val).onChange(spy);

            val = 42;

            expect(spy.calledOnce).to.be.true;
        });

        it("check all active expressions if not specified further", () => {
            let val1 = 17,
                val2 = 33,
                spy1 = sinon.spy(),
                spy2 = sinon.spy();

            aexpr(() => val1).onChange(spy1);
            aexpr(() => val2).onChange(spy2);

            val1 = 42;
            val2 = 51;

            expect(spy1.calledOnce).to.be.true;
            expect(spy2.calledOnce).to.be.true;
        });

        it("check all active expressions listening on a property", () => {
            let val = 17;
            const spy1 = sinon.spy();
            const spy2 = sinon.spy();
            const spy3 = sinon.spy();
            aexpr(() => val).onChange(spy1);
            aexpr(() => val).onChange(spy2);
            aexpr(() => val).onChange(spy3);

            val = 42;

            expect(spy1.calledOnce).to.be.true;
            expect(spy2.calledOnce).to.be.true;
            expect(spy3.calledOnce).to.be.true;
        });

        // TODO: should this really be part of the API?
        it("check a single active expressions", () => {
            let val = 17,
                spy = sinon.spy(),
                expr = aexpr(() => val).onChange(spy);

            val = 42;
            expr.checkAndNotify();

            expect(spy.calledOnce).to.be.true;
        });
    });

    describe('parametrizable aexprs', () => {
        it("handle aexprs with one single instance-bindong", () => {
            let obj = { val: 17 },
                spy = sinon.spy();

            aexpr(o => o.val, { params: [obj] }).onChange(spy);

            expect(spy).not.to.be.called;

            obj.val = 42;

            expect(spy).to.be.calledOnce;
        });

        it("handle aexprs with one instance-binding with multiple variables", () => {
            let obj1 = { val: 1 },
                obj2 = { val: 2 },
                obj3 = { val: 3 },
                spy = sinon.spy();

            aexpr((o1, o2, o3) => o1.val + o2.val + o3.val, { params: [obj1, obj2, obj3] }).onChange(spy);

            expect(spy).not.to.be.called;

            obj1.val = 10;
            expect(spy.withArgs(15)).to.be.calledOnce;

            obj2.val = 20;
            expect(spy.withArgs(33)).to.be.calledOnce;
        });

        it("handle aexprs with multiple instance-bindings", () => {
            let obj1 = { val: 1 },
                obj2 = { val: 2 },
                obj3 = { val: 3 },
                spy12 = sinon.spy(),
                spy23 = sinon.spy(),
                expr = (o1, o2) => o1.val + o2.val;

            aexpr(expr, { params: [obj1, obj2] }).onChange(spy12);
            aexpr(expr, { params: [obj2, obj3] }).onChange(spy23);

            expect(spy12).not.to.be.called;
            expect(spy23).not.to.be.called;

            obj1.val = 10;
            expect(spy12.withArgs(12)).to.be.calledOnce;
            expect(spy23).not.to.be.called;

            obj2.val = 20;
            expect(spy12.withArgs(30)).to.be.calledOnce;
            expect(spy23.withArgs(23)).to.be.calledOnce;

            obj3.val = 30;
            expect(spy12.withArgs(30)).to.be.calledOnce;
            expect(spy23.withArgs(50)).to.be.calledOnce;
        });
    });

        // TODO: is this useful?
    describe('Date.now()', () => {

        it("300 ms timery", async () => {
          let spy = sinon.spy();
          let referenceTime = Date.now();
          let timeMultiplier = 10;

          // fires in 300 milliseconds
          aexpr(() => Date.now() >= 300*timeMultiplier + referenceTime).onChange(spy);

          await wait(150*timeMultiplier);
          expect(spy).not.to.be.called;

          await wait(300*timeMultiplier);
          expect(spy).to.be.calledOnce;
        });

        it("100 ms timer but aexpr disposed in between", async () => {
          let spy = sinon.spy();
          let referenceTime = Date.now();

          // fires in 100 milliseconds
          const ae = aexpr(() => Date.now() >= 100 + referenceTime).onChange(spy);

          await wait(50);
          expect(spy).not.to.be.called;

          ae.dispose();

          await wait(100);
          expect(spy).not.to.be.called;
        });

        it("new Date()/detects the global Date object, referenced as constructor", async () => {
          let spy = sinon.spy();
          let referenceTime = Date.now();

          // fires in 100 milliseconds
          aexpr(() => new Date().getTime() >= 100 + referenceTime).onChange(spy);

          await wait(50);
          expect(spy).not.to.be.called;

          await wait(100);
          expect(spy).to.be.calledOnce;
        });

        it("date.toFormattedString", async () => {
          let spy = sinon.spy();

          aexpr(() => new Date().toFormattedString('hh.mm.ss')).onChange(spy);

          await wait(1000);
          expect(spy).to.be.calledOnce;
        });

    });

});
<commit_msg>fixed 300 ms timery<commit_after>'enable aexpr';
'use strict';

import chai, {expect} from 'src/external/chai.js';
import sinon from 'src/external/sinon-3.2.1.js';
import sinonChai from 'src/external/sinon-chai.js';
chai.use(sinonChai);

import { wait } from 'utils';

function check() {}
function clearDefaultActiveExpressions() {}

describe('Time-based Triggers for Active Expressions', () => {
    it("runs a basic aexpr", () => {
        let obj = {a: 2, b: 3},
            spy = sinon.spy();

        aexpr(() => obj.a).onChange(spy);

        expect(spy.called).to.be.false;

        obj.a = 42;

        expect(spy.calledOnce).to.be.true;
    });

    it("recognize changes to local variables", () => {
        let val = 17,
            spy = sinon.spy();

        aexpr(() => val).onChange(spy);

        val = 42;

        expect(spy.calledOnce).to.be.true;
    });

    it("provide the new value of the expression to the callback", () => {
        let val = 17,
            spy = sinon.spy();

        aexpr(() => val).onChange(spy);

        val = 42;

        expect(spy.withArgs(42).calledOnce).to.be.true;
    });

    it("provide additional information (e.g. the last value of the expression) to the callback", () => {
        let val = 17,
            spy = sinon.spy();

        aexpr(() => val).onChange(spy);

        val = 42;

        expect(spy).to.have.been.calledOnce;
        expect(spy.getCall(0).args[0]).to.equal(42);
        expect(spy.getCall(0).args[1]).to.have.property('lastValue', 17);
    });

    describe('check', () => {
        it("run multiple checks only invokes the callback once", () => {
            let val = 17,
                spy = sinon.spy();

            aexpr(() => val).onChange(spy);

            val = 42;

            expect(spy.calledOnce).to.be.true;
        });

        it("check all active expressions if not specified further", () => {
            let val1 = 17,
                val2 = 33,
                spy1 = sinon.spy(),
                spy2 = sinon.spy();

            aexpr(() => val1).onChange(spy1);
            aexpr(() => val2).onChange(spy2);

            val1 = 42;
            val2 = 51;

            expect(spy1.calledOnce).to.be.true;
            expect(spy2.calledOnce).to.be.true;
        });

        it("check all active expressions listening on a property", () => {
            let val = 17;
            const spy1 = sinon.spy();
            const spy2 = sinon.spy();
            const spy3 = sinon.spy();
            aexpr(() => val).onChange(spy1);
            aexpr(() => val).onChange(spy2);
            aexpr(() => val).onChange(spy3);

            val = 42;

            expect(spy1.calledOnce).to.be.true;
            expect(spy2.calledOnce).to.be.true;
            expect(spy3.calledOnce).to.be.true;
        });

        // TODO: should this really be part of the API?
        it("check a single active expressions", () => {
            let val = 17,
                spy = sinon.spy(),
                expr = aexpr(() => val).onChange(spy);

            val = 42;
            expr.checkAndNotify();

            expect(spy.calledOnce).to.be.true;
        });
    });

    describe('parametrizable aexprs', () => {
        it("handle aexprs with one single instance-bindong", () => {
            let obj = { val: 17 },
                spy = sinon.spy();

            aexpr(o => o.val, { params: [obj] }).onChange(spy);

            expect(spy).not.to.be.called;

            obj.val = 42;

            expect(spy).to.be.calledOnce;
        });

        it("handle aexprs with one instance-binding with multiple variables", () => {
            let obj1 = { val: 1 },
                obj2 = { val: 2 },
                obj3 = { val: 3 },
                spy = sinon.spy();

            aexpr((o1, o2, o3) => o1.val + o2.val + o3.val, { params: [obj1, obj2, obj3] }).onChange(spy);

            expect(spy).not.to.be.called;

            obj1.val = 10;
            expect(spy.withArgs(15)).to.be.calledOnce;

            obj2.val = 20;
            expect(spy.withArgs(33)).to.be.calledOnce;
        });

        it("handle aexprs with multiple instance-bindings", () => {
            let obj1 = { val: 1 },
                obj2 = { val: 2 },
                obj3 = { val: 3 },
                spy12 = sinon.spy(),
                spy23 = sinon.spy(),
                expr = (o1, o2) => o1.val + o2.val;

            aexpr(expr, { params: [obj1, obj2] }).onChange(spy12);
            aexpr(expr, { params: [obj2, obj3] }).onChange(spy23);

            expect(spy12).not.to.be.called;
            expect(spy23).not.to.be.called;

            obj1.val = 10;
            expect(spy12.withArgs(12)).to.be.calledOnce;
            expect(spy23).not.to.be.called;

            obj2.val = 20;
            expect(spy12.withArgs(30)).to.be.calledOnce;
            expect(spy23.withArgs(23)).to.be.calledOnce;

            obj3.val = 30;
            expect(spy12.withArgs(30)).to.be.calledOnce;
            expect(spy23.withArgs(50)).to.be.calledOnce;
        });
    });

        // TODO: is this useful?
    describe('Date.now()', () => {

        it("300 ms timery", async () => {
          let spy = sinon.spy();
          let referenceTime = Date.now();
          let timeMultiplier = 1;

          // fires in 300 milliseconds
          aexpr(() => Date.now() >= 300*timeMultiplier + referenceTime).onChange(spy);

          await wait(150*timeMultiplier);
          expect(spy).not.to.be.called;

          await wait(300*timeMultiplier);
          expect(spy).to.be.calledOnce;
        });

        it("100 ms timer but aexpr disposed in between", async () => {
          let spy = sinon.spy();
          let referenceTime = Date.now();

          // fires in 100 milliseconds
          const ae = aexpr(() => Date.now() >= 100 + referenceTime).onChange(spy);

          await wait(50);
          expect(spy).not.to.be.called;

          ae.dispose();

          await wait(100);
          expect(spy).not.to.be.called;
        });

        it("new Date()/detects the global Date object, referenced as constructor", async () => {
          let spy = sinon.spy();
          let referenceTime = Date.now();

          // fires in 100 milliseconds
          aexpr(() => new Date().getTime() >= 100 + referenceTime).onChange(spy);

          await wait(50);
          expect(spy).not.to.be.called;

          await wait(100);
          expect(spy).to.be.calledOnce;
        });

        it("date.toFormattedString", async () => {
          let spy = sinon.spy();

          aexpr(() => new Date().toFormattedString('hh.mm.ss')).onChange(spy);

          await wait(1000);
          expect(spy).to.be.calledOnce;
        });

    });

});
<|endoftext|>


<commit_before>// Current version turns on office light when there's motion
// this code has an adaptive algorithm that extends the time the
// light is on the more often it is turned on.

const hue = require("node-hue-api")
const moment = require("moment")
const constants = require('./constants')
const api = new hue.HueApi(constants.ip, constants.username)
const particle = require('./lib/Particle')
const _ = require('underscore')
const lightState = hue.lightState
const huelib = require('./lib/Hue')
const lightName = require('./lib/LightName')

const debug = function(m) { console.log(`${moment().format('MMM DD hh:mm:ss')}   ${m}`) }

const lightIntervals = [10, 20, 60, 5*60, 10*60, 15*60] // seconds
let lightIntervalIndex = 0

let senseTimerHandle = null
let lightTimerHandle = null
let motionSticky = false

// called whenever motion is seen
const onMotion = function() {
	debug(`saw motion`)
	debug(`setting light timer to ${getLightInterval()} seconds`)
	motionSticky = true
	turnLightsOn()
	clearTimeout(lightTimerHandle)
	lightTimerHandle = setTimeout(turnLightsOff, getLightInterval() * 1000)

	// set a timer that determines next time to see if there's been motion
	// set twice as long as light is on, so that user can wave to turn light on
	// when it goes off, and process is still sensing
	if (!senseTimerHandle) {
		debug(`setting sense timer to ${getLightInterval() *2} seconds`)
		senseTimerHandle = setTimeout(senseTimerExpired, getLightInterval() * 2 * 1000)
	}
}

const senseTimerExpired = function() {
	debug(`sense timer expired`)
	senseTimerHandle = null
	if (motionSticky) {
		debug(`there was motion during sense period`)
		motionSticky = false
		increaseLightInterval()
		// set timer again and wait another sense interval
		debug(`setting sense timer for ${getLightInterval() * 2} seconds`)
		senseTimerHandle = setTimeout(senseTimerExpired, getLightInterval() * 2 * 1000)
	} else {
		// no motion in last sense interval
		// set sense interval back to default and turn lights off
		resetLightInterval()
		debug(`no motion in sense interval, light interval is now ${getLightInterval()} seconds`)
	}
}

const getLightInterval = function() {
	return lightIntervals[lightIntervalIndex]
}

const increaseLightInterval = function() {
	lightIntervalIndex = Math.min(lightIntervalIndex + 1, lightIntervals.length)
}

const resetLightInterval = function() {
	lightIntervalIndex = 0
}

// turn light to low on then fade to on. Turn light to low off, then fade to off
var lights = null // set by main from argv
const lowOn = lightState.create().on(true).hsb(100, 30, 0)
const on = lightState.create().on(true).white(325, 100	).transition(3000)
const lowOff = lightState.create().on(true).hsb(250, 100, 0).transition(10000)
const off = lightState.create().on(false)

// used to transition between low on to on and low off to off states.
let lightTimer = null
let _lightState = 'off' // off, lowOff, on, lowOn

const turnLightsOn = function() {
	if (isDaytime()) { return }
	if (_lightState === 'off') {
		// set to lowOn, wait, On
		clearTimeout(lightTimer)
		setLights(lights, lowOn)
		_lightState = 'lowOn'
		lightTimer = setTimeout(function() {
			setLights(lights, on)
			_lightState = 'on'
		}, 1000)
	} else if (_lightState === 'lowOff') {
		// straight to on
		clearTimeout(lightTimer)
		setLights(lights, on)
		_lightState = 'on'
	}
	// if _lightState is lowOn or on, do nothing
}

const turnLightsOff = function() {
	if (_lightState === 'on' || _lightState === 'lowOn') {
		debug(`turning lights off`)
		clearTimeout(lightTimer)
		setLights(lights, lowOff)
		_lightState = 'lowOff'
		lightTimer = setTimeout(function() {
			setLights(lights, off)
			_lightState = 'off'
		}, 10000)
	}
}

const setLights = function(lights, lightState) {
	lights.forEach(function(light) {
		api.setLightState(light.id, lightState)
	})
}

const isDaytime = function() {
	let h = moment().hour()
	return (h > 7 && h < 17)
}

const loop = function() {
	particle.getMotion()
	.then(function(reply) {
		if (reply.return_value !== 0) {
			onMotion()
		}
		setTimeout(loop, 1000)
	}).catch(function(err) {
		debug(`Caught error: ${err}`)
		setTimeout(loop, 10000)
	})
}

const main = function() {
	if (process.argv.length < 3) {
		usage()
		process.exit(0)
	}

	debug(`starting`)
	lights = lightName.lightsFromNamesOrExit(process.argv.slice(2))
	setLights(lights, off)
	loop()
}

const usage = function() {
	debug("usage: node " + __filename + " LIGHTNAME LIGHTNAME ...")
	debug("Each LIGHTNAME is the name of a light defined in constants.js:")
	var lightNames = _.pluck(constants.lights, 'name').join(", ")
	debug('"' + lightNames + '"')
}

main()

<commit_msg>changed timing<commit_after>// Current version turns on office light when there's motion
// this code has an adaptive algorithm that extends the time the
// light is on the more often it is turned on.

const hue = require("node-hue-api")
const moment = require("moment")
const constants = require('./constants')
const api = new hue.HueApi(constants.ip, constants.username)
const particle = require('./lib/Particle')
const _ = require('underscore')
const lightState = hue.lightState
const huelib = require('./lib/Hue')
const lightName = require('./lib/LightName')

const debug = function(m) { console.log(`${moment().format('MMM DD hh:mm:ss')}   ${m}`) }

const lightIntervals = [10, 60, 5*60, 10*60, 15*60] // seconds
let lightIntervalIndex = 0

let senseTimerHandle = null
let lightTimerHandle = null
let motionSticky = false

// called whenever motion is seen
const onMotion = function() {
	debug(`saw motion`)
	debug(`setting light timer to ${getLightInterval()} seconds`)
	motionSticky = true
	turnLightsOn()
	clearTimeout(lightTimerHandle)
	lightTimerHandle = setTimeout(turnLightsOff, getLightInterval() * 1000)

	// set a timer that determines next time to see if there's been motion
	// set twice as long as light is on, so that user can wave to turn light on
	// when it goes off, and process is still sensing
	if (!senseTimerHandle) {
		debug(`setting sense timer to ${getLightInterval() *2} seconds`)
		senseTimerHandle = setTimeout(senseTimerExpired, getLightInterval() * 2 * 1000)
	}
}

const senseTimerExpired = function() {
	debug(`sense timer expired`)
	senseTimerHandle = null
	if (motionSticky) {
		debug(`there was motion during sense period`)
		motionSticky = false
		increaseLightInterval()
		// set timer again and wait another sense interval
		debug(`setting sense timer for ${getLightInterval() * 2} seconds`)
		senseTimerHandle = setTimeout(senseTimerExpired, getLightInterval() * 2 * 1000)
	} else {
		// no motion in last sense interval
		// set sense interval back to default and turn lights off
		resetLightInterval()
		debug(`no motion in sense interval, light interval is now ${getLightInterval()} seconds`)
	}
}

const getLightInterval = function() {
	return lightIntervals[lightIntervalIndex]
}

const increaseLightInterval = function() {
	lightIntervalIndex = Math.min(lightIntervalIndex + 1, lightIntervals.length)
}

const resetLightInterval = function() {
	lightIntervalIndex = 0
}

// turn light to low on then fade to on. Turn light to low off, then fade to off
var lights = null // set by main from argv
const lowOn = lightState.create().on(true).hsb(100, 30, 0)
const on = lightState.create().on(true).white(325, 100	).transition(3000)
const lowOff = lightState.create().on(true).hsb(250, 100, 0).transition(10000)
const off = lightState.create().on(false)

// used to transition between low on to on and low off to off states.
let lightTimer = null
let _lightState = 'off' // off, lowOff, on, lowOn

const turnLightsOn = function() {
	if (isDaytime()) { return }
	if (_lightState === 'off') {
		// set to lowOn, wait, On
		clearTimeout(lightTimer)
		setLights(lights, lowOn)
		_lightState = 'lowOn'
		lightTimer = setTimeout(function() {
			setLights(lights, on)
			_lightState = 'on'
		}, 1000)
	} else if (_lightState === 'lowOff') {
		// straight to on
		clearTimeout(lightTimer)
		setLights(lights, on)
		_lightState = 'on'
	}
	// if _lightState is lowOn or on, do nothing
}

const turnLightsOff = function() {
	if (_lightState === 'on' || _lightState === 'lowOn') {
		debug(`turning lights off`)
		clearTimeout(lightTimer)
		setLights(lights, lowOff)
		_lightState = 'lowOff'
		lightTimer = setTimeout(function() {
			setLights(lights, off)
			_lightState = 'off'
		}, 10000)
	}
}

const setLights = function(lights, lightState) {
	lights.forEach(function(light) {
		api.setLightState(light.id, lightState)
	})
}

const isDaytime = function() {
	let h = moment().hour()
	return (h > 7 && h < 17)
}

const loop = function() {
	particle.getMotion()
	.then(function(reply) {
		if (reply.return_value !== 0) {
			onMotion()
		}
		setTimeout(loop, 1000)
	}).catch(function(err) {
		debug(`Caught error: ${err}`)
		setTimeout(loop, 10000)
	})
}

const main = function() {
	if (process.argv.length < 3) {
		usage()
		process.exit(0)
	}

	debug(`starting`)
	lights = lightName.lightsFromNamesOrExit(process.argv.slice(2))
	setLights(lights, off)
	loop()
}

const usage = function() {
	debug("usage: node " + __filename + " LIGHTNAME LIGHTNAME ...")
	debug("Each LIGHTNAME is the name of a light defined in constants.js:")
	var lightNames = _.pluck(constants.lights, 'name').join(", ")
	debug('"' + lightNames + '"')
}

main()

<|endoftext|>


<commit_before>/*
	JsonML.js

	Created: 2006-11-09-0116
	Modified: 2007-03-08-2036

	Released under an open-source license:
	http://jsonml.org/License.htm

    This file adds these methods to JavaScript:

        string.parseJsonML(filter)

            This method produces a tree of DOM elements from a (JSON text) JsonML tree.

        array.parseJsonML(filter)

            This method produces a tree of DOM elements from a JsonML tree. The
            array must not contain any cyclical references.

            The optional filter parameter is a function which can filter and
            transform the results. It receives each of the DOM nodes, and
            its return value is used instead of the original value. If it
            returns what it received, then structure is not modified. If it
            returns undefined then the member is deleted.

			This is useful for binding unobtrusive JavaScript to the generated
			DOM elements.

            Example:

            // Parses the structure. If an element has a specific CSS value then
            // takes appropriate action: Remove from results, add special event
            // handlers, or bind to a custom component.

            var myUI = myUITemplate.parseJsonML(function (elem) {
				if (elem.className.indexOf("Remove-Me") >= 0) {
					// this will remove from resulting DOM tree
					return undefined;
				}

				if (elem.tagName && elem.tagName.toLowerCase() === "a" &&
					elem.className.indexOf("External-Link") >= 0) {
					// this is the equivalent of target="_blank"
					elem.onclick = function(evt) {
						window.open(elem.href); return false;
					};

				} else if (elem.className.indexOf("Fancy-Widgit") >= 0) {
					// bind to a custom component
					FancyWidgit.bindDOM(elem);
				}
				return elem;
			});

*/

/*element*/ Array.prototype.parseJsonML = function (/*element function(element)*/ filter) {

	//attribute name mapping
	var am = {
		"rowspan" : "rowSpan",
		"colspan" : "colSpan",
		"tabindex" : "tabIndex",
		"accesskey" : "accessKey",
		"hidefocus" : "hideFocus"
	};

	//addAttributes
	/*void*/ function aa(/*element*/ el, /*Object*/ a) {
		// for each attributeName
		for (var an in a) {
			if (!an || typeof(a[an]) !== "string") {
				continue;
			}
			if (an.toLowerCase() === "style") {
				if ("undefined" !== typeof el.style.cssText) {
					el.style.cssText = a[an];
				} else {
					el.style = a[an];
				}
			} else if (an.toLowerCase() === "class") {
				el.className = a[an];
			} else if (am[an.toLowerCase()]) {
				el.setAttribute(am[an.toLowerCase()], a[an]);
			} else {
				el.setAttribute(an, a[an]);
			}
		}
	}

	//appendChild
	/*void*/ function ac(/*element*/ el, /*Array or String*/ c) {
		if (c) {
			if (el.tagName.toLowerCase() === "table" && el.tBodies) {
				// in IE must explicitly nest TDs in TBODY
				var ct = c.tagName ? c.tagName.toLowerCase() : null;// child tagName
				if (ct && ct!=="tbody" && ct!=="thead") {
					// insert in last tbody
					var tb = el.tBodies.length>0 ? el.tBodies[el.tBodies.length-1] : null;// tBody
					if (!tb) {
						tb = document.createElement("tbody");
						el.appendChild(tb);
					}
					tb.appendChild(c);
				}
			} else {
				el.appendChild(c);
			}
		}
	}

	//parseJsonML
	/*element*/ function p(/*JsonML*/ jml) {
		if (!jml) {
			return null;
		}
		if (typeof(jml) === "string") {
			return document.createTextNode(jml);
		}

		if (!(jml instanceof Array) || jml.length < 1 || typeof(jml[0]) !== "string") {
			throw new Error("parseJsonML");
		}

		var t = jml[0]; // tagName
		var x = (t.toLowerCase() === "script"); // check for scripts
		var el = x ? null : document.createElement(t);

		for (var i=1; i<jml.length; i++) {
			if (!x) {
				if (jml[i] instanceof Array || typeof(jml[i]) === "string") {
					// append children
					ac(el, p(jml[i]));
				} else if (typeof(jml[i]) === "object") {
					// add attributes
					aa(el, jml[i]);
				}
			//} else if (typeof(jml[i]) === "string") {
				/*	JSLint: "eval is evil"
					uncomment at your own risk, executes script elements */
				//eval( "(" + jml[i] + ")" );
			}
		}

		return (el && "function" === typeof filter) ? filter(el) : el;
	}

	return p(this);
};

/*element*/ String.prototype.parseJsonML = function (/*function(element)*/ filter) {
	try {
		var jml = this.parseJSON();
	} catch (ex) {
		return null;
	}
	return (jml instanceof Array) ? jml.parseJsonML(filter) : null;
};
<commit_msg>- adding more attribute apps<commit_after>/*
	JsonML.js

	Created: 2006-11-09-0116
	Modified: 2007-03-08-2036

	Released under an open-source license:
	http://jsonml.org/License.htm

    This file adds these methods to JavaScript:

        string.parseJsonML(filter)

            This method produces a tree of DOM elements from a (JSON text) JsonML tree.

        array.parseJsonML(filter)

            This method produces a tree of DOM elements from a JsonML tree. The
            array must not contain any cyclical references.

            The optional filter parameter is a function which can filter and
            transform the results. It receives each of the DOM nodes, and
            its return value is used instead of the original value. If it
            returns what it received, then structure is not modified. If it
            returns undefined then the member is deleted.

			This is useful for binding unobtrusive JavaScript to the generated
			DOM elements.

            Example:

            // Parses the structure. If an element has a specific CSS value then
            // takes appropriate action: Remove from results, add special event
            // handlers, or bind to a custom component.

            var myUI = myUITemplate.parseJsonML(function (elem) {
				if (elem.className.indexOf("Remove-Me") >= 0) {
					// this will remove from resulting DOM tree
					return undefined;
				}

				if (elem.tagName && elem.tagName.toLowerCase() === "a" &&
					elem.className.indexOf("External-Link") >= 0) {
					// this is the equivalent of target="_blank"
					elem.onclick = function(evt) {
						window.open(elem.href); return false;
					};

				} else if (elem.className.indexOf("Fancy-Widgit") >= 0) {
					// bind to a custom component
					FancyWidgit.bindDOM(elem);
				}
				return elem;
			});

*/

/*element*/ Array.prototype.parseJsonML = function (/*element function(element)*/ filter) {

	//attribute name mapping
	var am = {
		"rowspan" : "rowSpan",
		"colspan" : "colSpan",
		"cellpadding" : "cellPadding",
		"cellspacing" : "cellSpacing",
		"tabindex" : "tabIndex",
		"accesskey" : "accessKey",
		"hidefocus" : "hideFocus",
		"usemap" : "useMap",
		"maxlength" : "maxLength",
		"readonly" : "readOnly",
		"contenteditable" : "contentEditable"
		// can add more attributes here as needed
	};

	//addAttributes
	/*void*/ function aa(/*element*/ el, /*Object*/ a) {
		// for each attributeName
		for (var an in a) {
			if (!an || typeof(a[an]) !== "string") {
				continue;
			}
			if (an.toLowerCase() === "style") {
				if ("undefined" !== typeof el.style.cssText) {
					el.style.cssText = a[an];
				} else {
					el.style = a[an];
				}
			} else if (an.toLowerCase() === "class") {
				el.className = a[an];
			} else if (am[an.toLowerCase()]) {
				el.setAttribute(am[an.toLowerCase()], a[an]);
			} else {
				el.setAttribute(an, a[an]);
			}
		}
	}

	//appendChild
	/*void*/ function ac(/*element*/ el, /*Array or String*/ c) {
		if (c) {
			if (el.tagName.toLowerCase() === "table" && el.tBodies) {
				// in IE must explicitly nest TDs in TBODY
				var ct = c.tagName ? c.tagName.toLowerCase() : null;// child tagName
				if (ct && ct!=="tbody" && ct!=="thead") {
					// insert in last tbody
					var tb = el.tBodies.length>0 ? el.tBodies[el.tBodies.length-1] : null;// tBody
					if (!tb) {
						tb = document.createElement("tbody");
						el.appendChild(tb);
					}
					tb.appendChild(c);
				}
			} else {
				el.appendChild(c);
			}
		}
	}

	//parseJsonML
	/*element*/ function p(/*JsonML*/ jml) {
		if (!jml) {
			return null;
		}
		if (typeof(jml) === "string") {
			return document.createTextNode(jml);
		}

		if (!(jml instanceof Array) || jml.length < 1 || typeof(jml[0]) !== "string") {
			throw new Error("parseJsonML");
		}

		var t = jml[0]; // tagName
		var x = (t.toLowerCase() === "script"); // check for scripts
		var el = x ? null : document.createElement(t);

		for (var i=1; i<jml.length; i++) {
			if (!x) {
				if (jml[i] instanceof Array || typeof(jml[i]) === "string") {
					// append children
					ac(el, p(jml[i]));
				} else if (typeof(jml[i]) === "object") {
					// add attributes
					aa(el, jml[i]);
				}
			//} else if (typeof(jml[i]) === "string") {
				/*	JSLint: "eval is evil"
					uncomment at your own risk, executes script elements */
				//eval( "(" + jml[i] + ")" );
			}
		}

		return (el && "function" === typeof filter) ? filter(el) : el;
	}

	return p(this);
};

/*element*/ String.prototype.parseJsonML = function (/*function(element)*/ filter) {
	try {
		var jml = this.parseJSON();
	} catch (ex) {
		return null;
	}
	return (jml instanceof Array) ? jml.parseJsonML(filter) : null;
};
<|endoftext|>