<commit_before>"use strict"

var config = require("config")
var utils = require("../lib/utils.js")
var errorLib = require("../lib/error.js")
var db = require("../lib/db.js")
var clc = require('cli-color')
var async = require("async")
var serializeGeneral = require("../lib/serialize_utils.js")

exports.prepareAgents = function(cb){
	db.prepareTripleStoreAgents(function(){

		if (cb) cb()

	})
}
exports.prepareResources = function(cb){
	db.prepareTripleStoreResources(function(){
		if (cb) cb()
	})
}

exports.insertAgents = function(cb){

	var map = {

		"personal" : "foaf:Person",
		false: "foaf:Person",
		"corporate" : 'foaf:Group'

	}

	//load up the agents
	var totalAgents = 0
	db.returnCollectionRegistry("agents",function(err,agents){

		var cursor = agents.find({}).stream()
		var batch = []

		cursor.on('data', function(agent) {

			process.stdout.cursorTo(0)
			process.stdout.write(clc.black.bgBlueBright("insertAgents | totalAgents: " + totalAgents ))

			var rdfAgent = new serializeGeneral.resourceObject('agent:' + agent.registry)

			rdfAgent.addTriple( 'rdf:type', 'edm:Agent', null,  null, "data:10000", agent.source)
			agent.viafAll.forEach(function(v){
				rdfAgent.addTriple( 'skos:exactMatch', 'http://viaf.org/viaf/'+v, null,  null, "data:10000", agent.source)
			})
			rdfAgent.addTriple( 'skos:prefLabel', null, agent.nameControlled,  null, "data:10000", agent.source)

			if (agent.type){
				rdfAgent.addTriple( 'rdf:type', map[agent.type], null,  null, "data:10000", agent.source)
			}

			if (agent.wikidata){
				rdfAgent.addTriple( 'skos:exactMatch', "wikidata:"+agent.wikidata, null,  null, "data:10000", agent.source)
			}
			if (agent.lcId){
				rdfAgent.addTriple( 'skos:exactMatch', "lc:"+agent.lcId, null,  null, "data:10000", agent.source)
			}
			if (agent.dbpedia){
				rdfAgent.addTriple( 'skos:exactMatch', "dbr:"+agent.dbpedia, null,  null, "data:10000", agent.source)
			}
			if (agent.birth){
				rdfAgent.addTriple( 'dbo:birthDate', null, agent.birth,  null, "data:10000", agent.source)
			}
			if (agent.death){
				rdfAgent.addTriple( 'dbo:deathDate', null, agent.birth,  null, "data:10000", agent.source)
			}
			if (agent.wikidataSlug){
				rdfAgent.addTriple( 'foaf:isPrimaryTopicOf', null, agent.wikidataSlug,  null, "data:10000", agent.source)
			}
			if (agent.wikidataImage){
				rdfAgent.addTriple( 'foaf:depiction', null, agent.wikidataImage,  null, "data:10000", agent.source)
			}
			if (agent.wikidataDesc){
				rdfAgent.addTriple( 'dcterms:description', null, agent.wikidataDesc,  null, "data:10000", agent.source)
			}

			rdfAgent.viaf = agent.viafAll

			delete rdfAgent['addTriple']
			delete rdfAgent['allAgents']
			delete rdfAgent['allTerms']

			// rdfAgent.wikidata = false
			// rdfAgent.lcId = false
			// rdfAgent.dbpedia = false
			// rdfAgent.birth = false
			// rdfAgent.death = false
			// rdfAgent.wikidataSlug = false
			// rdfAgent.wikidataImage = false
			// rdfAgent.wikidataDesc = false

			// if (agent.wikidata) rdfAgent.wikidata = agent.wikidata
			// if (agent.lcId) rdfAgent.lcId = agent.lcId
			// if (agent.dbpedia) rdfAgent.dbpedia = agent.dbpedia
			// if (agent.birth) rdfAgent.birth = agent.birth
			// if (agent.death) rdfAgent.death = agent.death
			// if (agent.wikidataSlug) rdfAgent.wikidataSlug = agent.wikidataSlug
			// if (agent.wikidataImage) rdfAgent.wikidataImage = agent.wikidataImage
			// if (agent.wikidataDesc) rdfAgent.wikidataDesc = agent.wikidataDesc

			batch.push(rdfAgent)
			totalAgents++

			if (batch.length===999){

				cursor.pause()

				var bulk = db.newTripleStoreBulkOp('agents',function(bulk){
					batch.forEach(function(b){
						bulk.insert(b)
					})
					batch = []
					bulk.execute(function(err, result) {

						if (err){
							console.log(err)
						}

						setTimeout(function(){ cursor.resume() },50)

					})

				})
			}

			//cursor.pause()
			// console.log(agent)
			// console.log(rdfAgent)

		})

		cursor.once('end', function() {

			var count = 0

			var i = setInterval(function(){

				if (count === totalAgents){

					clearInterval(i)

					//the last batch
					var bulk = db.newTripleStoreBulkOp('agents',function(bulk){
						batch.forEach(function(b){
							bulk.insert(b)
						})
						batch = []
						bulk.execute()

						if (cb) cb()

					})

				}else{
					count = totalAgents
				}

			},100)

			//console.log("populateArchivesAgentsCollctions - Done!\n")
			//process.stdout.cursorTo(0)
			//process.stdout.write(clc.black.bgGreenBright("populateArchivesAgentsCollctions | totalAgents: " + totalAgents + " totalAgentsWithViaf:" + totalAgentsWithViaf + " totalAgentsNotFoundInRegistry: " + totalAgentsNotFoundInRegistry ))
			if (cb) cb()
		})

	})
}<commit_msg>agents to triple store<commit_after>"use strict"

var config = require("config")
var utils = require("../lib/utils.js")
var errorLib = require("../lib/error.js")
var db = require("../lib/db.js")
var clc = require('cli-color')
var async = require("async")
var serializeGeneral = require("../lib/serialize_utils.js")

exports.prepareAgents = function(cb){
	db.prepareTripleStoreAgents(function(){

		if (cb) cb()

	})
}
exports.prepareResources = function(cb){
	db.prepareTripleStoreResources(function(){
		if (cb) cb()
	})
}

exports.insertAgents = function(cb){

	var map = {

		"personal" : "foaf:Person",
		false: "foaf:Person",
		"corporate" : 'foaf:Group'

	}

	//load up the agents
	var totalAgents = 0
	db.returnCollectionRegistry("agents",function(err,agents){

		var cursor = agents.find({}).stream()
		var batch = []

		cursor.on('data', function(agent) {

			process.stdout.cursorTo(0)
			process.stdout.write(clc.black.bgBlueBright("insertAgents | totalAgents: " + totalAgents ))

			var rdfAgent = new serializeGeneral.resourceObject(agent.registry)

			rdfAgent.addTriple( 'rdf:type', 'edm:Agent', null,  null, "data:10000", agent.source)
			agent.viafAll.forEach(function(v){
				rdfAgent.addTriple( 'skos:exactMatch', 'http://viaf.org/viaf/'+v, null,  null, "data:10000", agent.source)
			})
			rdfAgent.addTriple( 'skos:prefLabel', null, agent.nameControlled,  null, "data:10000", agent.source)

			if (agent.type){
				rdfAgent.addTriple( 'rdf:type', map[agent.type], null,  null, "data:10000", agent.source)
			}

			if (agent.wikidata){
				rdfAgent.addTriple( 'skos:exactMatch', "wikidata:"+agent.wikidata, null,  null, "data:10000", agent.source)
			}
			if (agent.lcId){
				rdfAgent.addTriple( 'skos:exactMatch', "lc:"+agent.lcId, null,  null, "data:10000", agent.source)
			}
			if (agent.dbpedia){
				rdfAgent.addTriple( 'skos:exactMatch', "dbr:"+agent.dbpedia, null,  null, "data:10000", agent.source)
			}
			if (agent.birth){
				rdfAgent.addTriple( 'dbo:birthDate', null, agent.birth,  null, "data:10000", agent.source)
			}
			if (agent.death){
				rdfAgent.addTriple( 'dbo:deathDate', null, agent.birth,  null, "data:10000", agent.source)
			}
			if (agent.wikidataSlug){
				rdfAgent.addTriple( 'foaf:isPrimaryTopicOf', null, agent.wikidataSlug,  null, "data:10000", agent.source)
			}
			if (agent.wikidataImage){
				rdfAgent.addTriple( 'foaf:depiction', null, agent.wikidataImage,  null, "data:10000", agent.source)
			}
			if (agent.wikidataDesc){
				rdfAgent.addTriple( 'dcterms:description', null, agent.wikidataDesc,  null, "data:10000", agent.source)
			}

			if (!agent.viafAll){
				rdfAgent.viaf = [agent.viaf]
			}else if (agent.viafAll.length==0){
				rdfAgent.viaf = [agent.viaf]
			}else{
				rdfAgent.viaf = agent.viafAll
			}

			delete rdfAgent['addTriple']
			delete rdfAgent['allAgents']
			delete rdfAgent['allTerms']

			for (var x in rdfAgent.triples){
				rdfAgent[x] = rdfAgent.triples[x]
			}

			// rdfAgent.wikidata = false
			// rdfAgent.lcId = false
			// rdfAgent.dbpedia = false
			// rdfAgent.birth = false
			// rdfAgent.death = false
			// rdfAgent.wikidataSlug = false
			// rdfAgent.wikidataImage = false
			// rdfAgent.wikidataDesc = false

			// if (agent.wikidata) rdfAgent.wikidata = agent.wikidata
			// if (agent.lcId) rdfAgent.lcId = agent.lcId
			// if (agent.dbpedia) rdfAgent.dbpedia = agent.dbpedia
			// if (agent.birth) rdfAgent.birth = agent.birth
			// if (agent.death) rdfAgent.death = agent.death
			// if (agent.wikidataSlug) rdfAgent.wikidataSlug = agent.wikidataSlug
			// if (agent.wikidataImage) rdfAgent.wikidataImage = agent.wikidataImage
			// if (agent.wikidataDesc) rdfAgent.wikidataDesc = agent.wikidataDesc

			batch.push(rdfAgent)
			totalAgents++

			if (batch.length===999){

				cursor.pause()

				var bulk = db.newTripleStoreBulkOp('agents',function(bulk){
					batch.forEach(function(b){
						bulk.insert(b)
					})
					batch = []
					bulk.execute(function(err, result) {

						if (err){
							console.log(err)
						}

						cursor.resume()

					})

				})
			}

			//cursor.pause()
			// console.log(agent)
			// console.log(rdfAgent)

		})

		cursor.once('end', function() {

			var count = 0

			var i = setInterval(function(){

				if (count === totalAgents){

					clearInterval(i)

					//the last batch
					var bulk = db.newTripleStoreBulkOp('agents',function(bulk){
						batch.forEach(function(b){
							bulk.insert(b)
						})
						batch = []
						bulk.execute()

						if (cb) cb()

					})

				}else{
					count = totalAgents
				}

			},100)

			//console.log("populateArchivesAgentsCollctions - Done!\n")
			//process.stdout.cursorTo(0)
			//process.stdout.write(clc.black.bgGreenBright("populateArchivesAgentsCollctions | totalAgents: " + totalAgents + " totalAgentsWithViaf:" + totalAgentsWithViaf + " totalAgentsNotFoundInRegistry: " + totalAgentsNotFoundInRegistry ))
			if (cb) cb()
		})

	})
}<|endoftext|>


