<commit_before>
module.exports = get

var GET = require("./request").GET
  , fs = require("../graceful-fs")
  , npm = require("../../../npm")
  , path = require("path")
  , log = require("../log")
  , mkdir = require("../mkdir-p")
  , cacheStat = null

function get (project, version, timeout, nofollow, staleOk, cb) {
  if (typeof cb !== "function") cb = staleOk, staleOk = false
  if (typeof cb !== "function") cb = nofollow, nofollow = false
  if (typeof cb !== "function") cb = timeout, timeout = -1
  if (typeof cb !== "function") cb = version, version = null
  if (typeof cb !== "function") cb = project, project = null
  if (typeof cb !== "function") {
    throw new Error("No callback provided to registry.get")
  }

  if ( process.env.COMP_CWORD !== undefined
    && process.env.COMP_LINE !== undefined
    && process.env.COMP_POINT !== undefined
    ) timeout = Math.max(timeout, 60000)

  var uri = []
  uri.push(project || "")
  if (version) uri.push(version)
  uri = uri.join("/")

  // /-/all is special.
  // It uses timestamp-based caching and partial updates,
  // because it is a monster.
  if (uri === "/-/all") {
    return requestAll(cb)
  }

  var cache = path.join(npm.cache, uri, ".cache.json")
  fs.stat(cache, function (er, stat) {
    if (!er) fs.readFile(cache, function (er, data) {
      try { data = JSON.parse(data) }
      catch (ex) { data = null }
      get_(uri, timeout, cache, stat, data, nofollow, staleOk, cb)
    })
    else get_(uri, timeout, cache, null, null, nofollow, staleOk, cb)
  })
}

function requestAll (cb) {
  var cache = path.join(npm.cache, "/-/all", ".cache.json")

  mkdir(path.join(npm.cache, "-", "all"), function (er) {
    fs.readFile(cache, function (er, data) {
      if (er) return requestAll_(0, {}, cb)
      try {
        data = JSON.parse(data)
      } catch (ex) {
        fs.writeFile(cache, "{}", function (er) {
          if (er) return cb(new Error("Broken cache. "
                                     +"Please run 'npm cache clean' "
                                     +"and try again."))
          return requestAll_(0, {}, cb)
        })
      }
      var t = +data._updated || 0
      requestAll_(t, data, cb)
    })
  })
}

function requestAll_ (c, data, cb) {
  // use the cache and update in the background if it's not too old
  if (Date.now() - c < 60000) {
    cb(null, data)
    cb = function () {}
  }

  var uri = "/-/all/since?startkey=" + c
    , cache = path.join(npm.cache, "-/all", ".cache.json")
  GET(uri, function (er, updates, _, res) {
    if (er) return cb(er, data)
    var headers = res.headers
      , updated = Date.parse(headers.date)
    Object.keys(updates).forEach(function (p) {
      data[p] = updates[p]
    })
    data._updated = updated
    fs.writeFile( cache, JSON.stringify(data)
                , function (er) {
      delete data._updated
      return cb(er, data)
    })
  })
}

function get_ (uri, timeout, cache, stat, data, nofollow, staleOk, cb) {
  var etag
  if (data && data._etag) etag = data._etag
  if (timeout && timeout > 0 && stat && data) {
    if ((Date.now() - stat.mtime.getTime())/1000 < timeout) {
      log.verbose("not expired, no request", "registry.get " +uri)
      delete data._etag
      return cb(null, data, JSON.stringify(data), {statusCode:304})
    }
    if (staleOk) {
      log.verbose("staleOk, background update", "registry.get " +uri)
      delete data._etag
      process.nextTick(cb.bind( null, null, data, JSON.stringify(data)
                              , {statusCode: 304} ))
      cb = function () {}
    }
  }

  GET(uri, etag, nofollow, function (er, remoteData, raw, response) {
    if (response) {
      log.silly([response.statusCode, response.headers], "get cb")
      if (response.statusCode === 304 && etag) {
        remoteData = data
        log.verbose(uri+" from cache", "etag")
      }
    }

    data = remoteData
    if (er) return cb(er, data, raw, response)

    // just give the write the old college try.  if it fails, whatever.
    function saved () {
      delete data._etag
      cb(er, data, raw, response)
    }

    saveToCache(cache, data, saved)
  })
}

function saveToCache (cache, data, saved) {
  if (cacheStat) {
    return saveToCache_(cache, data, cacheStat.uid, cacheStat.gid, saved)
  }
  fs.stat(npm.cache, function (er, st) {
    if (er) {
      return fs.stat(process.env.HOME || "", function (er, st) {
        // if this fails, oh well.
        if (er) return saved()
        cacheStat = st
        return saveToCache(cache, data, saved)
      })
    }
    cacheStat = st || { uid: null, gid: null }
    return saveToCache(cache, data, saved)
  })
}

function saveToCache_ (cache, data, uid, gid, saved) {
  mkdir(path.dirname(cache), 0755, uid, gid, function (er) {
    if (er) return saved()
    fs.writeFile(cache, JSON.stringify(data), function (er) {
      if (er || uid === null || gid === null) {
        return saved()
      }
      fs.chown(cache, uid, gid, saved)
    })
  })
}
<commit_msg>Print a warning when the search index builds for the first time<commit_after>
module.exports = get

var GET = require("./request").GET
  , fs = require("../graceful-fs")
  , npm = require("../../../npm")
  , path = require("path")
  , log = require("../log")
  , mkdir = require("../mkdir-p")
  , cacheStat = null

function get (project, version, timeout, nofollow, staleOk, cb) {
  if (typeof cb !== "function") cb = staleOk, staleOk = false
  if (typeof cb !== "function") cb = nofollow, nofollow = false
  if (typeof cb !== "function") cb = timeout, timeout = -1
  if (typeof cb !== "function") cb = version, version = null
  if (typeof cb !== "function") cb = project, project = null
  if (typeof cb !== "function") {
    throw new Error("No callback provided to registry.get")
  }

  if ( process.env.COMP_CWORD !== undefined
    && process.env.COMP_LINE !== undefined
    && process.env.COMP_POINT !== undefined
    ) timeout = Math.max(timeout, 60000)

  var uri = []
  uri.push(project || "")
  if (version) uri.push(version)
  uri = uri.join("/")

  // /-/all is special.
  // It uses timestamp-based caching and partial updates,
  // because it is a monster.
  if (uri === "/-/all") {
    return requestAll(cb)
  }

  var cache = path.join(npm.cache, uri, ".cache.json")
  fs.stat(cache, function (er, stat) {
    if (!er) fs.readFile(cache, function (er, data) {
      try { data = JSON.parse(data) }
      catch (ex) { data = null }
      get_(uri, timeout, cache, stat, data, nofollow, staleOk, cb)
    })
    else get_(uri, timeout, cache, null, null, nofollow, staleOk, cb)
  })
}

function requestAll (cb) {
  var cache = path.join(npm.cache, "/-/all", ".cache.json")

  mkdir(path.join(npm.cache, "-", "all"), function (er) {
    fs.readFile(cache, function (er, data) {
      if (er) return requestAll_(0, {}, cb)
      try {
        data = JSON.parse(data)
      } catch (ex) {
        fs.writeFile(cache, "{}", function (er) {
          if (er) return cb(new Error("Broken cache. "
                                     +"Please run 'npm cache clean' "
                                     +"and try again."))
          return requestAll_(0, {}, cb)
        })
      }
      var t = +data._updated || 0
      requestAll_(t, data, cb)
    })
  })
}

function requestAll_ (c, data, cb) {
  // use the cache and update in the background if it's not too old
  if (Date.now() - c < 60000) {
    cb(null, data)
    cb = function () {}
  }

  if (c === 0) {
    log.warn("Building the index for the first time, please be patient")
  }

  var uri = "/-/all/since?startkey=" + c
    , cache = path.join(npm.cache, "-/all", ".cache.json")
  GET(uri, function (er, updates, _, res) {
    if (er) return cb(er, data)
    var headers = res.headers
      , updated = Date.parse(headers.date)
    Object.keys(updates).forEach(function (p) {
      data[p] = updates[p]
    })
    data._updated = updated
    fs.writeFile( cache, JSON.stringify(data)
                , function (er) {
      delete data._updated
      return cb(er, data)
    })
  })
}

function get_ (uri, timeout, cache, stat, data, nofollow, staleOk, cb) {
  var etag
  if (data && data._etag) etag = data._etag
  if (timeout && timeout > 0 && stat && data) {
    if ((Date.now() - stat.mtime.getTime())/1000 < timeout) {
      log.verbose("not expired, no request", "registry.get " +uri)
      delete data._etag
      return cb(null, data, JSON.stringify(data), {statusCode:304})
    }
    if (staleOk) {
      log.verbose("staleOk, background update", "registry.get " +uri)
      delete data._etag
      process.nextTick(cb.bind( null, null, data, JSON.stringify(data)
                              , {statusCode: 304} ))
      cb = function () {}
    }
  }

  GET(uri, etag, nofollow, function (er, remoteData, raw, response) {
    if (response) {
      log.silly([response.statusCode, response.headers], "get cb")
      if (response.statusCode === 304 && etag) {
        remoteData = data
        log.verbose(uri+" from cache", "etag")
      }
    }

    data = remoteData
    if (er) return cb(er, data, raw, response)

    // just give the write the old college try.  if it fails, whatever.
    function saved () {
      delete data._etag
      cb(er, data, raw, response)
    }

    saveToCache(cache, data, saved)
  })
}

function saveToCache (cache, data, saved) {
  if (cacheStat) {
    return saveToCache_(cache, data, cacheStat.uid, cacheStat.gid, saved)
  }
  fs.stat(npm.cache, function (er, st) {
    if (er) {
      return fs.stat(process.env.HOME || "", function (er, st) {
        // if this fails, oh well.
        if (er) return saved()
        cacheStat = st
        return saveToCache(cache, data, saved)
      })
    }
    cacheStat = st || { uid: null, gid: null }
    return saveToCache(cache, data, saved)
  })
}

function saveToCache_ (cache, data, uid, gid, saved) {
  mkdir(path.dirname(cache), 0755, uid, gid, function (er) {
    if (er) return saved()
    fs.writeFile(cache, JSON.stringify(data), function (er) {
      if (er || uid === null || gid === null) {
        return saved()
      }
      fs.chown(cache, uid, gid, saved)
    })
  })
}
<|endoftext|>


<commit_before>/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */

(function(define) { 'use strict';
define(function() {

	return function array(Promise) {

		var arrayReduce = Array.prototype.reduce;

		var toPromise = Promise.resolve;
		var reject = Promise.reject;
		var all = Promise.all;

		// Additional array combinators

		Promise.any = any;
		Promise.some = some;
		Promise.settle = settle;

		Promise.map = map;
		Promise.filter = filter;
		Promise.reduce = reduce;
		Promise.reduceRight = reduceRight;

		/**
		 * When this promise fulfills with an array, do
		 * onFulfilled.apply(void 0, array)
		 * @param {function} onFulfilled function to apply
		 * @returns {Promise} promise for the result of applying onFulfilled
		 */
		Promise.prototype.spread = function(onFulfilled) {
			return this.then(all).then(function(array) {
				return onFulfilled.apply(void 0, array);
			});
		};

		return Promise;

		/**
		 * One-winner competitive race.
		 * Return a promise that will fulfill when one of the promises
		 * in the input array fulfills, or will reject when all promises
		 * have rejected.
		 * @param {array} promises
		 * @returns {Promise} promise for the first fulfilled value
		 */
		function any(promises) {
			return new Promise(function(resolve, reject) {
				var errors = [];
				var pending = initRace(promises, resolve, handleReject);

				if(pending === 0) {
					reject(new RangeError('any() input must not be empty'));
				}

				function handleReject(e) {
					errors.push(e);
					if(--pending === 0) {
						reject(errors);
					}
				}
			});
		}

		/**
		 * N-winner competitive race
		 * Return a promise that will fulfill when n input promises have
		 * fulfilled, or will reject when it becomes impossible for n
		 * input promises to fulfill (ie when promises.length - n + 1
		 * have rejected)
		 * @param {array} promises
		 * @param {number} n
		 * @returns {Promise} promise for the earliest n fulfillment values
		 *
		 * @deprecated
		 */
		function some(promises, n) {
			return new Promise(function(resolve, reject, notify) {
				var results = [];
				var errors = [];
				var nReject;
				var nFulfill = initRace(promises, handleResolve, handleReject, notify);

				n = Math.max(n, 0);
				nReject = (nFulfill - n + 1);
				nFulfill = Math.min(n, nFulfill);

				if(n > nFulfill) {
					reject(new RangeError('some() input must contain at least '
						+ n + ' element(s), but had ' + nFulfill));
				} else if(nFulfill === 0) {
					resolve(results);
				}

				function handleResolve(x) {
					if(nFulfill > 0) {
						--nFulfill;
						results.push(x);

						if(nFulfill === 0) {
							resolve(results);
						}
					}
				}

				function handleReject(e) {
					if(nReject > 0) {
						--nReject;
						errors.push(e);

						if(nReject === 0) {
							reject(errors);
						}
					}
				}
			});
		}

		/**
		 * Initialize a race observing each promise in the input promises
		 * @param {Array} promises
		 * @param {function} resolve
		 * @param {function} reject
		 * @param {?function=} notify
		 * @returns {Number} actual count of items being raced
		 */
		function initRace(promises, resolve, reject, notify) {
			return arrayReduce.call(promises, function(pending, p) {
				toPromise(p).then(resolve, reject, notify);
				return pending + 1;
			}, 0);
		}

		/**
		 * Apply f to the value of each promise in a list of promises
		 * and return a new list containing the results.
		 * @param {array} promises
		 * @param {function(x:*, index:Number):*} f mapping function
		 * @returns {Promise}
		 */
		function map(promises, f) {
			if(typeof promises !== 'object') {
				return toPromise([]);
			}

			return all(mapArray(function(x, i) {
				return toPromise(x).fold(mapWithIndex, i);
			}, promises));

			function mapWithIndex(k, x) {
				return f(x, k);
			}
		}

		/**
		 * Filter the provided array of promises using the provided predicate.  Input may
		 * contain promises and values
		 * @param {Array} promises array of promises and values
		 * @param {function(x:*, index:Number):boolean} predicate filtering predicate.
		 *  Must return truthy (or promise for truthy) for items to retain.
		 * @returns {Promise} promise that will fulfill with an array containing all items
		 *  for which predicate returned truthy.
		 */
		function filter(promises, predicate) {
			return all(promises).then(function(values) {
				return all(mapArray(predicate, values)).then(function(results) {
					var len = results.length;
					var filtered = new Array(len);
					for(var i=0, j= 0, x; i<len; ++i) {
						x = results[i];
						if(x === void 0 && !(i in results)) {
							continue;
						}
						if(results[i]) {
							filtered[j++] = values[i];
						}
					}
					filtered.length = j;
					return filtered;
				});
			});
		}

		/**
		 * Return a promise that will always fulfill with an array containing
		 * the outcome states of all input promises.  The returned promise
		 * will never reject.
		 * @param {array} promises
		 * @returns {Promise} promise for array of settled state descriptors
		 */
		function settle(promises) {
			return all(mapArray(function(p) {
				p = toPromise(p);
				return p.then(inspect, inspect);

				function inspect() {
					return p.inspect();
				}
			}, promises));
		}

		/**
		 * Reduce an array of promises and values
		 * @param {Array} a array of promises
		 * @param {function(accumulated:*, x:*, index:Number):*} f reduce function
		 * @returns {Promise} promise for reduced value
		 */
		function reduce(a, f) {
			var hasArg = arguments.length > 2;
			if((a.length>>>0) === 0) {
				return hasArg ? toPromise(arguments[2]) : reject(new TypeError('Cannot reduce empty array with no initial value'));
			}

			var s = findFirst(a);
			return hasArg ? runReduce(s, a, f, arguments[2])
				: runReduce(s + 1, a, f, a[s]);
		}

		function runReduce(i, a, f, z) {
			if (i === a.length) {
				return z;
			}

			var x = a[i];
			if(x === void 0 && !(i in a)) {
				return runReduce(i + 1, a, f, z);
			}

			return toPromise(a[i]).fold(function (z, x) {
				return runReduce(i + 1, a, f, f(z, x, i));
			}, z);
		}

		/**
		 * Reduce an array of promises and values from the right
		 * @param {Array} a array of promises
		 * @param {function(accumulated:*, x:*, index:Number):*} f reduce function
		 * @returns {Promise} promise for reduced value
		 */
		function reduceRight(a, f) {
			var hasArg = arguments.length > 2;
			if((a.length>>>0) === 0) {
				return hasArg ? toPromise(arguments[2]) : reject(new TypeError('Cannot reduce empty array with no initial value'));
			}

			var s = findLast(a);
			return hasArg ? runReduceRight(s, a, f, arguments[2])
				: runReduceRight(s - 1, a, f, a[s]);
		}

		function runReduceRight(i, a, f, z) {
			if (i < 0) {
				return z;
			}

			var x = a[i];
			if(x === void 0 && !(i in a)) {
				return runReduceRight(i - 1, a, f, z);
			}

			return toPromise(a[i]).fold(function (z, x) {
				return runReduceRight(i - 1, a, f, f(z, x, i));
			}, z);
		}

		function findFirst(a) {
			for(var i=0; i<a.length; ++i) {
				if(i in a) { break; }
			}
			return i;
		}

		function findLast(a) {
			for(var i=a.length-1; i>=0; i--) {
				if(i in a) { break; }
			}
			return i;
		}

		function mapArray(f, a) {
			var l = a.length;
			var b = new Array(l);
			for(var i=0, x; i<l; ++i) {
				x = a[i];
				if(x === void 0 && !(i in a)) {
					continue;
				}
				b[i] = f(a[i], i);
			}
			return b;
		}
	};

});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
<commit_msg>Inline sparse array find first/last<commit_after>/** @license MIT License (c) copyright 2010-2014 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */

(function(define) { 'use strict';
define(function() {

	return function array(Promise) {

		var arrayReduce = Array.prototype.reduce;

		var toPromise = Promise.resolve;
		var reject = Promise.reject;
		var all = Promise.all;

		// Additional array combinators

		Promise.any = any;
		Promise.some = some;
		Promise.settle = settle;

		Promise.map = map;
		Promise.filter = filter;
		Promise.reduce = reduce;
		Promise.reduceRight = reduceRight;

		/**
		 * When this promise fulfills with an array, do
		 * onFulfilled.apply(void 0, array)
		 * @param {function} onFulfilled function to apply
		 * @returns {Promise} promise for the result of applying onFulfilled
		 */
		Promise.prototype.spread = function(onFulfilled) {
			return this.then(all).then(function(array) {
				return onFulfilled.apply(void 0, array);
			});
		};

		return Promise;

		/**
		 * One-winner competitive race.
		 * Return a promise that will fulfill when one of the promises
		 * in the input array fulfills, or will reject when all promises
		 * have rejected.
		 * @param {array} promises
		 * @returns {Promise} promise for the first fulfilled value
		 */
		function any(promises) {
			return new Promise(function(resolve, reject) {
				var errors = [];
				var pending = initRace(promises, resolve, handleReject);

				if(pending === 0) {
					reject(new RangeError('any() input must not be empty'));
				}

				function handleReject(e) {
					errors.push(e);
					if(--pending === 0) {
						reject(errors);
					}
				}
			});
		}

		/**
		 * N-winner competitive race
		 * Return a promise that will fulfill when n input promises have
		 * fulfilled, or will reject when it becomes impossible for n
		 * input promises to fulfill (ie when promises.length - n + 1
		 * have rejected)
		 * @param {array} promises
		 * @param {number} n
		 * @returns {Promise} promise for the earliest n fulfillment values
		 *
		 * @deprecated
		 */
		function some(promises, n) {
			return new Promise(function(resolve, reject, notify) {
				var results = [];
				var errors = [];
				var nReject;
				var nFulfill = initRace(promises, handleResolve, handleReject, notify);

				n = Math.max(n, 0);
				nReject = (nFulfill - n + 1);
				nFulfill = Math.min(n, nFulfill);

				if(n > nFulfill) {
					reject(new RangeError('some() input must contain at least '
						+ n + ' element(s), but had ' + nFulfill));
				} else if(nFulfill === 0) {
					resolve(results);
				}

				function handleResolve(x) {
					if(nFulfill > 0) {
						--nFulfill;
						results.push(x);

						if(nFulfill === 0) {
							resolve(results);
						}
					}
				}

				function handleReject(e) {
					if(nReject > 0) {
						--nReject;
						errors.push(e);

						if(nReject === 0) {
							reject(errors);
						}
					}
				}
			});
		}

		/**
		 * Initialize a race observing each promise in the input promises
		 * @param {Array} promises
		 * @param {function} resolve
		 * @param {function} reject
		 * @param {?function=} notify
		 * @returns {Number} actual count of items being raced
		 */
		function initRace(promises, resolve, reject, notify) {
			return arrayReduce.call(promises, function(pending, p) {
				toPromise(p).then(resolve, reject, notify);
				return pending + 1;
			}, 0);
		}

		/**
		 * Apply f to the value of each promise in a list of promises
		 * and return a new list containing the results.
		 * @param {array} promises
		 * @param {function(x:*, index:Number):*} f mapping function
		 * @returns {Promise}
		 */
		function map(promises, f) {
			if(typeof promises !== 'object') {
				return toPromise([]);
			}

			return all(mapArray(function(x, i) {
				return toPromise(x).fold(mapWithIndex, i);
			}, promises));

			function mapWithIndex(k, x) {
				return f(x, k);
			}
		}

		/**
		 * Filter the provided array of promises using the provided predicate.  Input may
		 * contain promises and values
		 * @param {Array} promises array of promises and values
		 * @param {function(x:*, index:Number):boolean} predicate filtering predicate.
		 *  Must return truthy (or promise for truthy) for items to retain.
		 * @returns {Promise} promise that will fulfill with an array containing all items
		 *  for which predicate returned truthy.
		 */
		function filter(promises, predicate) {
			return all(promises).then(function(values) {
				return all(mapArray(predicate, values)).then(function(results) {
					var len = results.length;
					var filtered = new Array(len);
					for(var i=0, j= 0, x; i<len; ++i) {
						x = results[i];
						if(x === void 0 && !(i in results)) {
							continue;
						}
						if(results[i]) {
							filtered[j++] = values[i];
						}
					}
					filtered.length = j;
					return filtered;
				});
			});
		}

		/**
		 * Return a promise that will always fulfill with an array containing
		 * the outcome states of all input promises.  The returned promise
		 * will never reject.
		 * @param {array} promises
		 * @returns {Promise} promise for array of settled state descriptors
		 */
		function settle(promises) {
			return all(mapArray(function(p) {
				p = toPromise(p);
				return p.then(inspect, inspect);

				function inspect() {
					return p.inspect();
				}
			}, promises));
		}

		/**
		 * Reduce an array of promises and values
		 * @param {Array} a array of promises
		 * @param {function(accumulated:*, x:*, index:Number):*} f reduce function
		 * @returns {Promise} promise for reduced value
		 */
		function reduce(a, f) {
			var hasArg = arguments.length > 2;
			if((a.length>>>0) === 0) {
				return hasArg ? toPromise(arguments[2]) : reject(new TypeError('Cannot reduce empty array with no initial value'));
			}

			// Skip sparse array holes >:(
			// jshint curly:false
			for(var i=0; i<a.length && !(i in a); ++i);

			return hasArg ? runReduce(i, a, f, arguments[2])
				: runReduce(i + 1, a, f, a[i]);
		}

		function runReduce(i, a, f, z) {
			if (i === a.length) {
				return z;
			}

			var x = a[i];
			if(x === void 0 && !(i in a)) {
				return runReduce(i + 1, a, f, z);
			}

			return toPromise(a[i]).fold(function (z, x) {
				return runReduce(i + 1, a, f, f(z, x, i));
			}, z);
		}

		/**
		 * Reduce an array of promises and values from the right
		 * @param {Array} a array of promises
		 * @param {function(accumulated:*, x:*, index:Number):*} f reduce function
		 * @returns {Promise} promise for reduced value
		 */
		function reduceRight(a, f) {
			var hasArg = arguments.length > 2;
			if((a.length>>>0) === 0) {
				return hasArg ? toPromise(arguments[2]) : reject(new TypeError('Cannot reduce empty array with no initial value'));
			}

			// Skip sparse array holes >:(
			// jshint curly:false
			for(var i=a.length-1; i>=0 && !(i in a); i--);

			return hasArg ? runReduceRight(i, a, f, arguments[2])
				: runReduceRight(i - 1, a, f, a[i]);
		}

		function runReduceRight(i, a, f, z) {
			if (i < 0) {
				return z;
			}

			var x = a[i];
			if(x === void 0 && !(i in a)) {
				return runReduceRight(i - 1, a, f, z);
			}

			return toPromise(a[i]).fold(function (z, x) {
				return runReduceRight(i - 1, a, f, f(z, x, i));
			}, z);
		}

		function mapArray(f, a) {
			var l = a.length;
			var b = new Array(l);
			for(var i=0, x; i<l; ++i) {
				x = a[i];
				if(x === void 0 && !(i in a)) {
					continue;
				}
				b[i] = f(a[i], i);
			}
			return b;
		}
	};

});
}(typeof define === 'function' && define.amd ? define : function(factory) { module.exports = factory(); }));
<|endoftext|>


<commit_before>'use babel';
/**
  Delve Server Manager
  @description Manages the Delve server process
**/

const ChildProc    = require('child-process');
const EventEmitter = require('events');
const Path         = require('path');

import { Config } from './config.js';

const delveCommands = {
  debug: 'debug',
  test: 'test'
};

/**
  @class
  @extends EventEmitter
  @name DelveServerMgr
  @desc Manages the Delve server process and lifecycle of the session
  @emits 'startServer' on delve server start, gives host & port of server
  @emits 'error' on delve server error or unexpected disconnect
**/
export default class DelveServerMgr extends EventEmitter {
  constructor() {
    super();
    this.delveProc = null;
    this._pid = null;
    this.hasSessionStarted = false;
  }

  startDebugSession(packagePath, flags) {
    this.spawnProcess(delveCommands.debug, packagePath, flags);
  }

  startTestingSession(packagePath, flags) {
    this.spawnProcess(delveCommands.test, packagePath, flags);
  }

  endSession() {
    this.hasSessionStarted = false;
    this.delveProc.kill();
    this._pid = null;
    this.delveProc = null;
  }

  /**
    @private
    @name #spawnProcess
    @desc creates a new delve process with the given command and registers
      our event handlers with it
    @param { String } command
    @param { String } cwd => will affect what package is being run against
    @param { String|String[] } buildFlags :optional
  **/
  spawnProcess(command, cwd, buildFlags) {
    let { host, port, logServerMessages } = Config;
    let args = [ command, '--headless', `--listen=${host}:${port}` ];

    if (buildFlags)
      if (Array.isArray(buildFlags))
        args.push(`--build-flags=${buildFlags.join(' ')}`);
      else
        args.push(`--build-flags="${buildFlags}"`);

    if (logServerMessages) args.push('--log');

    this.delveProc = ChildProc.spawn('dlv', args, { cwd: Path.resolve(cwd) });
    this._pid = this.delveProc.pid;
    this.isRemote = false;

    this.delveProc.on('data', data => {
      if (!this.hasSessionStarted) {
        console.debug('Received server start from Delve server', data);
        this.hasSessionStarted = true;
        return this.emit('serverStart', host, port);
      }

      console.debug('Received Delve data: ', data);
    });

    this.delveProc.on('error', err => this.emit('error', err));
    this.delveProc.on('disconnect', () => {
      // make sure we think we have a session before declaring an error
      if (this.hasSessionStarted)
        this.emit('error', new Error('Delve server disconnected'));
    });

    this.delveProc.on('close', (exitCode, signal) =>
      this.emit(
        'error',
        new Error(`Delve exited with code ${exitCode} from ${signal}`)
      )
    );

    if (logServerMessages)
      this.delveProc.on('message', msg => console.debug('Delve: ', msg));
  }
}
<commit_msg>Fix typo<commit_after>'use babel';
/**
  Delve Server Manager
  @description Manages the Delve server process
**/

const ChildProc    = require('child_process');
const EventEmitter = require('events');
const Path         = require('path');

import { Config } from './config.js';

const delveCommands = {
  debug: 'debug',
  test: 'test'
};

/**
  @class
  @extends EventEmitter
  @name DelveServerMgr
  @desc Manages the Delve server process and lifecycle of the session
  @emits 'startServer' on delve server start, gives host & port of server
  @emits 'error' on delve server error or unexpected disconnect
**/
export default class DelveServerMgr extends EventEmitter {
  constructor() {
    super();
    this.delveProc = null;
    this._pid = null;
    this.hasSessionStarted = false;
  }

  startDebugSession(packagePath, flags) {
    this.spawnProcess(delveCommands.debug, packagePath, flags);
  }

  startTestingSession(packagePath, flags) {
    this.spawnProcess(delveCommands.test, packagePath, flags);
  }

  endSession() {
    this.hasSessionStarted = false;
    this.delveProc.kill();
    this._pid = null;
    this.delveProc = null;
  }

  /**
    @private
    @name #spawnProcess
    @desc creates a new delve process with the given command and registers
      our event handlers with it
    @param { String } command
    @param { String } cwd => will affect what package is being run against
    @param { String|String[] } buildFlags :optional
  **/
  spawnProcess(command, cwd, buildFlags) {
    let { host, port, logServerMessages } = Config;
    let args = [ command, '--headless', `--listen=${host}:${port}` ];

    if (buildFlags)
      if (Array.isArray(buildFlags))
        args.push(`--build-flags=${buildFlags.join(' ')}`);
      else
        args.push(`--build-flags="${buildFlags}"`);

    if (logServerMessages) args.push('--log');

    this.delveProc = ChildProc.spawn('dlv', args, { cwd: Path.resolve(cwd) });
    this._pid = this.delveProc.pid;
    this.isRemote = false;

    this.delveProc.on('data', data => {
      if (!this.hasSessionStarted) {
        console.debug('Received server start from Delve server', data);
        this.hasSessionStarted = true;
        return this.emit('serverStart', host, port);
      }

      console.debug('Received Delve data: ', data);
    });

    this.delveProc.on('error', err => this.emit('error', err));
    this.delveProc.on('disconnect', () => {
      // make sure we think we have a session before declaring an error
      if (this.hasSessionStarted)
        this.emit('error', new Error('Delve server disconnected'));
    });

    this.delveProc.on('close', (exitCode, signal) =>
      this.emit(
        'error',
        new Error(`Delve exited with code ${exitCode} from ${signal}`)
      )
    );

    if (logServerMessages)
      this.delveProc.on('message', msg => console.debug('Delve: ', msg));
  }
}
<|endoftext|>


<commit_before>(() => {
	var constNameRgxp = /\[(['"`])(.*?)\1]/g,
		propAssignRgxp = /[.\[]/;

	Snakeskin.addDirective(
		'const',

		{
			group: [
				'inherit',
				'inlineInherit'
			]
		},

		function (command, commandLength, type) {
			var output = false;

			if (command.slice(-1) === '?') {
				output = true;
				command = command.slice(0, -1);
			}

			var tplName = this.tplName,
				source = `^[\$${symbols}_${!this.scope.length ? L_MOD : ''}][$${w}[\\].\\s]*=[^=]`;

			var rgxp = rgxpCache[source] || new RegExp(source, 'i');
			rgxpCache[source] = rgxp;

			if (type === 'global' || (!tplName || rgxp.test(command)) && type !== 'output') {
				if (tplName && type !== 'global') {
					let parts = command.split('='),
						prop = parts[0] && parts[0].trim();

					if (!parts[1] || !parts[1].trim()) {
						return this.error(`invalid "constant" declaration`);
					}

					let name = this.pasteDangerBlocks(prop);

					if (name.charAt(0) === L_MOD) {
						return this.error(`can\'t declare constant "${name.substring(1)}" with the context modifier (${L_MOD})`);
					}

					name = name.replace(constNameRgxp, '.$2');
					this.startInlineDir('const', {
						name: name
					});

					if (this.isReady()) {
						if (!propAssignRgxp.test(prop)) {
							this.consts.push(`var ${prop};`);
						}

						if (output) {
							this.text = true;
							this.append(this.wrap(`${prop} = ${this.prepareOutput(parts.slice(1).join('='))};`));

						} else {
							this.append(`${prop} = ${this.prepareOutput(parts.slice(1).join('='), true)};`);
						}
					}

					if (this.isAdvTest()) {
						if (constCache[tplName][name]) {
							return this.error(`constant "${name}" is already defined`);
						}

						if (this.varCache[tplName][name]) {
							return this.error(`constant "${name}" is already defined as variable`);
						}

						if (sysConst[name]) {
							return this.error(`can't declare constant "${name}", try another name`);
						}

						let start = this.i - this.startTemplateI;
						let parent,
							parentTpl = this.parentTplName;

						if (parentTpl) {
							parent = constCache[parentTpl][name];
						}

						var insideCallBlock = this.hasParentBlock({
							'block': true,
							'proto': true
						}, true);

						if (insideCallBlock && insideCallBlock.name === 'block' && !insideCallBlock.params.args) {
							insideCallBlock = false;
						}

						constCache[tplName][name] = {
							from: start - commandLength,
							to: start,
							block: Boolean(insideCallBlock || parentTpl && parent && parent.block),
							needPrfx: this.needPrfx,
							output: output ?
								'?' : null
						};

						if (!insideCallBlock) {
							fromConstCache[tplName] = start + 1;
						}
					}

				} else {
					this.startInlineDir('global');
					let desc = isAssign(command, true);

					if (!desc) {
						return this.error(`invalid "${this.name}" declaration`);
					}

					let mod = G_MOD + G_MOD;

					if (command.charAt(0) !== G_MOD) {
						command = mod + command;

					} else {
						command = command
							.replace(scopeModRgxp, mod);
					}

					if (output && tplName) {
						this.text = true;
						this.append(this.wrap(`${this.prepareOutput(desc.key, true)} = ${this.prepareOutput(desc.value)};`));

					} else {
						this.save(`${this.prepareOutput(command, true)};`);
					}
				}

			} else {
				this.startInlineDir('output');
				this.text = true;

				if (!tplName) {
					return this.error(`Directive "${this.name}" can be used only within a ${groupsList['template'].join(', ')}`);
				}

				if (this.isReady()) {
					let desc = isAssign(command);

					if (desc) {
						if (output) {
							this.append(this.wrap(`${this.prepareOutput(desc.key, true)} = ${this.prepareOutput(desc.value)};`));

						} else {
							this.text = false;
							this.append(`${this.prepareOutput(command, true)};`);
						}

						return;
					}

					this.append(this.wrap(this.prepareOutput(command)));
				}
			}
		}
	);

	Snakeskin.addDirective(
		'output',

		{
			placement: 'template',
			notEmpty: true
		},

		function () {
			Snakeskin.Directions['const'].apply(this, arguments);
		}
	);

	Snakeskin.addDirective(
		'global',

		{
			notEmpty: true
		},

		function () {
			Snakeskin.Directions['const'].apply(this, arguments);
		}
	);

	/**
	 * Вернуть объект-описание выражения,
	 * если в строке идёт присвоение значения переменной или свойству,
	 * или false
	 *
	 * @param {string} str - исходная строка
	 * @param {?boolean=} [opt_global=false] - если true, то идёт проверка суперглобальной переменной
	 * @return {({key: string, value: string}|boolean)}
	 */
	function isAssign(str, opt_global) {
		var source = `^[${G_MOD}${L_MOD}$${symbols}_${opt_global ? '[' : ''}]`,
			key = `${source}[i`;

		var rgxp = rgxpCache[key] || new RegExp(source, 'i');
		rgxpCache[key] = rgxp;

		if (!rgxp.test(str)) {
			return false;
		}

		var prop = '';
		var count = 0,
			eq = false;

		var advEqMap = {
			'+': true,
			'-': true,
			'*': true,
			'/': true,
			'^': true,
			'~': true,
			'|': true,
			'&': true
		};

		var bAdvMap = {
			'<': true,
			'>': true
		};

		for (let i = -1; ++i < str.length;) {
			let el = str.charAt(i);
			prop += el;

			if (bMap[el]) {
				count++;
				continue;

			} else if (closeBMap[el]) {
				count--;
				continue;
			}

			let prev = str.charAt(i - 1),
				next = str.charAt(i + 1);

			if (!eq && !count &&
				(
					el === '=' && next !== '=' && prev !== '=' && !advEqMap[prev] && !bAdvMap[prev] ||
					advEqMap[el] && next === '=' ||
					bAdvMap[el] && bAdvMap[next] && str.charAt(i + 2) === '='
				)
			) {

				let diff = 1;

				if (advEqMap[el]) {
					diff = 2;

				} else if (bAdvMap[el]) {
					diff = 3;
				}

				return {
					key: prop.slice(0, -1),
					value: str.substring(i + diff)
				};
			}

			eq = el === '=';
		}

		return false;
	}
})();
<commit_msg>Исправление ошибки<commit_after>(() => {
	var constNameRgxp = /\[(['"`])(.*?)\1]/g,
		propAssignRgxp = /[.\[]/;

	Snakeskin.addDirective(
		'const',

		{
			group: [
				'inherit',
				'inlineInherit'
			]
		},

		function (command, commandLength, type) {
			var output = false;

			if (command.slice(-1) === '?') {
				output = true;
				command = command.slice(0, -1);
			}

			var tplName = this.tplName,
				source = `^[\$${symbols}_${!this.scope.length ? L_MOD : ''}][$${w}[\\].\\s]*=[^=]`;

			var rgxp = rgxpCache[source] || new RegExp(source, 'i');
			rgxpCache[source] = rgxp;

			if (type === 'global' || (!tplName || rgxp.test(command)) && type !== 'output') {
				if (tplName && type !== 'global') {
					let parts = command.split('='),
						prop = parts[0] && parts[0].trim();

					if (!parts[1] || !parts[1].trim()) {
						return this.error(`invalid "constant" declaration`);
					}

					let name = this.pasteDangerBlocks(prop);

					if (name.charAt(0) === L_MOD) {
						return this.error(`can\'t declare constant "${name.substring(1)}" with the context modifier (${L_MOD})`);
					}

					name = name.replace(constNameRgxp, '.$2');
					this.startInlineDir('const', {
						name: name
					});

					if (this.isReady()) {
						if (!propAssignRgxp.test(prop)) {
							this.consts.push(`var ${prop};`);
						}

						if (output) {
							this.text = true;
							this.append(this.wrap(`${prop} = ${this.prepareOutput(parts.slice(1).join('='))};`));

						} else {
							this.append(`${prop} = ${this.prepareOutput(parts.slice(1).join('='), true)};`);
						}
					}

					if (this.isAdvTest()) {
						if (constCache[tplName][name]) {
							return this.error(`constant "${name}" is already defined`);
						}

						if (this.varCache[tplName][name]) {
							return this.error(`constant "${name}" is already defined as variable`);
						}

						if (sysConst[name]) {
							return this.error(`can't declare constant "${name}", try another name`);
						}

						let start = this.i - this.startTemplateI;
						let parent,
							parentTpl = this.parentTplName;

						if (parentTpl) {
							parent = constCache[parentTpl][name];
						}

						var insideCallBlock = this.hasParentBlock({
							'block': true,
							'proto': true
						}, true);

						if (insideCallBlock && insideCallBlock.name === 'block' && !insideCallBlock.params.args) {
							insideCallBlock = false;
						}

						constCache[tplName][name] = {
							from: start - commandLength,
							to: start,
							block: Boolean(insideCallBlock || parentTpl && parent && parent.block),
							needPrfx: this.needPrfx,
							output: output ?
								'?' : null
						};

						if (!insideCallBlock) {
							fromConstCache[tplName] = start + 1;
						}
					}

				} else {
					this.startInlineDir('global');
					let desc = isAssign(command, true);

					if (!desc) {
						return this.error(`invalid "${this.name}" declaration`);
					}

					let mod = G_MOD + G_MOD;

					if (command.charAt(0) !== G_MOD) {
						command = mod + command;

					} else {
						command = command
							.replace(scopeModRgxp, mod);
					}

					if (output && tplName) {
						this.text = true;
						this.append(this.wrap(`${this.prepareOutput(desc.key, true)} = ${this.prepareOutput(desc.value)};`));

					} else {
						this.save(`${this.prepareOutput(command, true)};`);
					}
				}

			} else {
				this.startInlineDir('output');
				this.text = true;

				if (!tplName) {
					return this.error(`Directive "${this.name}" can be used only within a ${groupsList['template'].join(', ')}`);
				}

				if (this.isReady()) {
					let desc = isAssign(command);

					if (desc) {
						if (output) {
							this.append(this.wrap(`${this.prepareOutput(desc.key, true)} = ${this.prepareOutput(desc.value)};`));

						} else {
							this.text = false;
							this.append(`${this.prepareOutput(command, true)};`);
						}

						return;
					}

					this.append(this.wrap(this.prepareOutput(command)));
				}
			}
		}
	);

	Snakeskin.addDirective(
		'output',

		{
			placement: 'template',
			notEmpty: true,
			text: true
		},

		function () {
			Snakeskin.Directions['const'].apply(this, arguments);
		}
	);

	Snakeskin.addDirective(
		'global',

		{
			notEmpty: true
		},

		function () {
			Snakeskin.Directions['const'].apply(this, arguments);
		}
	);

	/**
	 * Вернуть объект-описание выражения,
	 * если в строке идёт присвоение значения переменной или свойству,
	 * или false
	 *
	 * @param {string} str - исходная строка
	 * @param {?boolean=} [opt_global=false] - если true, то идёт проверка суперглобальной переменной
	 * @return {({key: string, value: string}|boolean)}
	 */
	function isAssign(str, opt_global) {
		var source = `^[${G_MOD}${L_MOD}$${symbols}_${opt_global ? '[' : ''}]`,
			key = `${source}[i`;

		var rgxp = rgxpCache[key] || new RegExp(source, 'i');
		rgxpCache[key] = rgxp;

		if (!rgxp.test(str)) {
			return false;
		}

		var prop = '';
		var count = 0,
			eq = false;

		var advEqMap = {
			'+': true,
			'-': true,
			'*': true,
			'/': true,
			'^': true,
			'~': true,
			'|': true,
			'&': true
		};

		var bAdvMap = {
			'<': true,
			'>': true
		};

		for (let i = -1; ++i < str.length;) {
			let el = str.charAt(i);
			prop += el;

			if (bMap[el]) {
				count++;
				continue;

			} else if (closeBMap[el]) {
				count--;
				continue;
			}

			let prev = str.charAt(i - 1),
				next = str.charAt(i + 1);

			if (!eq && !count &&
				(
					el === '=' && next !== '=' && prev !== '=' && !advEqMap[prev] && !bAdvMap[prev] ||
					advEqMap[el] && next === '=' ||
					bAdvMap[el] && bAdvMap[next] && str.charAt(i + 2) === '='
				)
			) {

				let diff = 1;

				if (advEqMap[el]) {
					diff = 2;

				} else if (bAdvMap[el]) {
					diff = 3;
				}

				return {
					key: prop.slice(0, -1),
					value: str.substring(i + diff)
				};
			}

			eq = el === '=';
		}

		return false;
	}
})();
<|endoftext|>


<commit_before>var _ = require('lodash');
var sub = require('string-sub');

var REGEX = require('../regex_new');

var getRegExp = REGEX.getRegExp;

var css = {};

_.forEach(
	REGEX.MAP_CLASS_NAMES_CONVERT_CSS,
	function(item, index) {
		css[index] = {
			message: function(result, rule, context) {
				var match = result[0];

				return sub('"{0}" has changed to "{1}"', match.replace(/.*\.(.*)/, '$1'), item);
			},
			regex: getRegExp('(^|\\s)\\.' + index, 'g'),
			replacer: function(result, rule, context) {
				var rawContent = context.rawContent;

				rawContent = rawContent.replace(rule.regex, '$1.' + item);

				return rawContent;
			},
			test: 'match'
		};
	}
);

_.forEach(
	REGEX.MAP_REGEX_CLASS_NAMES_CONVERT_CSS,
	function(item, index) {
		css[index] = {
			message: function(result, rule, context) {
				var match = result[0];

				return sub(
					'"{0}" has changed to "{1}"',
					match.replace(/(^|\s)\.(.*)/, '$2'),
					match.replace(rule.regex, item)
				);
			},
			regex: getRegExp('(^|\\s)\\.' + index, 'g'),
			replacer: function(result, rule, context) {
				var rawContent = context.rawContent;

				rawContent = rawContent.replace(rule.regex, '$1.' + item);

				return rawContent;
			},
			test: 'match'
		};
	}
);

module.exports = css;<commit_msg>Abstract css class replacer<commit_after>var _ = require('lodash');
var sub = require('string-sub');

var REGEX = require('../regex_new');

var getRegExp = REGEX.getRegExp;

var css = {};

var getClassReplacer = function(item) {
	return function(result, rule, context) {
		var rawContent = context.rawContent;

		rawContent = rawContent.replace(rule.regex, '$1.' + item);

		return rawContent;
	};
};

_.forEach(
	REGEX.MAP_CLASS_NAMES_CONVERT_CSS,
	function(item, index) {
		css[index] = {
			message: function(result, rule, context) {
				var match = result[0];

				return sub('"{0}" has changed to "{1}"', match.replace(/.*\.(.*)/, '$1'), item);
			},
			regex: getRegExp('(^|\\s)\\.' + index, 'g'),
			replacer: getClassReplacer(item),
			test: 'match'
		};
	}
);

_.forEach(
	REGEX.MAP_REGEX_CLASS_NAMES_CONVERT_CSS,
	function(item, index) {
		css[index] = {
			message: function(result, rule, context) {
				var match = result[0];

				return sub(
					'"{0}" has changed to "{1}"',
					match.replace(/(^|\s)\.(.*)/, '$2'),
					match.replace(rule.regex, item)
				);
			},
			regex: getRegExp('(^|\\s)\\.' + index, 'g'),
			replacer: getClassReplacer(item),
			test: 'match'
		};
	}
);

module.exports = css;<|endoftext|>


<commit_before>"use strict";

var test = require("tape");

var EntityPool = require("./entity-pool");

test("create returns an entity id", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	t.equal(typeof id, "number");
});

test("create returns different ids each time", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id1 = pool.create();
	var id2 = pool.create();
	t.notEqual(id1, id2);
});

test("destroy deletes a whole entity", function(t) {
	t.plan(1);

	t.throws(function() {
		var pool = new EntityPool();
		var id = pool.create();
		pool.destroy(id);
		pool.get(id, "id");
	});
});

test("get with id returns the id", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	var result = pool.get(id, "id");
	t.equal(result, id);
});

test("set with component can be fetched with get", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	var name = pool.get(id, "name");

	t.equal(name, "jimmy");
});
test("set with component can be fetched with get", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	var name = pool.get(id, "name");

	t.equal(name, "jimmy");
});
test("set with same component twice isn't in search twice", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	pool.set(id, "name", "jimmy");
	var results = pool.find("name");

	t.equal(results.length, 1);
});
test("set with existing component to undefined removes from search", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	pool.set(id, "name", undefined);
	var results = pool.find("name");

	t.equal(results.length, 0);
});

test("remove with component makes it unable to be fetched", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	pool.remove(id, "name");
	var name = pool.get(id, "name");

	t.equal(name, undefined);
});

test("find with no matching components returns empty list", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	var results = pool.find("does-not-exist");

	t.deepEqual(results, []);
});

test("find with matching component returns list with entities", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	var id2 = pool.create();
	pool.set(id2, "name", "amy");
	var results = pool.find("name");

	t.deepEqual(results, [id, id2]);
});

