<commit_before>var express = require("express");
var methods = require("methods");
var assert = require('assert');
var querystring = require("querystring");
var EventEmitter = require("events").EventEmitter;
var util = require("util");
var deepEqual = require('deep-equal');

module.exports = function(port) {
  var app = express();

  app.on("error", function(err) {
    throw err;
  });

  app.use(express.json());
  app.use(express.urlencoded());

  app.use(function(req, res, next){
    if (req.is('text/*')) {
      req.text = '';
      req.setEncoding('utf8');
      req.on('data', function(chunk){ req.text += chunk });
      req.on('end', next);
    } else {
      next();
    }
  });

  var server;
  if(port) {
    server = app.listen(port);
  } else {
    server = app.listen();
  }

  methods.forEach(function(method) {
    server[method] = function(path) {
      return new Assertion(app, method, path);
    }
  });

  server.clean = function() {
    app._router.map = {};
  }

  return server;
}

function Assertion(app, method, path) {
  var self = this;
  this.app = app;
  this.method = method;
  this.path = path;
  this.headers = {};
  this.removeWhenMet = true;
  this.raiseUnmatchedRequests();

  this.parseExpectedRequestBody = function() {
    if(!self.headers["content-type"]) {
      if(typeof self.data == "string") {
        return self.requestBody = querystring.parse(self.data);
      }
    }
    self.requestBody = self.data;
  }
}

Assertion.prototype.send = function(data) {
  this.data = data;
  return this;
}

Assertion.prototype.query = function(qs) {
  var q;
  if(typeof qs == "string") {
    q = querystring.parse(qs);
  } else {
    q = qs;
  }
  if(!this.qs) {
    this.qs = {};
  }
  for(var n in q) {
    this.qs[n] = "" + q[n];
  }
  return this;
}

Assertion.prototype.set = function(name, value) {
  this.headers[name.toLowerCase()] = value;
  return this;
}

Assertion.prototype.delay = function(ms) {
  this.delay = ms;
  return this;
}

Assertion.prototype.persist = function() {
  this.removeWhenMet = false;
  return this;
}

Assertion.prototype.reply = function(status, responseBody) {
  var self = this;

  self.parseExpectedRequestBody();

  self.app[self.method](self.path, function(req, res, next) {

    var assertionsPassed = self.testAssertions(req, next);

    if (assertionsPassed) {

      // Remove route from express since the expectation was met
      // Unless this mock is suposed to persist
      if (self.removeWhenMet) {
        self.app._router.map[self.method].splice(req._route_index, 1);
      }

      var reply = function() {
        self.handler.emit("done", req.url);
        res.status(status).send(responseBody);
      };

      if(self.hasOwnProperty("delay")) {
        setTimeout(reply, self.delay);
      } else {
        reply();
      }
    }
    else {
      self.handleUnmatchedRequest(next);
    }
  });

  this.handler = new Handler(this);
  return this.handler;
}

Assertion.prototype.testAssertions = function(req, next) {

  var allPassed = true;

  if(allPassed && this.qs) {
    allPassed = deepEqual(req.query, this.qs);
  }
  if(allPassed && this.requestBody) {
    if(req.text) {
      allPassed = deepEqual(req.text, this.requestBody);
    } else {
      allPassed = deepEqual(req.body, this.requestBody);
    }
  }
  for(var name in this.headers) {
    if (allPassed) {
      allPassed = deepEqual(req.headers[name], this.headers[name]);
    }
  }
  return allPassed;
}

Assertion.prototype.skipUnmatchedRequests = function() {
  this.handleUnmatchedRequest = function(next) {
    next();
  };
  return this;
}

Assertion.prototype.raiseUnmatchedRequests = function() {
  this.handleUnmatchedRequest = function(next) {
    next(new Error("Unmatched request"));
  };
  return this;
}

function Handler(assertion) {
  this.defaults = {
    waitTimeout: 2000
  };
  var self = this;
  this.assertion = assertion;
  this.isDone = false;
  this.responseCount = 0;
  this.completedUrlsList = []
  this.on("done", function(requestedUrl) {
    self.isDone = true;
    self.responseCount++;
    self.completedUrlsList.push(requestedUrl);
  });
}

util.inherits(Handler, EventEmitter);

Handler.prototype.done = function() {
  if(!this.isDone) {
    throw new Error(this.assertion.method + " " + this.assertion.path + " was not made yet.");
  }
}

Handler.prototype.count = function() {
  return this.responseCount;
}

Handler.prototype.completedUrls = function() {
  return this.completedUrlsList;
}

Handler.prototype.wait = function(ms, fn) {
  if(!fn && typeof ms == "function") {
    fn = ms;
    ms = this.defaults.waitTimeout;
  }

  var self = this;
  var timeout = null;
  var cb = function() {
    clearTimeout(timeout);
    fn();
  }
  this.once("done", cb);
  timeout = setTimeout(function() {
    self.removeListener("done", cb);
    fn(new Error(self.assertion.method + " " + self.assertion.path + " was not called within " + ms + "ms."));
  }, ms);
}
<commit_msg>add debug<commit_after>var express = require("express");
var methods = require("methods");
var assert = require('assert');
var querystring = require("querystring");
var EventEmitter = require("events").EventEmitter;
var util = require("util");
var deepEqual = require('deep-equal');

module.exports = function(port) {
  var app = express();

  app.on("error", function(err) {
    throw err;
  });

  app.use(express.json());
  app.use(express.urlencoded());

  app.use(function(req, res, next){
    if (req.is('text/*')) {
      req.text = '';
      req.setEncoding('utf8');
      req.on('data', function(chunk){ req.text += chunk });
      req.on('end', next);
    } else {
      next();
    }
  });

  if (process.env.DEBUG) {
    app.use(function(req,res,next){
      console.log("Received " + req.method + " to " + req.url + ". req.body: ");
      console.log(JSON.stringify(req.body, {}, 2));
      next();
    })
  }

  var server;
  if(port) {
    server = app.listen(port);
  } else {
    server = app.listen();
  }

  methods.forEach(function(method) {
    server[method] = function(path) {
      return new Assertion(app, method, path);
    }
  });

  server.clean = function() {
    app._router.map = {};
  }

  return server;
}

function Assertion(app, method, path) {
  var self = this;
  this.app = app;
  this.method = method;
  this.path = path;
  this.headers = {};
  this.removeWhenMet = true;
  this.raiseUnmatchedRequests();
  this.unmetAction = 'Throwing';

  this.parseExpectedRequestBody = function() {
    if(!self.headers["content-type"]) {
      if(typeof self.data == "string") {
        return self.requestBody = querystring.parse(self.data);
      }
    }
    self.requestBody = self.data;
  }
}

Assertion.prototype.send = function(data) {
  this.data = data;
  return this;
}

Assertion.prototype.query = function(qs) {
  var q;
  if(typeof qs == "string") {
    q = querystring.parse(qs);
  } else {
    q = qs;
  }
  if(!this.qs) {
    this.qs = {};
  }
  for(var n in q) {
    this.qs[n] = "" + q[n];
  }
  return this;
}

Assertion.prototype.set = function(name, value) {
  this.headers[name.toLowerCase()] = value;
  return this;
}

Assertion.prototype.delay = function(ms) {
  this.delay = ms;
  return this;
}

Assertion.prototype.persist = function() {
  this.removeWhenMet = false;
  return this;
}

Assertion.prototype.reply = function(status, responseBody) {
  var self = this;

  self.parseExpectedRequestBody();

  self.app[self.method](self.path, function(req, res, next) {

    var assertionsPassed = self.testAssertions(req, next);

    if (assertionsPassed) {

      // Remove route from express since the expectation was met
      // Unless this mock is suposed to persist
      if (self.removeWhenMet) {
        self.app._router.map[self.method].splice(req._route_index, 1);
      }

      var reply = function() {
        if (process.env.DEBUG) {
          console.log("Responding to " + self.method.toUpperCase() + " to " + req.url + " with: ");
          console.log(JSON.stringify(responseBody, {}, 2));
        }

        self.handler.emit("done", req.url);
        res.status(status).send(responseBody);
      };

      if(self.hasOwnProperty("delay")) {
        setTimeout(reply, self.delay);
      } else {
        reply();
      }
    }
    else {
      console.log("Assertions for " + self.method.toUpperCase() + " to " + req.url + " were not met. " + self.unmetAction);
      self.handleUnmatchedRequest(next);
    }
  });

  this.handler = new Handler(this);
  return this.handler;
}

Assertion.prototype.testAssertions = function(req, next) {

  var allPassed = true;

  if(allPassed && this.qs) {
    allPassed = deepEqual(req.query, this.qs);
  }
  if(allPassed && this.requestBody) {
    if(req.text) {
      allPassed = deepEqual(req.text, this.requestBody);
    } else {
      allPassed = deepEqual(req.body, this.requestBody);
    }
  }
  for(var name in this.headers) {
    if (allPassed) {
      allPassed = deepEqual(req.headers[name], this.headers[name]);
    }
  }
  return allPassed;
}

Assertion.prototype.skipUnmatchedRequests = function() {
  this.unmetAction = 'Skipping';
  this.handleUnmatchedRequest = function(next) {
    next();
  };
  return this;
}

Assertion.prototype.raiseUnmatchedRequests = function() {
  this.handleUnmatchedRequest = function(next) {
    next(new Error("Unmatched request"));
  };
  return this;
}

function Handler(assertion) {
  this.defaults = {
    waitTimeout: 2000
  };
  var self = this;
  this.assertion = assertion;
  this.isDone = false;
  this.responseCount = 0;
  this.completedUrlsList = []
  this.on("done", function(requestedUrl) {
    self.isDone = true;
    self.responseCount++;
    self.completedUrlsList.push(requestedUrl);
  });
}

util.inherits(Handler, EventEmitter);

Handler.prototype.done = function() {
  if(!this.isDone) {
    throw new Error(this.assertion.method + " " + this.assertion.path + " was not made yet.");
  }
}

Handler.prototype.count = function() {
  return this.responseCount;
}

Handler.prototype.completedUrls = function() {
  return this.completedUrlsList;
}

Handler.prototype.wait = function(ms, fn) {
  if(!fn && typeof ms == "function") {
    fn = ms;
    ms = this.defaults.waitTimeout;
  }

  var self = this;
  var timeout = null;
  var cb = function() {
    clearTimeout(timeout);
    fn();
  }
  this.once("done", cb);
  timeout = setTimeout(function() {
    self.removeListener("done", cb);
    fn(new Error(self.assertion.method + " " + self.assertion.path + " was not called within " + ms + "ms."));
  }, ms);
}
<|endoftext|>


<commit_before>// vim: tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab

// GLOBAL VARIABLES
var overpassapi = "http://overpass-api.de/api/interpreter?data=";
var datasets = {
	"Belgium": {
		"BE_dl":
		{
			"url": "http://raw.githubusercontent.com/POI-Importer/Haltes-De-Lijn/master/haltes_de_lijn_dataset.json",
			"name": "Haltes De Lijn",
		}
	},
};
var datasetSettings = {};
var tiledData = {};
var appSettings = {};
var queryStatus = {"busy": false, "waiting": false};

function loadDatasets()
{
	for (var country in datasets)
	{
		for (var dataset in datasets[country])
		{
			htmlHelper.addDataset(country, dataset);
			var req = new XMLHttpRequest();
			req.overrideMimeType("application/json");
			req.onreadystatechange = function()
			{
				if (req.readyState != 4)
					return;
				var settings = JSON.parse(req.responseText);
				settings.layer = new L.LayerGroup();
				datasetSettings[dataset] = settings;
				var el = document.getElementById(dataset + "Dataset");
				if (el.checked)
					toggleDataset(dataset, el);
			}
			req.open("GET", datasets[country][dataset].url, true);
			req.send(null);
		}
	}
}