<commit_before>'use strict';

var CallbackStore = require('famous-utilities').CallbackStore;

var ELEMENT = 'element';
var ID = 'id';
var OPACITY = 'opacity';
var WITH = 'WITH';
var CHANGE_TRANSFORM = 'CHANGE_TRANSFORM';
var CHANGE_PROPERTY = 'CHANGE_PROPERTY';
var INIT_DOM = 'INIT_DOM';
var CHANGE_ATTRIBUTE = 'CHANGE_ATTRIBUTE';
var ADD_CLASS = 'ADD_CLASS';
var REMOVE_CLASS = 'REMOVE_CLASS';
var CHANGE_ATTRIBUTE = 'CHANGE_ATTRIBUTE';
var CHANGE_CONTENT = 'CHANGE_CONTENT';
var ADD_EVENT_LISTENER = 'ADD_EVENT_LISTENER';
var EVENT_PROPERTIES = 'EVENT_PROPERTIES';
var EVENT_END = 'EVENT_END';
var RECALL = 'RECALL';

/**
 * The Element class is responsible for providing the API for how
 *   a RenderNode will interact with the DOM API's.  The element is
 *   responsible for adding a set of commands to the renderer.
 *
 * @class HTMLElement
 * @constructor
 * @component
 * @param {RenderNode} RenderNode to which the instance of Element will be a component of
 */
function HTMLElement(dispatch, tagName) {
    this._dispatch = dispatch;
    this._id = dispatch.addRenderable(this);
    this._trueSized = [false, false];
    this._size = [0, 0, 0];
    this._queue = [];

    this._queue.push(INIT_DOM);
    this._queue.push(tagName ? tagName : 'div');

    this._callbacks = new CallbackStore();
    this._dispatch.onTransformChange(this._receiveTransformChange.bind(this));
    this._dispatch.onSizeChange(this._receiveSizeChange.bind(this));
    this._dispatch.onOpacityChange(this._receiveOpacityChange.bind(this));
    this._receiveTransformChange(this._dispatch.getContext()._transform);
    this._receiveSizeChange(this._dispatch.getContext()._size);
    this._receiveOpacityChange(this._dispatch.getContext()._opacity);
}

// Return the name of the Element Class: 'element'
HTMLElement.toString = function toString() {
    return ELEMENT;
};

HTMLElement.prototype.clean = function clean() {
    var len = this._queue.length;
    if (len) {
    	var path = this._dispatch.getRenderPath();
    	this._dispatch
            .sendDrawCommand(WITH)
            .sendDrawCommand(path)
            .sendDrawCommand('DOM');

    	for (var i = 0 ; i < len ; i++)
    	    this._dispatch.sendDrawCommand(this._queue.shift());
    }
    return false;
};

HTMLElement.prototype._receiveTransformChange = function _receiveTransformChange(transform) {
    this._dispatch.dirtyRenderable(this._id);
    var queue = this._queue;
    queue.push(CHANGE_TRANSFORM);
    queue.push(transform._matrix[0]);
    queue.push(transform._matrix[1]);
    queue.push(transform._matrix[2]);
    queue.push(transform._matrix[3]);
    queue.push(transform._matrix[4]);
    queue.push(transform._matrix[5]);
    queue.push(transform._matrix[6]);
    queue.push(transform._matrix[7]);
    queue.push(transform._matrix[8]);
    queue.push(transform._matrix[9]);
    queue.push(transform._matrix[10]);
    queue.push(transform._matrix[11]);
    queue.push(transform._matrix[12]);
    queue.push(transform._matrix[13]);
    queue.push(transform._matrix[14]);
    queue.push(transform._matrix[15]);
};

HTMLElement.prototype._receiveSizeChange = function _receiveSizeChange(size) {
    this._dispatch.dirtyRenderable(this._id);
    var width = this._trueSized[0] ? this._trueSized[0] : size._size[0];
    var height = this._trueSized[1] ? this._trueSized[1] : size._size[1];
    this._queue.push('CHANGE_SIZE');
    this._queue.push(width);
    this._queue.push(height);
    this._size[0] = width;
    this._size[1] = height;
};

HTMLElement.prototype._receiveOpacityChange = function _receiveOpacityChange(opacity) {
    this.property(OPACITY, opacity.value);
};

HTMLElement.prototype.getSize = function getSize() {
    return this._size;
};

HTMLElement.prototype.on = function on (ev, methods, properties) {
    this.eventListener(ev, methods, properties);
    return this;
};

HTMLElement.prototype.kill = function kill () {
    this._dispatch.sendDrawCommand(WITH).sendDrawCommand(this._dispatch.getRenderPath()).sendDrawCommand(RECALL);
};

/**
 * Set the value of a CSS property
 *
 * @method property
 * @chainable
 *
 * @param {String} key name of the property to set
 * @param {Any} value associated value for this property
 * @return {HTMLElement} current HTMLElement
 */
HTMLElement.prototype.property = function property(key, value) {
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(CHANGE_PROPERTY);
    this._queue.push(key);
    this._queue.push(value);
    return this;
};

/**
 * The method by which a user tells the element to ignore the context
 *   size and get size from the content instead.
 *
 * @method trueSize
 * @chainable
 *
 * @param {Boolean} trueWidth
 * @param {Boolean} trueHeight
 * @return {HTMLElement} this
 */
HTMLElement.prototype.trueSize = function trueSize(trueWidth, trueHeight) {
    if (trueWidth === undefined) trueWidth = true;
    if (trueHeight === undefined) trueHeight = true;

    if (this._trueSized[0] !== trueWidth || this._trueSized[1] !== trueHeight) {
        this._dispatch.dirtyRenderable(this._id);
    }
    this._trueSized[0] = trueWidth;
    this._trueSized[1] = trueHeight;
    return this;
};

/**
 * Set an attribute on the DOM element
 *
 * @method attribute
 * @chainable
 *
 * @param {String} key name of the attribute to set
 * @param {Any} value associated value for this attribute
 * @return {HTMLElement} current HTMLElement
 */
HTMLElement.prototype.attribute = function attribute(key, value) {
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(CHANGE_ATTRIBUTE);
    this._queue.push(key);
    this._queue.push(value);
    return this;
};

/**
 * Define a CSS class to be added to the DOM element
 *
 * @method addClass
 * @chainable
 *
 * @param {String} value name of the class
 * @return {HTMLElement} current HTMLElement
 */
HTMLElement.prototype.addClass = function addClass(value) {
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(ADD_CLASS);
    this._queue.push(value);
    return this;
};
/**
 * Define a CSS class to be removed from the DOM element
 *
 * @method removeClass
 * @chainable
 *
 * @param {String} value name of the class
 * @return {HTMLElement} current HTMLElement
 */
HTMLElement.prototype.removeClass = function removeClass(value) {
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(REMOVE_CLASS);
    this._queue.push(value);
    return this;
};

/**
 * Define the id of the DOM element
 *
 * @method id
 * @chainable
 *
 * @param {String} value id
 * @return {HTMLElement} current HTMLElement
 */
HTMLElement.prototype.id = function id(value) {
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(CHANGE_ATTRIBUTE);
    this._queue.push(ID);
    this._queue.push(value);
    return this;
};

/**
 * Define the content of the DOM element
 *
 * @method content
 * @chainable
 *
 * @param {String|DOMElement|DocumentFragment} value content to be inserted into the DOM element
 * @return {HTMLElement} current HTMLElement
 */
HTMLElement.prototype.content = function content(value) {
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(CHANGE_CONTENT);
    this._queue.push(value);
    return this;
};

/**
 * Get a component of the RenderNode that the HTMLElement component is
 *   attached to by name.
 *
 * @method get
 *
 * @param {String} key name of the component to grab from the RenderNode
 * @return {Component} a component that exists on the RenderNode
 */
HTMLElement.prototype.get = function get (key) {
    return this.owner.get(key);
};

/**
 * Adds a command to add an eventListener to the current DOM HTMLElement.
 *   primarily used internally.
 *
 * @method eventListener
 * @chainable
 *
 * @param {String} ev the name of the DOM event to be targeted
 *
 * @return {Component} this
 */
HTMLElement.prototype.eventListener = function eventListener (ev, methods, properties) {
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(ADD_EVENT_LISTENER);
    this._queue.push(ev);
    if (methods != null) this._queue.push(methods);
    this._queue.push(EVENT_PROPERTIES);
    if (properties != null) this._queue.push(properties);
    this._queue.push(EVENT_END);
    return this;
};

/**
 * isRenderable returns whether or not this HTMLElement currently has any Information to render
 *
 * @method isRenderable
 *
 * @return {Bool} whether or not this HTMLElement can be rendered
 */
HTMLElement.prototype.isRenderable = function isRenderable () {
    return !!this._queue.length;
};

/**
 * Clears the state on the HTMLElement so that it can be rendered next frame.
 *
 * @method clear
 * @chainable
 *
 * @return {HTMLElement} this
 */
HTMLElement.prototype.clear = function clear () {
    this._trueSized[0] = false;
    this._trueSized[1] = false;

    for (var i = 0, l = this._queue.length; i < l; i++)
        this._queue.pop();

    return this;
};

