<commit_before>    "use strict";
    var x,
        y,
        i,
        a,
        b,
        id,
        d = document,
		highlight,
        currentPath,
        div,
		whiteTurn,
        board = [],
        convertColor = ["w", "b"], //used to give white and black number values (handy for finding the right coordinates on pieces.png)
        convertType = [null, "P", "N", "B", "R", "Q", "K"], // same as above but for piece names (Pawn, kNight, Bishop, etc.)
        convertX = [0, "a", "b", "c", "d", "e", "f", "g", "h"];

    /*
    creates 8 arrays each containing 8 arrays that will be used to store and access piece location and color on the chess board
    also styles each div element of the chess board on the html page with the pieces.png sprite centred on a blank area
    */
    for (y = 1; y < 9; y += 1) {
        board[y] = [];
        for (x = 1; x < 9; x += 1) {
            board[y][x] = {"id": convertX[x] + y, "piece": 0};
            div = d.createElement("DIV");
            div.setAttribute("class", "square");
            d.getElementById(board[y][x].id).style.backgroundImage = "url('sprites/pieces.png')";
            d.getElementById(board[y][x].id).style.backgroundPosition = "0 -200";
        }
    } //uhhhh....
    function findPath(x, y) {
        var path = [],
            clear,
            moves,
            type,
            color,
            hasMoved;
        if (board[y] === undefined)
            console.log("Doesn't exist");
        else if (board[y][x] === undefined)
            console.log("Doesn't exist");
        else if (board[y][x].piece === 0)
            console.log("Nothing here");
        else {
            type = board[y][x].piece.type;
            color = board[y][x].piece.color,
            hasMoved = board[y][x].piece.moved;
			if (color === (whiteTurn ? "w" : "b")) {
				if (type === "B") {
					moves = [{x: 1, y: 1},
							 {x: 1, y: -1},
							 {x: -1, y: 1},
							 {x: -1, y: -1}];
					for (i = 0; i < moves.length; i += 1) {
						clear = true;
						a = x;
						b = y;
						while (clear === true) {
							a += (moves[i].x);
							b += (moves[i].y);
							if (board[b] === undefined)
								clear = false;
							else if (board[b][a] === undefined)
								clear = false;
							else if (board[b][a].piece === 0)
								path[path.length] = convertX[a] + b;
							else if (board[b][a].piece.color !== color) {
								path[path.length] = convertX[a] + b;
								clear = false;
							} else
								clear = false;
						}
					}
				} else if (type === "R") {
					moves = [{x: 0, y: 1},
							 {x: 0, y: -1},
							 {x: 1, y: 0},
							 {x: -1, y: 0}];
					for (i = 0; i < moves.length; i += 1) {
						clear = true;
						a = x;
						b = y;
						while (clear === true) {
							a += (moves[i].x);
							b += (moves[i].y);
							if (board[b] === undefined){
								clear = false;
							} else if (board[b][a] === undefined) {
								clear = false;
							} else if (board[b][a].piece === 0) {
								path[path.length] = convertX[a] + b;
							} else if (board[b][a].piece.color !== color) {
								path[path.length] = convertX[a] + b;
								clear = false;
							} else {
								clear = false;
							}
						}
					}
				} else if (type === "Q") {
					moves = [{x: 0, y: 1},
							 {x: 0, y: -1},
							 {x: 1, y: 0},
							 {x: -1, y: 0},
							 {x: 1, y: 1},
							 {x: 1, y: -1},
							 {x: -1, y: 1},
							 {x: -1, y: -1}];
					for (i = 0; i < moves.length; i += 1) {
						clear = true;
						a = x;
						b = y;
						while (clear === true) {
							a += (moves[i].x);
							b += (moves[i].y);
							if (board[b] === undefined){
								clear = false;
							} else if (board[b][a] === undefined) {
								clear = false;
							} else if (board[b][a].piece === 0) {
								path[path.length] = convertX[a] + b;
							} else if (board[b][a].piece.color !== color) {
								path[path.length] = convertX[a] + b;
								clear = false;
							} else {
								clear = false;
							}
						}
					}
				} else if (type === "N") {
					moves = [{x: 2, y: 1},
							 {x: 2, y: -1},
							 {x: -2, y: 1},
							 {x: -2, y: -1},
							 {x: 1,  y: 2},
							 {x: 1, y: -2},
							 {x: -1, y: 2},
							 {x: -1, y: -2}];
					for (i = 0; i < moves.length; i += 1) {
						a = x + (moves[i].x);
						b = y + (moves[i].y);

						if (board[b] === undefined) {
						} else if (board[b][a] === undefined) {
						} else if (board[b][a].piece === 0) {
							path[path.length] = convertX[a] + b;
						} else if (board[b][a].piece.color !== color) {
							path[path.length] = convertX[a] + b;
						}
					}
				} else if (type === "P") {
					a = (2 * convertColor.indexOf(color)) - 1;
					if (board[y] === undefined) {
					} else if (board[y][x + a] === undefined) {
					} else if (board[y][x + a].piece === 0) {
						path[path.length] = convertX[x + a] + y;
					}
					if (!hasMoved && board[y][x + 2 * a].piece === 0) {
						path[path.length] = convertX[x + 2 * a] + y;
					}
					if (board[y + 1] === undefined) {
					} else if (board[y + 1][x + a] === undefined) {
					} else if (board[y + 1][x + a].piece === 0) {
					} else if (board[y + 1][x + a].piece.color !== color) {
						path[path.length] = convertX[x + a] + (y + 1);
					}
					if (board[y - 1] === undefined) {
					} else if (board[y - 1][x + a] === undefined) {
					} else if (board[y - 1][x + a].piece === 0) {
					} else if (board[y - 1][x + a].piece.color !== color) {
						path[path.length] = convertX[x + a] + (y - 1);
					}
				} else if (type === "K") {
					moves = [{x: 0, y: 1},
							{x: 0, y: -1},
							{x: 1, y: 0},
							{x: -1, y: 0},
							{x: 1, y: 1},
							{x: 1, y: -1},
							{x: -1, y: 1},
							{x: -1, y: -1}];
					for (i = 0; i < moves.length; i += 1) {
						a = x + (moves[i].x);
						b = y + (moves[i].y);
						if (board[b][a] === undefined) {
						} else if (board[b][a].piece === 0) {
							path[path.length] = convertX[a] + b;
						} else if (board[b][a].piece.color !== color) {
							path[path.length] = convertX[a] + b;
						}
					}
				}
				console.log("Legal moves for " + color + type + " at "+ board[y][x].id + ":\n" + path);
				return path;
			} else {
			console.log("The turn belongs to " + (whiteTurn ? "white" : "black"))
			}
		}
    }

    /*
    stores input data to corresponding board coordinates (board[y][x].piece = {color: color, type: type}),
    and finds the right location for it
    */
    function spawn(x, y, type, color, first) {
        var t = convertType.indexOf(type),
            c = convertColor.indexOf(color),
            bgY = Math.round(t / 2) * -50 + "px",
            bgX = ((((t) - 1) % 2) + ((c) * 2)) * -50 + "px",
            bg = bgX + " " + bgY;
        board[y][x].piece = {};
        board[y][x].piece.color = color;
        board[y][x].piece.type = type;
        if (first) {
            board[y][x].piece.moved = false;
        } else {
            board[y][x].piece.moved = true;
        }
        d.getElementById(board[y][x].id).style.backgroundPosition = bg;
        return;
    }

    function clear(x, y) {
        board[y][x].piece = 0;
        d.getElementById(board[y][x].id).style.backgroundPositionX = "-200";
		d.getElementById(board[y][x].id).style.backgroundPositionY = "0";
    }

	function clearVisualPath() {
		for (a = 0; a < 8; a += 1) {
			for (b = 0; b < 8; b += 1) {
                id = board[a + 1][b + 1].id;
                d.getElementById(id).style.backgroundColor = "";
            }
        }
	}

	function clicked(almostX, y) {
        var isPathSquare = false,
			x = convertX.indexOf(almostX),
            type,
            color;
		//check if the clicked square is a potential move
        if (currentPath) {
			for (i = 0; i < currentPath.length && !isPathSquare; i += 1) {
				isPathSquare = currentPath[i] === (almostX + y);
			}
			if (isPathSquare) {
				type = board[highlight.y][highlight.x].piece.type;
				color = board[highlight.y][highlight.x].piece.color;
				clear(highlight.x, highlight.y);
				spawn(x, y, type, color);
				whiteTurn = !whiteTurn;
			}
			clearVisualPath();
			currentPath = "";
        } else {
			highlight = {"x": x, "y": y};
            currentPath = findPath(x, y);
			if (currentPath) {
				for (i = 0; i < currentPath.length; i += 1) {
					d.getElementById(currentPath[i]).style.backgroundColor = "red";
				}
			}
		}
		return;
    }

    //could be a new program
    function newGame() {
		for (a = 1; a < 9; a += 1) {
			for (b = 1; b < 9; b += 1) {
				clear(a, b);
			}
		}
		whiteTurn = true;
        spawn(1, 1, "R", "b", 1);
        spawn(1, 8, "R", "b", 1);
        spawn(1, 2, "N", "b", 1);
        spawn(1, 7, "N", "b", 1);
        spawn(1, 3, "B", "b", 1);
        spawn(1, 6, "B", "b", 1);
        spawn(1, 4, "Q", "b", 1);
        spawn(1, 5, "K", "b", 1);
        spawn(8, 1, "R", "w", 1);
        spawn(8, 8, "R", "w", 1);
        spawn(8, 2, "N", "w", 1);
        spawn(8, 7, "N", "w", 1);
        spawn(8, 3, "B", "w", 1);
        spawn(8, 6, "B", "w", 1);
        spawn(8, 4, "Q", "w", 1);
        spawn(8, 5, "K", "w", 1);
        for (i = 1; i < 9; i += 1) {
            spawn(2, i, "P", "b", 1);
            spawn(7, i, "P", "w", 1);
        }
    }

	newGame();