function toggleDataset(dataset, element)
{
	if (!datasetSettings[dataset] || !datasetSettings[dataset].layer)
		return; // wait until the page is loaded

	if (element.checked)
		mapObj.addLayer(datasetSettings[dataset].layer);
	else
		mapObj.removeLayer(datasetSettings[dataset].layer);
	loadData();
}

function changeSetting(element)
{
	var id = element.id;
	if (id.slice(-7) != "Setting")
		return;
	id = id.slice(0, -7);
	appSettings[id] = element.checked;
	if (id == "hideCompletePOI") // re-render points
		for (var dataset in tiledData)
			for (var tileName in tiledData[dataset])
				if (tiledData[dataset][tileName].data)
					for (var p = 0; p < tiledData[dataset][tileName].data.length; p++)
						displayPoint(dataset, tileName, p)
	saveAppState();
}

function loadData()
{
	var mapBounds = mapObj.getBounds();
	var mapCenter = mapObj.getCenter();
	var mapZoom = mapObj.getZoom();
	// get center in tile coordinates
	for (var datasetName in datasetSettings)
	{
		if (!tiledData[datasetName])
			tiledData[datasetName] = {};
		var settings = datasetSettings[datasetName];

		if (!mapBounds.intersects([[settings.bbox.b,settings.bbox.l],[settings.bbox.t,settings.bbox.r]]))
			continue;
		if (!mapObj.hasLayer(settings.layer))
			continue;

		if (!settings.icons)
			loadIcons(settings);

		var tileCoordinates = geoHelper.latlonToTilenumber(
			mapCenter.lat,
			mapCenter.lng,
			settings.data.zoom);

		// Load 9 tiles around the center
		for (var x = tileCoordinates.x - 1; x <= tileCoordinates.x + 1; x++)
		{
			for (var y = tileCoordinates.y - 1; y <= tileCoordinates.y + 1; y++)
			{
				var tileName = x + "_" + y;
				if (tiledData[datasetName][tileName])
					continue; // tile already loaded or loading
				tiledData[datasetName][tileName] = {};

				(function(datasetName, tileName, source)
				{
					var req = new XMLHttpRequest();
					req.overrideMimeType("application/json");
					req.onreadystatechange = function()
					{
						if (req.readyState != 4)
							return;
						var data = geojsonToPointlist(JSON.parse(req.responseText));
						tiledData[datasetName][tileName].data = data;
						for (var p = 0; p < data.length; p++)
							displayPoint(datasetName, tileName, p);
						loadOverpass();
					}
					req.open("GET", source + "/" + tileName + ".json", true);
					try { req.send(null); } catch (e) {}
				})(datasetName, tileName, settings.data.source);
			}
		}
		loadOverpass();
	}
	saveAppState();
}

function loadOverpass()
{
	// Overpass only accepts one query at a time, schedule new query if query is waiting
	if (queryStatus.busy)
	{
		queryStatus.waiting = true;
		return;
	}
	queryStatus.waiting = false;
	var mapZoom = mapObj.getZoom();
	var mapBounds = mapObj.getBounds();
	var mapCenter = mapObj.getCenter();
	// Make query
	var query = "[out:json];\n";
	var types = ["node"/*,"way","rel"*/];
	var queriedDatasets = [];
	for (var datasetName in datasetSettings)
	{
		if (!tiledData[datasetName])
			continue;
		var settings = datasetSettings[datasetName];
		var tileCoordinates = geoHelper.latlonToTilenumber(
			mapCenter.lat,
			mapCenter.lng,
			settings.data.zoom);

		for (var x = tileCoordinates.x - 1; x <= tileCoordinates.x + 1; x++)
		{
			for (var y = tileCoordinates.y - 1; y <= tileCoordinates.y + 1; y++)
			{
				var tileName = x + "_" + y;
				var tileBbox = geoHelper.tilenumberToBbox(x, y, settings.data.zoom);
				// add a margin to the bbox
				geoHelper.padBbox(tileBbox, settings.dist);

				if (!tiledData[datasetName][tileName] ||
					!tiledData[datasetName][tileName].data ||
					!tiledData[datasetName][tileName].data.length)
						continue;
				if (tiledData[datasetName][tileName].overpassQueried)
					continue;
				tiledData[datasetName][tileName].overpassQueried = true;

				query += "(";
				for (var t = 0; t < types.length; t++)
					query += types[t] + settings.query + "(" + tileBbox.b + "," + tileBbox.l + "," + tileBbox.t + "," + tileBbox.r + ");";
				query += "); out center; out count;\n";

				queriedDatasets.push({"tileName": tileName, "datasetName": datasetName});
			}
		}
	}

	if (!queriedDatasets.length)
		return;

	console.log("overpass query:\n" + query);

	// Send query to overpass
	var req = new XMLHttpRequest();
	req.onreadystatechange = function()
	{
		if (req.readyState != 4)
			return;
		if (req.status != 200)
			return;
		var osmData = JSON.parse(req.responseText).elements;
		compareData(queriedDatasets, osmData);
		queryStatus.busy = false;
		if (queryStatus.waiting)
			loadOverpass();
	}
	queryStatus.busy = true;
	req.open("GET", overpassapi + encodeURIComponent(query), true);
	req.send(null);
}

function displayPoint(datasetName, tileName, idx)
{
	var point = tiledData[datasetName][tileName].data[idx];
	var settings = datasetSettings[datasetName];
	// add marker to the data for future reference
	if (!point.marker)
		point.marker = L.marker(point.coordinates, {icon: settings.greyIcon})
			.addTo(settings.layer);

	if (point.score == undefined)
		return; // only initial display

	// if the point has been compared, change its colour
	if (point.score == point.maxScore && appSettings.hideCompletePOI)
		point.marker.setOpacity(0);
	else
		point.marker.setOpacity(1);

	point.marker.setIcon(settings.icons[Math.floor(10 * point.score/point.maxScore)]);
	point.marker.bindPopup(htmlHelper.getPopup(datasetName, tileName, idx), {"maxWidth": 900});
}

function loadIcons(settings)
{
	// create the icon in 11 colours from red to green + a grey one
	settings.icons = [];
	for (var i = 0; i <= 10; i++)
	{
		var colour = hslToRgb(i / 30, 1, 0.5);
		settings.icons.push(L.MakiMarkers.icon({icon: settings.icon, color: colour, size: "m"}));
	}
	settings.greyIcon = L.MakiMarkers.icon({icon: settings.icon, color: "#808080", size: "m"});
}

function geojsonToPointlist(geojson)
{
	var results = [];
	if (!geojson.features)
		return results;

	for (var i = 0; i < geojson.features.length; i++)
	{
		// TODO skip non-point features
		var point = {};
		// switch lat and lon to the OSM standard order
		point.coordinates = {};
		point.coordinates.lat = geojson.features[i].geometry.coordinates[1];
		point.coordinates.lon = geojson.features[i].geometry.coordinates[0];
		point.properties = geojson.features[i].properties;
		results.push(point);
	}
	return results;
}

/**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes h, s, and l are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param   Number  h       The hue
 * @param   Number  s       The saturation
 * @param   Number  l       The lightness
 * @return  Array           The RGB representation
 */
function hslToRgb(h, s, l){
	var r, g, b;

	if(s == 0)
	{
		r = g = b = l; // achromatic
	}
	else
	{
		function hue2rgb(p, q, t) {
			if(t < 0) t += 1;
			if(t > 1) t -= 1;
			if(t < 1/6) return p + (q - p) * 6 * t;
			if(t < 1/2) return q;
			if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
			return p;
		}

		var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
		var p = 2 * l - q;
		r = hue2rgb(p, q, h + 1/3);
		g = hue2rgb(p, q, h);
		b = hue2rgb(p, q, h - 1/3);
	}

	function toHex(n) {
		var h = Math.round(n * 255).toString(16);
		if (h.length < 2)
			h = "0" + h;
		return h;
	}

	return "#" + toHex(r) + toHex(g) + toHex(b);
}