test("find with deleted component returns empty list", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	pool.remove(id, "name");
	var results = pool.find("name");

	t.deepEqual(results, []);
});

test("registerSearch with two components and entities already added returns entities", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	pool.set(id, "age", 8);
	pool.registerSearch("peopleWithAge", ["name", "age"]);
	var results = pool.find("peopleWithAge");

	t.deepEqual(results, [id]);
});

test("registerSearch with two components and entities added after returns entities", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.registerSearch("peopleWithAge", ["name", "age"]);
	pool.set(id, "name", "jimmy");
	pool.set(id, "age", 8);
	var results = pool.find("peopleWithAge");

	t.deepEqual(results, [id]);
});

test("registerSearch twice with same name throws", function(t) {
	t.plan(1);

	t.throws(function() {
		var pool = new EntityPool();
		pool.registerSearch("search", ["search"]);
		pool.registerSearch("search", ["search"]);
	});
});

test("remove removes an entity from a complex search", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.registerSearch("peopleWithAge", ["name", "age"]);
	pool.set(id, "name", "jimmy");
	pool.set(id, "age", 8);
	pool.remove(id, "age");
	var results = pool.find("peopleWithAge");

	t.deepEqual(results, []);
});

test("load creates an entity", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	pool.load([{ id: 1, name: "jimmy" }]);
	var name = pool.get(1, "name");

	t.deepEqual(name, "jimmy");
});