<commit_msg>Added piece selection and movement.<commit_after>    "use strict";
    var x,
        y,
        i,
        a,
        b,
        id,
        d = document,
        highlight,
        currentPath,
        div,
		whiteTurn,
        board = [],
        convertColor = ["w", "b"], //used to give white and black number values (handy for finding the right coordinates on pieces.png)
        convertType = [null, "P", "N", "B", "R", "Q", "K"], // same as above but for piece names (Pawn, kNight, Bishop, etc.)
        convertX = [0, "a", "b", "c", "d", "e", "f", "g", "h"];

    /*
    creates 8 arrays each containing 8 arrays that will be used to store and access piece location and color on the chess board
    also styles each div element of the chess board on the html page with the pieces.png sprite centred on a blank area
    */
    for (y = 1; y < 9; y += 1) {
        board[y] = [];
        for (x = 1; x < 9; x += 1) {
            board[y][x] = {"id": convertX[x] + y, "piece": 0};
            div = d.createElement("DIV");
            div.setAttribute("class", "square");
            d.getElementById(board[y][x].id).style.backgroundImage = "url('sprites/pieces.png')";
            d.getElementById(board[y][x].id).style.backgroundPosition = "0 -200";
        }
    } //uhhhh....
    function findPath(x, y) {
        var path = [],
            clear,
            moves,
            type,
            color,
            hasMoved;
        if (board[y] === undefined)
            console.log("Doesn't exist");
        else if (board[y][x] === undefined)
            console.log("Doesn't exist");
        else if (board[y][x].piece === 0)
            console.log("Nothing here");
        else {
            type = board[y][x].piece.type;
            color = board[y][x].piece.color,
            hasMoved = board[y][x].piece.moved;
			if (color === (whiteTurn ? "w" : "b")) {
				if (type === "B") {
					moves = [{x: 1, y: 1},
							 {x: 1, y: -1},
							 {x: -1, y: 1},
							 {x: -1, y: -1}];
					for (i = 0; i < moves.length; i += 1) {
						clear = true;
						a = x;
						b = y;
						while (clear === true) {
							a += (moves[i].x);
							b += (moves[i].y);
							if (board[b] === undefined)
								clear = false;
							else if (board[b][a] === undefined)
								clear = false;
							else if (board[b][a].piece === 0)
								path[path.length] = convertX[a] + b;
							else if (board[b][a].piece.color !== color) {
								path[path.length] = convertX[a] + b;
								clear = false;
							} else
								clear = false;
						}
					}
				} else if (type === "R") {
					moves = [{x: 0, y: 1},
							 {x: 0, y: -1},
							 {x: 1, y: 0},
							 {x: -1, y: 0}];
					for (i = 0; i < moves.length; i += 1) {
						clear = true;
						a = x;
						b = y;
						while (clear === true) {
							a += (moves[i].x);
							b += (moves[i].y);
							if (board[b] === undefined){
								clear = false;
							} else if (board[b][a] === undefined) {
								clear = false;
							} else if (board[b][a].piece === 0) {
								path[path.length] = convertX[a] + b;
							} else if (board[b][a].piece.color !== color) {
								path[path.length] = convertX[a] + b;
								clear = false;
							} else {
								clear = false;
							}
						}
					}
				} else if (type === "Q") {
					moves = [{x: 0, y: 1},
							 {x: 0, y: -1},
							 {x: 1, y: 0},
							 {x: -1, y: 0},
							 {x: 1, y: 1},
							 {x: 1, y: -1},
							 {x: -1, y: 1},
							 {x: -1, y: -1}];
					for (i = 0; i < moves.length; i += 1) {
						clear = true;
						a = x;
						b = y;
						while (clear === true) {
							a += (moves[i].x);
							b += (moves[i].y);
							if (board[b] === undefined){
								clear = false;
							} else if (board[b][a] === undefined) {
								clear = false;
							} else if (board[b][a].piece === 0) {
								path[path.length] = convertX[a] + b;
							} else if (board[b][a].piece.color !== color) {
								path[path.length] = convertX[a] + b;
								clear = false;
							} else {
								clear = false;
							}
						}
					}
				} else if (type === "N") {
					moves = [{x: 2, y: 1},
							 {x: 2, y: -1},
							 {x: -2, y: 1},
							 {x: -2, y: -1},
							 {x: 1,  y: 2},
							 {x: 1, y: -2},
							 {x: -1, y: 2},
							 {x: -1, y: -2}];
					for (i = 0; i < moves.length; i += 1) {
						a = x + (moves[i].x);
						b = y + (moves[i].y);

						if (board[b] === undefined) {
						} else if (board[b][a] === undefined) {
						} else if (board[b][a].piece === 0) {
							path[path.length] = convertX[a] + b;
						} else if (board[b][a].piece.color !== color) {
							path[path.length] = convertX[a] + b;
						}
					}
				} else if (type === "P") {
					a = (2 * convertColor.indexOf(color)) - 1;
					if (board[y] === undefined) {
					} else if (board[y][x + a] === undefined) {
					} else if (board[y][x + a].piece === 0) {
						path[path.length] = convertX[x + a] + y;
					}
					if (!hasMoved && board[y][x + 2 * a].piece === 0) {
						path[path.length] = convertX[x + 2 * a] + y;
					}
					if (board[y + 1] === undefined) {
					} else if (board[y + 1][x + a] === undefined) {
					} else if (board[y + 1][x + a].piece === 0) {
					} else if (board[y + 1][x + a].piece.color !== color) {
						path[path.length] = convertX[x + a] + (y + 1);
					}
					if (board[y - 1] === undefined) {
					} else if (board[y - 1][x + a] === undefined) {
					} else if (board[y - 1][x + a].piece === 0) {
					} else if (board[y - 1][x + a].piece.color !== color) {
						path[path.length] = convertX[x + a] + (y - 1);
					}
				} else if (type === "K") {
					moves = [{x: 0, y: 1},
							{x: 0, y: -1},
							{x: 1, y: 0},
							{x: -1, y: 0},
							{x: 1, y: 1},
							{x: 1, y: -1},
							{x: -1, y: 1},
							{x: -1, y: -1}];
					for (i = 0; i < moves.length; i += 1) {
						a = x + (moves[i].x);
						b = y + (moves[i].y);
						if (board[b][a] === undefined) {
						} else if (board[b][a].piece === 0) {
							path[path.length] = convertX[a] + b;
						} else if (board[b][a].piece.color !== color) {
							path[path.length] = convertX[a] + b;
						}
					}
				}
				console.log("Legal moves for " + color + type + " at "+ board[y][x].id + ":\n" + path);
				return path;
			} else {
			console.log("The turn belongs to " + (whiteTurn ? "white" : "black"))
			}
		}
    }

    /*
    stores input data to corresponding board coordinates (board[y][x].piece = {color: color, type: type}),
    and finds the right location for it
    */
    function spawn(x, y, type, color, first) {
        var t = convertType.indexOf(type),
            c = convertColor.indexOf(color),
            bgY = Math.round(t / 2) * -50 + "px",
            bgX = ((((t) - 1) % 2) + ((c) * 2)) * -50 + "px",
            bg = bgX + " " + bgY;
        board[y][x].piece = {};
        board[y][x].piece.color = color;
        board[y][x].piece.type = type;
        if (first) {
            board[y][x].piece.moved = false;
        } else {
            board[y][x].piece.moved = true;
        }
        d.getElementById(board[y][x].id).style.backgroundPosition = bg;
        return;
    }

    function clear(x, y) {
        board[y][x].piece = 0;
        d.getElementById(board[y][x].id).style.backgroundPositionX = "-200";
		d.getElementById(board[y][x].id).style.backgroundPositionY = "0";
    }

	function clearVisualPath() {
		for (a = 0; a < 8; a += 1) {
			for (b = 0; b < 8; b += 1) {
                id = board[a + 1][b + 1].id;
                d.getElementById(id).style.backgroundColor = "";
            }
        }
	}

	function clicked(almostX, y) {
        var isPathSquare = false,
			x = convertX.indexOf(almostX),
            type,
            color;
		//check if the clicked square is a potential move
        if (currentPath) {
			for (i = 0; i < currentPath.length && !isPathSquare; i += 1) {
				isPathSquare = currentPath[i] === (almostX + y);
			}
			if (isPathSquare) {
				type = board[highlight.y][highlight.x].piece.type;
				color = board[highlight.y][highlight.x].piece.color;
				clear(highlight.x, highlight.y);
				spawn(x, y, type, color);
				whiteTurn = !whiteTurn;
			}
			clearVisualPath();
			currentPath = "";
        } else {
			highlight = {"x": x, "y": y};
            currentPath = findPath(x, y);
			if (currentPath) {
				for (i = 0; i < currentPath.length; i += 1) {
					d.getElementById(currentPath[i]).style.backgroundColor = "red";
				}
			}
		}
		return;
    }

    //could be a new program
    function newGame() {
		for (a = 1; a < 9; a += 1) {
			for (b = 1; b < 9; b += 1) {
				clear(a, b);
			}
		}
		whiteTurn = true;
        spawn(1, 1, "R", "b", 1);
        spawn(1, 8, "R", "b", 1);
        spawn(1, 2, "N", "b", 1);
        spawn(1, 7, "N", "b", 1);
        spawn(1, 3, "B", "b", 1);
        spawn(1, 6, "B", "b", 1);
        spawn(1, 4, "Q", "b", 1);
        spawn(1, 5, "K", "b", 1);
        spawn(8, 1, "R", "w", 1);
        spawn(8, 8, "R", "w", 1);
        spawn(8, 2, "N", "w", 1);
        spawn(8, 7, "N", "w", 1);
        spawn(8, 3, "B", "w", 1);
        spawn(8, 6, "B", "w", 1);
        spawn(8, 4, "Q", "w", 1);
        spawn(8, 5, "K", "w", 1);
        for (i = 1; i < 9; i += 1) {
            spawn(2, i, "P", "b", 1);
            spawn(7, i, "P", "w", 1);
        }
    }

	newGame();
<|endoftext|>


<commit_before>'use strict';
var darkSkyKey = process.env.DARK_SKY_KEY
  , googleGeoCodeKey = process.env.GOOGLE_GEOCODE_KEY
  , express = require('express')
  , bodyParser = require('body-parser')
  , log = require('fhlog').get('temperature')
  , request = require('request');

var route = module.exports = new express.Router();

route.use(bodyParser.json());

route.get('/temperature/:city/:state', doGetTemp);

function doGetLatLngLikeService(city, state, callback) {
  var url = 'https://maps.googleapis.com/maps/api/geocode/json?'
  + 'address=' + city + ',+' + state +'&key=' + googleGeoCodeKey;
  request(url, function(err, response, body) {
    if(err) {
      return callback(err);
    }
    var lat = '';
    var lng = '';
    try {
      lat = JSON.parse(body).results[0].geometry.location.lat;
      lng = JSON.parse(body).results[0].geometry.location.lng;
    }
    catch(e) {
      return callback(500, {message: 'Geocoding Service did not respond appropriately'});
    }
    log.d('lat %s lng %s', lat, lng);
    callback(null, {lat: lat, lng: lng});
  });
}

function doGetTemperatureLikeService(params, callback) {
  var url = 'https://api.forecast.io/forecast/' + darkSkyKey + '/' + params.lat + ',' + params.lng;
  request(url, function(err, response, body) {
    if(err) {
      return callback(err);
    }
    var temperature = '';
    try {
      temperature = JSON.parse(body).currently.temperature;
    }
    catch(e) {
      return callback(500, {message: 'Temperature Service did not respond appropriately'});
    }
    log.d('temperature %s', temperature);
    callback(null, temperature);
  });
}

function doGetTemp(req, res) {
  if(!req.params.city || !req.params.state) {
    res.json(400, {
      message: 'Please provide city and state input.'
    });
  }
  else {
    doGetLatLngLikeService(req.params.city, req.params.state, function(err, location) {
      if(err) {
        res.json(400, {
          message: 'Error contacting Geocoding Service.'
        })
      }
      else {
        doGetTemperatureLikeService(location, function(err, temperature) {
          if(err) {
            res.json(400, {
              message: 'Error contacting Weather Service.'
            })
          }
          else {
            res.json({temperature:temperature});
          }
        })
      }
    })
  }
}
<commit_msg>final<commit_after>'use strict';
var darkSkyKey = process.env.DARK_SKY_KEY
  , googleGeoCodeKey = process.env.GOOGLE_GEOCODE_KEY
  , express = require('express')
  , bodyParser = require('body-parser')
  , log = require('fhlog').get('temperature')
  , request = require('request')
  , route = module.exports = new express.Router();