module.exports = HTMLElement;
<commit_msg>feat: Make HTMLElement stateful<commit_after>'use strict';

var CallbackStore = require('famous-utilities').CallbackStore;

var WITH = 'WITH';
var CHANGE_TRANSFORM = 'CHANGE_TRANSFORM';
var CHANGE_PROPERTY = 'CHANGE_PROPERTY';
var INIT_DOM = 'INIT_DOM';
var CHANGE_ATTRIBUTE = 'CHANGE_ATTRIBUTE';
var ADD_CLASS = 'ADD_CLASS';
var REMOVE_CLASS = 'REMOVE_CLASS';
var CHANGE_ATTRIBUTE = 'CHANGE_ATTRIBUTE';
var CHANGE_CONTENT = 'CHANGE_CONTENT';
var ADD_EVENT_LISTENER = 'ADD_EVENT_LISTENER';
var EVENT_PROPERTIES = 'EVENT_PROPERTIES';
var EVENT_END = 'EVENT_END';
var RECALL = 'RECALL';

/**
 * The Element class is responsible for providing the API for how
 *   a RenderNode will interact with the DOM API's.  The element is
 *   responsible for adding a set of commands to the renderer.
 *
 * @class HTMLElement
 * @constructor
 * @component
 * @param {RenderNode} RenderNode to which the instance of Element will be a component of
 */
function HTMLElement(dispatch, tagName) {
    this._dispatch = dispatch;
    this._id = dispatch.addRenderable(this);
    this._queue = [];

    this._tagName = tagName ? tagName : 'div';
    this._transform = new Float32Array(16);
    this._size = [0, 0, 0];
    this._trueSized = [false, false];
    this._opacity = 1;
    this._properties = {};
    this._classes = {};
    this._attributes = {};
    this._content = '';

    this._queue.push(INIT_DOM);
    this._queue.push(this._tagName);

    this._callbacks = new CallbackStore();
    this._dispatch.onTransformChange(this._receiveTransformChange.bind(this));
    this._dispatch.onSizeChange(this._receiveSizeChange.bind(this));
    this._dispatch.onOpacityChange(this._receiveOpacityChange.bind(this));
}

// Return the name of the Element Class: 'element'
HTMLElement.toString = function toString() {
    return 'element';
};

HTMLElement.prototype.getState = function getState() {
    return {
        renderable: this.constructor.toString(),
        tagName: this._tagName,
        transform: this._transform,
        size: this._size,
        trueSized: this._trueSized,
        opacity: this._opacity,
        properties: this._properties,
        classes: this._classes,
        attributes: this._attributes,
        content: this._content
    };
};

HTMLElement.prototype.clean = function clean() {
    var len = this._queue.length;
    if (len) {
    	var path = this._dispatch.getRenderPath();
    	this._dispatch
            .sendDrawCommand(WITH)
            .sendDrawCommand(path)
            .sendDrawCommand('DOM');

    	for (var i = 0 ; i < len ; i++)
    	    this._dispatch.sendDrawCommand(this._queue.shift());
    }
    return false;
};

HTMLElement.prototype._receiveTransformChange = function _receiveTransformChange(transform) {
    this._transform = transform._matrix;
    this._dispatch.dirtyRenderable(this._id);
    var queue = this._queue;
    queue.push(CHANGE_TRANSFORM);
    queue.push(transform._matrix[0]);
    queue.push(transform._matrix[1]);
    queue.push(transform._matrix[2]);
    queue.push(transform._matrix[3]);
    queue.push(transform._matrix[4]);
    queue.push(transform._matrix[5]);
    queue.push(transform._matrix[6]);
    queue.push(transform._matrix[7]);
    queue.push(transform._matrix[8]);
    queue.push(transform._matrix[9]);
    queue.push(transform._matrix[10]);
    queue.push(transform._matrix[11]);
    queue.push(transform._matrix[12]);
    queue.push(transform._matrix[13]);
    queue.push(transform._matrix[14]);
    queue.push(transform._matrix[15]);
};

HTMLElement.prototype._receiveSizeChange = function _receiveSizeChange(size) {
    this._size = size;
    this._dispatch.dirtyRenderable(this._id);
    var width = this._trueSized[0] ? this._trueSized[0] : size._size[0];
    var height = this._trueSized[1] ? this._trueSized[1] : size._size[1];
    this._queue.push('CHANGE_SIZE');
    this._queue.push(width);
    this._queue.push(height);
    this._size[0] = width;
    this._size[1] = height;
};

HTMLElement.prototype._receiveOpacityChange = function _receiveOpacityChange(opacity) {
    this._opacity = opacity;
    this.property('opacity', opacity.value);
};

HTMLElement.prototype.getSize = function getSize() {
    return this._size;
};

HTMLElement.prototype.on = function on (ev, methods, properties) {
    this.eventListener(ev, methods, properties);
    return this;
};

HTMLElement.prototype.kill = function kill () {
    this._dispatch.sendDrawCommand(WITH).sendDrawCommand(this._dispatch.getRenderPath()).sendDrawCommand(RECALL);
};

/**
 * Set the value of a CSS property
 *
 * @method property
 * @chainable
 *
 * @param {String} key name of the property to set
 * @param {Any} value associated value for this property
 * @return {HTMLElement} current HTMLElement
 */
HTMLElement.prototype.property = function property(key, value) {
    this._properties[key] = value;
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(CHANGE_PROPERTY);
    this._queue.push(key);
    this._queue.push(value);
    return this;
};

/**
 * The method by which a user tells the element to ignore the context
 *   size and get size from the content instead.
 *
 * @method trueSize
 * @chainable
 *
 * @param {Boolean} trueWidth
 * @param {Boolean} trueHeight
 * @return {HTMLElement} this
 */
HTMLElement.prototype.trueSize = function trueSize(trueWidth, trueHeight) {
    if (trueWidth === undefined) trueWidth = true;
    if (trueHeight === undefined) trueHeight = true;

    if (this._trueSized[0] !== trueWidth || this._trueSized[1] !== trueHeight) {
        this._dispatch.dirtyRenderable(this._id);
    }
    this._trueSized[0] = trueWidth;
    this._trueSized[1] = trueHeight;
    return this;
};

/**
 * Set an attribute on the DOM element
 *
 * @method attribute
 * @chainable
 *
 * @param {String} key name of the attribute to set
 * @param {Any} value associated value for this attribute
 * @return {HTMLElement} current HTMLElement
 */
HTMLElement.prototype.attribute = function attribute(key, value) {
    this._attributes[key] = value;
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(CHANGE_ATTRIBUTE);
    this._queue.push(key);
    this._queue.push(value);
    return this;
};

/**
 * Define a CSS class to be added to the DOM element
 *
 * @method addClass
 * @chainable
 *
 * @param {String} value name of the class
 * @return {HTMLElement} current HTMLElement
 */
HTMLElement.prototype.addClass = function addClass(value) {
    this._classes[value] = true;
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(ADD_CLASS);
    this._queue.push(value);
    return this;
};
/**
 * Define a CSS class to be removed from the DOM element
 *
 * @method removeClass
 * @chainable
 *
 * @param {String} value name of the class
 * @return {HTMLElement} current HTMLElement
 */
HTMLElement.prototype.removeClass = function removeClass(value) {
    delete this._classes[value];
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(REMOVE_CLASS);
    this._queue.push(value);
    return this;
};

/**
 * Define the id of the DOM element
 *
 * @method id
 * @chainable
 *
 * @param {String} value id
 * @return {HTMLElement} current HTMLElement
 */
HTMLElement.prototype.id = function id(value) {
    this._attributes.id = value;
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(CHANGE_ATTRIBUTE);
    this._queue.push('id');
    this._queue.push(value);
    return this;
};

/**
 * Define the content of the DOM element
 *
 * @method content
 * @chainable
 *
 * @param {String|DOMElement|DocumentFragment} value content to be inserted into the DOM element
 * @return {HTMLElement} current HTMLElement
 */
HTMLElement.prototype.content = function content(value) {
    this._content = value;
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(CHANGE_CONTENT);
    this._queue.push(value);
    return this;
};

/**
 * Get a component of the RenderNode that the HTMLElement component is
 *   attached to by name.
 *
 * @method get
 *
 * @param {String} key name of the component to grab from the RenderNode
 * @return {Component} a component that exists on the RenderNode
 */
HTMLElement.prototype.get = function get (key) {
    return this.owner.get(key);
};

/**
 * Adds a command to add an eventListener to the current DOM HTMLElement.
 *   primarily used internally.
 *
 * @method eventListener
 * @chainable
 *
 * @param {String} ev the name of the DOM event to be targeted
 *
 * @return {Component} this
 */
HTMLElement.prototype.eventListener = function eventListener (ev, methods, properties) {
    this._dispatch.dirtyRenderable(this._id);
    this._queue.push(ADD_EVENT_LISTENER);
    this._queue.push(ev);
    if (methods != null) this._queue.push(methods);
    this._queue.push(EVENT_PROPERTIES);
    if (properties != null) this._queue.push(properties);
    this._queue.push(EVENT_END);
    return this;
};

/**
 * isRenderable returns whether or not this HTMLElement currently has any Information to render
 *
 * @method isRenderable
 *
 * @return {Bool} whether or not this HTMLElement can be rendered
 */
HTMLElement.prototype.isRenderable = function isRenderable () {
    return !!this._queue.length;
};

/**
 * Clears the state on the HTMLElement so that it can be rendered next frame.
 *
 * @method clear
 * @chainable
 *
 * @return {HTMLElement} this
 */
HTMLElement.prototype.clear = function clear () {
    this._trueSized[0] = false;
    this._trueSized[1] = false;

    for (var i = 0, l = this._queue.length; i < l; i++)
        this._queue.pop();

    return this;
};

module.exports = HTMLElement;
<|endoftext|>


<commit_before>'use strict';

var util = require('util'),
    EventEmitter = require('events').EventEmitter,
    Docker = require('dockerode'),
    Promise = require('bluebird'),
    uuid = require('uuid'),
    debug = require('debug')('docker-runner');

var DockerExec = function (options) {

    // linux
    options = options ||  {
        socketPath: '/var/run/docker.sock'
    };

    this.docker = new Docker(options);

    this.container = null;
};