test("load increments next id", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	pool.load([{ id: 1, name: "jimmy" }]);
	var id = pool.create();

	t.deepEqual(id, 2);
});

test("save returns entities", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	var output = pool.save();

	t.deepEqual(output, [{ id: id, name: "jimmy"}]);
});

test("onAddComponent with callback is called after set", function(t) {
	t.plan(6);

	var pool = new EntityPool();
	var id = pool.create();
	function callback(entity, component, value) {
		t.equal(entity, id);
		t.equal(component, "name");
		t.equal(value, "jimmy");
	}
	pool.onAddComponent("name", callback);
	pool.onAddComponent("name", callback);

	pool.set(id, "name", "jimmy");
});
test("onAddComponent with callback is not called on modifying existing component", function(t) {
	t.plan(3);

	var pool = new EntityPool();
	var id = pool.create();
	function callback(entity, component, value) {
		t.equal(entity, id);
		t.equal(component, "name");
		t.equal(value, "jimmy");
	}
	pool.onAddComponent("name", callback);

	pool.set(id, "name", "jimmy");
	pool.set(id, "name", "salazar");
});
test("onChangeComponent with callback is called after set", function(t) {
	t.plan(6);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	function callback(entity, component, value) {
		t.equal(entity, id);
		t.equal(component, "name");
		t.equal(value, "salazar");
	}
	pool.onChangeComponent("name", callback);
	pool.onChangeComponent("name", callback);

	pool.set(id, "name", "salazar");
});
<commit_msg>Add test<commit_after>"use strict";