route.use(bodyParser.json());

route.get('/temperature/:city/:state', doGetTemp);

function doGetLatLngLikeService(city, state, callback) {
  var url = 'https://maps.googleapis.com/maps/api/geocode/json?'
  + 'address=' + city + ',+' + state +'&key=' + googleGeoCodeKey;
  request(url, function(err, response, body) {
    if(err) {
      return callback(err);
    }
    var lat = '';
    var lng = '';
    try {
      lat = JSON.parse(body).results[0].geometry.location.lat;
      lng = JSON.parse(body).results[0].geometry.location.lng;
    }
    catch(e) {
      return callback(e);
    }
    log.d('lat %s lng %s', lat, lng);
    callback(null, {lat: lat, lng: lng});
  });
}

function doGetTemperatureLikeService(params, callback) {
  var url = 'https://api.forecast.io/forecast/' + darkSkyKey + '/' + params.lat + ',' + params.lng;
  request(url, function(err, response, body) {
    if(err) {
      return callback(err);
    }
    var temperature = '';
    try {
      temperature = JSON.parse(body).currently.temperature;
    }
    catch(e) {
      return callback(e);
    }
    log.d('temperature %s', temperature);
    callback(null, temperature);
  });
}

function doGetTemp(req, res) {
  if(!req.params.city || !req.params.state) {
    res.json(400, {
      message: 'Please provide city and state input.'
    });
  }
  else {
    doGetLatLngLikeService(req.params.city, req.params.state, function(err, location) {
      if(err) {
        res.json(400, {
          message: 'Error contacting Geocoding Service.'
        })
      }
      else {
        doGetTemperatureLikeService(location, function(err, temperature) {
          if(err) {
            res.json(400, {
              message: 'Error contacting Weather Service.'
            })
          }
          else {
            res.json({temperature:temperature});
          }
        })
      }
    })
  }
}
<|endoftext|>


<commit_before>var ElizaBot = require('elizabot')
var elizas = {}

listen(/PRIVMSG #nurds :(.+)/, function(match, data, to, from) {
  var message = String(match[1])
  var nick = "x0p"
  // relevant ?
  if (to !== nick && message.indexOf(nick) === -1) {
    return
  }
  // clean
  if (message.indexOf(nick) >= 0) {
    message = message.replace('x0p', '')
    message = message.replace(/^[,:]/, '').trim()
  }
  // reply style
  var private = nick === to
  var where = private ? from : to
  var prefix = private ? '' : (from + ': ')
  // grab old session
  var eliza = elizas[from]
  // if no session, init session
  if (!eliza) {
    eliza = elizas[from] = new ElizaBot
    var init = prefix + elizas[from].getInitial()
    irc.privmsg(to, init, false);
    eliza.transform(message)
    return
  }
  // has session, proceed
  irc.privmsg(where, prefix + eliza.transform(message), false);
  if (eliza.quit) delete elizas[from]
});
<commit_msg>eliza only on directed message<commit_after>var ElizaBot = require('elizabot')
var elizas = {}

listen(/PRIVMSG #nurds :(.+)/, function(match, data, to, from) {
  var message = String(match[1])
  var nick = "x0p"
  // relevant ?
  if (to !== nick && message.indexOf(nick) !== 0) {
    return
  }
  // clean
  if (message.indexOf(nick) == 0) {
    message = message.replace('x0p', '')
    message = message.replace(/^[,:]/, '').trim()
  }
  // reply style
  var private = nick === to
  var where = private ? from : to
  var prefix = private ? '' : (from + ': ')
  // grab old session
  var eliza = elizas[from]
  // if no session, init session
  if (!eliza) {
    eliza = elizas[from] = new ElizaBot
    var init = prefix + elizas[from].getInitial()
    irc.privmsg(to, init, false);
    eliza.transform(message)
    return
  }
  // has session, proceed
  irc.privmsg(where, prefix + eliza.transform(message), false);
  if (eliza.quit) delete elizas[from]
});
<|endoftext|>


<commit_before>var H5P = H5P || {};