util.inherits(DockerExec, EventEmitter);

/**
 * Creates a new docker container
 */
DockerExec.prototype.createContainer = function (options) {
    debug('create a new container');

    var docker = this.docker;
    return new Promise(function (resolve, reject) {
        docker.createContainer(options, function handler(err, container) {
            if (err) {
                reject(err);
            } else {
                resolve(container);
            }
        });
    });
};

/**
 * attaches a stream to a docker container
 */
DockerExec.prototype.attachContainer = function (container) {
    debug('attach to a container');

    var self = this;

    var attach_opts = {
        stream: true,
        stdin: true,
        stdout: true,
        stderr: true
    };

    return new Promise(function (resolve, reject) {
        container.attach(attach_opts, function handler(err, stream) {
            if (err) {
                reject(err);
            } else {

                // attach stream to stdout
                // stream.pipe(process.stdout);

                /*stream.on('data', function(data){
                    debug(data.toString('utf8'));
                });*/

                self.emit('container:attach');
                resolve(stream);
            }
        });
    });
};

/**
 * starts a docker container
 */
DockerExec.prototype.startContainer = function (container) {
    debug('start a new container');

    var self = this;

    return new Promise(function (resolve, reject) {
        container.start({}, function handler(err, data) {
            if (err) {
                reject(err);
            } else {
                self.emit('container:start');
                resolve(data);
            }
        });
    });
};

/**
 * register for container end
 */
DockerExec.prototype.waitContainer = function (container) {
    debug('register wait event for a new container');

    var self = this;
    container.wait(function handler(err) {
        if (err) {
            self.emit('error', err);
            self.emit('container:stop');
            debug(err);
        } else {
            // emit stream end event;
            self.emit('container:stop');
        }
    });

};