var test = require("tape");

var EntityPool = require("./entity-pool");

test("create returns an entity id", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	t.equal(typeof id, "number");
});

test("create returns different ids each time", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id1 = pool.create();
	var id2 = pool.create();
	t.notEqual(id1, id2);
});

test("destroy deletes a whole entity", function(t) {
	t.plan(1);

	t.throws(function() {
		var pool = new EntityPool();
		var id = pool.create();
		pool.destroy(id);
		pool.get(id, "id");
	});
});

test("get with id returns the id", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	var result = pool.get(id, "id");
	t.equal(result, id);
});

test("set with component can be fetched with get", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	var name = pool.get(id, "name");

	t.equal(name, "jimmy");
});
test("set with component can be fetched with get", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	var name = pool.get(id, "name");

	t.equal(name, "jimmy");
});
test("set with same component twice isn't in search twice", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	pool.set(id, "name", "jimmy");
	var results = pool.find("name");

	t.equal(results.length, 1);
});
test("set with existing component to undefined removes from search", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	pool.set(id, "name", undefined);
	var results = pool.find("name");

	t.equal(results.length, 0);
});

test("remove with component makes it unable to be fetched", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	pool.remove(id, "name");
	var name = pool.get(id, "name");

	t.equal(name, undefined);
});

test("find with no matching components returns empty list", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	var results = pool.find("does-not-exist");

	t.deepEqual(results, []);
});

test("find with matching component returns list with entities", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	var id2 = pool.create();
	pool.set(id2, "name", "amy");
	var results = pool.find("name");

	t.deepEqual(results, [id, id2]);
});

test("find with deleted component returns empty list", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	pool.remove(id, "name");
	var results = pool.find("name");

	t.deepEqual(results, []);
});

test("registerSearch with two components and entities already added returns entities", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	pool.set(id, "age", 8);
	pool.registerSearch("peopleWithAge", ["name", "age"]);
	var results = pool.find("peopleWithAge");

	t.deepEqual(results, [id]);
});

test("registerSearch with two components and entities added after returns entities", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.registerSearch("peopleWithAge", ["name", "age"]);
	pool.set(id, "name", "jimmy");
	pool.set(id, "age", 8);
	var results = pool.find("peopleWithAge");

	t.deepEqual(results, [id]);
});

test("registerSearch twice with same name throws", function(t) {
	t.plan(1);

	t.throws(function() {
		var pool = new EntityPool();
		pool.registerSearch("search", ["search"]);
		pool.registerSearch("search", ["search"]);
	});
});

test("remove removes an entity from a complex search", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.registerSearch("peopleWithAge", ["name", "age"]);
	pool.set(id, "name", "jimmy");
	pool.set(id, "age", 8);
	pool.remove(id, "age");
	var results = pool.find("peopleWithAge");

	t.deepEqual(results, []);
});

test("load creates an entity", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	pool.load([{ id: 1, name: "jimmy" }]);
	var name = pool.get(1, "name");

	t.deepEqual(name, "jimmy");
});

test("load increments next id", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	pool.load([{ id: 1, name: "jimmy" }]);
	var id = pool.create();

	t.deepEqual(id, 2);
});

test("save returns entities", function(t) {
	t.plan(1);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	var output = pool.save();

	t.deepEqual(output, [{ id: id, name: "jimmy"}]);
});

test("onAddComponent with callback is called after set", function(t) {
	t.plan(6);

	var pool = new EntityPool();
	var id = pool.create();
	function callback(entity, component, value) {
		t.equal(entity, id);
		t.equal(component, "name");
		t.equal(value, "jimmy");
	}
	pool.onAddComponent("name", callback);
	pool.onAddComponent("name", callback);

	pool.set(id, "name", "jimmy");
});
test("onAddComponent with callback is not called on modifying existing component", function(t) {
	t.plan(3);

	var pool = new EntityPool();
	var id = pool.create();
	function callback(entity, component, value) {
		t.equal(entity, id);
		t.equal(component, "name");
		t.equal(value, "jimmy");
	}
	pool.onAddComponent("name", callback);

	pool.set(id, "name", "jimmy");
	pool.set(id, "name", "salazar");
});
test("onChangeComponent with callback is called on modifying existing component", function(t) {
	t.plan(6);

	var pool = new EntityPool();
	var id = pool.create();
	pool.set(id, "name", "jimmy");
	function callback(entity, component, value) {
		t.equal(entity, id);
		t.equal(component, "name");
		t.equal(value, "salazar");
	}
	pool.onChangeComponent("name", callback);
	pool.onChangeComponent("name", callback);

	pool.set(id, "name", "salazar");
});
test("onChangeComponent with callback isn't called on adding new component", function(t) {
	t.plan(3);

	var pool = new EntityPool();
	var id = pool.create();
	function callback(entity, component, value) {
		t.equal(entity, id);
		t.equal(component, "name");
		t.equal(value, "salazar");
	}
	pool.onChangeComponent("name", callback);

	pool.set(id, "name", "jimmy");
	pool.set(id, "name", "salazar");
});
<|endoftext|>


<commit_before>var fx = function(a) {
  return function(b) {
    return [a, b]
  }
}

var throttledEvent = function(name) {}

var rawEvent = function(name) {
  return fx(function(dispatch, action) {
    var listener = function(event) {
      dispatch(action, event)
    }
    addEventListener(name, listener)
    return function() {
      removeEventListener(name, listener)
    }
  })
}

// Mouse
export var onMouseEnter = rawEvent("mouseenter")
export var onMouseLeave = rawEvent("mouseleave")
export var onMouseMove = rawEvent("mousemove")
export var onMouseOut = rawEvent("mouseout")
export var onMouseOver = rawEvent("mouseover")
export var onMouseDown = rawEvent("mousedown")
export var onMouseUp = rawEvent("mouseup")

// Touch
export var onTouchStart = rawEvent("touchstart")
export var onTouchMove = rawEvent("touchmove")
export var onTouchEnd = rawEvent("touchend")

// Keyboard
export var onKeyDown = rawEvent("keydown")
export var onKeyUp = rawEvent("keyup")

// Window
export var onFocus = rawEvent("focus")
export var onBlur = rawEvent("blur")

// AnimationFrame
export var onAnimationFrame = fx(function(dispatch, action) {
  var id = requestAnimationFrame(function frame(timestamp) {
    dispatch(action, timestamp)
    id = requestAnimationFrame(frame)
  })
  return function() {
    cancelAnimationFrame(id)
  }
})

// Other
export var eventKey = function(event) {
  return event.key
}
export var targetChecked = function(event) {
  return event.target.checked
}
export var targetValue = function(event) {
  return event.target.value
}

export var eventOptions = fx(function(dispatch, props) {
  if (props.preventDefault) props.event.preventDefault()
  if (props.stopPropagation) props.event.stopPropagation()
  if (props.action != undefined) dispatch(props.action, props.event)
})