H5P.Essay = function ($, Question) {
  'use strict';
  /**
   * @constructor
   *
   * @param {Object} config - Config from semantics.json.
   * @param {string} contentId - ContentId.
   * @param {Object} contentData - contentData.
   */
  function Essay(config, contentId, contentData) {
    // Initialize
    if (!config) {
      return;
    }

    // Inheritance
    Question.call(this, 'essay');

    this.config = config;
    this.contentId = contentId;
    this.contentData = contentData || {};

    // map function
    var toPoints = function (keywords) {
      return keywords.options && keywords.options.points || 0;
    };

    // reduce function
    var sum = function (a, b) {
      return a + b;
    };

    // scoreMax = Maximum number of points available by all keyword groups
    var scoreMax = this.config.keywords
      .map(toPoints)
      .reduce(sum, 0);

    // scoreMastering: score indicating mastery and maximum number on progress bar (can be < scoreMax)
    this.scoreMastering = (typeof this.config.behaviour.percentageMastering === 'undefined') ?
        scoreMax :
        this.config.behaviour.percentageMastering * scoreMax / 100;

    // scorePassing: score to pass the task (<= scoreMastering)
    this.scorePassing = Math.min(this.scoreMastering, this.config.behaviour.percentagePassing * scoreMax / 100 || 0);
  }

  // Extends Question
  Essay.prototype = Object.create(Question.prototype);
  Essay.prototype.constructor = Essay;

  /**
   * Register the DOM elements with H5P.Question.
   */
  Essay.prototype.registerDomElements = function () {
    // Get previous state
    if (!!this.contentData && !!this.contentData.previousState) {
      this.previousState = this.contentData.previousState;
    }

    // Create InputField
    this.inputField = new H5P.Essay.InputField({
      'taskDescription': this.config.taskDescription,
      'placeholderText': this.config.placeholderText,
      'maximumLength': this.config.behaviour.maximumLength,
      'remainingChars': this.config.remainingChars,
      'inputFieldSize': this.config.behaviour.inputFieldSize
    }, this.previousState);

    // Register task introduction text
    this.setIntroduction(this.inputField.getIntroduction());

    // Register content
    this.setContent(this.inputField.getContent());

    // Register Buttons
    this.addButtons();
  };

  /**
   * Add all the buttons that shall be passed to H5P.Question
   */
  Essay.prototype.addButtons = function () {
    var that = this;

    // Show solution button
    that.addButton('show-solution', that.config.showSolution, function () {
      that.showSolution();
    }, false, {}, {});

    // Check answer button
    that.addButton('check-answer', that.config.checkAnswer, function () {
      that.showEvaluation();
    }, true, {}, {});

    // Retry button
    that.addButton('try-again', that.config.tryAgain, function () {
      that.showEvaluation();
    }, false, {}, {});
  };

  /**
   * Show solution.
   */
  Essay.prototype.showSolution = function () {
    if (!this.$solution) {
      this.$solution = $('
')
          .addClass('h5p-essay-solution-container')
          .attr('tabIndex', '0')
          .append($('
')
          .addClass('h5p-essay-solution-title')
          .html(this.config.solutionTitle))
          .append($('
')
          .addClass('h5p-essay-solution-introduction')
          .html(this.config.solution.introduction))
          .append($('
')
          .addClass('h5p-essay-solution-sample')
          .html($(this.config.solution.sample).addClass('h5p-essay-solution-sample-text')))
          .insertAfter('.h5p-question-explanation');

      this.trigger('resize');
    }
  };

  /**
   * Show results.
   */
  Essay.prototype.showEvaluation = function () {
    var feedback = this.computeResults();

    // Add explanations if available
    var emptyWord = '...';
    var explanations = [];
    feedback.explanation.forEach(function (element) {
      if (element.found) {
        explanations.push({correct: element.keyword, text: element.message});
      }
      else {
        explanations.push({correct: emptyWord, text: element.message});
      }
    });
    if (explanations.length > 0) {
      // Included before not included, but keep order otherwise
      explanations.sort(function (a, b) {
        return a.correct === emptyWord && b.correct !== emptyWord;
      });
      this.setExplanation(explanations, this.config.feedbackHeader);
    }
    else {
      this.setExplanation();
    }

    // Not all keyword groups might be necessary for mastering
    var score = Math.min(feedback.score, this.scoreMastering);

    var textScore = H5P.Question.determineOverallFeedback(
        this.config.overallFeedback, score / this.scoreMastering)
            .replace('@score', score)
            .replace('@total', this.scoreMastering);

    this.setFeedback(textScore, score, this.scoreMastering);

    this.hideButton('check-answer');
    this.trigger(this.createEssayXAPIEvent('completed'));

    var xAPIEvent = this.createEssayXAPIEvent('scored');
    xAPIEvent.setScoredResult(score, this.scoreMastering, this, true,
        feedback.score >= this.scorePassing);
    xAPIEvent.data.statement.result.response = this.getInput();
    /*
     * We could think about adding support for the "correct response pattern",
     * but the official xAPI documentation discourages to use it if the
     * criteria for a question are complex and correct responses cannot be
     * exhaustively listed. They kind of can and can't.
     */
    this.trigger(xAPIEvent);

    if (feedback.score < this.scorePassing) {
      this.trigger(this.createEssayXAPIEvent('failed'));
    }
    else {
      this.trigger(this.createEssayXAPIEvent('passed'));
    }

    if (this.config.solution.sample) {
      this.showButton('show-solution');
    }

    if (score < this.scoreMastering) {
      if (this.config.behaviour.enableRetry) {
        this.showButton('try-again');
      }
    }
    else {
      this.trigger(this.createEssayXAPIEvent('mastered'));
      this.hideButton('try-again');
    }

    this.trigger('resize');
  };

  /**
   * Compute the feedback.
   * @return {Object} Feedback of {score: number, text: [{message: String, found: boolean}]}.
   */
  Essay.prototype.computeResults = function () {
    var that = this;
    var score = 0;
    var explanation = [];

    // We don't want EOLs to mess up the string.
    var input = this.getInput();
    var inputLowerCase = input.toLowerCase();

    // Should not happen, but just to be sure ...
    this.config.keywords = this.config.keywords || [];

    // optional = false is ignored in Editor
    this.config.keywords = this.config.keywords.filter(function (element) {
      return (typeof element.keyword !== 'undefined');
    });

    /*
     * If you don't want to only find exact matches of keywords, but also
     * close resemblences or phrases, things get more complicated then you
     * might expect. This could probably be improved.
     */

    // Within each keyword group check if one of the alternatives is a keyword
    this.config.keywords.forEach(function (alternativeGroup) {
      var options = alternativeGroup.options;
      var alternatives = [alternativeGroup.keyword || []].concat(alternativeGroup.alternatives || []);

      var found = alternatives.some(function (alternative) {
        var inputTest = input;

        // Check for case sensitivity
        if  (!options.caseSensitive ||
            that.config.behaviour.overrideCaseSensitive === 'off') {
              alternative = alternative.toLowerCase();
              inputTest = inputLowerCase;
        }

        // Exact matching
        if (inputTest.indexOf(alternative) !== -1 && H5P.TextUtilities.isIsolated(alternative, inputTest)) {
          return true;
        }

        // Wildcard matching
        var regex = new RegExp(alternative.replace(/\*/g, '[A-z]*'), 'g');
        var found = (inputTest.match(regex) || []).some(function (match) {
          if (regex.test(inputTest) && H5P.TextUtilities.isIsolated(match, inputTest)) {
            return true;
          }
        });
        if (found) {
          return true;
        }

        // Fuzzy matching
        if ((options.forgiveMistakes || that.config.behaviour.overrideForgiveMistakes === 'on') &&
            H5P.TextUtilities.fuzzyContains(alternative, inputTest)) {
          return true;
        }
      });

      if (found) {
        score += options.points;
        if (options.feedbackIncluded) {
          explanation.push({
            "keyword": alternatives.join(' | '),
            "message": options.feedbackIncluded,
            "found": true});
        }
      }
      else {
        if (options.feedbackMissed) {
          explanation.push({
            "keyword": alternatives.join(' | '),
            "message": options.feedbackMissed,
            "found": false});
        }
      }
    });

    return {"score": score, "explanation": explanation};
  };

  /**
   * Get the user input from DOM.
   * @return {string} Cleaned input.
   */
  Essay.prototype.getInput = function () {
    return this.inputField.getText()
        .replace(/(\r\n|\r|\n)/g, ' ')
        .replace(/\s\s/g, ' ');
  };

  /**
   * Extend an array just like JQuery's extend.
   * @param {Object} arguments - Objects to be merged.
   * @return {Object} Merged objects.
   */
  Essay.prototype.extend = function () {
    for(var i = 1; i < arguments.length; i++) {
      for(var key in arguments[i]) {
        if (arguments[i].hasOwnProperty(key)) {
          if (typeof arguments[0][key] === 'object' && typeof arguments[i][key] === 'object') {
            this.extend(arguments[0][key], arguments[i][key]);
          }
          else {
            arguments[0][key] = arguments[i][key];
          }
        }
      }
    }
    return arguments[0];
  };

  /**
   * Get current state for H5P.Question.
   * @return {Object} Current state.
   */
  Essay.prototype.getCurrentState = function () {
    this.inputField.updateMessageSaved(this.config.messageSave);

    // We could have just used a string, but you never know when you need to store more parameters
    return {
      'inputField': this.inputField.getText()
    };
  };

  /**
   * Create an xAPI event for Essay.
   * @param {string} verb - Short id of the verb we want to trigger.
   * @return {H5P.XAPIEvent} Event template.
   */
  Essay.prototype.createEssayXAPIEvent = function (verb) {
    var xAPIEvent = this.createXAPIEventTemplate(verb);
    this.extend(
        xAPIEvent.getVerifiedStatementValue(['object', 'definition']),
        this.getxAPIDefinition());
    return xAPIEvent;
  };

  /**
   * Get the xAPI definition for the xAPI object.
   * return {Object} XAPI definition.
   */
  Essay.prototype.getxAPIDefinition = function () {
    var definition = {};
    definition.name = {'en-US': 'Essay'};
    definition.description = {'en-US': this.config.taskDescription};
    definition.type = 'http://id.tincanapi.com/activitytype/essay';
    definition.interactionType = 'long-fill-in';
    return definition;
  };

  return Essay;
}(H5P.jQuery, H5P.Question);
<commit_msg>clean code a bit<commit_after>var H5P = H5P || {};

H5P.Essay = function ($, Question) {
  'use strict';

  /**
   * @constructor
   *
   * @param {Object} config - Config from semantics.json.
   * @param {string} contentId - ContentId.
   * @param {Object} contentData - contentData.
   */
  function Essay(config, contentId, contentData) {
    // Initialize
    if (!config) {
      return;
    }

    // Inheritance
    Question.call(this, 'essay');

    this.config = config;
    this.contentId = contentId;
    this.contentData = contentData || {};

    // map function
    var toPoints = function (keywords) {
      return keywords.options && keywords.options.points || 0;
    };

    // reduce function
    var sum = function (a, b) {
      return a + b;
    };

    // scoreMax = Maximum number of points available by all keyword groups
    var scoreMax = this.config.keywords
      .map(toPoints)
      .reduce(sum, 0);

    // scoreMastering: score indicating mastery and maximum number on progress bar (can be < scoreMax)
    this.scoreMastering = (typeof this.config.behaviour.percentageMastering === 'undefined') ?
        scoreMax :
        this.config.behaviour.percentageMastering * scoreMax / 100;

    // scorePassing: score to pass the task (<= scoreMastering)
    this.scorePassing = Math.min(
          this.scoreMastering,
          this.config.behaviour.percentagePassing * scoreMax / 100 || 0);
  }

  // Extends Question
  Essay.prototype = Object.create(Question.prototype);
  Essay.prototype.constructor = Essay;

  /**
   * Register the DOM elements with H5P.Question.
   */
  Essay.prototype.registerDomElements = function () {
    // Get previous state
    if (!!this.contentData && !!this.contentData.previousState) {
      this.previousState = this.contentData.previousState;
    }

    // Create InputField
    this.inputField = new H5P.Essay.InputField({
      'taskDescription': this.config.taskDescription,
      'placeholderText': this.config.placeholderText,
      'maximumLength': this.config.behaviour.maximumLength,
      'remainingChars': this.config.remainingChars,
      'inputFieldSize': this.config.behaviour.inputFieldSize
    }, this.previousState);

    // Register task introduction text
    this.setIntroduction(this.inputField.getIntroduction());

    // Register content
    this.setContent(this.inputField.getContent());

    // Register Buttons
    this.addButtons();
  };

  /**
   * Add all the buttons that shall be passed to H5P.Question
   */
  Essay.prototype.addButtons = function () {
    var that = this;

    // Show solution button
    that.addButton('show-solution', that.config.showSolution, function () {
      that.showSolution();
    }, false, {}, {});

    // Check answer button
    that.addButton('check-answer', that.config.checkAnswer, function () {
      that.showEvaluation();
    }, true, {}, {});

    // Retry button
    that.addButton('try-again', that.config.tryAgain, function () {
      that.showEvaluation();
    }, false, {}, {});
  };

  /**
   * Show solution.
   */
  Essay.prototype.showSolution = function () {
    if (!this.$solution) {
      this.$solution = $('
')
          .addClass('h5p-essay-solution-container')
          .attr('tabIndex', '0')
          .append($('
')
          .addClass('h5p-essay-solution-title')
          .html(this.config.solutionTitle))
          .append($('
')
          .addClass('h5p-essay-solution-introduction')
          .html(this.config.solution.introduction))
          .append($('
')
          .addClass('h5p-essay-solution-sample')
          .html(
              $(this.config.solution.sample)
              .addClass('h5p-essay-solution-sample-text')))
          .insertAfter('.h5p-question-explanation');

      this.trigger('resize');
    }
  };

  /**
   * Show results.
   */
  Essay.prototype.showEvaluation = function () {
    var feedback = this.computeResults();

    // Add explanations if available
    const emptyWord = '...';

    var explanations = [];
    feedback.explanation.forEach(function (element) {
      if (element.found) {
        explanations.push({correct: element.keyword, text: element.message});
      }
      else {
        explanations.push({correct: emptyWord, text: element.message});
      }
    });

    if (explanations.length > 0) {
      // Included before not included, but keep order otherwise
      explanations.sort(function (a, b) {
        return a.correct === emptyWord && b.correct !== emptyWord;
      });
      this.setExplanation(explanations, this.config.feedbackHeader);
    }
    else {
      this.setExplanation();
    }

    // Not all keyword groups might be necessary for mastering
    var score = Math.min(feedback.score, this.scoreMastering);

    var textScore = H5P.Question.determineOverallFeedback(
        this.config.overallFeedback, score / this.scoreMastering)
            .replace('@score', score)
            .replace('@total', this.scoreMastering);

    this.setFeedback(textScore, score, this.scoreMastering);

    this.hideButton('check-answer');
    this.trigger(this.createEssayXAPIEvent('completed'));

    var xAPIEvent = this.createEssayXAPIEvent('scored');
    xAPIEvent.setScoredResult(score, this.scoreMastering, this, true,
        feedback.score >= this.scorePassing);
    xAPIEvent.data.statement.result.response = this.getInput();
    /*
     * We could think about adding support for the "correct response pattern",
     * but the official xAPI documentation discourages to use it if the
     * criteria for a question are complex and correct responses cannot be
     * exhaustively listed. They kind of can and can't.
     */
    this.trigger(xAPIEvent);

    if (feedback.score < this.scorePassing) {
      this.trigger(this.createEssayXAPIEvent('failed'));
    }
    else {
      this.trigger(this.createEssayXAPIEvent('passed'));
    }

    if (this.config.solution.sample) {
      this.showButton('show-solution');
    }

    if (score < this.scoreMastering) {
      if (this.config.behaviour.enableRetry) {
        this.showButton('try-again');
      }
    }
    else {
      this.trigger(this.createEssayXAPIEvent('mastered'));
      this.hideButton('try-again');
    }

    this.trigger('resize');
  };

  /**
   * Compute the feedback.
   * @return {Object} Feedback of {score: number, text: [{message: String, found: boolean}]}.
   */
  Essay.prototype.computeResults = function () {
    var that = this;
    var score = 0;
    var explanation = [];

    // We don't want EOLs to mess up the string.
    var input = this.getInput();
    var inputLowerCase = input.toLowerCase();

    // Should not happen, but just to be sure ...
    this.config.keywords = this.config.keywords || [];

    // optional = false is ignored in Editor
    this.config.keywords = this.config.keywords.filter(function (element) {
      return (typeof element.keyword !== 'undefined');
    });

    /*
     * If you don't want to only find exact matches of keywords, but also
     * close resemblences or phrases, things get more complicated then you
     * might expect. This could probably be improved.
     */

    // Within each keyword group check if one of the alternatives is a keyword
    this.config.keywords.forEach(function (alternativeGroup) {
      var options = alternativeGroup.options;
      var alternatives = [alternativeGroup.keyword || []].concat(alternativeGroup.alternatives || []);

      var found = alternatives.some(function (alternative) {
        var inputTest = input;

        // Check for case sensitivity
        if  (!options.caseSensitive ||
            that.config.behaviour.overrideCaseSensitive === 'off') {
              alternative = alternative.toLowerCase();
              inputTest = inputLowerCase;
        }

        // Exact matching
        if (inputTest.indexOf(alternative) !== -1 && H5P.TextUtilities.isIsolated(alternative, inputTest)) {
          return true;
        }

        // Wildcard matching
        var regex = new RegExp(alternative.replace(/\*/g, '[A-z]*'), 'g');
        var found = (inputTest.match(regex) || []).some(function (match) {
          if (regex.test(inputTest) && H5P.TextUtilities.isIsolated(match, inputTest)) {
            return true;
          }
        });
        if (found) {
          return true;
        }

        // Fuzzy matching
        if ((options.forgiveMistakes || that.config.behaviour.overrideForgiveMistakes === 'on') &&
            H5P.TextUtilities.fuzzyContains(alternative, inputTest)) {
          return true;
        }
      });

      if (found) {
        score += options.points;
        if (options.feedbackIncluded) {
          explanation.push({
            "keyword": alternatives.join(' | '),
            "message": options.feedbackIncluded,
            "found": true});
        }
      }
      else {
        if (options.feedbackMissed) {
          explanation.push({
            "keyword": alternatives.join(' | '),
            "message": options.feedbackMissed,
            "found": false});
        }
      }
    });

    return {"score": score, "explanation": explanation};
  };

  /**
   * Get the user input from DOM.
   * @return {string} Cleaned input.
   */
  Essay.prototype.getInput = function () {
    return this.inputField.getText()
        .replace(/(\r\n|\r|\n)/g, ' ')
        .replace(/\s\s/g, ' ');
  };

  /**
   * Extend an array just like JQuery's extend.
   * @param {Object} arguments - Objects to be merged.
   * @return {Object} Merged objects.
   */
  Essay.prototype.extend = function () {
    for(var i = 1; i < arguments.length; i++) {
      for(var key in arguments[i]) {
        if (arguments[i].hasOwnProperty(key)) {
          if (typeof arguments[0][key] === 'object' &&
              typeof arguments[i][key] === 'object') {
            this.extend(arguments[0][key], arguments[i][key]);
          }
          else {
            arguments[0][key] = arguments[i][key];
          }
        }
      }
    }
    return arguments[0];
  };

  /**
   * Get current state for H5P.Question.
   * @return {Object} Current state.
   */
  Essay.prototype.getCurrentState = function () {
    this.inputField.updateMessageSaved(this.config.messageSave);

    // We could have just used a string, but you never know when you need to store more parameters
    return {
      'inputField': this.inputField.getText()
    };
  };

  /**
   * Create an xAPI event for Essay.
   * @param {string} verb - Short id of the verb we want to trigger.
   * @return {H5P.XAPIEvent} Event template.
   */
  Essay.prototype.createEssayXAPIEvent = function (verb) {
    var xAPIEvent = this.createXAPIEventTemplate(verb);
    this.extend(
        xAPIEvent.getVerifiedStatementValue(['object', 'definition']),
        this.getxAPIDefinition());
    return xAPIEvent;
  };

  /**
   * Get the xAPI definition for the xAPI object.
   * return {Object} XAPI definition.
   */
  Essay.prototype.getxAPIDefinition = function () {
    var definition = {};
    definition.name = {'en-US': 'Essay'};
    definition.description = {'en-US': this.config.taskDescription};
    definition.type = 'http://id.tincanapi.com/activitytype/essay';
    definition.interactionType = 'long-fill-in';
    return definition;
  };

  return Essay;
}(H5P.jQuery, H5P.Question);
<|endoftext|>


<commit_before>'use strict';

/*global nodeca, _*/

// 3rd-party
var Store = require('nlib').Settings.Store;
var async = require('nlib').Vendor.Async;

////////////////////////////////////////////////////////////////////////////////

// Helper to fetch usergroups by IDs
//
function fetchUsrGrpSettings(ids, callback) {
  nodeca.models.users.UserGroup.find()
    .select('is_forced settings.usergroup')
    .where('_id').in(ids)
    .setOptions({ lean: true })
    .exec(callback);
}

// Memoized version of fetchUserGroups helper
//
var fetchUsrGrpSettingsCached = nodeca.components.memoizee(fetchUsrGrpSettings, {
  // memoizee options. revalidate cache after 30 sec
  async:  true,
  maxAge: 30000
});

////////////////////////////////////////////////////////////////////////////////

// ##### Params
//
// - usergroup_ids (Array)
//
module.exports = new Store({
  get: function (keys, params, options, callback) {
    var self = this;
    var func = options.skipCache ? fetchUsrGrpSettings : fetchUsrGrpSettingsCached;

    if (!_.isArray(params.usergroup_ids) || !params.usergroup_ids.length) {
      callback("usergroup_ids param required to be non-empty array for getting settings from usergroup store");
      return;
    }

    // Convert list of Mongoose ObjectID's to sorted Array of String's
    var usergroups = params.usergroup_ids.map(function (g) {
      return String(g);
    }).sort();

    func(usergroups, function (err, grps) {
      var results = {};

      if (err) {
        callback(err);
        return;
      }

      keys.forEach(function (k) {
        var values = [];

        grps.forEach(function (grp) {
          var settings = (grp.settings || {}).usergroup;

          if (settings && settings[k]) {
            values.push({
              value: settings[k].value,
              force: !!grp.is_forced
            });
          } else {
            values.push({
              value: self.getDefaultValue(k),
              // default value SHOULD NOT be forced
              force: false
            });
          }
        });

        // get merged value
        results[k] = Store.mergeValues(values);
      });

      callback(null, results);
    });
  },
  // ##### Params
  //
  // - usergroup_id (String|ObjectId)
  //
  set: function (settings, params, callback) {
    var self = this;

    if (!params.usergroup_id) {
      callback("usergroup_id param is required for saving settings into usergroup store");
      return;
    }

    nodeca.models.users.UserGroup.findOne({
      _id: params.usergroup_id
    }).exec(function (err, grp) {
      if (err) {
        callback(err);
        return;
      }

      // make sure we have settings storages
      grp.settings = grp.settings || {};
      grp.settings.usergroup = grp.settings.usergroup || {};

      _.each(settings, function (opts, key) {
        if (null === opts) {
          delete grp.settings.usergroup[key];
        } else {
          grp.settings.usergroup[key] = {
            value: opts.value,
            // UserGroup store does not use force flag - it's dynamically
            // calculated on UserGroup#is_forced property of model.
            //
            // RESERVED for possible future use
            force: false
          };
        }
      });

      grp.markModified('settings.usergroup');
      grp.save(callback);
    });
  }
});
<commit_msg>Fix memoization<commit_after>'use strict';

/*global nodeca, _*/

// 3rd-party
var Store = require('nlib').Settings.Store;
var async = require('nlib').Vendor.Async;

////////////////////////////////////////////////////////////////////////////////

// Helper to fetch usergroups by IDs
//
function fetchUsrGrpSettings(ids, callback) {
  nodeca.models.users.UserGroup.find()
    .select('is_forced settings.usergroup')
    .where('_id').in(ids)
    .setOptions({ lean: true })
    .exec(callback);
}

// Memoized version of fetchUserGroups helper
//
var fetchUsrGrpSettingsCached = nodeca.components.memoizee(fetchUsrGrpSettings, {
  // memoizee options. revalidate cache after 30 sec
  async:      true,
  maxAge:     30000,
  primitive:  true
});

////////////////////////////////////////////////////////////////////////////////

// ##### Params
//
// - usergroup_ids (Array)
//
module.exports = new Store({
  get: function (keys, params, options, callback) {
    var self = this;
    var func = options.skipCache ? fetchUsrGrpSettings : fetchUsrGrpSettingsCached;

    if (!_.isArray(params.usergroup_ids) || !params.usergroup_ids.length) {
      callback("usergroup_ids param required to be non-empty array for getting settings from usergroup store");
      return;
    }

    func(params.usergroup_ids.sort(), function (err, grps) {
      var results = {};

      if (err) {
        callback(err);
        return;
      }

      keys.forEach(function (k) {
        var values = [];

        grps.forEach(function (grp) {
          var settings = (grp.settings || {}).usergroup;

          if (settings && settings[k]) {
            values.push({
              value: settings[k].value,
              force: !!grp.is_forced
            });
          } else {
            values.push({
              value: self.getDefaultValue(k),
              // default value SHOULD NOT be forced
              force: false
            });
          }
        });

        // get merged value
        results[k] = Store.mergeValues(values);
      });

      callback(null, results);
    });
  },
  // ##### Params
  //
  // - usergroup_id (String|ObjectId)
  //
  set: function (settings, params, callback) {
    var self = this;

    if (!params.usergroup_id) {
      callback("usergroup_id param is required for saving settings into usergroup store");
      return;
    }

    nodeca.models.users.UserGroup.findOne({
      _id: params.usergroup_id
    }).exec(function (err, grp) {
      if (err) {
        callback(err);
        return;
      }

      // make sure we have settings storages
      grp.settings = grp.settings || {};
      grp.settings.usergroup = grp.settings.usergroup || {};

      _.each(settings, function (opts, key) {
        if (null === opts) {
          delete grp.settings.usergroup[key];
        } else {
          grp.settings.usergroup[key] = {
            value: opts.value,
            // UserGroup store does not use force flag - it's dynamically
            // calculated on UserGroup#is_forced property of model.
            //
            // RESERVED for possible future use
            force: false
          };
        }
      });

      grp.markModified('settings.usergroup');
      grp.save(callback);
    });
  }
});
<|endoftext|>


<commit_before>var resolveSrv = require('./dns.js'),
    EventEmitter = require('events').EventEmitter,
    jptr = require('json-ptr'),
    assert = require('assert-plus'),
    async = require('async');
/**
* Attempts to provide a configuration based on a required configuration input
* @module lib
*/
module.exports =(function () {
  'use strict';

  var CONSUL_HOST = 'CONSUL_HOST',
      CONSUL_PORT = 'CONSUL_PORT',
      CONSUL_SECURE = 'CONSUL_SECURE',
      ee = new EventEmitter(),
      matchSlashGreedy = new RegExp('/', 'g');

  /**
   * Does Service Have Ptr checks to see if a value **bound** to this is present in
   * any of the three fields used to define a JSON Ptr in a service spec
   *
   * This function can be used on its own, but it is intended to be used with the Array.some(...) function
   *
   * @param {object} val - An object, generally expected to be a service specification from configuration-schema.json
   * @returns {boolean} True or false based on whether the bound name 'this' was present in any of the JSON Ptr fields
   */
  function doesServiceHavePtr(val) {
    //Stop jshint from whining about 'this' since this func is explicitly meant
    //to be used with 'bind()'
    /*jshint validthis:true */
    return val.key === this || val.prefix === this || val.suffix === this;
  }

  /**
   * Match and set env var looks for an environment variable given an name prefix and a JSON ptr
   * @param {object} finalConfig - The object to store the value of the env var in if found
   * @param {string} prefix - A prefix to apply to names being looked for in the environment
   * @param {object} ptr - A JSON ptr object
   */
  function matchAndSetEnvVar(finalConfig, prefix, ptr) {
    var requestedProp,
        envVal;
    requestedProp = ptr.pointer.replace(matchSlashGreedy, '_').toUpperCase();
    envVal = process.env[prefix.concat(requestedProp)];
    if (envVal !== undefined) {
      ensureObjPath(finalConfig, ptr);
      ptr.set(finalConfig, envVal);
    }
  }

  /**
   * Ensure object path is a small utility function for use with JSON pointers that ensures a particular pointer path
   * exists in an object. The function is non-destructive and only creates a path component as necessary.
   * @param {object} object - A JSON object to ensure that a particular pointer path exists in
   * @param {object} ptr - A {@link https://github.com/flitbit/json-ptr|JSON pointer object}
   */
  function ensureObjPath(object, ptr) {
    var cursor = object,
        currentName,
        idx,
        pathAry = ptr.path;
    for (idx = 0; idx < pathAry.length; idx++) {
      currentName = pathAry[idx];
      if (!cursor[currentName]) {
        cursor[currentName] = {};
      }
      cursor = cursor[currentName];
    }

  }

  /**
   * Determines if we should use a consul agent to add configuration
   * @param {object} configurationRequest - A configuration request object that may contain information required to contact a consul agent
   * @returns {object} A reference to an active consule agent if one could be configured, undefined otherwise
   */
  function setupConsul(configRequest) {
    //client must fully specify how we will talk to consul
    var consulClient,
        consulHost = process.env[CONSUL_HOST],
        consulPort = process.env[CONSUL_PORT],
        consulSecure = (process.env[CONSUL_SECURE] === 'true');
    if (consulHost !== undefined &&
    consulPort !== undefined &&
    consulSecure !== undefined) {
      consulClient = require('consul')({
        host: consulHost,
        port: consulPort,
        secure: consulSecure
      });
    }
    return consulClient;
  }

  /**
   * Handles the retrieval of KV pairs from a consul agent, if the given consulClient isn't defined then
   * the method is a no-op
   * @param {object} configRequest - A client provided object that may have a key value section for required key value pairs
   * @param {object} consulClient - A pre-configured client used to communicate with a consul agent, may be undefined to indicate that
   * we are not supposed to use consul
   * @param {object[]} errors - An initially null array of errors that can be added as configuration is processed
   * @param {object} finalConfig - A object in which to store the retrieved configuration
   */
  function createConsulProcessor(configRequest, consulClient, errors, finalConfig) {
    return function processKeyValuePairsInConsul(callback) {
      var requestedKeys = configRequest.keys,
          requestedServices = configRequest.services,
          requestedKey,
          pathPrefixIdx,
          idx,
          resultPtr;
      if (consulClient !== undefined && (requestedKeys || requestedServices)) {
        requestedKeys = requestedKeys || [];
        requestedServices = requestedServices || [];
        consulClient.kv.get({
          key: configRequest.name + '/',
          recurse: true
        }, function (err, result) {
          var foundKeyIdx,
              hasService,
              pathPtr;
          if (err) {
            if (!errors) {
              errors = [];
            }
            errors.push(err);
          } else if (result) {
            //MSH 2015-03-03 The following loop & block deserve some explanation. The goal
            //is to take keys returned from Consul and set config values for services & keys
            //services is, unfortunately, an array of objects with *3* properties that may need
            //to be fulfilled (key, prefix, and suffix).
            for (idx = 0; idx < result.length; idx++) {
              pathPrefixIdx = result[idx].Key.indexOf(configRequest.name);
              pathPtr = '#' + result[idx].Key.slice(pathPrefixIdx + configRequest.name.length);
              foundKeyIdx = requestedKeys.indexOf(pathPtr);
              resultPtr = jptr.create(pathPtr);
              if (foundKeyIdx !== -1) {
                ensureObjPath(finalConfig, resultPtr);
                resultPtr.set(finalConfig, result[idx].Value);
              } else {
                hasService = requestedServices.some(doesServiceHavePtr, pathPtr);
                if (hasService) {
                  ensureObjPath(finalConfig, resultPtr);
                  resultPtr.set(finalConfig, result[idx].Value);
                }
              }
            }
          }
          callback(null, configRequest, errors, finalConfig);
        });
      } else {
        callback(null, configRequest, errors, finalConfig);
      }
    };
  }

  /**
   * Handles the retrieval of KV pairs from the application's runtime environment
   * @param {object} configRequest - A client provided object that may have a key value section for required key value pairs
   * @param {object[]} errors - An initially null array of errors that can be added as configuration is processed
   * @param {object} finalConfig - A object in which to store the retrieved configuration
   */
  function processKeyValuesPairsInEnv(configRequest, errors, finalConfig, callback) {
    var requestedKeys = configRequest.keys,
        requestedServices = configRequest.services,
        prefix,
        ptr,
        idx;
    if (configRequest.name) {
      prefix = configRequest.name.toUpperCase();
    } else {
      prefix = '';
    }
    if (requestedKeys) {
      for (idx = 0; idx < requestedKeys.length; idx++) {
        ptr = jptr.create(requestedKeys[idx]);
        matchAndSetEnvVar(finalConfig, prefix, ptr);
      }
    }
    if (requestedServices) {
      for (idx = 0; idx < requestedServices.length; idx++) {
        ptr = jptr.create(requestedServices[idx].key);
        matchAndSetEnvVar(finalConfig, prefix, ptr);
        if (requestedServices[idx].prefix) {
          ptr = jptr.create(requestedServices[idx].prefix);
          matchAndSetEnvVar(finalConfig, prefix, ptr);
        }
        if (requestedServices[idx].suffix) {
          ptr = jptr.create(requestedServices[idx].suffix);
          matchAndSetEnvVar(finalConfig, prefix, ptr);
        }
      }
    }
    callback(null, configRequest, errors, finalConfig);
  }

  /**
  * Handles the retrieval of KV pairs based on DNS SRV requests
  * @param {object} configRequest - A client provided object that may have a services section
  * @param {object[]} errors - An initially null array of errors that can be added as configuration is processed
  * @param {object} finalConfig - A object in which to store the retrieved configuration
  */
  function processServicesInDNS(configRequest, errors, finalConfig, callback) {
    assert.optionalArrayOfObject(configRequest.services, 'configRequest.services');
    if (configRequest.services !== undefined) {
      resolveSrv(configRequest, finalConfig, function (err, config) {
        var serviceName,
            ptr;
        if (!err) {
          for (serviceName in config) {
            if (config.hasOwnProperty(serviceName)) {
              ptr = jptr.create(config[serviceName].key);
              ensureObjPath(finalConfig, ptr);
              ptr.set(finalConfig, config[serviceName].value);
            }
          }
        } else {
          if (!errors) {
            errors = [];
          }
          errors.push(err);
        }
        callback(null, configRequest, errors, finalConfig);
      });
    } else {
      callback(null, configRequest, errors, finalConfig);
    }

  }

  /**
   * Finish processing wraps up configuration processing and determines if required parameters were met
   * @param {object} configRequest - A client provided object that determines the configuration to request
   * @param {object[]} errors - An initially null array of errors that can be added as configuration is processed
   * @param {object} finalConfig - The output configuration object
   */
  function finishProcessing(configRequest, errors, finalConfig, callback) {
    var ptr,
        idx,
        optionalSet = {},
        keyToCheck;
    if (configRequest.optional) {
      for (idx = 0; idx < configRequest.optional.length; idx++) {
        optionalSet[configRequest.optional[idx]] = true;
      }
    }
    if (configRequest.keys) {
      for (idx = 0; idx < configRequest.keys.length; idx++) {
        keyToCheck = configRequest.keys[idx];
        //if the key was *not* optional and the key was not in our final config object then....
        if (!(keyToCheck in optionalSet) && jptr.create(keyToCheck).get(finalConfig) === undefined) {
          if (errors === null) {
            errors = [];
          }
          errors.push(new Error('Missing key value specified by ' + keyToCheck));
        }
      }
    }
    if (configRequest.services) {
      for (idx = 0; idx < configRequest.services.length; idx++) {
        keyToCheck = configRequest.services[idx].key;
        if (!(keyToCheck in optionalSet) && jptr.create(keyToCheck).get(finalConfig) === undefined) {
          if (errors === null) {
            errors = [];
          }
          errors.push(new Error('Missing service key value specified by service with name ' + keyToCheck));
        }
      }
    }
    callback(errors, finalConfig);
  }

  //Client facing code

  /**
   * @callback doneCallback
   * @param {object[]} err - An array of configuration errors, if not null then at least one configuration values failed to be retrieved
   * @param {object} config - The finished configuration object
   */

  /**
   * Allows a client to request configuration
   * @name module:lib
   * @param {object} config - A env-configurator config object that determines what properties will be looked up
   * @param {doneCallback} callback - A callback function to notify the client of the library with the new configuration object
   */
  function getConfiguration(config, callback) {
    assert.func(callback, 'callback');
    assert.object(config, 'config');
    var finalConfig = {};
    var consulClient = setupConsul(config);
    async.waterfall([
      createConsulProcessor(config, consulClient, null, finalConfig),
      processKeyValuesPairsInEnv,
      processServicesInDNS,
      finishProcessing
    ], callback);
  }

  return getConfiguration;
})();<commit_msg>added spec name context to error messages<commit_after>var resolveSrv = require('./dns.js'),
    EventEmitter = require('events').EventEmitter,
    jptr = require('json-ptr'),
    assert = require('assert-plus'),
    async = require('async');
/**
* Attempts to provide a configuration based on a required configuration input
* @module lib
*/
module.exports =(function () {
  'use strict';

  var CONSUL_HOST = 'CONSUL_HOST',
      CONSUL_PORT = 'CONSUL_PORT',
      CONSUL_SECURE = 'CONSUL_SECURE',
      ee = new EventEmitter(),
      matchSlashGreedy = new RegExp('/', 'g');

  /**
   * Does Service Have Ptr checks to see if a value **bound** to this is present in
   * any of the three fields used to define a JSON Ptr in a service spec
   *
   * This function can be used on its own, but it is intended to be used with the Array.some(...) function
   *
   * @param {object} val - An object, generally expected to be a service specification from configuration-schema.json
   * @returns {boolean} True or false based on whether the bound name 'this' was present in any of the JSON Ptr fields
   */
  function doesServiceHavePtr(val) {
    //Stop jshint from whining about 'this' since this func is explicitly meant
    //to be used with 'bind()'
    /*jshint validthis:true */
    return val.key === this || val.prefix === this || val.suffix === this;
  }

  /**
   * Match and set env var looks for an environment variable given an name prefix and a JSON ptr
   * @param {object} finalConfig - The object to store the value of the env var in if found
   * @param {string} prefix - A prefix to apply to names being looked for in the environment
   * @param {object} ptr - A JSON ptr object
   */
  function matchAndSetEnvVar(finalConfig, prefix, ptr) {
    var requestedProp,
        envVal;
    requestedProp = ptr.pointer.replace(matchSlashGreedy, '_').toUpperCase();
    envVal = process.env[prefix.concat(requestedProp)];
    if (envVal !== undefined) {
      ensureObjPath(finalConfig, ptr);
      ptr.set(finalConfig, envVal);
    }
  }

  /**
   * Ensure object path is a small utility function for use with JSON pointers that ensures a particular pointer path
   * exists in an object. The function is non-destructive and only creates a path component as necessary.
   * @param {object} object - A JSON object to ensure that a particular pointer path exists in
   * @param {object} ptr - A {@link https://github.com/flitbit/json-ptr|JSON pointer object}
   */
  function ensureObjPath(object, ptr) {
    var cursor = object,
        currentName,
        idx,
        pathAry = ptr.path;
    for (idx = 0; idx < pathAry.length; idx++) {
      currentName = pathAry[idx];
      if (!cursor[currentName]) {
        cursor[currentName] = {};
      }
      cursor = cursor[currentName];
    }

  }

  /**
   * Determines if we should use a consul agent to add configuration
   * @param {object} configurationRequest - A configuration request object that may contain information required to contact a consul agent
   * @returns {object} A reference to an active consule agent if one could be configured, undefined otherwise
   */
  function setupConsul(configRequest) {
    //client must fully specify how we will talk to consul
    var consulClient,
        consulHost = process.env[CONSUL_HOST],
        consulPort = process.env[CONSUL_PORT],
        consulSecure = (process.env[CONSUL_SECURE] === 'true');
    if (consulHost !== undefined &&
    consulPort !== undefined &&
    consulSecure !== undefined) {
      consulClient = require('consul')({
        host: consulHost,
        port: consulPort,
        secure: consulSecure
      });
    }
    return consulClient;
  }

  /**
   * Handles the retrieval of KV pairs from a consul agent, if the given consulClient isn't defined then
   * the method is a no-op
   * @param {object} configRequest - A client provided object that may have a key value section for required key value pairs
   * @param {object} consulClient - A pre-configured client used to communicate with a consul agent, may be undefined to indicate that
   * we are not supposed to use consul
   * @param {object[]} errors - An initially null array of errors that can be added as configuration is processed
   * @param {object} finalConfig - A object in which to store the retrieved configuration
   */
  function createConsulProcessor(configRequest, consulClient, errors, finalConfig) {
    return function processKeyValuePairsInConsul(callback) {
      var requestedKeys = configRequest.keys,
          requestedServices = configRequest.services,
          requestedKey,
          pathPrefixIdx,
          idx,
          resultPtr;
      if (consulClient !== undefined && (requestedKeys || requestedServices)) {
        requestedKeys = requestedKeys || [];
        requestedServices = requestedServices || [];
        consulClient.kv.get({
          key: configRequest.name + '/',
          recurse: true
        }, function (err, result) {
          var foundKeyIdx,
              hasService,
              pathPtr;
          if (err) {
            if (!errors) {
              errors = [];
            }
            errors.push(err);
          } else if (result) {
            //MSH 2015-03-03 The following loop & block deserve some explanation. The goal
            //is to take keys returned from Consul and set config values for services & keys
            //services is, unfortunately, an array of objects with *3* properties that may need
            //to be fulfilled (key, prefix, and suffix).
            for (idx = 0; idx < result.length; idx++) {
              pathPrefixIdx = result[idx].Key.indexOf(configRequest.name);
              pathPtr = '#' + result[idx].Key.slice(pathPrefixIdx + configRequest.name.length);
              foundKeyIdx = requestedKeys.indexOf(pathPtr);
              resultPtr = jptr.create(pathPtr);
              if (foundKeyIdx !== -1) {
                ensureObjPath(finalConfig, resultPtr);
                resultPtr.set(finalConfig, result[idx].Value);
              } else {
                hasService = requestedServices.some(doesServiceHavePtr, pathPtr);
                if (hasService) {
                  ensureObjPath(finalConfig, resultPtr);
                  resultPtr.set(finalConfig, result[idx].Value);
                }
              }
            }
          }
          callback(null, configRequest, errors, finalConfig);
        });
      } else {
        callback(null, configRequest, errors, finalConfig);
      }
    };
  }

  /**
   * Handles the retrieval of KV pairs from the application's runtime environment
   * @param {object} configRequest - A client provided object that may have a key value section for required key value pairs
   * @param {object[]} errors - An initially null array of errors that can be added as configuration is processed
   * @param {object} finalConfig - A object in which to store the retrieved configuration
   */
  function processKeyValuesPairsInEnv(configRequest, errors, finalConfig, callback) {
    var requestedKeys = configRequest.keys,
        requestedServices = configRequest.services,
        prefix,
        ptr,
        idx;
    if (configRequest.name) {
      prefix = configRequest.name.toUpperCase();
    } else {
      prefix = '';
    }
    if (requestedKeys) {
      for (idx = 0; idx < requestedKeys.length; idx++) {
        ptr = jptr.create(requestedKeys[idx]);
        matchAndSetEnvVar(finalConfig, prefix, ptr);
      }
    }
    if (requestedServices) {
      for (idx = 0; idx < requestedServices.length; idx++) {
        ptr = jptr.create(requestedServices[idx].key);
        matchAndSetEnvVar(finalConfig, prefix, ptr);
        if (requestedServices[idx].prefix) {
          ptr = jptr.create(requestedServices[idx].prefix);
          matchAndSetEnvVar(finalConfig, prefix, ptr);
        }
        if (requestedServices[idx].suffix) {
          ptr = jptr.create(requestedServices[idx].suffix);
          matchAndSetEnvVar(finalConfig, prefix, ptr);
        }
      }
    }
    callback(null, configRequest, errors, finalConfig);
  }

  /**
  * Handles the retrieval of KV pairs based on DNS SRV requests
  * @param {object} configRequest - A client provided object that may have a services section
  * @param {object[]} errors - An initially null array of errors that can be added as configuration is processed
  * @param {object} finalConfig - A object in which to store the retrieved configuration
  */
  function processServicesInDNS(configRequest, errors, finalConfig, callback) {
    assert.optionalArrayOfObject(configRequest.services, 'configRequest.services');
    if (configRequest.services !== undefined) {
      resolveSrv(configRequest, finalConfig, function (err, config) {
        var serviceName,
            ptr;
        if (!err) {
          for (serviceName in config) {
            if (config.hasOwnProperty(serviceName)) {
              ptr = jptr.create(config[serviceName].key);
              ensureObjPath(finalConfig, ptr);
              ptr.set(finalConfig, config[serviceName].value);
            }
          }
        } else {
          if (!errors) {
            errors = [];
          }
          errors.push(err);
        }
        callback(null, configRequest, errors, finalConfig);
      });
    } else {
      callback(null, configRequest, errors, finalConfig);
    }

  }

  /**
   * Finish processing wraps up configuration processing and determines if required parameters were met
   * @param {object} configRequest - A client provided object that determines the configuration to request
   * @param {object[]} errors - An initially null array of errors that can be added as configuration is processed
   * @param {object} finalConfig - The output configuration object
   */
  function finishProcessing(configRequest, errors, finalConfig, callback) {
    var ptr,
        idx,
        optionalSet = {},
        keyToCheck;
    if (configRequest.optional) {
      for (idx = 0; idx < configRequest.optional.length; idx++) {
        optionalSet[configRequest.optional[idx]] = true;
      }
    }
    if (configRequest.keys) {
      for (idx = 0; idx < configRequest.keys.length; idx++) {
        keyToCheck = configRequest.keys[idx];
        //if the key was *not* optional and the key was not in our final config object then....
        if (!(keyToCheck in optionalSet) && jptr.create(keyToCheck).get(finalConfig) === undefined) {
          if (errors === null) {
            errors = [];
          }
          errors.push(new Error('Missing key value specified by ['.concat(configRequest.name, ']', keyToCheck)));
        }
      }
    }
    if (configRequest.services) {
      for (idx = 0; idx < configRequest.services.length; idx++) {
        keyToCheck = configRequest.services[idx].key;
        if (!(keyToCheck in optionalSet) && jptr.create(keyToCheck).get(finalConfig) === undefined) {
          if (errors === null) {
            errors = [];
          }
          errors.push(new Error('Missing service key value specified by service with name ['.concat(configRequest.name, ']', keyToCheck)));
        }
      }
    }
    callback(errors, finalConfig);
  }

  //Client facing code

  /**
   * @callback doneCallback
   * @param {object[]} err - An array of configuration errors, if not null then at least one configuration values failed to be retrieved
   * @param {object} config - The finished configuration object
   */

  /**
   * Allows a client to request configuration
   * @name module:lib
   * @param {object} config - A env-configurator config object that determines what properties will be looked up
   * @param {doneCallback} callback - A callback function to notify the client of the library with the new configuration object
   */
  function getConfiguration(config, callback) {
    assert.func(callback, 'callback');
    assert.object(config, 'config');
    var finalConfig = {};
    var consulClient = setupConsul(config);
    async.waterfall([
      createConsulProcessor(config, consulClient, null, finalConfig),
      processKeyValuesPairsInEnv,
      processServicesInDNS,
      finishProcessing
    ], callback);
  }

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


<commit_before>/* eslint no-console:0 */
import path from 'path'
import fs from 'fs'
import glob from 'glob'
import Rx from 'rxjs/Rx'
import format from 'prettier-eslint'
import chalk from 'chalk'
import getStdin from 'get-stdin'
import nodeIgnore from 'ignore'
import findUp from 'find-up'
import memoize from 'lodash.memoize'
import indentString from 'indent-string'
import getLogger from 'loglevel-colored-level-prefix'
import * as messages from './messages'

const LINE_SEPERATOR_REGEX = /(\r|\n|\r\n)/
const rxGlob = Rx.Observable.bindNodeCallback(glob)
const rxReadFile = Rx.Observable.bindNodeCallback(fs.readFile)
const rxWriteFile = Rx.Observable.bindNodeCallback(fs.writeFile)
const findUpSyncMemoized = memoize(findUpSync, function resolver(...args) {
  return args.join('::')
})
const getIsIgnoredMemoized = memoize(getIsIgnored)

const logger = getLogger({prefix: 'prettier-eslint-cli'})

export default formatFilesFromArgv

async function formatFilesFromArgv(
  {
    _: fileGlobs,
    logLevel = logger.getLevel(),
    stdin,
    write,
    eslintPath,
    prettierPath,
    ignore: ignoreGlobs = [],
    eslintIgnore: applyEslintIgnore = true,
  },
) {
  logger.setLevel(logLevel)
  const prettierESLintOptions = {logLevel, eslintPath, prettierPath}
  const cliOptions = {write}
  if (stdin) {
    return formatStdin(prettierESLintOptions)
  } else {
    return formatFilesFromGlobs(
      fileGlobs,
      [...ignoreGlobs], // make a copy to avoid manipulation
      cliOptions,
      prettierESLintOptions,
      applyEslintIgnore,
    )
  }
}

async function formatStdin(prettierESLintOptions) {
  const stdinValue = (await getStdin()).trim()
  try {
    const formatted = format({text: stdinValue, ...prettierESLintOptions})
    console.log(formatted)
    return Promise.resolve(formatted)
  } catch (error) {
    logger.error(
      'There was a problem trying to format the stdin text',
      error.stack,
    )
    process.exitCode = 1
    return Promise.resolve(stdinValue)
  }
}

async function formatFilesFromGlobs(
  fileGlobs,
  ignoreGlobs,
  cliOptions,
  prettierESLintOptions,
  applyEslintIgnore,
) {
  const concurrentGlobs = 3
  const concurrentFormats = 10
  return new Promise(resolve => {
    const successes = []
    const failures = []
    const unchanged = []
    Rx.Observable
      .from(fileGlobs)
      .mergeMap(
        getFilesFromGlob.bind(null, ignoreGlobs, applyEslintIgnore),
        null,
        concurrentGlobs,
      )
      .concatAll()
      .distinct()
      .mergeMap(filePathToFormatted, null, concurrentFormats)
      .subscribe(onNext, onError, onComplete)

    function filePathToFormatted(filePath) {
      return formatFile(filePath, prettierESLintOptions, cliOptions)
    }

    function onNext(info) {
      if (info.error) {
        failures.push(info)
      } else if (info.unchanged) {
        unchanged.push(info)
      } else {
        successes.push(info)
      }
    }

    function onError(error) {
      logger.error(
        'There was an unhandled error while formatting the files',
        error.stack,
      )
      process.exitCode = 1
      resolve({error, successes, failures})
    }

    function onComplete() {
      if (successes.length) {
        console.log(
          messages.success({
            success: chalk.green('success'),
            count: successes.length,
            countString: chalk.bold(successes.length),
          }),
        )
      }
      if (failures.length) {
        process.exitCode = 1
        console.log(
          messages.failure({
            failure: chalk.red('failure'),
            count: failures.length,
            countString: chalk.bold(failures.length),
          }),
        )
      }
      if (unchanged.length) {
        console.log(
          messages.unchanged({
            unchanged: chalk.gray('unchanged'),
            count: unchanged.length,
            countString: chalk.bold(unchanged.length),
          }),
        )
      }
      resolve({successes, failures})
    }
  })
}

function getFilesFromGlob(ignoreGlobs, applyEslintIgnore, fileGlob) {
  const globOptions = {ignore: ignoreGlobs}
  if (!fileGlob.includes('node_modules')) {
    // basically, we're going to protect you from doing something
    // not smart unless you explicitly include it in your glob
    globOptions.ignore.push('**/node_modules/**')
  }
  return rxGlob(fileGlob, globOptions).map(filePaths => {
    return filePaths.filter(filePath => {
      return applyEslintIgnore ?
        !isFilePathMatchedByEslintignore(filePath) :
        true
    })
  })
}

function formatFile(filePath, prettierESLintOptions, cliOptions) {
  const fileInfo = {filePath}
  let format$ = rxReadFile(filePath, 'utf8').map(text => {
    fileInfo.text = text
    fileInfo.formatted = format({text, filePath, ...prettierESLintOptions})
    return fileInfo
  })

  if (cliOptions.write) {
    format$ = format$.mergeMap(info => {
      if (info.text === info.formatted) {
        return Rx.Observable.of(Object.assign(fileInfo, {unchanged: true}))
      } else {
        return rxWriteFile(filePath, info.formatted).map(() => fileInfo)
      }
    })
  } else {
    format$ = format$.map(info => {
      console.log(info.formatted)
      return info
    })
  }

  return format$.catch(error => {
    logger.error(
      `There was an error formatting "${fileInfo.filePath}":`,
      indentString(error.stack, 4),
    )
    return Rx.Observable.of(Object.assign(fileInfo, {error}))
  })
}

function getNearestEslintignorePath(filePath) {
  const {dir} = path.parse(filePath)
  return findUpSyncMemoized('.eslintignore', dir)
}

function isFilePathMatchedByEslintignore(filePath) {
  const eslintignorePath = getNearestEslintignorePath(filePath)
  if (!eslintignorePath) {
    return false
  }

  const eslintignoreDir = path.parse(eslintignorePath).dir
  const filePathRelativeToEslintignoreDir = path.relative(
    eslintignoreDir,
    filePath,
  )
  const isIgnored = getIsIgnoredMemoized(eslintignorePath)
  return isIgnored(filePathRelativeToEslintignoreDir)
}

function findUpSync(filename, cwd) {
  return findUp.sync('.eslintignore', {cwd})
}

function getIsIgnored(filename) {
  const ignoreLines = fs
    .readFileSync(filename, 'utf8')
    .split(LINE_SEPERATOR_REGEX)
    .filter(line => Boolean(line.trim()))
  const instance = nodeIgnore()
  instance.add(ignoreLines)
  return instance.ignores.bind(instance)
}
<commit_msg>fix(errors): format errors a little nicer<commit_after>/* eslint no-console:0 */
import path from 'path'
import fs from 'fs'
import glob from 'glob'
import Rx from 'rxjs/Rx'
import format from 'prettier-eslint'
import chalk from 'chalk'
import getStdin from 'get-stdin'
import nodeIgnore from 'ignore'
import findUp from 'find-up'
import memoize from 'lodash.memoize'
import indentString from 'indent-string'
import getLogger from 'loglevel-colored-level-prefix'
import * as messages from './messages'

const LINE_SEPERATOR_REGEX = /(\r|\n|\r\n)/
const rxGlob = Rx.Observable.bindNodeCallback(glob)
const rxReadFile = Rx.Observable.bindNodeCallback(fs.readFile)
const rxWriteFile = Rx.Observable.bindNodeCallback(fs.writeFile)
const findUpSyncMemoized = memoize(findUpSync, function resolver(...args) {
  return args.join('::')
})
const getIsIgnoredMemoized = memoize(getIsIgnored)

const logger = getLogger({prefix: 'prettier-eslint-cli'})

export default formatFilesFromArgv

async function formatFilesFromArgv(
  {
    _: fileGlobs,
    logLevel = logger.getLevel(),
    stdin,
    write,
    eslintPath,
    prettierPath,
    ignore: ignoreGlobs = [],
    eslintIgnore: applyEslintIgnore = true,
  },
) {
  logger.setLevel(logLevel)
  const prettierESLintOptions = {logLevel, eslintPath, prettierPath}
  const cliOptions = {write}
  if (stdin) {
    return formatStdin(prettierESLintOptions)
  } else {
    return formatFilesFromGlobs(
      fileGlobs,
      [...ignoreGlobs], // make a copy to avoid manipulation
      cliOptions,
      prettierESLintOptions,
      applyEslintIgnore,
    )
  }
}

async function formatStdin(prettierESLintOptions) {
  const stdinValue = (await getStdin()).trim()
  try {
    const formatted = format({text: stdinValue, ...prettierESLintOptions})
    console.log(formatted)
    return Promise.resolve(formatted)
  } catch (error) {
    logger.error(
      'There was a problem trying to format the stdin text',
      `\n${indentString(error.stack, 4)}`,
    )
    process.exitCode = 1
    return Promise.resolve(stdinValue)
  }
}

async function formatFilesFromGlobs(
  fileGlobs,
  ignoreGlobs,
  cliOptions,
  prettierESLintOptions,
  applyEslintIgnore,
) {
  const concurrentGlobs = 3
  const concurrentFormats = 10
  return new Promise(resolve => {
    const successes = []
    const failures = []
    const unchanged = []
    Rx.Observable
      .from(fileGlobs)
      .mergeMap(
        getFilesFromGlob.bind(null, ignoreGlobs, applyEslintIgnore),
        null,
        concurrentGlobs,
      )
      .concatAll()
      .distinct()
      .mergeMap(filePathToFormatted, null, concurrentFormats)
      .subscribe(onNext, onError, onComplete)

    function filePathToFormatted(filePath) {
      return formatFile(filePath, prettierESLintOptions, cliOptions)
    }

    function onNext(info) {
      if (info.error) {
        failures.push(info)
      } else if (info.unchanged) {
        unchanged.push(info)
      } else {
        successes.push(info)
      }
    }

    function onError(error) {
      logger.error(
        'There was an unhandled error while formatting the files',
        `\n${indentString(error.stack, 4)}`,
      )
      process.exitCode = 1
      resolve({error, successes, failures})
    }

    function onComplete() {
      if (successes.length) {
        console.log(
          messages.success({
            success: chalk.green('success'),
            count: successes.length,
            countString: chalk.bold(successes.length),
          }),
        )
      }
      if (failures.length) {
        process.exitCode = 1
        console.log(
          messages.failure({
            failure: chalk.red('failure'),
            count: failures.length,
            countString: chalk.bold(failures.length),
          }),
        )
      }
      if (unchanged.length) {
        console.log(
          messages.unchanged({
            unchanged: chalk.gray('unchanged'),
            count: unchanged.length,
            countString: chalk.bold(unchanged.length),
          }),
        )
      }
      resolve({successes, failures})
    }
  })
}

function getFilesFromGlob(ignoreGlobs, applyEslintIgnore, fileGlob) {
  const globOptions = {ignore: ignoreGlobs}
  if (!fileGlob.includes('node_modules')) {
    // basically, we're going to protect you from doing something
    // not smart unless you explicitly include it in your glob
    globOptions.ignore.push('**/node_modules/**')
  }
  return rxGlob(fileGlob, globOptions).map(filePaths => {
    return filePaths.filter(filePath => {
      return applyEslintIgnore ?
        !isFilePathMatchedByEslintignore(filePath) :
        true
    })
  })
}

function formatFile(filePath, prettierESLintOptions, cliOptions) {
  const fileInfo = {filePath}
  let format$ = rxReadFile(filePath, 'utf8').map(text => {
    fileInfo.text = text
    fileInfo.formatted = format({text, filePath, ...prettierESLintOptions})
    return fileInfo
  })

  if (cliOptions.write) {
    format$ = format$.mergeMap(info => {
      if (info.text === info.formatted) {
        return Rx.Observable.of(Object.assign(fileInfo, {unchanged: true}))
      } else {
        return rxWriteFile(filePath, info.formatted).map(() => fileInfo)
      }
    })
  } else {
    format$ = format$.map(info => {
      console.log(info.formatted)
      return info
    })
  }

  return format$.catch(error => {
    logger.error(
      `There was an error formatting "${fileInfo.filePath}":`,
      `\n${indentString(error.stack, 4)}`,
    )
    return Rx.Observable.of(Object.assign(fileInfo, {error}))
  })
}

function getNearestEslintignorePath(filePath) {
  const {dir} = path.parse(filePath)
  return findUpSyncMemoized('.eslintignore', dir)
}

function isFilePathMatchedByEslintignore(filePath) {
  const eslintignorePath = getNearestEslintignorePath(filePath)
  if (!eslintignorePath) {
    return false
  }

  const eslintignoreDir = path.parse(eslintignorePath).dir
  const filePathRelativeToEslintignoreDir = path.relative(
    eslintignoreDir,
    filePath,
  )
  const isIgnored = getIsIgnoredMemoized(eslintignorePath)
  return isIgnored(filePathRelativeToEslintignoreDir)
}

function findUpSync(filename, cwd) {
  return findUp.sync('.eslintignore', {cwd})
}

function getIsIgnored(filename) {
  const ignoreLines = fs
    .readFileSync(filename, 'utf8')
    .split(LINE_SEPERATOR_REGEX)
    .filter(line => Boolean(line.trim()))
  const instance = nodeIgnore()
  instance.add(ignoreLines)
  return instance.ignores.bind(instance)
}
<|endoftext|>


<commit_before>var builder = require('./builder/builder');
var endpoint = require('./endpoint/');
var middleWare = require('./middleware/');

module.exports = function(){
	var b = builder();
	b.createEndpoints = function(){
		return endpoint(b.getRoot());
	};
	b.createMW = function(badRequest, notAllowed){
		var endpoints = this.createEndpoints();
		return middleWare(endpoints, badRequest, notAllowed);
	};
	return b;
};<commit_msg>Fix defaultOptionMethod<commit_after>var builder = require('./builder/builder');
var endpoint = require('./endpoint/');
var middleWare = require('./middleware/');

module.exports = function(options){
	var b = builder(options.defaultOptionMethod);
	b.createEndpoints = function(){
		return endpoint(b.getRoot());
	};
	b.createMW = function(badRequest, notAllowed){
		var endpoints = this.createEndpoints();
		return middleWare(endpoints, badRequest, notAllowed);
	};
	return b;
};<|endoftext|>