DockerExec.prototype.pullContainer = function () {
    debug('download operating system');

    var docker = this.docker;

    return new Promise(function (resolve, reject) {
        docker.pull('ubuntu:14.04', function (err, stream) {
            if(err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
};

DockerExec.prototype.streamEnd = function () {
    if (this.stream) {
        this.stream.end();
    }
};

/*
 * Starts a new docker container
 */
DockerExec.prototype.start = function () {
    debug('start a new container');

    var optsc = {
        'Hostname': '',
        'User': '',
        'AttachStdin': true,
        'AttachStdout': true,
        'AttachStderr': true,
        'Tty': true,
        'OpenStdin': true,
        'StdinOnce': false,
        'Env': null,
        'Cmd': ['bash'], // run a bash shell
        'Dns': ['8.8.8.8', '8.8.4.4'], // use google dns
        'Image': 'ubuntu:14.04', // TODO make os configurable
        'Volumes': {},
        'VolumesFrom': ''
    };

    var self = this;

    return new Promise(function (resolve, reject) {
        console.log('prepare new container');

        // attach a volume to docker to copy the files afterwards
        self.pullContainer().then(function() {
            return self.createContainer(optsc);
        }).then(function (container) {
            self.container = container;
            return self.attachContainer(self.container);
        }).then(function (stream) {
            self.stream = stream;
            return self.startContainer(self.container);
        }).then(function () {
            self.waitContainer(self.container);
        }).then(function () {
            console.log('container ready');
            resolve(self.stream);
        }).
        catch(function (err) {
            console.log(err);
            reject(err);
        });
    });
};

/**
 * execute one command
 * @param command a cli command like "apt-get update\n"
 */
DockerExec.prototype.run = function (command) {
    debug('run + ' + command);

    // generate process id
    var id = uuid.v4();

    // add exit code to command
    var cmd2 = command + ';echo exit task ' + id + ' $?\n';

    var self = this;
    return new Promise(function (resolve) {
        self.stream.write(cmd2);

        // wait for exit from process
        self.stream.on('data', function (data) {
            // wait for exit event
            // make abort condition with var exit = 'task ' + id;
            var match = data.toString('utf8').match(/((;echo\s)?exit)/i);
            if (match && match.length > 0 && match[0] === 'exit') {
                // TODO use buffer of 10 events
                // TODO make more robust
                // console.log('detected exit' + JSON.stringify(match));
                resolve();
            }
        });
    });
};

DockerExec.prototype.removeContainer = function (container) {
    return new Promise(function (resolve, reject) {
        container.remove(function handler(err) {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
};

DockerExec.prototype.killContainer = function (container) {
    return new Promise(function (resolve, reject) {
        container.kill(function handler(err) {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
};

/**
 * stops the docker container
 */
DockerExec.prototype.stop = function () {
    debug('stops the container');
    var self = this;
    var container = this.container;

    return new Promise(function (resolve, reject) {
        self.killContainer(container).then(function () {
            return self.removeContainer(container);
        }).then(function () {
            resolve();
        }).
        catch(function (err) {
            reject(err);
        });
    });
};

module.exports = DockerExec;<commit_msg>pull a specific image<commit_after>'use strict';

var util = require('util'),
    EventEmitter = require('events').EventEmitter,
    Docker = require('dockerode'),
    Promise = require('bluebird'),
    uuid = require('uuid'),
    debug = require('debug')('docker-runner');

var DockerExec = function (options) {

    // linux
    options = options ||  {
        socketPath: '/var/run/docker.sock'
    };

    this.docker = new Docker(options);

    this.container = null;
};

util.inherits(DockerExec, EventEmitter);

/**
 * Creates a new docker container
 */
DockerExec.prototype.createContainer = function (options) {
    debug('create a new container');

    var docker = this.docker;
    return new Promise(function (resolve, reject) {
        docker.createContainer(options, function handler(err, container) {
            if (err) {
                reject(err);
            } else {
                resolve(container);
            }
        });
    });
};

/**
 * attaches a stream to a docker container
 */
DockerExec.prototype.attachContainer = function (container) {
    debug('attach to a container');

    var self = this;

    var attach_opts = {
        stream: true,
        stdin: true,
        stdout: true,
        stderr: true
    };

    return new Promise(function (resolve, reject) {
        container.attach(attach_opts, function handler(err, stream) {
            if (err) {
                reject(err);
            } else {

                // attach stream to stdout
                // stream.pipe(process.stdout);

                /*stream.on('data', function(data){
                    debug(data.toString('utf8'));
                });*/

                self.emit('container:attach');
                resolve(stream);
            }
        });
    });
};

/**
 * starts a docker container
 */
DockerExec.prototype.startContainer = function (container) {
    debug('start a new container');

    var self = this;

    return new Promise(function (resolve, reject) {
        container.start({}, function handler(err, data) {
            if (err) {
                reject(err);
            } else {
                self.emit('container:start');
                resolve(data);
            }
        });
    });
};

/**
 * register for container end
 */
DockerExec.prototype.waitContainer = function (container) {
    debug('register wait event for a new container');

    var self = this;
    container.wait(function handler(err) {
        if (err) {
            self.emit('error', err);
            self.emit('container:stop');
            debug(err);
        } else {
            // emit stream end event;
            self.emit('container:stop');
        }
    });

};

DockerExec.prototype.pullContainer = function (image) {
    debug('download operating system');

    var docker = this.docker;

    return new Promise(function (resolve, reject) {
        docker.pull(image, function (err, stream) {
            if(err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
};

DockerExec.prototype.streamEnd = function () {
    if (this.stream) {
        this.stream.end();
    }
};

/*
 * Starts a new docker container
 */
DockerExec.prototype.start = function () {
    debug('start a new container');

    var optsc = {
        'Hostname': '',
        'User': '',
        'AttachStdin': true,
        'AttachStdout': true,
        'AttachStderr': true,
        'Tty': true,
        'OpenStdin': true,
        'StdinOnce': false,
        'Env': null,
        'Cmd': ['bash'], // run a bash shell
        'Dns': ['8.8.8.8', '8.8.4.4'], // use google dns
        'Image': 'ubuntu:14.04', // TODO make os configurable
        'Volumes': {},
        'VolumesFrom': ''
    };

    var self = this;

    return new Promise(function (resolve, reject) {
        console.log('prepare new container');

        // attach a volume to docker to copy the files afterwards
        self.pullContainer(optsc.Image).then(function() {
            return self.createContainer(optsc);
        }).then(function (container) {
            self.container = container;
            return self.attachContainer(self.container);
        }).then(function (stream) {
            self.stream = stream;
            return self.startContainer(self.container);
        }).then(function () {
            self.waitContainer(self.container);
        }).then(function () {
            console.log('container ready');
            resolve(self.stream);
        }).
        catch(function (err) {
            console.log(err);
            reject(err);
        });
    });
};

/**
 * execute one command
 * @param command a cli command like "apt-get update\n"
 */
DockerExec.prototype.run = function (command) {
    debug('run + ' + command);

    // generate process id
    var id = uuid.v4();

    // add exit code to command
    var cmd2 = command + ';echo exit task ' + id + ' $?\n';

    var self = this;
    return new Promise(function (resolve) {
        self.stream.write(cmd2);

        // wait for exit from process
        self.stream.on('data', function (data) {
            // wait for exit event
            // make abort condition with var exit = 'task ' + id;
            var match = data.toString('utf8').match(/((;echo\s)?exit)/i);
            if (match && match.length > 0 && match[0] === 'exit') {
                // TODO use buffer of 10 events
                // TODO make more robust
                // console.log('detected exit' + JSON.stringify(match));
                resolve();
            }
        });
    });
};

DockerExec.prototype.removeContainer = function (container) {
    return new Promise(function (resolve, reject) {
        container.remove(function handler(err) {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
};

DockerExec.prototype.killContainer = function (container) {
    return new Promise(function (resolve, reject) {
        container.kill(function handler(err) {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
};

/**
 * stops the docker container
 */
DockerExec.prototype.stop = function () {
    debug('stops the container');
    var self = this;
    var container = this.container;

    return new Promise(function (resolve, reject) {
        self.killContainer(container).then(function () {
            return self.removeContainer(container);
        }).then(function () {
            resolve();
        }).
        catch(function (err) {
            reject(err);
        });
    });
};

module.exports = DockerExec;<|endoftext|>


<commit_before>/**
 * index.js
 *
 * Async stuff for html-snapshots
 *   Notifier class
 *
 * Copyright (c) 2013 - 2017, Alex Grant, LocalNerve, contributors
 * Licensed under the MIT license.
 */
/* global Promise */
"use strict";

var fs = require("fs");
var crypto = require("crypto");
var AsyncLock = require("async-lock");
var combineErrors = require("combine-errors");
var nodeCall = require("../common/node");

// @@@
var countSpawnedProcesses = require("../../test/mocha/html-snapshots/utils").countSpawnedProcesses;
// @@@

var F_OK = (fs.constants && fs.constants.F_OK) || fs.F_OK;
var L_WAIT = 10000;
var TIMEOUT_PAD_FLOOR = 800;
function NOOP () {}

/**
 * Create the lock factory.
 */
function createLockFactory () {
  // Create the per instance async lock.
  var lock = new AsyncLock();

  // Make a random id
  var rid = crypto.randomBytes(16).toString("hex");

  /**
   * Force a serial execution context.
   *
   * @param {Function} fn - The function to guard.
   * @param {Number} timeout - The max time to wait for the lock.
   */
  return function lockFactory (fn, timeout) {
    return function protectedContext () {
      lock.acquire("cs-guard-" + rid, function (done) {
        fn(function () {
          done(null, 0);
        });
      }, NOOP, {
        timeout: timeout
      });
    };
  };
}

/**
 * Notifier Constructor
 * Polls the outputDir and when all the files exist,
 * calls the listener indicating the snapshots are done.
 */
function Notifier () {

  // Create the serial execution context mechanism.
  this.csFactory = createLockFactory();

  // The private files collection
  // Contains a file and timer: "filename": {timer: timerid}
  // Used for tracking work left to do. When empty, work is done.
  this.files = {};

  // Contains files successfully processed
  this.filesDone = [];

  // Contains files unsuccessfully processed
  this.filesNotDone = [];

  // true if a timeout occurred, or set by abort
  this.errors = [];

  // the holder of the current failure timeout padding
  this.padTimeout = TIMEOUT_PAD_FLOOR;

  // our reference to the listener
  this.callback = null;

  // our reference to the watcher (an interval id)
  // initial value undefined is important
  // this.watcher;

  // the working pollInterval for this run
  // this.interval

  // flag set by qEmpty callback
  // when the last item from the queue is given to a worker
  this.qempty = false;
}

Notifier.prototype = {
  /**
   * Set the qempty flag to true.
   */
  qEmpty: function qEmpty () {
    this.qempty = true;
  },

  /**
   * Start a watch interval, when a file exists, remove it from our files array.
   * If the files array is empty, call the listener and stop the watch interval.
   *
   * @param {Number} pollInterval - The millisecond poll interval to check for
   * file completion.
   * @param {Function} listener - The callback to notify when complete.
   * @param {Object} input - The inputGenerator.
   * @param {Function} input.EOI - The inputGenerator function that signals end
   * of input.
   * @returns {Boolean} true if started successfully, false otherwise.
   */
  start: function start (pollInterval, listener, input) {
    var result = (
      pollInterval > 0 &&
      typeof listener === "function" &&
      (!!input)
      );

    if (result) {
      if (this.isStarted()) {
        throw new Error("Notifier already started");
      }

      this.callback = listener;
      this.interval = parseInt(pollInterval, 10);

      // @@@
      var tmpFiles;
      // @@@

      // Poll the filesystem for the files to exist
      // Checks the child process expected output to determine success or failure
      // if the file exists, then it succeeded.
      this.watcher = setInterval(this.csFactory(function (done) {
        var self = this;
        var eoi = typeof input.EOI === "function" && input.EOI();

        if (eoi) {
          Promise.all(Object.keys(self.files).map(function (file) {
            return nodeCall(fs.access, file, F_OK)
              .then(function () {
                return file;
              })
              .catch(function () {
                return null;
              });
          }))
          // @@@
            .then(function (files) {
              tmpFiles = files;
              return new Promise(function (resolve) {
                countSpawnedProcesses(function (count) {
                  console.log('@@@ phantomjs count: '+count);
                  resolve(files);
                });
              });
            })
            .catch(function () {
              return tmpFiles;
            })
          // @@@
            .then(function (files) {
              var callback = self.callback;

              try {
                files.forEach(function (file) {
                  file && self._remove(file, true);
                });

                if (self._isDone()) {
                  self._closeWatcher();
                  if (self.callback) {
                    self.callback = null;
                    setImmediate(function () {
                      callback(self.getError(), self.filesDone);
                    });
                  }
                }
              } catch (e) {
                console.error(e);
              }
              done();
            });
        } else {
          done();
        }
      }.bind(this), L_WAIT), this.interval);

    } else {
      console.error("Bad poll interval, async listener, or input generator supplied");
    }

    return result;
  },

  /**
   * Indicates if start has been called.
   *
   * @returns {Boolean} true if start has been called, false otherwise.
   */
  isStarted: function isStarted () {
    return (typeof this.watcher !== "undefined");
  },

  /**
   * Add a file to the files array if it's not there.
   *
   * @param {String} outputFile - The full path to the output file to add.
   * @param {Number} timeout - timeout milliseconds to wait (max).
   */
  add: function add (outputFile, timeout) {
    var failTimeout = timeout;
    var timer;

    if (!this.isStarted()) {
      throw new Error("MUST call `start` before `add`");
    }

    if (!this._exists(outputFile)) {
      // make sure we evaluate after the child process
      failTimeout = parseInt(timeout, 10) + parseInt(this.padTimeout, 10);
      // Stagger and grow the failure timeout padding, add 1s every 10 processes
      this.padTimeout += 100;

      // setup a timeout handler to detect failure
      timer = setTimeout(this.csFactory(function (done) {
        var self = this;

        // if the output file has not already been removed
        if (self._exists(outputFile)) {
          nodeCall(fs.access, outputFile, F_OK)
            .then(function () { return true; })
            .catch(function () { return false; })
            .then(function (fsExists) {
              var callback = self.callback;

              try {
                if (!fsExists) {
                  self._setError(new Error(
                    "'" + outputFile + "' did not get a snapshot before timeout"
                  ));
                }
                self._remove(outputFile, fsExists);

                if (self._isDone()) {
                  self._closeWatcher();
                  if (self.callback) {
                    self.callback = null;
                    setImmediate(function () {
                      callback(self.getError(), self.filesDone);
                    });
                  }
                }
              } catch (e) {
                console.error(e);
              }
              done();
            });
        } else {
          done();
        }
      }.bind(this), L_WAIT), parseInt(failTimeout, 10));

      // add the file tracking object
      this.files[outputFile] = {
        timer: timer
      };
    }
  },

  /**
   * Check if a file exists in the internal files array.
   *
   * @param {String} outputFile - The outputFile to check.
   * @returns {Boolean} true if the file exists, false otherwise.
   */
  _exists: function _exists (outputFile) {
    return (!!this.files[outputFile]);
  },

  /**
   * Check if a file is being processed or has already been processed.
   *
   * @param {String} outputFile - The outputFile to check.
   * @returns {Boolean} true if the fi
   */
  known: function known (outputFile) {
    var result = false;
    this.csFactory(function (done) {
      result =
        this._exists(outputFile) || this.filesDone.indexOf(outputFile) > -1;
      done();
    }.bind(this), L_WAIT)();
    return result;
  },

  /**
   * Remove a file from the files array if it's there.
   * Unprotected version.
   *
   * @param {String} outputFile - The outputFile to remove.
   * @param {Boolean} done - If true, add to filesDone collection.
   */
  _remove: function _remove (outputFile, done) {
    if (this._exists(outputFile)) {
      if (done) {
        this.filesDone.push(outputFile);
      } else {
        this.filesNotDone.push(outputFile);
      }
      clearTimeout(this.files[outputFile].timer);
      delete this.files[outputFile];
    }
  },

  /**
   * Remove a file from the files array if it's there.
   * Protected version.
   *
   * @param {String} outputFile - The outputFile to remove.
   * @param {Boolean} done - If true, add to filesDone collection.
   */
  remove: function remove (outputFile, done) {
    this.csFactory(function (_done) {
      this._remove(outputFile, done);
      _done();
    }.bind(this), L_WAIT)();
  },

  /**
   * Audit the file count.
   *
   * @returns {Number} the number of files being watched at the moment.
   */
  _fileCount: function _fileCount () {
    return Object.keys(this.files).length;
  },

  /**
   * Check if there is anything left to watch.
   *
   * @returns {Boolean} true if processing is done, false otherwise.
   */
  _isDone: function _isDone () {
    return this._fileCount() === 0 && this.qempty;
  },

  /**
   * Set the error, saves to a collection of errors.
   * Unprotected version.
   *
   * @param {Object} value - The error to set.
   */
  _setError: function _setError (value) {
    this.errors.push(value);
  },

  /**
   * Set the error property, if not already set.
   * Protected version.
   *
   * @param {Object} value - The error to set.
   */
  setError: function setError (value) {
    this.csFactory(function (done) {
      this._setError(value);
      done();
    }.bind(this), L_WAIT)();
  },

  /**
   * Get the error property.
   *
   * @returns {Object} The error property.
   */
  getError: function getError () {
    return this.errors.length > 0 ? combineErrors(this.errors) : undefined;
  },

  /**
   * End file watching.
   */
  _closeWatcher: function _closeWatcher () {
    if (this.watcher) {
      clearInterval(this.watcher);
      this.watcher = null;
    }
  },

  /**
   * Provides a way to abort what this is doing.
   * Causes conditions so that, if isStarted, the poll interval will exit,
   *   cleanup, and call the listener back.
   * If not started, the listener does not get called. This relationship
   *   is set in html-snapshots.js: listener == notifier.start
   *
   * @param {Object} q - The worker queue, expected created by async.queue.
   * @param {Function} q.length - Gets the worker queue length.
   * @param {Object} [err] - The error object to set.
   */
  abort: function abort (q, err) {
    this.csFactory(function (done) {
      try {
        // for each file, clearTimeout and delete the object
        Object.keys(this.files).forEach(function(file) {
          clearTimeout(this.files[file].timer);
          delete this.files[file];
        }, this);

        // if nothing is waiting, make sure empty is set
        this.qempty = !q.length();

        // set the error
        this._setError(err);
      } catch (e) {
        console.error(e);
      }
      done();
    }.bind(this), L_WAIT)();
  }
};

module.exports = {
  Notifier: Notifier,
  TIMEOUT_PAD_FLOOR: TIMEOUT_PAD_FLOOR
};
<commit_msg>revert debug statement<commit_after>/**
 * index.js
 *
 * Async stuff for html-snapshots
 *   Notifier class
 *
 * Copyright (c) 2013 - 2017, Alex Grant, LocalNerve, contributors
 * Licensed under the MIT license.
 */
/* global Promise */
"use strict";

var fs = require("fs");
var crypto = require("crypto");
var AsyncLock = require("async-lock");
var combineErrors = require("combine-errors");
var nodeCall = require("../common/node");

var F_OK = (fs.constants && fs.constants.F_OK) || fs.F_OK;
var L_WAIT = 10000;
var TIMEOUT_PAD_FLOOR = 800;
function NOOP () {}

/**
 * Create the lock factory.
 */
function createLockFactory () {
  // Create the per instance async lock.
  var lock = new AsyncLock();

  // Make a random id
  var rid = crypto.randomBytes(16).toString("hex");

  /**
   * Force a serial execution context.
   *
   * @param {Function} fn - The function to guard.
   * @param {Number} timeout - The max time to wait for the lock.
   */
  return function lockFactory (fn, timeout) {
    return function protectedContext () {
      lock.acquire("cs-guard-" + rid, function (done) {
        fn(function () {
          done(null, 0);
        });
      }, NOOP, {
        timeout: timeout
      });
    };
  };
}

/**
 * Notifier Constructor
 * Polls the outputDir and when all the files exist,
 * calls the listener indicating the snapshots are done.
 */
function Notifier () {

  // Create the serial execution context mechanism.
  this.csFactory = createLockFactory();

  // The private files collection
  // Contains a file and timer: "filename": {timer: timerid}
  // Used for tracking work left to do. When empty, work is done.
  this.files = {};

  // Contains files successfully processed
  this.filesDone = [];

  // Contains files unsuccessfully processed
  this.filesNotDone = [];

  // true if a timeout occurred, or set by abort
  this.errors = [];

  // the holder of the current failure timeout padding
  this.padTimeout = TIMEOUT_PAD_FLOOR;

  // our reference to the listener
  this.callback = null;

  // our reference to the watcher (an interval id)
  // initial value undefined is important
  // this.watcher;

  // the working pollInterval for this run
  // this.interval

  // flag set by qEmpty callback
  // when the last item from the queue is given to a worker
  this.qempty = false;
}

Notifier.prototype = {
  /**
   * Set the qempty flag to true.
   */
  qEmpty: function qEmpty () {
    this.qempty = true;
  },

  /**
   * Start a watch interval, when a file exists, remove it from our files array.
   * If the files array is empty, call the listener and stop the watch interval.
   *
   * @param {Number} pollInterval - The millisecond poll interval to check for
   * file completion.
   * @param {Function} listener - The callback to notify when complete.
   * @param {Object} input - The inputGenerator.
   * @param {Function} input.EOI - The inputGenerator function that signals end
   * of input.
   * @returns {Boolean} true if started successfully, false otherwise.
   */
  start: function start (pollInterval, listener, input) {
    var result = (
      pollInterval > 0 &&
      typeof listener === "function" &&
      (!!input)
      );

    if (result) {
      if (this.isStarted()) {
        throw new Error("Notifier already started");
      }

      this.callback = listener;
      this.interval = parseInt(pollInterval, 10);

      // Poll the filesystem for the files to exist
      // Checks the child process expected output to determine success or failure
      // if the file exists, then it succeeded.
      this.watcher = setInterval(this.csFactory(function (done) {
        var self = this;
        var eoi = typeof input.EOI === "function" && input.EOI();

        if (eoi) {
          Promise.all(Object.keys(self.files).map(function (file) {
            return nodeCall(fs.access, file, F_OK)
              .then(function () {
                return file;
              })
              .catch(function () {
                return null;
              });
          }))
            .then(function (files) {
              var callback = self.callback;

              try {
                files.forEach(function (file) {
                  file && self._remove(file, true);
                });

                if (self._isDone()) {
                  self._closeWatcher();
                  if (self.callback) {
                    self.callback = null;
                    setImmediate(function () {
                      callback(self.getError(), self.filesDone);
                    });
                  }
                }
              } catch (e) {
                console.error(e);
              }
              done();
            });
        } else {
          done();
        }
      }.bind(this), L_WAIT), this.interval);

    } else {
      console.error("Bad poll interval, async listener, or input generator supplied");
    }

    return result;
  },

  /**
   * Indicates if start has been called.
   *
   * @returns {Boolean} true if start has been called, false otherwise.
   */
  isStarted: function isStarted () {
    return (typeof this.watcher !== "undefined");
  },

  /**
   * Add a file to the files array if it's not there.
   *
   * @param {String} outputFile - The full path to the output file to add.
   * @param {Number} timeout - timeout milliseconds to wait (max).
   */
  add: function add (outputFile, timeout) {
    var failTimeout = timeout;
    var timer;

    if (!this.isStarted()) {
      throw new Error("MUST call `start` before `add`");
    }

    if (!this._exists(outputFile)) {
      // make sure we evaluate after the child process
      failTimeout = parseInt(timeout, 10) + parseInt(this.padTimeout, 10);
      // Stagger and grow the failure timeout padding, add 1s every 10 processes
      this.padTimeout += 100;

      // setup a timeout handler to detect failure
      timer = setTimeout(this.csFactory(function (done) {
        var self = this;

        // if the output file has not already been removed
        if (self._exists(outputFile)) {
          nodeCall(fs.access, outputFile, F_OK)
            .then(function () { return true; })
            .catch(function () { return false; })
            .then(function (fsExists) {
              var callback = self.callback;

              try {
                if (!fsExists) {
                  self._setError(new Error(
                    "'" + outputFile + "' did not get a snapshot before timeout"
                  ));
                }
                self._remove(outputFile, fsExists);

                if (self._isDone()) {
                  self._closeWatcher();
                  if (self.callback) {
                    self.callback = null;
                    setImmediate(function () {
                      callback(self.getError(), self.filesDone);
                    });
                  }
                }
              } catch (e) {
                console.error(e);
              }
              done();
            });
        } else {
          done();
        }
      }.bind(this), L_WAIT), parseInt(failTimeout, 10));

      // add the file tracking object
      this.files[outputFile] = {
        timer: timer
      };
    }
  },

  /**
   * Check if a file exists in the internal files array.
   *
   * @param {String} outputFile - The outputFile to check.
   * @returns {Boolean} true if the file exists, false otherwise.
   */
  _exists: function _exists (outputFile) {
    return (!!this.files[outputFile]);
  },

  /**
   * Check if a file is being processed or has already been processed.
   *
   * @param {String} outputFile - The outputFile to check.
   * @returns {Boolean} true if the fi
   */
  known: function known (outputFile) {
    var result = false;
    this.csFactory(function (done) {
      result =
        this._exists(outputFile) || this.filesDone.indexOf(outputFile) > -1;
      done();
    }.bind(this), L_WAIT)();
    return result;
  },

  /**
   * Remove a file from the files array if it's there.
   * Unprotected version.
   *
   * @param {String} outputFile - The outputFile to remove.
   * @param {Boolean} done - If true, add to filesDone collection.
   */
  _remove: function _remove (outputFile, done) {
    if (this._exists(outputFile)) {
      if (done) {
        this.filesDone.push(outputFile);
      } else {
        this.filesNotDone.push(outputFile);
      }
      clearTimeout(this.files[outputFile].timer);
      delete this.files[outputFile];
    }
  },

  /**
   * Remove a file from the files array if it's there.
   * Protected version.
   *
   * @param {String} outputFile - The outputFile to remove.
   * @param {Boolean} done - If true, add to filesDone collection.
   */
  remove: function remove (outputFile, done) {
    this.csFactory(function (_done) {
      this._remove(outputFile, done);
      _done();
    }.bind(this), L_WAIT)();
  },

  /**
   * Audit the file count.
   *
   * @returns {Number} the number of files being watched at the moment.
   */
  _fileCount: function _fileCount () {
    return Object.keys(this.files).length;
  },

  /**
   * Check if there is anything left to watch.
   *
   * @returns {Boolean} true if processing is done, false otherwise.
   */
  _isDone: function _isDone () {
    return this._fileCount() === 0 && this.qempty;
  },

  /**
   * Set the error, saves to a collection of errors.
   * Unprotected version.
   *
   * @param {Object} value - The error to set.
   */
  _setError: function _setError (value) {
    this.errors.push(value);
  },

  /**
   * Set the error property, if not already set.
   * Protected version.
   *
   * @param {Object} value - The error to set.
   */
  setError: function setError (value) {
    this.csFactory(function (done) {
      this._setError(value);
      done();
    }.bind(this), L_WAIT)();
  },

  /**
   * Get the error property.
   *
   * @returns {Object} The error property.
   */
  getError: function getError () {
    return this.errors.length > 0 ? combineErrors(this.errors) : undefined;
  },

  /**
   * End file watching.
   */
  _closeWatcher: function _closeWatcher () {
    if (this.watcher) {
      clearInterval(this.watcher);
      this.watcher = null;
    }
  },

  /**
   * Provides a way to abort what this is doing.
   * Causes conditions so that, if isStarted, the poll interval will exit,
   *   cleanup, and call the listener back.
   * If not started, the listener does not get called. This relationship
   *   is set in html-snapshots.js: listener == notifier.start
   *
   * @param {Object} q - The worker queue, expected created by async.queue.
   * @param {Function} q.length - Gets the worker queue length.
   * @param {Object} [err] - The error object to set.
   */
  abort: function abort (q, err) {
    this.csFactory(function (done) {
      try {
        // for each file, clearTimeout and delete the object
        Object.keys(this.files).forEach(function(file) {
          clearTimeout(this.files[file].timer);
          delete this.files[file];
        }, this);

        // if nothing is waiting, make sure empty is set
        this.qempty = !q.length();

        // set the error
        this._setError(err);
      } catch (e) {
        console.error(e);
      }
      done();
    }.bind(this), L_WAIT)();
  }
};

module.exports = {
  Notifier: Notifier,
  TIMEOUT_PAD_FLOOR: TIMEOUT_PAD_FLOOR
};
<|endoftext|>


<commit_before>var chokidar = require('chokidar');
var http = require('http');
var fs = require('graceful-fs-extra')
var winston = require('winston');
var path = require('path');

var utils = require('./utils');
var winston = require('./utils').winston;

module.exports = {
  start: function(serverAddress, serverPort, sharedDir, maxRetries) {

    maxRetries = maxRetries || 1;

    // We don't really need filesystem events.
    // We just need to state be able to produce the same state as on monitored direcotry.
    // Unlink doesn't really care if it is directory or file.
    // Changed doesnt really care if the file is changed or just craeted.
    var generateChange = function(file, retry) {
      var localFile = path.join(sharedDir, file);
      var sharedFile = file;
      fs.stat(localFile, function(err, stats) {
        // Just send event directly to the remote machine for missing files
        if (err && err.code === 'ENOENT') {
          sendChange('unlink', sharedFile, retry, null);
          return;
        }
        if (err) {
          winston.error('unexpected error occured ' + JSON.stringify(err));
          return;
        }

        winston.debug('stats for ' + file + ' are ' + JSON.stringify(stats));
        if(stats.isFile()) {
          utils.calcHash(localFile, function(error, sha1) {
            sendChange('copy', sharedFile, retry, sha1);
          });
        } else {
          sendChange('addDir', sharedFile, retry, null);
        }
      })
    }

    var sendChange = function(event, file, retry, checkSum) {
      winston.debug('sending ' + event + ' for ' + file + ' retried ' + retry);
      if (retry > maxRetries) {
        winston.error('unabled to syncronize ' + file + ' retried ' + retry + ' times.');
        return;
      }
      var options = {
        host: serverAddress,
        port: serverPort,
        path: '/',
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        }
      };

      var req = http.request(options, function(res) {
        var data="";
        res.on('data', function (chunk) {
          data+=chunk;
        });

        res.on('end', function () {
          if (JSON.parse(data)["code"] === utils.ERROR_CODE) {
            winston.warn('error occured while syncing ' + file + ' ' + data);
            generateChange(file, retry + 1);
          } else {
            winston.debug('successful response received ' + data);
          }
        });
      });

      postData = JSON.stringify({
        event: event,
        file: file,
        checkSum: checkSum,
      });

      req.write(postData);

      req.end();
    };

    chokidar.watch(
      path.normalize(sharedDir),
      { ignored: /[\/\\]\./, ignoreInitial: true}
    ).on('all', function(event, file) {
      var sharedFile=file.replace(sharedDir, '');
      // replace all path.sep with / becuase in *nix file system \
      // and cause errors we have cross platform syncronization from windows to *nix
      // all commands thats should normalize the path before using it
      sharedFile = sharedFile.replace(path.sep, '/');
      generateChange(sharedFile, 0);
    }).on('error', function(err) {
      winston.error('unexpected error occured ' + JSON.stringify(err));
    });
  }
}
<commit_msg>added info massage for watcher start<commit_after>var chokidar = require('chokidar');
var http = require('http');
var fs = require('graceful-fs-extra')
var winston = require('winston');
var path = require('path');

var utils = require('./utils');
var winston = require('./utils').winston;

module.exports = {
  start: function(serverAddress, serverPort, sharedDir, maxRetries) {

    maxRetries = maxRetries || 1;

    // We don't really need filesystem events.
    // We just need to state be able to produce the same state as on monitored direcotry.
    // Unlink doesn't really care if it is directory or file.
    // Changed doesnt really care if the file is changed or just craeted.
    var generateChange = function(file, retry) {
      var localFile = path.join(sharedDir, file);
      var sharedFile = file;
      fs.stat(localFile, function(err, stats) {
        // Just send event directly to the remote machine for missing files
        if (err && err.code === 'ENOENT') {
          sendChange('unlink', sharedFile, retry, null);
          return;
        }
        if (err) {
          winston.error('unexpected error occured ' + JSON.stringify(err));
          return;
        }

        winston.debug('stats for ' + file + ' are ' + JSON.stringify(stats));
        if(stats.isFile()) {
          utils.calcHash(localFile, function(error, sha1) {
            sendChange('copy', sharedFile, retry, sha1);
          });
        } else {
          sendChange('addDir', sharedFile, retry, null);
        }
      })
    }

    var sendChange = function(event, file, retry, checkSum) {
      winston.debug('sending ' + event + ' for ' + file + ' retried ' + retry);
      if (retry > maxRetries) {
        winston.error('unabled to syncronize ' + file + ' retried ' + retry + ' times.');
        return;
      }
      var options = {
        host: serverAddress,
        port: serverPort,
        path: '/',
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        }
      };

      var req = http.request(options, function(res) {
        var data="";
        res.on('data', function (chunk) {
          data+=chunk;
        });

        res.on('end', function () {
          if (JSON.parse(data)["code"] === utils.ERROR_CODE) {
            winston.warn('error occured while syncing ' + file + ' ' + data);
            generateChange(file, retry + 1);
          } else {
            winston.debug('successful response received ' + data);
          }
        });
      });

      postData = JSON.stringify({
        event: event,
        file: file,
        checkSum: checkSum,
      });

      req.write(postData);

      req.end();
    };

    winston.info('watcher started');
    chokidar.watch(
      path.normalize(sharedDir),
      { ignored: /[\/\\]\./, ignoreInitial: true}
    ).on('all', function(event, file) {
      var sharedFile=file.replace(sharedDir, '');
      // replace all path.sep with / becuase in *nix file system \
      // and cause errors we have cross platform syncronization from windows to *nix
      // all commands thats should normalize the path before using it
      sharedFile = sharedFile.replace(path.sep, '/');
      generateChange(sharedFile, 0);
    }).on('error', function(err) {
      winston.error('unexpected error occured ' + JSON.stringify(err));
    });
  }
}
<|endoftext|>


<commit_before>'use strict';

function snapDirective($rootScope, SnapService) {
  return {
    restrict: 'A',
    link: function($scope, $element) {
      SnapService.createSnapper($element[0]);
      SnapService.disableSliding();
      $rootScope.noSwiping = '';
      $rootScope.isSnapVisible = false;

      SnapService.registerOpenCallback(snapperOpened);
      SnapService.registerCloseCallback(snapperClosed);
      SnapService.registerAnimatedCallback(snapperAnimated);

      function snapperOpened() {
        SnapService.enableSliding().then(function() {
          $rootScope.isSnapVisible = true;
          $rootScope.noSwiping = 'swiper-no-swiping';
        });
      }
      function snapperClosed() {
        SnapService.disableSliding().then(function() {
          $rootScope.noSwiping = '';
        });
      }
      function snapperAnimated(snap) {
        if(snap.state().state === 'closed') {
          $rootScope.isSnapVisible = false;
          snapperClosed();
        }
      }

      $scope.toggleSnap = function toggleSnap() {
        SnapService.toggle();
      };
    }
  };
}
snapDirective.$inject = ['$rootScope', 'SnapService'];
angular.module('em.directives').directive('snapDirective', snapDirective);
<commit_msg>Removed toggleSnap method from snapDirective.<commit_after>'use strict';

function snapDirective($rootScope, SnapService) {
  return {
    restrict: 'A',
    link: function($scope, $element) {
      SnapService.createSnapper($element[0]);
      SnapService.disableSliding();
      $rootScope.noSwiping = '';
      $rootScope.isSnapVisible = false;

      SnapService.registerOpenCallback(snapperOpened);
      SnapService.registerCloseCallback(snapperClosed);
      SnapService.registerAnimatedCallback(snapperAnimated);

      function snapperOpened() {
        SnapService.enableSliding().then(function() {
          $rootScope.isSnapVisible = true;
          $rootScope.noSwiping = 'swiper-no-swiping';
        });
      }
      function snapperClosed() {
        SnapService.disableSliding().then(function() {
          $rootScope.noSwiping = '';
        });
      }
      function snapperAnimated(snap) {
        if(snap.state().state === 'closed') {
          $rootScope.isSnapVisible = false;
          snapperClosed();
        }
      }
    }
  };
}
snapDirective.$inject = ['$rootScope', 'SnapService'];
angular.module('em.directives').directive('snapDirective', snapDirective);
<|endoftext|>


<commit_before>var spawn = require('child_process').spawn;
var path = require('path');
var chokidar = require('chokidar');
var configPath = '';

var watcher = function(config) {
    var noConfigFound = false;

    config = config || path.resolve(process.cwd(), 'watcherConfig.js');

    if (config.constructor === String) {
        var resolvedPath = path.resolve(config);
        configPath = resolvedPath;

        try {
            config = require(resolvedPath);
        } catch (e) {
            config = {};
            configPath = '';
            noConfigFound = true;
        }
    }

    config = ensureConfigValues(config);

    if (noConfigFound) {
        console.log('Couldn\'t find the config file »' + resolvedPath + '«. Will use default values.');
        console.log('Default config values are:');
        console.log(config + '\n');
        console.log();
    } else {
        console.log('Using following config:');
        console.log(config);
        console.log();
    }

    setupCommandsOnStart(config);
    setupCommandsOnChange(config);
};

var ensureConfigValues = function(config) {
    config.directory = config.directory || process.cwd();
    config.delay = config.delay || 1000;
    config.ignore = config.ignore || [];
    config.commandsOnStart = config.commandsOnStart || [];
    config.commandsOnChange = config.commandsOnChange || [];

    return config;
};

var setupCommandsOnStart = function(config) {
    for (var index in config.commandsOnStart) {
        spawnCommand(config.commandsOnStart[index].command);
    }
};

var setupCommandsOnChange = function(config) {
    var timeoutID = 0;
    var allEvents = [];

    if (config.commandsOnChange.length) {
        var directoryToWatch = config.directory.indexOf('/') !== 0 ? path.resolve(process.cwd(), config.directory) : config.directory;

        var watcher = chokidar.watch(directoryToWatch, {
            ignoreInitial: true,
            ignored: config.ignore
        });

        watcher.on('all', function(event, path) {
            clearTimeout(timeoutID);
            allEvents.push([event, path]);

            if (configPath && event === 'change' && configPath === path) { // The config file changed
                delete require.cache[configPath];
                config = require(configPath);
                config = ensureConfigValues(config);
                console.log('Config file changed, reloaded it. The new values are:');
                console.log(config);
                allEvents = [];
                return;
            }

            timeoutID = setTimeout(function() {
                console.log(allEvents);

                for (var index in config.commandsOnChange) {
                    var commandString;

                    if (config.commandsOnChange[index].command.constructor === String) {
                        spawnCommand(config.commandsOnChange[index].command);
                    } else if (config.commandsOnChange[index].command.constructor === Function) {
                        for (var i = 0; i < allEvents.length; i++) {
                            var event = allEvents[i][0];
                            var file = allEvents[i][1];
                            var callbackResult = config.commandsOnChange[index].command(event, file);
                            spawnCommand(callbackResult);
                        }
                    }
                }

                allEvents = [];
            }, config.delay);
        });
    }
};

var spawnCommand = function(commandString) {
    var command = parseCommand(commandString);
    var arguments = parseArguments(commandString);

    if (command) {
        console.log('Spawning:', command, arguments);
        spawn(command, arguments, {
            stdio: 'inherit'
        });
    }
}

var parseCommand = function(command) {
    var quoted = false;
    var parsedCommand = '';

    if (!command) {
        return parsedCommand;
    }

    for (var i = 0; i < command.length; i++) {
        var char = command.charAt(i);
        var previousChar = i > 0 ? command.charAt(i - 1) : '';

        if (char === ' ') {
            if (quoted || previousChar === '\\') {
                parsedCommand += char;
            } else {
                return parsedCommand;
            }
        } else if (char === '"' || char === '\'') {
            if (quoted) {
                return parsedCommand;
            }

            quoted = true;
        } else {
            parsedCommand += char;
        }
    }

    return parsedCommand;
}

var parseArguments = function(command) {
    var quoted = false;
    var parentheses = 0;
    var brackets = 0;
    var braces = 0;
    var parsedArguments = [];

    if (!command) {
        return parsedArguments;
    }

    var currentArgument = '';
    var arguments = command.replace(parseCommand(command), '').trim();

    var addArgument = function() {
        currentArgument = currentArgument.trim();

        if (currentArgument.indexOf('"') === 0 && currentArgument.match(/"$/)) {
            currentArgument = currentArgument.substring(1, currentArgument.length - 1);
        }

        if (currentArgument !== '') {
            parsedArguments.push(currentArgument);
        }

        currentArgument = '';
        quoted = false;
        var parentheses = 0;
        var brackets = 0;
        var braces = 0;
    };

    for (var i = 0; i < arguments.length; i++) {
        var char = arguments[i];
        var previousChar = i > 0 ? arguments.charAt(i - 1) : '';

        if (char === ' ') {
            if (quoted || previousChar === '\\') {
                currentArgument += char;
            } else {
                addArgument();
            }
        } else if (char === '"' || char === '\'') {
            currentArgument += char;

            if (quoted && !parentheses && !brackets && !braces) {
                addArgument();
            } else if (quoted) {
                quoted = false;
            } else {
                quoted = true;
            }
        } else if (char === '(') {
            currentArgument += char;
            parentheses++;

        } else if (char === ')') {
            currentArgument += char;
            parentheses--;

            if (!parentheses && !brackets && !braces && !quoted) {
                addArgument();
            }
        } else if (char === '[') {
            currentArgument += char;
            brackets++;
        } else if (char === ']') {
            currentArgument += char;
            brackets--;

            if (!parentheses && !brackets && !braces && !quoted) {
                addArgument();
            }
        } else if (char === '{') {
            currentArgument += char;
            braces++;
        } else if (char === '}') {
            currentArgument += char;
            braces--;

            if (!parentheses && !brackets && !braces && !quoted) {
                addArgument();
            }
        } else {
            currentArgument += char;
        }
    }

    if (currentArgument) {
        addArgument();
    }

    return parsedArguments;
};

module.exports = watcher;
<commit_msg>Pretty print console output<commit_after>var spawn = require('child_process').spawn;
var path = require('path');
var chokidar = require('chokidar');
var colors = require('colors/safe');
var configPath = '';

RegExp.prototype.toJSON = RegExp.prototype.toString;

var watcher = function(config) {
    var noConfigFound = false;

    config = config || path.resolve(process.cwd(), 'watcherConfig.js');

    if (config.constructor === String) {
        var resolvedPath = path.resolve(config);
        configPath = resolvedPath;

        try {
            config = require(resolvedPath);
        } catch (e) {
            config = {};
            configPath = '';
            noConfigFound = true;
        }
    }

    config = ensureConfigValues(config);

    if (noConfigFound) {
        console.log(colors.yellow('Couldn\'t find the config file »' + resolvedPath + '«. Will use default values.'));
        console.log(colors.yellow('Default config values are:'));
        console.log(JSON.stringify(config, null, 2));
        console.log();
    } else {
        console.log(colors.green('Using following config:'));
        console.log(JSON.stringify(config, null, 2));
        console.log();
    }

    setupCommandsOnStart(config);
    setupCommandsOnChange(config);
};

var ensureConfigValues = function(config) {
    config.directory = config.directory || process.cwd();
    config.delay = config.delay || 1000;
    config.ignore = config.ignore || [];
    config.commandsOnStart = config.commandsOnStart || [];
    config.commandsOnChange = config.commandsOnChange || [];

    return config;
};

var setupCommandsOnStart = function(config) {
    for (var index in config.commandsOnStart) {
        spawnCommand(config.commandsOnStart[index].command);
    }
};

var setupCommandsOnChange = function(config) {
    var timeoutID = 0;
    var allEvents = [];

    if (config.commandsOnChange.length) {
        var directoryToWatch = config.directory.indexOf('/') !== 0 ? path.resolve(process.cwd(), config.directory) : config.directory;

        var watcher = chokidar.watch(directoryToWatch, {
            ignoreInitial: true,
            ignored: config.ignore
        });

        watcher.on('all', function(event, path) {
            clearTimeout(timeoutID);
            allEvents.push([event, path]);

            if (configPath && event === 'change' && configPath === path) { // The config file changed
                delete require.cache[configPath];
                config = require(configPath);
                config = ensureConfigValues(config);
                console.log(colors.green('Config file changed, reloaded it. The new values are:'));
                console.log(JSON.stringify(config, null, 2));
                console.log();
                allEvents = [];
                return;
            }

            timeoutID = setTimeout(function() {
                console.log(colors.green('Executing callbacks for the following events:'));
                console.log(JSON.stringify(allEvents, null, 2));
                console.log();

                for (var index in config.commandsOnChange) {
                    var commandString;

                    if (config.commandsOnChange[index].command.constructor === String) {
                        spawnCommand(config.commandsOnChange[index].command);
                    } else if (config.commandsOnChange[index].command.constructor === Function) {
                        for (var i = 0; i < allEvents.length; i++) {
                            var event = allEvents[i][0];
                            var file = allEvents[i][1];
                            var callbackResult = config.commandsOnChange[index].command(event, file);
                            spawnCommand(callbackResult);
                        }
                    }
                }

                allEvents = [];
            }, config.delay);
        });
    }
};

var spawnCommand = function(commandString) {
    var command = parseCommand(commandString);
    var arguments = parseArguments(commandString);

    if (command) {
        console.log(colors.green('Spawning command:'), command);
        console.log(colors.green('with the following arguments:'));
        console.log(JSON.stringify(arguments, null, 2));
        console.log();

        var child = spawn(command, arguments, {
            stdio: 'inherit'
        });

        child.on('exit', function() {
            console.log();
        });
    }
}

var parseCommand = function(command) {
    var quoted = false;
    var parsedCommand = '';

    if (!command) {
        return parsedCommand;
    }

    for (var i = 0; i < command.length; i++) {
        var char = command.charAt(i);
        var previousChar = i > 0 ? command.charAt(i - 1) : '';

        if (char === ' ') {
            if (quoted || previousChar === '\\') {
                parsedCommand += char;
            } else {
                return parsedCommand;
            }
        } else if (char === '"' || char === '\'') {
            if (quoted) {
                return parsedCommand;
            }

            quoted = true;
        } else {
            parsedCommand += char;
        }
    }

    return parsedCommand;
}

var parseArguments = function(command) {
    var quoted = false;
    var parentheses = 0;
    var brackets = 0;
    var braces = 0;
    var parsedArguments = [];

    if (!command) {
        return parsedArguments;
    }

    var currentArgument = '';
    var arguments = command.replace(parseCommand(command), '').trim();

    var addArgument = function() {
        currentArgument = currentArgument.trim();

        if (currentArgument.indexOf('"') === 0 && currentArgument.match(/"$/)) {
            currentArgument = currentArgument.substring(1, currentArgument.length - 1);
        }

        if (currentArgument !== '') {
            parsedArguments.push(currentArgument);
        }

        currentArgument = '';
        quoted = false;
        var parentheses = 0;
        var brackets = 0;
        var braces = 0;
    };

    for (var i = 0; i < arguments.length; i++) {
        var char = arguments[i];
        var previousChar = i > 0 ? arguments.charAt(i - 1) : '';

        if (char === ' ') {
            if (quoted || previousChar === '\\') {
                currentArgument += char;
            } else {
                addArgument();
            }
        } else if (char === '"' || char === '\'') {
            currentArgument += char;

            if (quoted && !parentheses && !brackets && !braces) {
                addArgument();
            } else if (quoted) {
                quoted = false;
            } else {
                quoted = true;
            }
        } else if (char === '(') {
            currentArgument += char;
            parentheses++;

        } else if (char === ')') {
            currentArgument += char;
            parentheses--;

            if (!parentheses && !brackets && !braces && !quoted) {
                addArgument();
            }
        } else if (char === '[') {
            currentArgument += char;
            brackets++;
        } else if (char === ']') {
            currentArgument += char;
            brackets--;

            if (!parentheses && !brackets && !braces && !quoted) {
                addArgument();
            }
        } else if (char === '{') {
            currentArgument += char;
            braces++;
        } else if (char === '}') {
            currentArgument += char;
            braces--;

            if (!parentheses && !brackets && !braces && !quoted) {
                addArgument();
            }
        } else {
            currentArgument += char;
        }
    }

    if (currentArgument) {
        addArgument();
    }

    return parsedArguments;
};

module.exports = watcher;
<|endoftext|>