export var preventDefault = function(action) {
  return function(state, event) {
    return [
      state,
      eventOptions({ preventDefault: true, action: action, event: event })
    ]
  }
}
export var stopPropagation = function(action) {
  return function(state, event) {
    return [
      state,
      eventOptions({ stopPropagation: true, action: action, event: event })
    ]
  }
}
<commit_msg>onAnimationFrame sub doesn't stop; fix #868<commit_after>var fx = function(a) {
  return function(b) {
    return [a, b]
  }
}

var throttledEvent = function(name) {}

var rawEvent = function(name) {
  return fx(function(dispatch, action) {
    var listener = function(event) {
      dispatch(action, event)
    }
    addEventListener(name, listener)
    return function() {
      removeEventListener(name, listener)
    }
  })
}

// Mouse
export var onMouseEnter = rawEvent("mouseenter")
export var onMouseLeave = rawEvent("mouseleave")
export var onMouseMove = rawEvent("mousemove")
export var onMouseOut = rawEvent("mouseout")
export var onMouseOver = rawEvent("mouseover")
export var onMouseDown = rawEvent("mousedown")
export var onMouseUp = rawEvent("mouseup")

// Touch
export var onTouchStart = rawEvent("touchstart")
export var onTouchMove = rawEvent("touchmove")
export var onTouchEnd = rawEvent("touchend")

// Keyboard
export var onKeyDown = rawEvent("keydown")
export var onKeyUp = rawEvent("keyup")

// Window
export var onFocus = rawEvent("focus")
export var onBlur = rawEvent("blur")

// AnimationFrame
export var onAnimationFrame = fx(function(dispatch, action) {
  var id = requestAnimationFrame(function frame(timestamp) {
    id = requestAnimationFrame(frame)
    dispatch(action, timestamp)
  })
  return function() {
    cancelAnimationFrame(id)
  }
})

// Other
export var eventKey = function(event) {
  return event.key
}
export var targetChecked = function(event) {
  return event.target.checked
}
export var targetValue = function(event) {
  return event.target.value
}

export var eventOptions = fx(function(dispatch, props) {
  if (props.preventDefault) props.event.preventDefault()
  if (props.stopPropagation) props.event.stopPropagation()
  if (props.action != undefined) dispatch(props.action, props.event)
})

export var preventDefault = function(action) {
  return function(state, event) {
    return [
      state,
      eventOptions({ preventDefault: true, action: action, event: event })
    ]
  }
}
export var stopPropagation = function(action) {
  return function(state, event) {
    return [
      state,
      eventOptions({ stopPropagation: true, action: action, event: event })
    ]
  }
}
<|endoftext|>


<commit_before>var doT = require('dot');
var lib = require('./lib.js');
var path = require('path');
var format = lib.format;