function escapeXML(str)
{
	return str.replace(/&/g, "&")
		.replace(/'/g, "'")
		.replace(/>/g, ">")
		.replace(/</g, "<");
}

function saveAppState()
{
	var date = new Date();
	date.setFullYear(date.getFullYear() + 1);
	var stateString = getStateString();
	document.cookie = "state=" + encodeURIComponent(stateString) + ";expires=" + date.toUTCString();
	window.location.hash = stateString;
}

function loadAppState()
{
	var stateString;
	if (window.location.hash)
		stateString = window.location.hash.slice(1);
	else if (document.cookie)
		stateString = decodeURIComponent(document.cookie.slice(6));
	if (stateString)
		applyStateString(stateString);
}

function getStateString()
{
	var r = "";
	var center = mapObj.getCenter();
	r += "map=" + mapObj.getZoom() + "/" + center.lat.toFixed(4) + "/" + center.lng.toFixed(4);

	var loadedDatasets = [];
	for (var dataset in datasetSettings)
		if (mapObj.hasLayer(datasetSettings[dataset].layer))
			loadedDatasets.push(encodeURIComponent(dataset));
	if (loadedDatasets.length > 0)
		r+= "&datasets=" + loadedDatasets.join(";");

	var activatedSettings = [];
	for (var key in appSettings)
		if (appSettings[key] == true)
			activatedSettings.push(encodeURIComponent(key));
	if (activatedSettings.length > 0)
		r += "&settings=" + activatedSettings.join(";");
	return r;
}

function applyStateString(state)
{
	var splitState = state.split("&");
	for (var i = 0; i < splitState.length; i++)
	{
		if (splitState[i].indexOf("map=") == 0)
		{
			var mapState = splitState[i].match(/[0-9\.]+/g);
			mapObj.setView([+mapState[1], +mapState[2]], +mapState[0]);
		}
		else if (splitState[i].indexOf("datasets=") == 0)
		{
			var loadedDatasets = splitState[i].substr(9).split(";");
			for (var d = 0; d < loadedDatasets.length; d++)
			{
				var id = decodeURIComponent(loadedDatasets[d])
				var el = document.getElementById(id + "Dataset");
				el.checked = true;
				toggleDataset(id, el);
			}
		}
		else if (splitState[i].indexOf("settings=") == 0)
		{
			var activatedSettings = splitState[i].substr(9).split(";");
			for (var s = 0; s < activatedSettings.length; s++)
			{
				var id = decodeURIComponent(activatedSettings[s]);
				var el = document.getElementById(id + "Setting");
				el.checked = true;
				changeSetting(el);
			}
		}
	}
}
<commit_msg>Fix url<commit_after>// vim: tabstop=4:softtabstop=4:shiftwidth=4:noexpandtab

// GLOBAL VARIABLES
var overpassapi = "http://overpass-api.de/api/interpreter?data=";
var datasets = {
	"Belgium": {
		"BE_dl":
		{
			"url": "http://raw.githubusercontent.com/POI-Importer/Haltes-De-Lijn/master/dataset.json",
			"name": "Haltes De Lijn",
		}
	},
};
var datasetSettings = {};
var tiledData = {};
var appSettings = {};
var queryStatus = {"busy": false, "waiting": false};

function loadDatasets()
{
	for (var country in datasets)
	{
		for (var dataset in datasets[country])
		{
			htmlHelper.addDataset(country, dataset);
			var req = new XMLHttpRequest();
			req.overrideMimeType("application/json");
			req.onreadystatechange = function()
			{
				if (req.readyState != 4)
					return;
				var settings = JSON.parse(req.responseText);
				settings.layer = new L.LayerGroup();
				datasetSettings[dataset] = settings;
				var el = document.getElementById(dataset + "Dataset");
				if (el.checked)
					toggleDataset(dataset, el);
			}
			req.open("GET", datasets[country][dataset].url, true);
			req.send(null);
		}
	}
}

function toggleDataset(dataset, element)
{
	if (!datasetSettings[dataset] || !datasetSettings[dataset].layer)
		return; // wait until the page is loaded

	if (element.checked)
		mapObj.addLayer(datasetSettings[dataset].layer);
	else
		mapObj.removeLayer(datasetSettings[dataset].layer);
	loadData();
}

function changeSetting(element)
{
	var id = element.id;
	if (id.slice(-7) != "Setting")
		return;
	id = id.slice(0, -7);
	appSettings[id] = element.checked;
	if (id == "hideCompletePOI") // re-render points
		for (var dataset in tiledData)
			for (var tileName in tiledData[dataset])
				if (tiledData[dataset][tileName].data)
					for (var p = 0; p < tiledData[dataset][tileName].data.length; p++)
						displayPoint(dataset, tileName, p)
	saveAppState();
}

function loadData()
{
	var mapBounds = mapObj.getBounds();
	var mapCenter = mapObj.getCenter();
	var mapZoom = mapObj.getZoom();
	// get center in tile coordinates
	for (var datasetName in datasetSettings)
	{
		if (!tiledData[datasetName])
			tiledData[datasetName] = {};
		var settings = datasetSettings[datasetName];

		if (!mapBounds.intersects([[settings.bbox.b,settings.bbox.l],[settings.bbox.t,settings.bbox.r]]))
			continue;
		if (!mapObj.hasLayer(settings.layer))
			continue;

		if (!settings.icons)
			loadIcons(settings);

		var tileCoordinates = geoHelper.latlonToTilenumber(
			mapCenter.lat,
			mapCenter.lng,
			settings.data.zoom);

		// Load 9 tiles around the center
		for (var x = tileCoordinates.x - 1; x <= tileCoordinates.x + 1; x++)
		{
			for (var y = tileCoordinates.y - 1; y <= tileCoordinates.y + 1; y++)
			{
				var tileName = x + "_" + y;
				if (tiledData[datasetName][tileName])
					continue; // tile already loaded or loading
				tiledData[datasetName][tileName] = {};

				(function(datasetName, tileName, source)
				{
					var req = new XMLHttpRequest();
					req.overrideMimeType("application/json");
					req.onreadystatechange = function()
					{
						if (req.readyState != 4)
							return;
						var data = geojsonToPointlist(JSON.parse(req.responseText));
						tiledData[datasetName][tileName].data = data;
						for (var p = 0; p < data.length; p++)
							displayPoint(datasetName, tileName, p);
						loadOverpass();
					}
					req.open("GET", source + "/" + tileName + ".json", true);
					try { req.send(null); } catch (e) {}
				})(datasetName, tileName, settings.data.source);
			}
		}
		loadOverpass();
	}
	saveAppState();
}

function loadOverpass()
{
	// Overpass only accepts one query at a time, schedule new query if query is waiting
	if (queryStatus.busy)
	{
		queryStatus.waiting = true;
		return;
	}
	queryStatus.waiting = false;
	var mapZoom = mapObj.getZoom();
	var mapBounds = mapObj.getBounds();
	var mapCenter = mapObj.getCenter();
	// Make query
	var query = "[out:json];\n";
	var types = ["node"/*,"way","rel"*/];
	var queriedDatasets = [];
	for (var datasetName in datasetSettings)
	{
		if (!tiledData[datasetName])
			continue;
		var settings = datasetSettings[datasetName];
		var tileCoordinates = geoHelper.latlonToTilenumber(
			mapCenter.lat,
			mapCenter.lng,
			settings.data.zoom);

		for (var x = tileCoordinates.x - 1; x <= tileCoordinates.x + 1; x++)
		{
			for (var y = tileCoordinates.y - 1; y <= tileCoordinates.y + 1; y++)
			{
				var tileName = x + "_" + y;
				var tileBbox = geoHelper.tilenumberToBbox(x, y, settings.data.zoom);
				// add a margin to the bbox
				geoHelper.padBbox(tileBbox, settings.dist);

				if (!tiledData[datasetName][tileName] ||
					!tiledData[datasetName][tileName].data ||
					!tiledData[datasetName][tileName].data.length)
						continue;
				if (tiledData[datasetName][tileName].overpassQueried)
					continue;
				tiledData[datasetName][tileName].overpassQueried = true;

				query += "(";
				for (var t = 0; t < types.length; t++)
					query += types[t] + settings.query + "(" + tileBbox.b + "," + tileBbox.l + "," + tileBbox.t + "," + tileBbox.r + ");";
				query += "); out center; out count;\n";

				queriedDatasets.push({"tileName": tileName, "datasetName": datasetName});
			}
		}
	}

	if (!queriedDatasets.length)
		return;

	console.log("overpass query:\n" + query);

	// Send query to overpass
	var req = new XMLHttpRequest();
	req.onreadystatechange = function()
	{
		if (req.readyState != 4)
			return;
		if (req.status != 200)
			return;
		var osmData = JSON.parse(req.responseText).elements;
		compareData(queriedDatasets, osmData);
		queryStatus.busy = false;
		if (queryStatus.waiting)
			loadOverpass();
	}
	queryStatus.busy = true;
	req.open("GET", overpassapi + encodeURIComponent(query), true);
	req.send(null);
}

function displayPoint(datasetName, tileName, idx)
{
	var point = tiledData[datasetName][tileName].data[idx];
	var settings = datasetSettings[datasetName];
	// add marker to the data for future reference
	if (!point.marker)
		point.marker = L.marker(point.coordinates, {icon: settings.greyIcon})
			.addTo(settings.layer);

	if (point.score == undefined)
		return; // only initial display

	// if the point has been compared, change its colour
	if (point.score == point.maxScore && appSettings.hideCompletePOI)
		point.marker.setOpacity(0);
	else
		point.marker.setOpacity(1);

	point.marker.setIcon(settings.icons[Math.floor(10 * point.score/point.maxScore)]);
	point.marker.bindPopup(htmlHelper.getPopup(datasetName, tileName, idx), {"maxWidth": 900});
}

function loadIcons(settings)
{
	// create the icon in 11 colours from red to green + a grey one
	settings.icons = [];
	for (var i = 0; i <= 10; i++)
	{
		var colour = hslToRgb(i / 30, 1, 0.5);
		settings.icons.push(L.MakiMarkers.icon({icon: settings.icon, color: colour, size: "m"}));
	}
	settings.greyIcon = L.MakiMarkers.icon({icon: settings.icon, color: "#808080", size: "m"});
}

function geojsonToPointlist(geojson)
{
	var results = [];
	if (!geojson.features)
		return results;

	for (var i = 0; i < geojson.features.length; i++)
	{
		// TODO skip non-point features
		var point = {};
		// switch lat and lon to the OSM standard order
		point.coordinates = {};
		point.coordinates.lat = geojson.features[i].geometry.coordinates[1];
		point.coordinates.lon = geojson.features[i].geometry.coordinates[0];
		point.properties = geojson.features[i].properties;
		results.push(point);
	}
	return results;
}

/**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes h, s, and l are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param   Number  h       The hue
 * @param   Number  s       The saturation
 * @param   Number  l       The lightness
 * @return  Array           The RGB representation
 */
function hslToRgb(h, s, l){
	var r, g, b;

	if(s == 0)
	{
		r = g = b = l; // achromatic
	}
	else
	{
		function hue2rgb(p, q, t) {
			if(t < 0) t += 1;
			if(t > 1) t -= 1;
			if(t < 1/6) return p + (q - p) * 6 * t;
			if(t < 1/2) return q;
			if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
			return p;
		}

		var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
		var p = 2 * l - q;
		r = hue2rgb(p, q, h + 1/3);
		g = hue2rgb(p, q, h);
		b = hue2rgb(p, q, h - 1/3);
	}

	function toHex(n) {
		var h = Math.round(n * 255).toString(16);
		if (h.length < 2)
			h = "0" + h;
		return h;
	}

	return "#" + toHex(r) + toHex(g) + toHex(b);
}

function escapeXML(str)
{
	return str.replace(/&/g, "&")
		.replace(/'/g, "'")
		.replace(/>/g, ">")
		.replace(/</g, "<");
}

function saveAppState()
{
	var date = new Date();
	date.setFullYear(date.getFullYear() + 1);
	var stateString = getStateString();
	document.cookie = "state=" + encodeURIComponent(stateString) + ";expires=" + date.toUTCString();
	window.location.hash = stateString;
}

function loadAppState()
{
	var stateString;
	if (window.location.hash)
		stateString = window.location.hash.slice(1);
	else if (document.cookie)
		stateString = decodeURIComponent(document.cookie.slice(6));
	if (stateString)
		applyStateString(stateString);
}

function getStateString()
{
	var r = "";
	var center = mapObj.getCenter();
	r += "map=" + mapObj.getZoom() + "/" + center.lat.toFixed(4) + "/" + center.lng.toFixed(4);

	var loadedDatasets = [];
	for (var dataset in datasetSettings)
		if (mapObj.hasLayer(datasetSettings[dataset].layer))
			loadedDatasets.push(encodeURIComponent(dataset));
	if (loadedDatasets.length > 0)
		r+= "&datasets=" + loadedDatasets.join(";");

	var activatedSettings = [];
	for (var key in appSettings)
		if (appSettings[key] == true)
			activatedSettings.push(encodeURIComponent(key));
	if (activatedSettings.length > 0)
		r += "&settings=" + activatedSettings.join(";");
	return r;
}

function applyStateString(state)
{
	var splitState = state.split("&");
	for (var i = 0; i < splitState.length; i++)
	{
		if (splitState[i].indexOf("map=") == 0)
		{
			var mapState = splitState[i].match(/[0-9\.]+/g);
			mapObj.setView([+mapState[1], +mapState[2]], +mapState[0]);
		}
		else if (splitState[i].indexOf("datasets=") == 0)
		{
			var loadedDatasets = splitState[i].substr(9).split(";");
			for (var d = 0; d < loadedDatasets.length; d++)
			{
				var id = decodeURIComponent(loadedDatasets[d])
				var el = document.getElementById(id + "Dataset");
				el.checked = true;
				toggleDataset(id, el);
			}
		}
		else if (splitState[i].indexOf("settings=") == 0)
		{
			var activatedSettings = splitState[i].substr(9).split(";");
			for (var s = 0; s < activatedSettings.length; s++)
			{
				var id = decodeURIComponent(activatedSettings[s]);
				var el = document.getElementById(id + "Setting");
				el.checked = true;
				changeSetting(el);
			}
		}
	}
}
<|endoftext|>


<commit_before>var readline = require('readline');

var defaultSpinnerString = 0;
var defaultSpinnerDelay = 60;

var Spinner = function(textToShow){
  this.text = textToShow || '';
  this.setSpinnerString(defaultSpinnerString);
  this.setSpinnerDelay(defaultSpinnerDelay);
};

Spinner.spinners = [
  '|/-\\',
  '⠂-–—–-',
  '◐◓◑◒',
  '←↖↑↗→↘↓↙',
  '┤┘┴└├┌┬┐',
  '◢◣◤◥',
  '.oO°Oo.',
  '.oO@*',
  '☱☲☴',
  '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏',
  '⠋⠙⠚⠞⠖⠦⠴⠲⠳⠓',
  '⠄⠆⠇⠋⠙⠸⠰⠠⠰⠸⠙⠋⠇⠆',
  '⠋⠙⠚⠒⠂⠂⠒⠲⠴⠦⠖⠒⠐⠐⠒⠓⠋',
  '⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠴⠲⠒⠂⠂⠒⠚⠙⠉⠁',
  '⠈⠉⠋⠓⠒⠐⠐⠒⠖⠦⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈',
  '⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈',
  '⢄⢂⢁⡁⡈⡐⡠',
  '⢹⢺⢼⣸⣇⡧⡗⡏',
  '⣾⣽⣻⢿⡿⣟⣯⣷',
  '⠁⠂⠄⡀⢀⠠⠐⠈'
];

Spinner.setDefaultSpinnerString = function(value) {
  defaultSpinnerString = value;
};

Spinner.setDefaultSpinnerDelay = function(value) {
  defaultSpinnerDelay = value;
};

Spinner.prototype.start = function() {
  var current = 0;
  var self = this;
  var hasPos = self.text.indexOf('%s') > -1;
  this.id = setInterval(function() {
    var msg = hasPos ? self.text.replace('%s', self.chars[current]) : self.chars[current] + ' ' + self.text;
    clearLine();
    process.stdout.write(msg);
    current = ++current % self.chars.length;
  }, this.delay);
};

Spinner.prototype.setSpinnerDelay = function(n) {
  this.delay = n;
};

Spinner.prototype.setSpinnerString = function(str) {
  this.chars = mapToSpinner(str, this.spinners).split('');
};

Spinner.prototype.stop = function(clear) {
  clearInterval(this.id);
  if (clear) {
    clearLine();
  }
};

// Helpers

function isInt(value) {
  return (typeof value==='number' && (value%1)===0);
}

function mapToSpinner(value, spinners) {
  // Not an integer, return as strng
  if (!isInt(value)) {
    return value + '';
  }

  // Check if index is within bounds
  value = (value >= Spinner.spinners.length) ? 0 : value;
  // If negative, count from the end
  value = (value < 0) ? Spinner.spinners.length + value : value;

  return Spinner.spinners[value];
}

function clearLine() {
    readline.clearLine(process.stdout, 0);
    readline.cursorTo(process.stdout, 0);
}

exports.Spinner = Spinner;<commit_msg>Add more spinners<commit_after>var readline = require('readline');

var defaultSpinnerString = 0;
var defaultSpinnerDelay = 60;

var Spinner = function(textToShow){
  this.text = textToShow || '';
  this.setSpinnerString(defaultSpinnerString);
  this.setSpinnerDelay(defaultSpinnerDelay);
};

Spinner.spinners = [
  '|/-\\',
  '⠂-–—–-',
  '◐◓◑◒',
  '◴◷◶◵',
  '◰◳◲◱',
  '▖▘▝▗',
  '▌▀▐▄',
  '▉▊▋▌▍▎▏▎▍▌▋▊▉',
  '▁▃▄▅▆▇█▇▆▅▄▃',
  '←↖↑↗→↘↓↙',
  '┤┘┴└├┌┬┐',
  '◢◣◤◥',
  '.oO°Oo.',
  '.oO@*',
  '◡◡ ⊙⊙ ◠◠',
  '☱☲☴',
  '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏',
  '⠋⠙⠚⠞⠖⠦⠴⠲⠳⠓',
  '⠄⠆⠇⠋⠙⠸⠰⠠⠰⠸⠙⠋⠇⠆',
  '⠋⠙⠚⠒⠂⠂⠒⠲⠴⠦⠖⠒⠐⠐⠒⠓⠋',
  '⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠴⠲⠒⠂⠂⠒⠚⠙⠉⠁',
  '⠈⠉⠋⠓⠒⠐⠐⠒⠖⠦⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈',
  '⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈',
  '⢄⢂⢁⡁⡈⡐⡠',
  '⢹⢺⢼⣸⣇⡧⡗⡏',
  '⣾⣽⣻⢿⡿⣟⣯⣷',
  '⠁⠂⠄⡀⢀⠠⠐⠈'
];

Spinner.setDefaultSpinnerString = function(value) {
  defaultSpinnerString = value;
};

Spinner.setDefaultSpinnerDelay = function(value) {
  defaultSpinnerDelay = value;
};

Spinner.prototype.start = function() {
  var current = 0;
  var self = this;
  var hasPos = self.text.indexOf('%s') > -1;
  this.id = setInterval(function() {
    var msg = hasPos ? self.text.replace('%s', self.chars[current]) : self.chars[current] + ' ' + self.text;
    clearLine();
    process.stdout.write(msg);
    current = ++current % self.chars.length;
  }, this.delay);
};

Spinner.prototype.setSpinnerDelay = function(n) {
  this.delay = n;
};

Spinner.prototype.setSpinnerString = function(str) {
  this.chars = mapToSpinner(str, this.spinners).split('');
};

Spinner.prototype.stop = function(clear) {
  clearInterval(this.id);
  if (clear) {
    clearLine();
  }
};

// Helpers

function isInt(value) {
  return (typeof value==='number' && (value%1)===0);
}

function mapToSpinner(value, spinners) {
  // Not an integer, return as strng
  if (!isInt(value)) {
    return value + '';
  }

  // Check if index is within bounds
  value = (value >= Spinner.spinners.length) ? 0 : value;
  // If negative, count from the end
  value = (value < 0) ? Spinner.spinners.length + value : value;

  return Spinner.spinners[value];
}

function clearLine() {
    readline.clearLine(process.stdout, 0);
    readline.cursorTo(process.stdout, 0);
}

exports.Spinner = Spinner;
<|endoftext|>


<commit_before>Ext.define('Datanium.view.IndicatorSearchCombo', {
	extend : 'Ext.form.field.ComboBox',
	alias : 'widget.searchcombo',
	displayField : 'text',
	valueField : 'uniqueName',
	queryMode : 'remote',
	minChars : 2,
	triggerAction : 'all',
	lastQuery : '',
	store : 'Indicators',
	typeAhead : true,
	forceFit : true,
	selectOnFocus : true,
	forceSelection : false,
	allowBlank : true,
	emptyText : 'Search for Indicators',
	height : 22,
	margin : '5 5'
});<commit_msg>remove combo trigger button<commit_after>Ext.define('Datanium.view.IndicatorSearchCombo', {
	extend : 'Ext.form.field.ComboBox',
	alias : 'widget.searchcombo',
	displayField : 'text',
	valueField : 'uniqueName',
	queryMode : 'remote',
	minChars : 2,
	hideTrigger : true,
	lastQuery : '',
	store : 'Indicators',
	typeAhead : true,
	forceFit : true,
	selectOnFocus : true,
	forceSelection : false,
	allowBlank : true,
	emptyText : 'Search for Indicators',
	height : 22,
	margin : '5 5'
});<|endoftext|>


<commit_before>{
  "id":28,
  "name":"Managed Migration",
  "description": "Lets users manage and complete their move from existing benefits and Tax Credits to Universal Credit.",
  "theme": "Universal Credit",
  "location": "Leeds",
  "phase":"backlog",
  "sro":"Neil Couling",
  "service_man":"TBC",
  "phase-history":{
    "discovery": [
      {"label":"Estimated start date", "date": "Feb 2016"}
    ]
  },
  "priority":"Top"
}
<commit_msg>Update uc-migration.js<commit_after>{
  "id":28,
  "name":"Managed Migration",
  "description": "Lets users manage and complete their move from existing benefits and Tax Credits to Universal Credit.",
  "theme": "Universal Credit",
  "location": "Leeds",
  "phase":"backlog",
  "sro":"Neil Couling",
  "service_man":"TBC",
  "phase-history":{
    "discovery": [
      {"label":"Predicted Start Date", "date": "Feb 2016"}
    ]
  },
  "priority":"Top"
}
<|endoftext|>


<commit_before>var canvas;

function setup(){
  canvas = createCanvas(500, 500);
}

function draw(){

}

function keyTyped(){
  if (key == 'p'){
    saveCanvas(canvas, 'screenshot', 'png');
  }
}
<commit_msg>p5js/random-1 - Add click-and-drag to draw circles<commit_after>var canvas;
var currentShape;
var shapes;

function setup(){
  canvas = createCanvas(500, 500);
  colorMode(HSB);

  ellipseMode(CENTER);
  shapes = [];
}

function draw(){
  background(47, 10, 10, 100);

  shapes.forEach(s => s.draw());
}

function keyTyped(){
  if (key == 'p'){
    saveCanvas(canvas, 'screenshot', 'png');
  }
}

function mousePressed(){
  currentShape = new Circle(mouseX, mouseY);
  shapes.push(currentShape);
}

function mouseDragged(){
  currentShape.size = dist(currentShape.x, currentShape.y, mouseX, mouseY);
}

function mouseRelease(){
  currentShape = null;
}

class Circle {
  constructor(x, y){
    this.pos = createVector(x, y);
    this.size = 1;
  }

  get x(){ return this.pos.x; }
  get y(){ return this.pos.y; }

  draw(){
    ellipse(this.x, this.y, this.size*2, this.size*2);
  }
}
<|endoftext|>


<commit_before>/* global global, GLOBAL, module */
/**
 * Created by kaelyang on 2015/5/19.
 */
var http = require('http');

var log4js = require('log4js');
var logger = log4js.getLogger();
var _ = require('underscore');
var UserService = require('./UserService');
var StatisticsService = require('./StatisticsService');

var send_email = require("../utils/" + GLOBAL.pjconfig.email.module);
var dateFormat = require("../utils/dateFormat");

var EmailService = function() {
    this.userService = new UserService();
    this.statisticsService = new StatisticsService();
    this.top = parseInt(GLOBAL.pjconfig.email.top, 10) || 20;
    this.from = GLOBAL.pjconfig.email.from || "noreply-badjs@tencent.com";
};

var getYesterday = function() {
    var date = new Date();
    date.setDate(date.getDate() - 1);
    date.setHours(0, 0, 0, 0);
    return date;
};

EmailService.prototype = {
    render: function(data) {
        var that = this;
        data = data || {};
        var html = [];
        html.push('');
        html.push('
【BadJS日报邮件】 ' + data.title + '
');
        var content = data.content;
        if (content && content.length) {
            html.push('

#	出现次数	错误内容
');
            var total_top = 0;
            content.forEach(function(v, i) {
                v = typeof v === 'object' ? v : null;
                if (v) {
                    html.push('
{{index}}	{{times}}	{{desc}}
'
                        .replace(/{{index}}/g, i + 1)
                        .replace(/{{times}}/g, v.total)
                        .replace(/{{desc}}/g, v.title)
                        .replace(/{{bgc}}/g, i % 2 ? '#fff' : '#eee')
                    );
                    total_top += v.total;
                }
            });
            html.push('
');
            var total = data.total;
            total > 0 && html.push('
共 {{total}} 条记录, Top {{top}} 占 {{per}}.
'
                .replace(/{{total}}/g, total)
                .replace(/{{top}}/g, that.top)
                .replace(/{{per}}/g, (total_top * 100 / total).toFixed(2) + '%')
            );
        } else {
            html.push('
暂无数据
');
        }

        html.push('');
        return html.join('');
    },
    queryAll: function(isRetry, times) {
        var that = this;
        that.date = getYesterday();
        logger.info('Send mail query all start');
        that.userService.queryListByCondition({
            applyId: -1,
            role: -1
        }, function(err, userlist) {
            if (err) {
                return logger.error('Send email userService queryListByCondition error');
            } else {
                var orderByApplyId = {};
                userlist.forEach(function(v) {
                    if (orderByApplyId[v.applyId]) {
                        orderByApplyId[v.applyId].push(v);
                    } else {
                        orderByApplyId[v.applyId] = [v];
                    }
                });
                for (var applyId in orderByApplyId) {
                    var users = orderByApplyId[applyId];
                    var to_list = []; // 收件方
                    var cc_list = []; // 抄送方
                    var name = '';
                    users.forEach(function(v) {
                        v.role === 0 ? cc_list.push(v.email) : to_list.push(v.email);
                        name = v.name;
                    }); // jshint ignore:line
                    that.statisticsService.queryById({
                        top: 1, // kael
                        projectId: applyId,
                        startDate: that.date
                    }, function(err, data) {
                        if (err) return logger.error('Send email statisticsService queryById error');
                        if (data && data.data && data.data[0]) {
                            var temp = data.data[0];
                            temp.content = JSON.parse(temp.content);
                            temp.content = _.map(temp.content, function(value, key) {
                                return {
                                    title: key,
                                    total: value
                                };
                            });

                            temp.content = temp.content.sort(function(a, b) {
                                if (a.total < b.total) {
                                    return 1;
                                } else {
                                    return -1;
                                }
                            });

                            temp.content = temp.content.slice(0, that.top);
                            that.sendEmail({
                                to: to_list,
                                cc: cc_list,
                                title: name
                            }, temp);
                        } else {
                            logger.error('Send email data format error');
                        }
                    }); // jshint ignore:line
                }
            }
        });
        !isRetry && setTimeout(function() {
            that.queryAll();
        }, 86400000);
    },
    sendEmail: function(emails, data) {
        var title = "【" + dateFormat(this.date, "yyyy-MM-dd") + " BadJS 日报】- " + emails.title;
        data.title = emails.title;
        var content = this.render(data);
        send_email(this.from, emails.to, emails.cc, title, content, data);
    },
    start: function() {
        var that = this;
        var date = new Date();
        date.setDate(date.getDate() + 1);
        var time = GLOBAL.pjconfig.email.time.toString().split(':');
        date.setHours(parseInt(time[0], 10) || 9, parseInt(time[1], 10) || 0, parseInt(time[2], 10) || 0, 0);
        var timeDiff = date.valueOf() - (new Date()).valueOf();
        setTimeout(function() {
            that.queryAll();
        }, timeDiff);
        logger.debug('Email service will start after: ' + timeDiff);
    }
};

module.exports = EmailService;<commit_msg>add test code<commit_after>/* global global, GLOBAL, module */
/**
 * Created by kaelyang on 2015/5/19.
 */
var http = require('http');

var log4js = require('log4js');
var logger = log4js.getLogger();
var _ = require('underscore');
var UserService = require('./UserService');
var StatisticsService = require('./StatisticsService');

var send_email = require("../utils/" + GLOBAL.pjconfig.email.module);
var dateFormat = require("../utils/dateFormat");

var EmailService = function() {
    this.userService = new UserService();
    this.statisticsService = new StatisticsService();
    this.top = parseInt(GLOBAL.pjconfig.email.top, 10) || 20;
    this.from = GLOBAL.pjconfig.email.from || "noreply-badjs@tencent.com";
};

var getYesterday = function() {
    var date = new Date();
    date.setDate(date.getDate() - 1);
    date.setHours(0, 0, 0, 0);
    return date;
};

EmailService.prototype = {
    render: function(data) {
        var that = this;
        data = data || {};
        var html = [];
        html.push('');
        html.push('
【BadJS日报邮件】 ' + data.title + '
');
        var content = data.content;
        if (content && content.length) {
            html.push('

#	出现次数	错误内容
');
            var total_top = 0;
            content.forEach(function(v, i) {
                v = typeof v === 'object' ? v : null;
                if (v) {
                    html.push('
{{index}}	{{times}}	{{desc}}
'
                        .replace(/{{index}}/g, i + 1)
                        .replace(/{{times}}/g, v.total)
                        .replace(/{{desc}}/g, v.title)
                        .replace(/{{bgc}}/g, i % 2 ? '#fff' : '#eee')
                    );
                    total_top += v.total;
                }
            });
            html.push('
');
            var total = data.total;
            total > 0 && html.push('
共 {{total}} 条记录, Top {{top}} 占 {{per}}.
'
                .replace(/{{total}}/g, total)
                .replace(/{{top}}/g, that.top)
                .replace(/{{per}}/g, (total_top * 100 / total).toFixed(2) + '%')
            );
        } else {
            html.push('
暂无数据
');
        }

        html.push('');
        return html.join('');
    },
    queryAll: function(isRetry, times) {
        var that = this;
        that.date = getYesterday();
        logger.info('Send mail query all start');
        that.userService.queryListByCondition({
            applyId: -1,
            role: -1
        }, function(err, userlist) {
            if (err) {
                return logger.error('Send email userService queryListByCondition error');
            } else {
                var orderByApplyId = {};
                userlist.forEach(function(v) {
                    if (orderByApplyId[v.applyId]) {
                        orderByApplyId[v.applyId].push(v);
                    } else {
                        orderByApplyId[v.applyId] = [v];
                    }
                });
                for (var applyId in orderByApplyId) {
                    var users = orderByApplyId[applyId];
                    var to_list = []; // 收件方
                    var cc_list = []; // 抄送方
                    var name = '';
                    users.forEach(function(v) {
                        v.role === 0 ? cc_list.push(v.email) : to_list.push(v.email);
                        name = v.name;
                    }); // jshint ignore:line
                    if(applyId != 8){
                        continue;
                    }
                    that.statisticsService.queryById({
                        projectId: applyId,
                        startDate: that.date
                    }, function(err, data) {
                        if (err) return logger.error('Send email statisticsService queryById error');
                        if (data && data.data && data.data[0]) {
                            var temp = data.data[0];
                            temp.content = JSON.parse(temp.content);

                            temp.content = temp.content.slice(0, that.top);
                            that.sendEmail({
                                to: to_list,
                                cc: cc_list,
                                title: name
                            }, temp);
                        } else {
                            logger.error('Send email data format error');
                        }
                    }); // jshint ignore:line
                }
            }
        });
        !isRetry && setTimeout(function() {
            that.queryAll();
        }, 86400000);
    },
    sendEmail: function(emails, data) {
        var title = "【" + dateFormat(this.date, "yyyy-MM-dd") + " BadJS 日报】- " + emails.title;
        data.title = emails.title;
        var content = this.render(data);
        send_email(this.from, emails.to, emails.cc, title, content, data);
    },
    start: function() {
        var that = this;
        var date = new Date();
        date.setDate(date.getDate() + 1);
        var time = GLOBAL.pjconfig.email.time.toString().split(':');
        date.setHours(parseInt(time[0], 10) || 9, parseInt(time[1], 10) || 0, parseInt(time[2], 10) || 0, 0);
        var timeDiff = date.valueOf() - (new Date()).valueOf();
        setTimeout(function() {
            that.queryAll();
        }, timeDiff);
        logger.debug('Email service will start after: ' + timeDiff);
    }
};

module.exports = EmailService;<|endoftext|>


<commit_before>'use strict';

/**
 * This microservice inserts the post data in the DynamoDB database.
 *
 * @author Sam Verschueren      <sam.verschueren@gmail.com>
 * @since  26 Jul. 2015
 */

// module dependencies
var db = require('dynongo'),
    moment = require('moment'),
    Q = require('q');

// connect with the database
db.connect();

var Selfie = db.table('Selfie');

/**
 * Main entrypoint of the service.
 *
 * @param {object}  event       The data regarding the event.
 * @param {object}  context     The AWS Lambda execution context.
 */
exports.handler = function(event, context) {
    Q.fcall(function() {
        // Make sure the date is in utc
        var date = moment().utc().format('YYYY-MM-DD[T]HH:mm:ss[Z]');

        // Insert the selfie in the database
        return Selfie.insert({email: event.email, date: date}, {description: event.description || '', selfie: event.selfie}).exec();
    }).then(function() {
        // Selfie successfully inserted
        context.succeed();
    }).catch(function(err) {
        // Something went wrong
        context.fail(err);
    });
};<commit_msg>Printing the error when something goes wrong<commit_after>'use strict';

/**
 * This microservice inserts the post data in the DynamoDB database.
 *
 * @author Sam Verschueren      <sam.verschueren@gmail.com>
 * @since  26 Jul. 2015
 */

// module dependencies
var db = require('dynongo'),
    moment = require('moment'),
    Q = require('q');

// connect with the database
db.connect();

var Selfie = db.table('Selfie');

/**
 * Main entrypoint of the service.
 *
 * @param {object}  event       The data regarding the event.
 * @param {object}  context     The AWS Lambda execution context.
 */
exports.handler = function(event, context) {
    Q.fcall(function() {
        // Make sure the date is in utc
        var date = moment().utc().format('YYYY-MM-DD[T]HH:mm:ss[Z]');

        // Insert the selfie in the database
        return Selfie.insert({email: event.email, date: date}, {description: event.description || '', selfie: event.selfie}).exec();
    }).then(function() {
        // Selfie successfully inserted
        context.succeed();
    }).catch(function(err) {
        // Print the error if something went wrong
        console.error(err, err.stack);

        // Something went wrong
        context.fail(err);
    });
};<|endoftext|>


<commit_before>function PathDbMods() {
  console.log("PathDbMods()...");
  // put the auth jwt in cookie as token
  fetch("/jwt/token", {
    method: 'GET',
    credentials: 'include'
  }).then(x => x.json()).then(x => {
    console.log(x)
    if (x.hasOwnProperty('token') && x.token) {
      document.cookie = "token=" + x.token + ";"
    }
  })
  /**
  Gets a named cookie value
  * @param {string} key - the key to get from the cookie
  **/
  function getCookie(key) {
    var cookiestring = RegExp("" + key + "[^;]+").exec(document.cookie);
    return decodeURIComponent(!!cookiestring ? cookiestring.toString().replace(/^[^=]+./, "") : "");
  }
  console.warn("{PathDB mods enabled}")
  Store.prototype.default_findMark = Store.prototype.findMark
  Store.prototype.findMark = function(slide, name, specimen, study, footprint, source, x0, x1, y0, y1) {
    var suffix = "Mark/find"
    var url = this.base + suffix;
    var query = {}
    var bySlideId
    if (slide) {
      query.slide = slide
    }
    if (name) {
      query.name = name
    }
    if (specimen) {
      query.specimen = specimen
    }
    if (study) {
      query.study = study
    }
    if (footprint) {
      query.footprint = footprint
    }
    if (source) {
      query.source = source
    }
    if (x0) {
      query.x0 = x0;
    }
    if (x1) {
      query.x1 = x1;
    }
    if (y0) {
      query.y0 = y0;
    }
    if (y1) {
      query.y1 = y1;
    }
    let bySlide = fetch(url + "?" + objToParamStr(query), {
      credentials: "same-origin",
      mode: "cors"
    }).then(this.errorHandler).then(x => this.filterBroken(x, "mark"))
    return bySlide
  }
  Store.prototype.getMarkByIds = function(ids, slide, study, specimen, source, footprint, x0, x1, y0, y1) {
    if (!Array.isArray(ids) || !slide) {
      return {
        hasError: true,
        message: 'args are illegal'
      }
    }
    var bySlideId
    var suffix = "Mark/multi"
    var url = this.base + suffix;
    var query = {}
    var stringifiedIds = ids.map(id => `"${id}"`).join(',');
    query.name = `[${stringifiedIds}]`;
    query.slide = slide;
    if (study) {
      query.study = study;
    }
    if (specimen) {
      query.specimen = specimen;
    }
    if (source) {
      query.source = source;
    }
    if (footprint) {
      query.footprint = footprint;
    }
    if (x0) {
      query.x0 = x0;
    }
    if (x1) {
      query.x1 = x1;
    }
    if (y0) {
      query.y0 = y0;
    }
    if (y1) {
      query.y1 = y1;
    }
    let bySlide = fetch(url + "?" + objToParamStr(query), {
      credentials: "same-origin",
      mode: "cors"
    }).then(this.errorHandler).then(x => this.filterBroken(x, "mark"))
    return bySlide
  }
  Store.prototype.findMarkTypes = function(slide, name) {
    var suffix = "Mark/types"
    var url = this.base + suffix;
    var query = {}
    var bySlideId
    if (name) {
      query.name = name
    }
    if (slide) {
      query.slide = slide
    }
    let bySlide = fetch(url + "?" + objToParamStr(query), {
      credentials: "same-origin",
      mode: "cors"
    }).then(this.errorHandler)
    return bySlide
  }
  Store.prototype.default_findSlide = Store.prototype.findSlide;
  Store.prototype.findSlide = function(slide, specimen, study, location) {
    var url = "/node/" + slide + "?_format=json"
    return fetch(url, {
      mode: "cors",
      headers: new Headers({
        'Authorization': 'Bearer ' + getCookie("token"),
      })
    }).then(function(response) {
      if (!response.ok) return {
        error: !response.ok,
        text: response.statusText,
        url: response.url
      };
      return response.json().then(x => [x]);
    })
  }
  Store.prototype.default_getSlide = Store.prototype.getSlide
  Store.prototype.getSlide = function(id) {
    var url = "/node/" + id + "?_format=json"
    return fetch(url, {
      mode: "cors",
      headers: new Headers({
        'Authorization': 'Bearer ' + getCookie("token"),
      })
    }).then(function(response) {
      if (!response.ok) return {
        error: !response.ok,
        text: response.statusText,
        url: response.url
      };
      return response.json().then(x => [x]);
    })
  }

  Store.prototype.default_findHeatmap = Store.prototype.findHeatmap
  Store.prototype.findHeatmap = function(slide, name) {
    var suffix = "Heatmap/find"
    var url = this.base + suffix;
    var query = {}
    var bySlideId
    if (name) {
      query.name = name
    }
    if (slide) {
      query.slide = slide
    }
    return fetch(url + "?" + objToParamStr(query), {
      credentials: "same-origin",
      mode: "cors"
    }).then(this.errorHandler).then(x => this.filterBroken(x, "heatmap"))
  }

  Store.prototype.default_findHeatmapType = Store.prototype.findHeatmapType
  Store.prototype.findHeatmapType = function(slide, name) {
    var suffix = "Heatmap/types"
    var url = this.base + suffix;
    var query = {}
    var bySlideId
    if (name) {
      query.name = name
    }
    if (slide) {
      query.slide = slide
    }
    return fetch(url + "?" + objToParamStr(query), {
      credentials: "same-origin",
      mode: "cors"
    }).then(this.errorHandler).then(x=>this.filterBroken(x, "heatmap"))
  }

  CaMic.prototype.default_loadImg = CaMic.prototype.loadImg
  CaMic.prototype.loadImg = function(func) {
    var urlParams = new URLSearchParams(window.location.search);
    var pathdb_id = urlParams.get('slideId');
    this.slideId = pathdb_id // default value
    this.slideName = pathdb_id
    this.study = ""
    this.specimen = ""
    this.store.getSlide(pathdb_id).then(data => {
      data = data[0]
      console.log(data)
      // get slide metadata
      if (data.field_study && data.field_study.length >=1){
        this.study = data.field_study[0].value
      }
      if (data.field_study && data.field_study.length >=1){
        this.study = data.field_study[0].value
      }
      if (data.field_study_id && data.field_study_id.length >=1){
        this.study = data.field_study_id[0].value
      }
      if (data.field_specimen_id && data.field_specimen_id.length >=1){
        this.specien = data.field_specimen_id[0].value
      }
      if (data.title && data.title.length >=1){
        this.slideName = data.title[0].value
        this.slideId = data.title[0].value
      }
      if (data.field_case_id && data.field_case_id.length >=1){
        this.slideName = data.field_case_id[0].value
        this.slideId = data.field_case_id[0].value
      }
      // set mpp
      this.mpp = 1e9

      if (data.field_mpp_y && data.field_mpp_y.length >= 1) {
        this.mpp_y = data.field_mpp_y[0].value
        this.mpp = this.mpp_y
      }
      if (data.field_mpp_x && data.field_mpp_x.length >= 1) {
        this.mpp_x = data.field_mpp_x[0].value
        this.mpp = this.mpp_x
      }
      if (data.field_iip_path && data.field_iip_path.length >= 1) {
        this.location = data.field_iip_path[0].value
        // MAKE URL FOR IIP
        this.url = "../../img/IIP/raw/?DeepZoom=" + this.location + ".dzi"
        this.viewer.open(this.url);
      } else {
        throw "No image location --could be token"
      }

      this.viewer.mpp = this.mpp;
      this.viewer.mpp_x = this.mpp_x;
      this.viewer.mpp_y = this.mpp_y;

      //set scalebar
      if (this.mpp && this.mpp != 1e9) this.createScalebar(this.mpp)
      var imagingHelper = new OpenSeadragonImaging.ImagingHelper({
        viewer: this.viewer
      });
      imagingHelper.setMaxZoom(1);
      // create item to pass to the callback function, previously x[0] (slide data)
      let x = {}
      x['_id'] = this.slideId
      x.name = this.slideName
      x.study = this.study
      x.specimen = this.specimen
      x.mpp = this.mpp;
      x.mpp_x = this.mpp_x;
      x.mpp_y = this.mpp_y;
      x.location = this.location;
      x.url = this.url;
      if (func && typeof func === 'function') {
        func.call(null, x);
      }
      Loading.text.textContent = `loading slide's tiles...`;
      // we may want another init.js or our own callback
    }).catch(e => {
      console.error(e)
      Loading.text.textContent = "ERROR - PathDB Image Error (Try a refresh)"
      //if(func && typeof func === 'function') func.call(null,{hasError:true,message:e});
    })

  }
}

PathDbMods()
console.warn("This setup is intended for pathdb")
<commit_msg>update<commit_after>function PathDbMods() {
  console.log("PathDbMods()...");
  // put the auth jwt in cookie as token
  fetch("/jwt/token", {
    method: 'GET',
    credentials: 'include'
  }).then(x => x.json()).then(x => {
    console.log(x)
    if (x.hasOwnProperty('token') && x.token) {
      document.cookie = "token=" + x.token + ";"
    }
  })
  /**
  Gets a named cookie value
  * @param {string} key - the key to get from the cookie
  **/
  function getCookie(key) {
    var cookiestring = RegExp("" + key + "[^;]+").exec(document.cookie);
    return decodeURIComponent(!!cookiestring ? cookiestring.toString().replace(/^[^=]+./, "") : "");
  }
  console.warn("{PathDB mods enabled}")
  Store.prototype.default_findMark = Store.prototype.findMark
  Store.prototype.findMark = function(slide, name, specimen, study, footprint, source, x0, x1, y0, y1) {
    var suffix = "Mark/find"
    var url = this.base + suffix;
    var query = {}
    var bySlideId
    if (slide) {
      query.slide = slide
    }
    if (name) {
      query.name = name
    }
    if (specimen) {
      query.specimen = specimen
    }
    if (study) {
      query.study = study
    }
    if (footprint) {
      query.footprint = footprint
    }
    if (source) {
      query.source = source
    }
    if (x0) {
      query.x0 = x0;
    }
    if (x1) {
      query.x1 = x1;
    }
    if (y0) {
      query.y0 = y0;
    }
    if (y1) {
      query.y1 = y1;
    }
    let bySlide = fetch(url + "?" + objToParamStr(query), {
      credentials: "same-origin",
      mode: "cors"
    }).then(this.errorHandler).then(x => this.filterBroken(x, "mark"))
    return bySlide
  }
  Store.prototype.getMarkByIds = function(ids, slide, study, specimen, source, footprint, x0, x1, y0, y1) {
    if (!Array.isArray(ids) || !slide) {
      return {
        hasError: true,
        message: 'args are illegal'
      }
    }
    var bySlideId
    var suffix = "Mark/multi"
    var url = this.base + suffix;
    var query = {}
    var stringifiedIds = ids.map(id => `"${id}"`).join(',');
    query.name = `[${stringifiedIds}]`;
    query.slide = slide;
    if (study) {
      query.study = study;
    }
    if (specimen) {
      query.specimen = specimen;
    }
    if (source) {
      query.source = source;
    }
    if (footprint) {
      query.footprint = footprint;
    }
    if (x0) {
      query.x0 = x0;
    }
    if (x1) {
      query.x1 = x1;
    }
    if (y0) {
      query.y0 = y0;
    }
    if (y1) {
      query.y1 = y1;
    }
    let bySlide = fetch(url + "?" + objToParamStr(query), {
      credentials: "same-origin",
      mode: "cors"
    }).then(this.errorHandler).then(x => this.filterBroken(x, "mark"))
    return bySlide
  }
  Store.prototype.findMarkTypes = function(slide, name) {
    var suffix = "Mark/types"
    var url = this.base + suffix;
    var query = {}
    var bySlideId
    if (name) {
      query.name = name
    }
    if (slide) {
      query.slide = slide
    }
    let bySlide = fetch(url + "?" + objToParamStr(query), {
      credentials: "same-origin",
      mode: "cors"
    }).then(this.errorHandler)
    return bySlide
  }
  Store.prototype.default_findSlide = Store.prototype.findSlide;
  Store.prototype.findSlide = function(slide, specimen, study, location) {
    var url = "/node/" + slide + "?_format=json"
    return fetch(url, {
      mode: "cors",
      headers: new Headers({
        'Authorization': 'Bearer ' + getCookie("token"),
      })
    }).then(function(response) {
      if (!response.ok) return {
        error: !response.ok,
        text: response.statusText,
        url: response.url
      };
      return response.json().then(x => [x]);
    })
  }
  Store.prototype.default_getSlide = Store.prototype.getSlide
  Store.prototype.getSlide = function(id) {
    var url = "/node/" + id + "?_format=json"
    return fetch(url, {
      mode: "cors",
      headers: new Headers({
        'Authorization': 'Bearer ' + getCookie("token"),
      })
    }).then(function(response) {
      if (!response.ok) return {
        error: !response.ok,
        text: response.statusText,
        url: response.url
      };
      return response.json().then(x => [x]);
    })
  }

  CaMic.prototype.default_loadImg = CaMic.prototype.loadImg
  CaMic.prototype.loadImg = function(func) {
    var urlParams = new URLSearchParams(window.location.search);
    var pathdb_id = urlParams.get('slideId');
    this.slideId = pathdb_id // default value
    this.slideName = pathdb_id
    this.study = ""
    this.specimen = ""
    this.store.getSlide(pathdb_id).then(data => {
      data = data[0]
      console.log(data)
      // get slide metadata
      if (data.field_study && data.field_study.length >=1){
        this.study = data.field_study[0].value
      }
      if (data.field_study && data.field_study.length >=1){
        this.study = data.field_study[0].value
      }
      if (data.field_study_id && data.field_study_id.length >=1){
        this.study = data.field_study_id[0].value
      }
      if (data.field_specimen_id && data.field_specimen_id.length >=1){
        this.specien = data.field_specimen_id[0].value
      }
      if (data.title && data.title.length >=1){
        this.slideName = data.title[0].value
        this.slideId = data.title[0].value
      }
      if (data.field_case_id && data.field_case_id.length >=1){
        this.slideName = data.field_case_id[0].value
        this.slideId = data.field_case_id[0].value
      }
      // set mpp
      this.mpp = 1e9

      if (data.field_mpp_y && data.field_mpp_y.length >= 1) {
        this.mpp_y = data.field_mpp_y[0].value
        this.mpp = this.mpp_y
      }
      if (data.field_mpp_x && data.field_mpp_x.length >= 1) {
        this.mpp_x = data.field_mpp_x[0].value
        this.mpp = this.mpp_x
      }
      if (data.field_iip_path && data.field_iip_path.length >= 1) {
        this.location = data.field_iip_path[0].value
        // MAKE URL FOR IIP
        this.url = "../../img/IIP/raw/?DeepZoom=" + this.location + ".dzi"
        this.viewer.open(this.url);
      } else {
        throw "No image location --could be token"
      }

      this.viewer.mpp = this.mpp;
      this.viewer.mpp_x = this.mpp_x;
      this.viewer.mpp_y = this.mpp_y;

      //set scalebar
      if (this.mpp && this.mpp != 1e9) this.createScalebar(this.mpp)
      var imagingHelper = new OpenSeadragonImaging.ImagingHelper({
        viewer: this.viewer
      });
      imagingHelper.setMaxZoom(1);
      // create item to pass to the callback function, previously x[0] (slide data)
      let x = {}
      x['_id'] = this.slideId
      x.name = this.slideName
      x.study = this.study
      x.specimen = this.specimen
      x.mpp = this.mpp;
      x.mpp_x = this.mpp_x;
      x.mpp_y = this.mpp_y;
      x.location = this.location;
      x.url = this.url;
      if (func && typeof func === 'function') {
        func.call(null, x);
      }
      Loading.text.textContent = `loading slide's tiles...`;
      // we may want another init.js or our own callback
    }).catch(e => {
      console.error(e)
      Loading.text.textContent = "ERROR - PathDB Image Error (Try a refresh)"
      //if(func && typeof func === 'function') func.call(null,{hasError:true,message:e});
    })

  }
}

PathDbMods()
console.warn("This setup is intended for pathdb")
<|endoftext|>


<commit_before>const { exec } = require('child_process')

module.exports = {
  name: 'template',
  version: '0.2.0',
  source: 'template',
  metadata: {
    year: new Date().getFullYear()
  },
  prompts: {
    name: {
      type: 'input',
      message: 'Template name'
    },
    version: {
      type: 'input',
      message: 'Template version'
    },
    description: {
      type: 'input',
      message: 'Template description',
      default: 'Awesome zce-cli template'
    },
    author: {
      type: 'input',
      message: 'Template author'
    },
    organization: {
      type: 'input',
      message: 'Template organization name',
      default: 'zce-templates'
    },
    source: {
      type: 'input',
      message: 'Template source directory name',
      default: 'template'
    },
    features: {
      type: 'checkbox',
      message: 'Choose the features you need',
      choices: [
        { name: 'Use metadata', value: 'metadata' },
        { name: 'Custom prompts', value: 'prompts', checked: true },
        { name: 'Custom filters', value: 'filters' },
        { name: 'Custom helpers', value: 'helpers' },
        { name: 'Custom plugin', value: 'plugin' },
        { name: 'Additional docs', value: 'docs' },
        { name: 'Automatic test', value: 'test' }
      ]
    },
    complete: {
      type: 'list',
      message: 'Complete type',
      choices: [
        { name: 'Message', value: 'message' },
        { name: 'Callback', value: 'callback' }
      ]
    }
  },
  filters: {
    'docs/**': answers => answers.features.docs,
    'test/**': answers => answers.features.test,
    '.travis.yml': answers => answers.features.test
  },
  complete: context => {
    const { dest } = context
    exec('git init', { cwd: dest }, () => {
      console.log('👏  template init into ' + dest)
    })
  }
}
<commit_msg>:bookmark: v0.2.0<commit_after>const path = require('path')
const { exec } = require('child_process')

module.exports = {
  name: 'template',
  version: '0.2.0',
  source: 'template',
  metadata: {
    year: new Date().getFullYear()
  },
  prompts: {
    name: {
      type: 'input',
      message: 'Template name'
    },
    version: {
      type: 'input',
      message: 'Template version'
    },
    description: {
      type: 'input',
      message: 'Template description',
      default: 'Awesome zce-cli template'
    },
    author: {
      type: 'input',
      message: 'Template author'
    },
    organization: {
      type: 'input',
      message: 'Template organization name',
      default: 'zce-templates'
    },
    source: {
      type: 'input',
      message: 'Template source directory name',
      default: 'template'
    },
    features: {
      type: 'checkbox',
      message: 'Choose the features you need',
      choices: [
        { name: 'Use metadata', value: 'metadata' },
        { name: 'Custom prompts', value: 'prompts', checked: true },
        { name: 'Custom filters', value: 'filters' },
        { name: 'Custom helpers', value: 'helpers' },
        { name: 'Custom plugin', value: 'plugin' },
        { name: 'Additional docs', value: 'docs' },
        { name: 'Automatic test', value: 'test' }
      ]
    },
    complete: {
      type: 'list',
      message: 'Complete type',
      choices: [
        { name: 'Message', value: 'message' },
        { name: 'Callback', value: 'callback' }
      ]
    }
  },
  filters: {
    'docs/**': answers => answers.features.docs,
    'test/**': answers => answers.features.test,
    '.travis.yml': answers => answers.features.test
  },
  complete: context => {
    const { dest } = context
    const reldest = path.relative(process.cwd(), dest)
    exec('git init', { cwd: dest }, () => console.log('✨  new template →', reldest, '✨\n\n'))
  }
}
<|endoftext|>


<commit_before>import 'isomorphic-fetch';
import Promise from 'bluebird';
import * as queryString from 'query-string';

export default class UserService {
  constructor(options) {
    if (!options) {
      throw new Error('options params is required.');
    }
    if (!options.apiURL || options.apiURL === '') {
      throw new Error('options.apiURL param is required.');
    }
    this.opts = options;
  }

  /**
   * Gets the user that is currently logged
   * @returns {Promise}
   */
  getLoggedUser(token) {
    return new Promise((resolve) => {
      fetch(`${this.opts.apiURL}/auth/check-logged`, {
        headers: {
          Authorization: token,
          'Upgrade-Insecure-Requests': 1
        }
      })
        .then(response => resolve(response.json()));
    });
  }

  /**
  * Updates the user that is currently logged in
  */
  updateUser(user, token) {
    return fetch(`${this.opts.apiURL}/auth/user/me`, {
      method: 'PATCH',
      body: JSON.stringify(user),
      headers: {
        'Content-Type': 'application/json',
        Authorization: token
      }
    })
      .then(response => response.json());
  }

  /**
   * Register a new user based on email + password combination
   */
  registerUser({ email, password, repeatPassword }) {
    return fetch(`${this.opts.apiURL}/auth/sign-up`, {
      method: 'POST',
      body: JSON.stringify({ email, password, repeatPassword }),
      headers: { 'Content-Type': 'application/json' }
    })
      .then((response) => {
        if (response.ok) return response.json();
        throw response;
      });
  }

  /**
   * Logs in a user based on email + password combination
   */
  loginUser({ email, password }) {
    const queryParams = queryString.stringify({
      callbackUrl: process.env.CALLBACK_URL,
      applications: 'rw',
      token: true,
      origin: 'rw'
    });

    return fetch(`${this.opts.apiURL}/auth/login?${queryParams}`, {
      method: 'POST',
      body: JSON.stringify({ email, password }),
      headers: { 'Content-Type': 'application/json' }
    })
      .then((response) => {
        if (response.ok) return response.json();
        throw response;
      });
  }

  /**
   * Gets the widgets that have been starred/favourited by the user that is
   * currently logged
   * @param {token} User token
   * @returns {Promise}
   */
  getFavouriteWidgets(token) {
    return this.setFavourites(token, 'widget', true);
  }

  /**
   * Gets the datasets that have been starred/favourited by the user that is
   * currently logged
   * @param {token} User token
   * @returns {Promise}
   */
  getFavouriteDatasets(token) {
    return this.setFavourites(token, 'dataset', true);
  }

  /**
   * Gets the contents that have been starred/favourited by the user that is
   * currently logged
    * @param {token} User token
   * @returns {Promise}
   */
  setFavourites(token, resourceType = null, include = true) {
    const resourceTypeSt = (resourceType !== null) ? `&resource-type=${resourceType}` : '';
    return fetch(`${this.opts.apiURL}/favourite?include=${include}${resourceTypeSt}&application=${process.env.APPLICATIONS}`, {
      headers: {
        Authorization: token,
        'Upgrade-Insecure-Requests': 1
      }
    })
      .then((response) => {
        if (response.ok) return response.json();
        throw new Error(response.statusText);
      });
  }

  /**
   * Deletes a favourite
   * @param {resourceId} ID of the resource that will be unfavourited
   * @param {token} User token
   * @returns {Promise}
   */
  deleteFavourite(resourceId, token) {
    return fetch(`${this.opts.apiURL}/favourite/${resourceId}`, {
      method: 'DELETE',
      headers: {
        Authorization: token
      }
    })
      .then((response) => {
        if (response.ok) return response.json();
        throw new Error(response.statusText);
      });
  }

  /**
   * Creates a new favourite for a resource
   * @param {resourceType} Type of the resource (dataset|layer|widget)
   * @param {resourceId} Resource ID
   * @param {token} User token
   * @returns {Promise}
   */
  createFavourite(resourceType, resourceId, token) {
    const bodyObj = {
      resourceType,
      resourceId
    };
    return fetch(`${this.opts.apiURL}/favourite`, {
      method: 'POST',
      body: JSON.stringify(bodyObj),
      headers: {
        'Content-Type': 'application/json',
        Authorization: token
      }
    })
      .then((response) => {
        if (response.ok) return response.json();
        throw new Error(response.statusText);
      });
  }

  /**
   * Creates a subscription for a pair of dataset and country
   * @param {datasetID} ID of the dataset
   * @param {object} Either { type; 'iso', id:'ESP' } or { type: 'geostore', id: 'sakldfa7ads0ka'}
   * @param {string} language Two-letter locale
   * @returns {Promise}
   */
  createSubscriptionToArea(areaId, datasets, datasetsQuery, user, language, name = '') {
    const bodyObj = {
      name,
      application: process.env.APPLICATIONS,
      language: language || 'en',
      datasets,
      datasetsQuery,
      resource: {
        type: 'EMAIL',
        content: user.email
      },
      params: {
        area: areaId
      }
    };

    return fetch(`${this.opts.apiURL}/subscriptions`, {
      method: 'POST',
      body: JSON.stringify(bodyObj),
      headers: {
        'Content-Type': 'application/json',
        Authorization: user.token
      }
    })
      .then(response => response.json());
  }

  /**
   *  Update Subscription
   */
  updateSubscriptionToArea(subscriptionId, datasets, datasetsQuery, user, language) {

    const bodyObj = {
      application: process.env.APPLICATIONS,
      language: language || 'en',
      datasets,
      datasetsQuery
    };

    return fetch(`${this.opts.apiURL}/subscriptions/${subscriptionId}`, {
      method: 'PATCH',
      body: JSON.stringify(bodyObj),
      headers: {
        'Content-Type': 'application/json',
        Authorization: user.token
      }
    })
      .then(response => response.json());
  }

  /**
   *  Get Subscriptions
   */
  getSubscriptions(token) {
    return new Promise((resolve) => {
      fetch(`${this.opts.apiURL}/subscriptions?application=${process.env.APPLICATIONS}`, {
        headers: {
          Authorization: token
        }
      })
        .then(response => response.json())
        .then(jsonData => resolve(jsonData.data));
    });
  }

  /**
   *  Get Subscription
   */
  getSubscription(subscriptionId, token) {
    return new Promise((resolve) => {
      fetch(`${this.opts.apiURL}/v1/subscriptions/${subscriptionId}/data`, {
        headers: {
          Authorization: token
        }
      })
        .then(response => response.json())
        .then(jsonData => resolve(jsonData.data));
    });
  }

  /**
   * Deletes a subscription
   * @param {subscriptionId} ID of the subscription that will be deleted
   * @param {token} User token
   * @returns {Promise}
   */
  deleteSubscription(subscriptionId, token) {
    return fetch(`${this.opts.apiURL}/subscriptions/${subscriptionId}`, {
      method: 'DELETE',
      headers: {
        Authorization: token
      }
    })
      .then(response => response.json());
  }

  /**
   * Get user areas
   */
  getUserAreas(token) {
    return new Promise((resolve, reject) => {
      fetch(`${this.opts.apiURL}/area?application=${process.env.APPLICATIONS}`, {
        headers: {
          Authorization: token,
          'Upgrade-Insecure-Requests': 1
        }
      })
        .then((response) => {
          if (response.ok) return response.json();
          throw new Error(response.statusText);
        })
        .then(jsonData => resolve(jsonData.data))
        .catch(err => reject(err.message));
    });
  }

  /**
   * Create new area
   */
  createNewArea(name, geostore, token) {
    const bodyObj = {
      name,
      application: process.env.APPLICATIONS,
      geostore
    };

    return fetch(`${this.opts.apiURL}/area`, {
      method: 'POST',
      body: JSON.stringify(bodyObj),
      headers: {
        'Content-Type': 'application/json',
        Authorization: token
      }
    })
      .then(response => response.json());
  }

  /**
  * Update area
  */
  updateArea(id, name, token, geostore) {
    const bodyObj = {
      name,
      application: process.env.APPLICATIONS,
      geostore
    };

    return fetch(`${this.opts.apiURL}/area/${id}`, {
      method: 'PATCH',
      body: JSON.stringify(bodyObj),
      headers: {
        'Content-Type': 'application/json',
        Authorization: token
      }
    })
      .then(response => response.json());
  }

  /**
   * Deletes an area
   * @param {areaId} ID of the area that will be deleted
   * @param {token} User token
   * @returns {Promise}
   */
  deleteArea(areaId, token) {
    return fetch(`${this.opts.apiURL}/area/${areaId}`, {
      method: 'DELETE',
      headers: {
        Authorization: token
      }
    });
  }

  /**
   * Get area
   */
  getArea(id, token) {
    return fetch(`${this.opts.apiURL}/area/${id}`, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        Authorization: token,
        'Upgrade-Insecure-Requests': 1
      }
    })
      .then(response => response.json());
  }

  uploadPhoto(file, user) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(file);

      reader.onload = () => {
        const bodyObj = {
          data: {
            attributes: {
              user_id: user.id,
              avatar: reader.result
            }
          }
        };

        return fetch(`${process.env.API_URL}/profiles`, {
          method: 'POST',
          body: JSON.stringify(bodyObj),
          headers: {
            'Content-Type': 'application/json',
            Authorization: user.token
          }
        })
          .then(response => response.json())
          .then(({ data }) => {
            resolve(data.attributes.avatar.original);
          });
      };

      reader.onerror = (error) => {
        reject(error);
      };
    });
  }
}
<commit_msg>Adds apps property to register user call<commit_after>import 'isomorphic-fetch';
import Promise from 'bluebird';
import * as queryString from 'query-string';

export default class UserService {
  constructor(options) {
    if (!options) {
      throw new Error('options params is required.');
    }
    if (!options.apiURL || options.apiURL === '') {
      throw new Error('options.apiURL param is required.');
    }
    this.opts = options;
  }

  /**
   * Gets the user that is currently logged
   * @returns {Promise}
   */
  getLoggedUser(token) {
    return new Promise((resolve) => {
      fetch(`${this.opts.apiURL}/auth/check-logged`, {
        headers: {
          Authorization: token,
          'Upgrade-Insecure-Requests': 1
        }
      })
        .then(response => resolve(response.json()));
    });
  }

  /**
  * Updates the user that is currently logged in
  */
  updateUser(user, token) {
    return fetch(`${this.opts.apiURL}/auth/user/me`, {
      method: 'PATCH',
      body: JSON.stringify(user),
      headers: {
        'Content-Type': 'application/json',
        Authorization: token
      }
    })
      .then(response => response.json());
  }

  /**
   * Register a new user based on email + password combination
   */
  registerUser({ email, password, repeatPassword }) {
    return fetch(`${this.opts.apiURL}/auth/sign-up`, {
      method: 'POST',
      body: JSON.stringify({
        email,
        password,
        repeatPassword,
        apps: ['rw']
      }),
      headers: { 'Content-Type': 'application/json' }
    })
      .then((response) => {
        if (response.ok) return response.json();
        throw response;
      });
  }

  /**
   * Logs in a user based on email + password combination
   */
  loginUser({ email, password }) {
    const queryParams = queryString.stringify({
      callbackUrl: process.env.CALLBACK_URL,
      applications: 'rw',
      token: true,
      origin: 'rw'
    });

    return fetch(`${this.opts.apiURL}/auth/login?${queryParams}`, {
      method: 'POST',
      body: JSON.stringify({ email, password }),
      headers: { 'Content-Type': 'application/json' }
    })
      .then((response) => {
        if (response.ok) return response.json();
        throw response;
      });
  }

  /**
   * Gets the widgets that have been starred/favourited by the user that is
   * currently logged
   * @param {token} User token
   * @returns {Promise}
   */
  getFavouriteWidgets(token) {
    return this.setFavourites(token, 'widget', true);
  }

  /**
   * Gets the datasets that have been starred/favourited by the user that is
   * currently logged
   * @param {token} User token
   * @returns {Promise}
   */
  getFavouriteDatasets(token) {
    return this.setFavourites(token, 'dataset', true);
  }

  /**
   * Gets the contents that have been starred/favourited by the user that is
   * currently logged
    * @param {token} User token
   * @returns {Promise}
   */
  setFavourites(token, resourceType = null, include = true) {
    const resourceTypeSt = (resourceType !== null) ? `&resource-type=${resourceType}` : '';
    return fetch(`${this.opts.apiURL}/favourite?include=${include}${resourceTypeSt}&application=${process.env.APPLICATIONS}`, {
      headers: {
        Authorization: token,
        'Upgrade-Insecure-Requests': 1
      }
    })
      .then((response) => {
        if (response.ok) return response.json();
        throw new Error(response.statusText);
      });
  }

  /**
   * Deletes a favourite
   * @param {resourceId} ID of the resource that will be unfavourited
   * @param {token} User token
   * @returns {Promise}
   */
  deleteFavourite(resourceId, token) {
    return fetch(`${this.opts.apiURL}/favourite/${resourceId}`, {
      method: 'DELETE',
      headers: {
        Authorization: token
      }
    })
      .then((response) => {
        if (response.ok) return response.json();
        throw new Error(response.statusText);
      });
  }

  /**
   * Creates a new favourite for a resource
   * @param {resourceType} Type of the resource (dataset|layer|widget)
   * @param {resourceId} Resource ID
   * @param {token} User token
   * @returns {Promise}
   */
  createFavourite(resourceType, resourceId, token) {
    const bodyObj = {
      resourceType,
      resourceId
    };
    return fetch(`${this.opts.apiURL}/favourite`, {
      method: 'POST',
      body: JSON.stringify(bodyObj),
      headers: {
        'Content-Type': 'application/json',
        Authorization: token
      }
    })
      .then((response) => {
        if (response.ok) return response.json();
        throw new Error(response.statusText);
      });
  }

  /**
   * Creates a subscription for a pair of dataset and country
   * @param {datasetID} ID of the dataset
   * @param {object} Either { type; 'iso', id:'ESP' } or { type: 'geostore', id: 'sakldfa7ads0ka'}
   * @param {string} language Two-letter locale
   * @returns {Promise}
   */
  createSubscriptionToArea(areaId, datasets, datasetsQuery, user, language, name = '') {
    const bodyObj = {
      name,
      application: process.env.APPLICATIONS,
      language: language || 'en',
      datasets,
      datasetsQuery,
      resource: {
        type: 'EMAIL',
        content: user.email
      },
      params: {
        area: areaId
      }
    };

    return fetch(`${this.opts.apiURL}/subscriptions`, {
      method: 'POST',
      body: JSON.stringify(bodyObj),
      headers: {
        'Content-Type': 'application/json',
        Authorization: user.token
      }
    })
      .then(response => response.json());
  }

  /**
   *  Update Subscription
   */
  updateSubscriptionToArea(subscriptionId, datasets, datasetsQuery, user, language) {

    const bodyObj = {
      application: process.env.APPLICATIONS,
      language: language || 'en',
      datasets,
      datasetsQuery
    };

    return fetch(`${this.opts.apiURL}/subscriptions/${subscriptionId}`, {
      method: 'PATCH',
      body: JSON.stringify(bodyObj),
      headers: {
        'Content-Type': 'application/json',
        Authorization: user.token
      }
    })
      .then(response => response.json());
  }

  /**
   *  Get Subscriptions
   */
  getSubscriptions(token) {
    return new Promise((resolve) => {
      fetch(`${this.opts.apiURL}/subscriptions?application=${process.env.APPLICATIONS}`, {
        headers: {
          Authorization: token
        }
      })
        .then(response => response.json())
        .then(jsonData => resolve(jsonData.data));
    });
  }

  /**
   *  Get Subscription
   */
  getSubscription(subscriptionId, token) {
    return new Promise((resolve) => {
      fetch(`${this.opts.apiURL}/v1/subscriptions/${subscriptionId}/data`, {
        headers: {
          Authorization: token
        }
      })
        .then(response => response.json())
        .then(jsonData => resolve(jsonData.data));
    });
  }

  /**
   * Deletes a subscription
   * @param {subscriptionId} ID of the subscription that will be deleted
   * @param {token} User token
   * @returns {Promise}
   */
  deleteSubscription(subscriptionId, token) {
    return fetch(`${this.opts.apiURL}/subscriptions/${subscriptionId}`, {
      method: 'DELETE',
      headers: {
        Authorization: token
      }
    })
      .then(response => response.json());
  }

  /**
   * Get user areas
   */
  getUserAreas(token) {
    return new Promise((resolve, reject) => {
      fetch(`${this.opts.apiURL}/area?application=${process.env.APPLICATIONS}`, {
        headers: {
          Authorization: token,
          'Upgrade-Insecure-Requests': 1
        }
      })
        .then((response) => {
          if (response.ok) return response.json();
          throw new Error(response.statusText);
        })
        .then(jsonData => resolve(jsonData.data))
        .catch(err => reject(err.message));
    });
  }

  /**
   * Create new area
   */
  createNewArea(name, geostore, token) {
    const bodyObj = {
      name,
      application: process.env.APPLICATIONS,
      geostore
    };

    return fetch(`${this.opts.apiURL}/area`, {
      method: 'POST',
      body: JSON.stringify(bodyObj),
      headers: {
        'Content-Type': 'application/json',
        Authorization: token
      }
    })
      .then(response => response.json());
  }

  /**
  * Update area
  */
  updateArea(id, name, token, geostore) {
    const bodyObj = {
      name,
      application: process.env.APPLICATIONS,
      geostore
    };

    return fetch(`${this.opts.apiURL}/area/${id}`, {
      method: 'PATCH',
      body: JSON.stringify(bodyObj),
      headers: {
        'Content-Type': 'application/json',
        Authorization: token
      }
    })
      .then(response => response.json());
  }

  /**
   * Deletes an area
   * @param {areaId} ID of the area that will be deleted
   * @param {token} User token
   * @returns {Promise}
   */
  deleteArea(areaId, token) {
    return fetch(`${this.opts.apiURL}/area/${areaId}`, {
      method: 'DELETE',
      headers: {
        Authorization: token
      }
    });
  }

  /**
   * Get area
   */
  getArea(id, token) {
    return fetch(`${this.opts.apiURL}/area/${id}`, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        Authorization: token,
        'Upgrade-Insecure-Requests': 1
      }
    })
      .then(response => response.json());
  }

  uploadPhoto(file, user) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(file);

      reader.onload = () => {
        const bodyObj = {
          data: {
            attributes: {
              user_id: user.id,
              avatar: reader.result
            }
          }
        };

        return fetch(`${process.env.API_URL}/profiles`, {
          method: 'POST',
          body: JSON.stringify(bodyObj),
          headers: {
            'Content-Type': 'application/json',
            Authorization: user.token
          }
        })
          .then(response => response.json())
          .then(({ data }) => {
            resolve(data.attributes.avatar.original);
          });
      };

      reader.onerror = (error) => {
        reject(error);
      };
    });
  }
}
<|endoftext|>