module.exports = function (grunt) {
  'use strict';

  var log = grunt.log,
    verbose = grunt.verbose,
    gruntFile = grunt.file;

  return {

    createMinFromRegular: function (dest) {
      return this.insertRev(dest, 'min');
    },

    insertRev : function (dest, rev, min) {
      var rgex = /(.+)\.(\w+)$/gi;
      return dest.replace(rgex, lib.format('$1{0}{1}.$2', rev ? '.' + rev : '', min ? '.min' : ''));
    },

    inferType: function (dest) {
      var rgex = /(.+)\.(\w+)$/gi;
      return dest.replace(rgex, '$2');
    },

    createGroupTask : function (cfg, group) {
      var me = this;
      var groupName = group.name;
      var destination = group.dest;
      if (lib.isNull(destination)) {
        var msg = "dest field on group " + groupName + " is required";
        log.error(msg);
        throw new Error(msg);
      }

      destination = me.insertRev(destination, group.options.assetsVersion);

      var type = group.type || me.inferType(destination);

      var minifiedDestination = group.minDest || me.createMinFromRegular(destination);

      if (type === 'js') {
        var preprocess = cfg.preprocess = (cfg.preprocess || {});
        var uglify = cfg.uglify = (cfg.uglify || {});

        var preprocessTask = preprocess[groupName] = {
          src: group.src,
          dest: destination,
          options : group.options
        };

        grunt.config.set(['preprocess', groupName], preprocessTask);

        var ugGroup = uglify[groupName] = {
          files: {},
          options: group.options
        };

        ugGroup.files[minifiedDestination] = [destination];

        grunt.config.set(['uglify', groupName], ugGroup);

      }
      if (type === 'css') {
        var cLess = cfg.cLess = (cfg.cLess || {});
        var csso = cfg.csso = (cfg.csso || {});
        var autoprefixer = cfg.autoprefixer = (cfg.autoprefixer || {});

        var cLessTask = cLess[groupName] = {
          src: group.src,
          dest: destination,
          options: lib.extend(group.options, {
            yuicompress: false,
            banner : ''
          })
        };

        grunt.config.set(['cLess', groupName], cLessTask);

        var autoPrefixGroup = autoprefixer[groupName] = {
          src : destination,
          dest: destination,
          options : lib.extend(group.options, {
            browsers : ['last 2 version']
          })
        };

        grunt.config.set(['autoprefixer', groupName], autoPrefixGroup);

        var minGroup = csso[groupName] = {
          files: {},
          options : group.options
        };

        minGroup.files[minifiedDestination] = [destination];

        grunt.config.set(['csso', groupName], minGroup);

      }
    },

    compileTemplates : function (src, dest, options) {
      var opts = {
        ext : '.doT',
        template : '(function (window) { \n  var ns = function (ns, root) {\n    if (!ns) {\n      return null;\n    }\n    var nsParts = ns.split(".");\n    var innerRoot = root || window;\n    for (var i = 0, len = nsParts.length; i < len; i++) {\n      if (typeof innerRoot[nsParts[i]] === "undefined") {\n        innerRoot[nsParts[i]] = {};\n      }\n      innerRoot = innerRoot[nsParts[i]];\n    }\n    return innerRoot;\n  };\n  \n  //[CONTENT]\n  \n}(window));',
        templateFile : null,
        objNamespace : '__dotTemplates',
        normalizeName : function (filename) {
          var t = /-(.)/g;
          return filename.replace(t, function (a, b) {
            return b.toUpperCase();
          });
        }
      };

      lib.extend(opts, options);

      if (opts.templateFile) {
        opts.template = gruntFile.read(opts.templateFile);
      }

      var sourceFiles = gruntFile.expand(src);

      var tplContent = lib.format('var obj = ns("{0}");\n', opts.objNamespace);

      sourceFiles.forEach(function (ele, i) {
        var fileName = path.basename(ele, opts.ext);
        log.writeln('compiling template : ' + fileName);

        var content = gruntFile.read(ele);

        try {
          var templateFn = doT.compile(content);
          var name = opts.normalizeName(fileName);

          tplContent += lib.format('obj["{0}"] = {1};\n', name, templateFn.toString().replace('function anonymous', 'function '));

          verbose.writeln('template ' + fileName + ': OK');
        } catch (ex) {
          //log.error('template ' + ele + ': failed to compile');
          //log.error('error: ' + ex.message);
          var message = lib.format('{0} failed : {1}. {2}', templateFn, ele, ex.message);
          throw new Error(message);
        }
      });

      gruntFile.write(dest, opts.template.replace('//[[CONTENT]]', tplContent));

    },

    registerTasks : function (gruntTasks) {
      // Register tasks
      Object.keys(gruntTasks).forEach(function (tName) {

        var task = gruntTasks[tName],
          multiTask = task.multiTask,
          description = lib.trim(task.description) || 'Task with name ' + tName;

        if (typeof multiTask === 'function') {
          grunt.registerMultiTask(tName, description, multiTask);
        }
        else {
          grunt.registerTask(tName, task);
        }
      });
    },

    beautifier : {
      onVerificationFailed: function (content, args) {
        var file = args.file,
          log = grunt.log;

        log.warn('\n\n\n\n*************************************************');
        log.warn(' A file does not conform with the coding styles! ');
        log.warn('*************************************************');
        log.warn('The file ' + file.yellow + ' is not beautified. run: \n\n   ' +
          'grunt jsbeautifier --beautify'.yellow +
          ' \n\nin order to ensure the file is properly formatted then try to build it again.\n\n' +
          'Do not forget to commit the formatted file\n\n'.cyan);
        log.warn('IMPORTANT: '.yellow + 'grunt-js-beautifier ' + 'REWRITE YOUR CODE!\n\n'.cyan +
          'In most situations this is fairly safe... but it is always good to have a backup if things get messed up');
      },
      onBeautified: function (content, args) {
        // TODO: check when doing this replacement is safer
        // example inside strings this could be potentially dangerous
        content = content.replace(/!!\s/g, function () {
          grunt.log.writeln('replacing "!! " with "!!');
          return '!!';
        });

        return content.replace(/\(\s!!/g, function () {
          grunt.log.writeln('replacing "( !!" with "!!');
          return '(!!';
        });
      }
    },

    validateTemplates : function (templates) {

      var templateFiles = gruntFile.expand(templates);

      var passed = 0;
      var failed = 0;

      templateFiles.forEach(function (ele, i) {
        var fileName = path.basename(ele);
        log.writeln('processing template : ' + fileName);
        var content = gruntFile.read(ele);

        try {
          var template = doT.compile(content);
          verbose.writeln('template ' + ele + ': OK');
          passed++;
        } catch (ex) {
          failed++;
          log.error('template ' + ele + ': failed to compile');
          log.error('error: ' + ex.message);
        }
      });
      if (failed > 0) {
        log.error(format('Templates validating result passed: {0}, failed: {1}, total : {2}', passed, failed, passed + failed));
        return false;
      }
      log.writeln('=========================================');
      log.writeln(format('Templates processed : {0}', passed));
    }
  };
};<commit_msg>Adding better logging on the tokens replaced by the jsbeautifier onBeautified callback<commit_after>var doT = require('dot');
var lib = require('./lib.js');
var path = require('path');
var format = lib.format;

module.exports = function (grunt) {
  'use strict';

  var log = grunt.log,
    verbose = grunt.verbose,
    gruntFile = grunt.file,
    gruntTaskUtils;

  gruntTaskUtils = {

    createMinFromRegular: function (dest) {
      return this.insertRev(dest, 'min');
    },

    insertRev : function (dest, rev, min) {
      var rgex = /(.+)\.(\w+)$/gi;
      return dest.replace(rgex, lib.format('$1{0}{1}.$2', rev ? '.' + rev : '', min ? '.min' : ''));
    },

    inferType: function (dest) {
      var rgex = /(.+)\.(\w+)$/gi;
      return dest.replace(rgex, '$2');
    },

    createGroupTask : function (cfg, group) {
      var me = this;
      var groupName = group.name;
      var destination = group.dest;
      if (lib.isNull(destination)) {
        var msg = "dest field on group " + groupName + " is required";
        log.error(msg);
        throw new Error(msg);
      }

      destination = me.insertRev(destination, group.options.assetsVersion);

      var type = group.type || me.inferType(destination);

      var minifiedDestination = group.minDest || me.createMinFromRegular(destination);

      if (type === 'js') {
        var preprocess = cfg.preprocess = (cfg.preprocess || {});
        var uglify = cfg.uglify = (cfg.uglify || {});

        var preprocessTask = preprocess[groupName] = {
          src: group.src,
          dest: destination,
          options : group.options
        };

        grunt.config.set(['preprocess', groupName], preprocessTask);

        var ugGroup = uglify[groupName] = {
          files: {},
          options: group.options
        };

        ugGroup.files[minifiedDestination] = [destination];

        grunt.config.set(['uglify', groupName], ugGroup);

      }
      if (type === 'css') {
        var cLess = cfg.cLess = (cfg.cLess || {});
        var csso = cfg.csso = (cfg.csso || {});
        var autoprefixer = cfg.autoprefixer = (cfg.autoprefixer || {});

        var cLessTask = cLess[groupName] = {
          src: group.src,
          dest: destination,
          options: lib.extend(group.options, {
            yuicompress: false,
            banner : ''
          })
        };

        grunt.config.set(['cLess', groupName], cLessTask);

        var autoPrefixGroup = autoprefixer[groupName] = {
          src : destination,
          dest: destination,
          options : lib.extend(group.options, {
            browsers : ['last 2 version']
          })
        };

        grunt.config.set(['autoprefixer', groupName], autoPrefixGroup);

        var minGroup = csso[groupName] = {
          files: {},
          options : group.options
        };

        minGroup.files[minifiedDestination] = [destination];

        grunt.config.set(['csso', groupName], minGroup);

      }
    },

    compileTemplates : function (src, dest, options) {
      var opts = {
        ext : '.doT',
        template : '(function (window) { \n  var ns = function (ns, root) {\n    if (!ns) {\n      return null;\n    }\n    var nsParts = ns.split(".");\n    var innerRoot = root || window;\n    for (var i = 0, len = nsParts.length; i < len; i++) {\n      if (typeof innerRoot[nsParts[i]] === "undefined") {\n        innerRoot[nsParts[i]] = {};\n      }\n      innerRoot = innerRoot[nsParts[i]];\n    }\n    return innerRoot;\n  };\n  \n  //[CONTENT]\n  \n}(window));',
        templateFile : null,
        objNamespace : '__dotTemplates',
        normalizeName : function (filename) {
          var t = /-(.)/g;
          return filename.replace(t, function (a, b) {
            return b.toUpperCase();
          });
        }
      };

      lib.extend(opts, options);

      if (opts.templateFile) {
        opts.template = gruntFile.read(opts.templateFile);
      }

      var sourceFiles = gruntFile.expand(src);

      var tplContent = lib.format('var obj = ns("{0}");\n', opts.objNamespace);

      sourceFiles.forEach(function (ele, i) {
        var fileName = path.basename(ele, opts.ext);
        log.writeln('compiling template : ' + fileName);

        var content = gruntFile.read(ele);

        try {
          var templateFn = doT.compile(content);
          var name = opts.normalizeName(fileName);

          tplContent += lib.format('obj["{0}"] = {1};\n', name, templateFn.toString().replace('function anonymous', 'function '));

          verbose.writeln('template ' + fileName + ': OK');
        } catch (ex) {
          //log.error('template ' + ele + ': failed to compile');
          //log.error('error: ' + ex.message);
          var message = lib.format('{0} failed : {1}. {2}', templateFn, ele, ex.message);
          throw new Error(message);
        }
      });

      gruntFile.write(dest, opts.template.replace('//[[CONTENT]]', tplContent));

    },

    registerTasks : function (gruntTasks) {
      // Register tasks
      Object.keys(gruntTasks).forEach(function (tName) {

        var task = gruntTasks[tName],
          multiTask = task.multiTask,
          description = lib.trim(task.description) || 'Task with name ' + tName;

        if (typeof multiTask === 'function') {
          grunt.registerMultiTask(tName, description, multiTask);
        }
        else {
          grunt.registerTask(tName, task);
        }
      });
    },

    beautifier : {
      replacements : [{
        replace : /!!\s/g,
        using : '!!'
      }, {
        replace : /\(\s!!/g,
        using : '(!!'
      }, {
        replace : /\(\sfunction/g,
        using : '(function'
      }, {
        replace : /\)\s\)/g,
        using : '))'
      }],

      addReplacements : function (replacements) {
        //gruntTasksUtils.beautifier.replacements = gruntTasksUtils.beautifier.replacements || [];
        gruntTasksUtils.beautifier.replacements = gruntTasksUtils.beautifier.replacements.concat(replacements);
      },

      onVerificationFailed: function (content, args) {
        var file = args.file,
          log = grunt.log;

        log.warn('\n\n\n\n*************************************************');
        log.warn(' A file does not conform with the coding styles! ');
        log.warn('*************************************************');
        log.warn('The file ' + file.yellow + ' is not beautified. run: \n\n   ' +
          'grunt jsbeautifier --beautify'.yellow +
          ' \n\nin order to ensure the file is properly formatted then try to build it again.\n\n' +
          'Do not forget to commit the formatted file\n\n'.cyan);
        log.warn('IMPORTANT: '.yellow + 'grunt-js-beautifier ' + 'REWRITE YOUR CODE!\n\n'.cyan +
          'In most situations this is fairly safe... but it is always good to have a backup if things get messed up');
      },
      onBeautified: function (content, args) {
        // TODO: check when doing this replacement is safer
        // example inside strings this could be potentially dangerous
        var replacements = gruntTasksUtils.beautifier.replacements || [];

        var counts = {};
        replacements.forEach(function (entry) {
          var token = entry.using,
            regex = entry.replace;

          counts[token] = {
            count: 0,
            regex: regex
          };
          content = content.replace(regex, function () {
            counts[token].count++;
            return token;
          });
        });

        Object.keys(counts).forEach(function (key) {
          //grunt.log.writeln('Replacing ' + counts[key].regex + ' with: ' + key  + ' ' + )
          var entry = counts[key],
            count = entry.count,
            regex = entry.regex;
          if (count > 0) {
            var msg = lib.format('Replacing {0} with {1}, {2} time(s)', regex, key, count);
          }
        });

        return content;
      }
    },

    validateTemplates : function (templates) {

      var templateFiles = gruntFile.expand(templates);

      var passed = 0;
      var failed = 0;

      templateFiles.forEach(function (ele, i) {
        var fileName = path.basename(ele);
        log.writeln('processing template : ' + fileName);
        var content = gruntFile.read(ele);

        try {
          var template = doT.compile(content);
          verbose.writeln('template ' + ele + ': OK');
          passed++;
        } catch (ex) {
          failed++;
          log.error('template ' + ele + ': failed to compile');
          log.error('error: ' + ex.message);
        }
      });
      if (failed > 0) {
        log.error(format('Templates validating result passed: {0}, failed: {1}, total : {2}', passed, failed, passed + failed));
        return false;
      }
      log.writeln('=========================================');
      log.writeln(format('Templates processed : {0}', passed));
    }
  };

  return gruntTaskUtils;
};<|endoftext|>


<commit_before>const localconf = require('../localconf');
const spawn = require('child_process').spawn;
const exec = require('child_process').exec;
const kill = require('tree-kill');
const gpio = require('rpi-gpio');

const WebSocket = require('ws');
const argv = require('yargs')
	.option('server', { describe: 'addres of remote server' })
	.option('secret', { describe: 'secreed needed for streaming' })
	.option('port', { describe: 'port' })
	.help()
	.argv;

if (!argv.server) {
	argv.server = localconf.server;
	argv.port = localconf.port;
}

// COMMANDS /////////////////////////////////////////////////////////////////////////

var keyToGpio = {
	turnl: { gpio: 11, mode: 'press', val: null, tid: false },
	turnr: { gpio: 12, mode: 'press', val: null, tid: false },
	forth: { gpio: 13, mode: 'hold',  val: null, tid: false },
	back:  { gpio: 15, mode: 'press', val: null, tid: false, reset: 'forth' },
	caml:  { gpio: 16, mode: 'press', val: null, tid: false },
	camr:  { gpio: 18, mode: 'press', val: null, tid: false },
	camu:  { gpio: 22, mode: 'press', val: null, tid: false },
	camd:  { gpio: 7,  mode: 'press', val: null, tid: false },
};

/// INITIALIZE GPIO ////////////////////////////////////////////////////////////

for (var gpioh in keyToGpio) {
	gpio.setup(gpioh.gpio, gpio.DIR_OUT);
}

/// command handlers ///////////////////////////////////////////////////////////

var handlers = {
	handle_stop_stream: function(socket) {
		streamer.stop();
		console.log('# received request to stop stream');
		socket.send('$ executing stream stop');
	},
	handle_restart_stream: function(socket) {
		streamer.restart();
		console.log('# received request to restart stream');
		socket.send('$ executing stream start');
	},
	handle_request_ship_status: function(socket) {
		console.log('# generating ship status');
		socket.send('$ ship is probably ok');
	},
	handle_unload_video_driver: function(socket) {
		exec(localconf['command:unloadDriver'], function(err, stdout, stderr) {
			if (err) {
				console.log('# unloading driver failed');
				socket.send('$ unloading driver failed');
			}
			else {
				console.log('# unloading driver done');
				socket.send('$ unloading driver done');
			}
		});
	},
	handle_load_video_driver: function(socket) {
		exec(localconf['command:loadDriver'], function(err, stdout, stderr) {
			if (err) {
				console.log('# loading driver failed');
				socket.send('$ loading driver failed');
			}
			else {
				console.log('# loading driver done');
				socket.send('$ loading driver done');
			}
		});
	}
};

/// prepeare working commands //////////////////////////////////////////////////

/* function(error, stdout, stderr) {
	console.log('stdout: ' + stdout);
	console.log('stderr: ' + stderr);
	if (error !== null) {
		console.log('exec error: ' + error);
	} */

function createExternalCommand(localconfCommand, logAlias, socket) {
	var tmpl = localconf[ localconfCommand ];
	tmpl = tmpl.replace(/%SERVER%/, cutProtocolFromUrl(localconf.server));
	tmpl = tmpl.replace(/%PORT%/, localconf.port);
	tmpl = tmpl.replace(/%SECRET%/, localconf.secret);

	var child = null;

	var fStart = function() {
		var args = tmpl.split(/\s+/);
		var cmd = args.shift();

		child = spawn(cmd, args);

		child.stdout.on('data', function(data) {
			if (socket.readyState === 1) {
				socket.send(`$ ${logAlias} stdout: ${data}`);
			}

			console.log(`${logAlias} stdout: ${data}`);
		});
		child.stderr.on('data', function(data) {
			if (data.indexOf('frame=') >= 0 && data.indexOf('size=') >= 0 && data.indexOf('time=') >= 0 && data.indexOf('bitrate=') >= 0) { // TODO separate
				return;
			}

			if (socket.readyState === 1) {
				socket.send(`$ ${logAlias} stderr: ${data}`);
			}

			console.log(`${logAlias} stderr: ${data}`);
		});
		child.on('close', function(code) {
			socket.readyState === 1 && socket.send(`$ ${logAlias} child process exited with code ${code}`);
			console.log(`${logAlias} child process exited with code ${code}`);
		});
		child.on('error', (err) => {
			socket.readyState === 1 && socket.send(`$ ${logAlias} failed to start child process.`);
			console.log(`${logAlias} failed to start child process.`);
		});
	};

	var fRestart = function() {
		if (child) {
			fStop();
		}

		setTimeout(
			function() {
				fStart();
			},
			1000
		);
	};

	var fStop = function() {
		if (child) {
			kill(child.pid);
		}
	}

	return {
		start: fStart,
		restart: fRestart,
		stop: fStop
	}
}

function createWebServiceConnection(callback) {
	console.log(`# conecting to: ${argv.server}:${argv.port}`);

	return new WebSocket(argv.server + ':' + argv.port);

	/* let ws;
	goto: do {
		try {
			ws = new WebSocket(argv.server + ':' + argv.port);
		}
		catch(ex) {
			console.error('#### ', ex);
			continue goto;
		}
	} while(false);

	console.log('!!!');

	return ws; */
}

var ws = createWebServiceConnection();
ws.on('open', function open() {
	ws.send('EHLOSHIP');
	console.info('# Send EHLOSHIP. Waiting for SHIPEHLO ...');
});

ws.on('message', function incoming(data, flags) {
	if (typeof data !== 'string') {
		ws.send('$ received non command frame');
		console.warn('# received non command frame');
		return;
	}

	if (data === 'SHIPEHLO') {
		console.info('# ... SHIPEHLO Received. Connected. Waiting for commands');
	}
	else if (data.substring(0, 2) === '! ') {
		var command = data.substr(2).replace(/-/g, '_')

		if (handlers['handle_' + command]) {
			try {
				handlers['handle_' + command](ws);
			}
			catch(ex) {
				console.log(ex);
				ws.send(`$ error while executing ${command}`);
			}

			return;
		}

		var gpioh = keyToGpio[ command ];
		if (gpioh) {
			var gpio = gpioh.gpio;
			var gpmode = gpioh.mode;
			var gpval = gpioh.val;
			var gptid = gpioh.tid;
			var gpreset = gpioh.reset;

			if (gptid) {
				clearTimeout(gptid);
				gpioh.tid = null;
			}

			if (gpreset && keyToGpio[ gpreset ].value) {
				keyToGpio[ gpreset ].value = false;
				ws.send(`$ canceled ${gpreset}`);
				console.log(`# canceled ${gpreset}`);
				gpioSet(gpio, 0, ws);
			}
			else {
				if (gpmode === 'press') {
					if (gpioh.value !== true) {
						ws.send(`$ activate ${command}`);
						console.log(`# activate ${command}`);
					}

					gpioh.value = true;
					gpioSet(gpio, 1, ws);

					gpioh.tid = setTimeout(
						function() {
							ws.send(`$ de-activate ${command}`);
							console.log(`# de-activate ${command}`);
							gpioh.tid = null;
							gpioh.value = false;
							gpioSet(gpio, 0, ws);
						},
						200 // TODO configure. Should be a little bit longer than key read interval on index.html
					);
				}
				else if (gpmode === 'hold') {
					if (gpioh.value !== true) {
						gpioh.value = true;
						gpioSet(gpio, 1, ws);
						ws.send(`$ activate ${command}`);
						console.log(`# activate ${command}`);
					}
				}
				else {
					ws.send(`$ unknown command ${command}`);
					console.log(`# unknown command ${command}`);
				}
			}

			return;
		}

		ws.send(`$ Unknown command: ${data} / ${command}`);
		console.warn('# Unknown command:', data);
	}
});

const dialer = createExternalCommand('command:dialin', 'ii', ws);
dialer.start();

const streamer = createExternalCommand('command:stream', 'vv', ws);

///

var pinger = setInterval(function() {
	if (ws.readyState === 1) {
		ws.send('$ ping');
	}
	else {
		console.warn(`# Unable to send ping. Socket readyState: ${ws.readyState}`);
		// clearInterval(pinger);
		// process.exit();
		ws = createWebServiceConnection();
	}
}, 1000 * 7);

/// UTILITIES //////////////////////////////////////////////////////////////////

function cutProtocolFromUrl(url) {
	return url.replace(/^\w{2,5}:\/\//, '');
}

function gpioSet(pin, value, socket) {
	gpio.write(pin, value, function(err) {
		if (err) {
			if (socket) {
				socket.send(`# error while setting gpio ${pin}`);
			}

			console.log(`# error while setting gpio ${pin}`, err);
		}
	});
}
<commit_msg>init hotfix<commit_after>const localconf = require('../localconf');
const spawn = require('child_process').spawn;
const exec = require('child_process').exec;
const kill = require('tree-kill');
const gpio = require('rpi-gpio');

const WebSocket = require('ws');
const argv = require('yargs')
	.option('server', { describe: 'addres of remote server' })
	.option('secret', { describe: 'secreed needed for streaming' })
	.option('port', { describe: 'port' })
	.help()
	.argv;

if (!argv.server) {
	argv.server = localconf.server;
	argv.port = localconf.port;
}

// COMMANDS /////////////////////////////////////////////////////////////////////////

var keyToGpio = {
	turnl: { gpio: 11, mode: 'press', val: null, tid: false },
	turnr: { gpio: 12, mode: 'press', val: null, tid: false },
	forth: { gpio: 13, mode: 'hold',  val: null, tid: false },
	back:  { gpio: 15, mode: 'press', val: null, tid: false, reset: 'forth' },
	caml:  { gpio: 16, mode: 'press', val: null, tid: false },
	camr:  { gpio: 18, mode: 'press', val: null, tid: false },
	camu:  { gpio: 22, mode: 'press', val: null, tid: false },
	camd:  { gpio: 7,  mode: 'press', val: null, tid: false },
};

/// INITIALIZE GPIO ////////////////////////////////////////////////////////////

for (var gpio in keyToGpio) {
	var gpioh = keyToGpio[ gpio ];

	gpio.setup(gpioh.gpio, gpio.DIR_OUT);
}

/// command handlers ///////////////////////////////////////////////////////////

var handlers = {
	handle_stop_stream: function(socket) {
		streamer.stop();
		console.log('# received request to stop stream');
		socket.send('$ executing stream stop');
	},
	handle_restart_stream: function(socket) {
		streamer.restart();
		console.log('# received request to restart stream');
		socket.send('$ executing stream start');
	},
	handle_request_ship_status: function(socket) {
		console.log('# generating ship status');
		socket.send('$ ship is probably ok');
	},
	handle_unload_video_driver: function(socket) {
		exec(localconf['command:unloadDriver'], function(err, stdout, stderr) {
			if (err) {
				console.log('# unloading driver failed');
				socket.send('$ unloading driver failed');
			}
			else {
				console.log('# unloading driver done');
				socket.send('$ unloading driver done');
			}
		});
	},
	handle_load_video_driver: function(socket) {
		exec(localconf['command:loadDriver'], function(err, stdout, stderr) {
			if (err) {
				console.log('# loading driver failed');
				socket.send('$ loading driver failed');
			}
			else {
				console.log('# loading driver done');
				socket.send('$ loading driver done');
			}
		});
	}
};

/// prepeare working commands //////////////////////////////////////////////////

/* function(error, stdout, stderr) {
	console.log('stdout: ' + stdout);
	console.log('stderr: ' + stderr);
	if (error !== null) {
		console.log('exec error: ' + error);
	} */

function createExternalCommand(localconfCommand, logAlias, socket) {
	var tmpl = localconf[ localconfCommand ];
	tmpl = tmpl.replace(/%SERVER%/, cutProtocolFromUrl(localconf.server));
	tmpl = tmpl.replace(/%PORT%/, localconf.port);
	tmpl = tmpl.replace(/%SECRET%/, localconf.secret);

	var child = null;

	var fStart = function() {
		var args = tmpl.split(/\s+/);
		var cmd = args.shift();

		child = spawn(cmd, args);

		child.stdout.on('data', function(data) {
			if (socket.readyState === 1) {
				socket.send(`$ ${logAlias} stdout: ${data}`);
			}

			console.log(`${logAlias} stdout: ${data}`);
		});
		child.stderr.on('data', function(data) {
			if (data.indexOf('frame=') >= 0 && data.indexOf('size=') >= 0 && data.indexOf('time=') >= 0 && data.indexOf('bitrate=') >= 0) { // TODO separate
				return;
			}

			if (socket.readyState === 1) {
				socket.send(`$ ${logAlias} stderr: ${data}`);
			}

			console.log(`${logAlias} stderr: ${data}`);
		});
		child.on('close', function(code) {
			socket.readyState === 1 && socket.send(`$ ${logAlias} child process exited with code ${code}`);
			console.log(`${logAlias} child process exited with code ${code}`);
		});
		child.on('error', (err) => {
			socket.readyState === 1 && socket.send(`$ ${logAlias} failed to start child process.`);
			console.log(`${logAlias} failed to start child process.`);
		});
	};

	var fRestart = function() {
		if (child) {
			fStop();
		}

		setTimeout(
			function() {
				fStart();
			},
			1000
		);
	};

	var fStop = function() {
		if (child) {
			kill(child.pid);
		}
	}

	return {
		start: fStart,
		restart: fRestart,
		stop: fStop
	}
}

function createWebServiceConnection(callback) {
	console.log(`# conecting to: ${argv.server}:${argv.port}`);

	return new WebSocket(argv.server + ':' + argv.port);

	/* let ws;
	goto: do {
		try {
			ws = new WebSocket(argv.server + ':' + argv.port);
		}
		catch(ex) {
			console.error('#### ', ex);
			continue goto;
		}
	} while(false);

	console.log('!!!');

	return ws; */
}

var ws = createWebServiceConnection();
ws.on('open', function open() {
	ws.send('EHLOSHIP');
	console.info('# Send EHLOSHIP. Waiting for SHIPEHLO ...');
});

ws.on('message', function incoming(data, flags) {
	if (typeof data !== 'string') {
		ws.send('$ received non command frame');
		console.warn('# received non command frame');
		return;
	}

	if (data === 'SHIPEHLO') {
		console.info('# ... SHIPEHLO Received. Connected. Waiting for commands');
	}
	else if (data.substring(0, 2) === '! ') {
		var command = data.substr(2).replace(/-/g, '_')

		if (handlers['handle_' + command]) {
			try {
				handlers['handle_' + command](ws);
			}
			catch(ex) {
				console.log(ex);
				ws.send(`$ error while executing ${command}`);
			}

			return;
		}

		var gpioh = keyToGpio[ command ];
		if (gpioh) {
			var gpio = gpioh.gpio;
			var gpmode = gpioh.mode;
			var gpval = gpioh.val;
			var gptid = gpioh.tid;
			var gpreset = gpioh.reset;

			if (gptid) {
				clearTimeout(gptid);
				gpioh.tid = null;
			}

			if (gpreset && keyToGpio[ gpreset ].value) {
				keyToGpio[ gpreset ].value = false;
				ws.send(`$ canceled ${gpreset}`);
				console.log(`# canceled ${gpreset}`);
				gpioSet(gpio, 0, ws);
			}
			else {
				if (gpmode === 'press') {
					if (gpioh.value !== true) {
						ws.send(`$ activate ${command}`);
						console.log(`# activate ${command}`);
					}

					gpioh.value = true;
					gpioSet(gpio, 1, ws);

					gpioh.tid = setTimeout(
						function() {
							ws.send(`$ de-activate ${command}`);
							console.log(`# de-activate ${command}`);
							gpioh.tid = null;
							gpioh.value = false;
							gpioSet(gpio, 0, ws);
						},
						200 // TODO configure. Should be a little bit longer than key read interval on index.html
					);
				}
				else if (gpmode === 'hold') {
					if (gpioh.value !== true) {
						gpioh.value = true;
						gpioSet(gpio, 1, ws);
						ws.send(`$ activate ${command}`);
						console.log(`# activate ${command}`);
					}
				}
				else {
					ws.send(`$ unknown command ${command}`);
					console.log(`# unknown command ${command}`);
				}
			}

			return;
		}

		ws.send(`$ Unknown command: ${data} / ${command}`);
		console.warn('# Unknown command:', data);
	}
});

const dialer = createExternalCommand('command:dialin', 'ii', ws);
dialer.start();

const streamer = createExternalCommand('command:stream', 'vv', ws);

///

var pinger = setInterval(function() {
	if (ws.readyState === 1) {
		ws.send('$ ping');
	}
	else {
		console.warn(`# Unable to send ping. Socket readyState: ${ws.readyState}`);
		// clearInterval(pinger);
		// process.exit();
		ws = createWebServiceConnection();
	}
}, 1000 * 7);

/// UTILITIES //////////////////////////////////////////////////////////////////

function cutProtocolFromUrl(url) {
	return url.replace(/^\w{2,5}:\/\//, '');
}

function gpioSet(pin, value, socket) {
	gpio.write(pin, value, function(err) {
		if (err) {
			if (socket) {
				socket.send(`# error while setting gpio ${pin}`);
			}

			console.log(`# error while setting gpio ${pin}`, err);
		}
	});
}
<|endoftext|>