<commit_before>import Head from 'next/head'
import { THEMES, COLORS } from '../lib/constants'
import Reset from './style/Reset'
import Font from './style/Font'
import Typography from './style/Typography'

export default () => (

      <meta charSet="utf-8" />
      <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1" />
      <meta
        name="description"
        content="Carbon is the easiest way to create and share beautiful images of your source code."
      />
      <meta name="application-name" content="Carbon" />
      <meta name="twitter:title" content="Carbon" />
      <meta
        name="twitter:description"
        content="Carbon is the easiest way to create and share beautiful images of your source code."
      />
      <meta name="og:title" content="Carbon" />
      <meta
        name="og:description"
        content="Carbon is the easiest way to create and share beautiful images of your source code."
      />
      <meta name="og:image" content="/static/banner.png" />
      <title>Carbon</title>
      <link rel="shortcut icon" href="/static/favicon.ico" />
      <link
        rel="stylesheet"
        href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/codemirror.min.css"
      />
      <link
        rel="stylesheet"
        href={`//cdnjs.cloudflare.com/ajax/libs/codemirror/5.30.0/theme/solarized.min.css`}
      />
      {THEMES.filter(t => t.hasStylesheet !== false).map((theme, i) => (
        <link
          key={i}
          rel="stylesheet"
          href={
            theme.link ||
            `//cdnjs.cloudflare.com/ajax/libs/codemirror/5.34.0/theme/${theme.id}.min.css`
          }
        />
      ))}
      <link rel="stylesheet" type="text/css" href="/static/react-spinner.css" />
      <link rel="stylesheet" type="text/css" href="/static/react-crop.css" />

    <Reset />

    <Typography />

)
<commit_msg>Theme color [PWA]<commit_after>import Head from 'next/head'
import { THEMES, COLORS } from '../lib/constants'
import Reset from './style/Reset'
import Font from './style/Font'
import Typography from './style/Typography'

export default () => (

      <meta charSet="utf-8" />
      <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1" />
      <meta
        name="description"
        content="Carbon is the easiest way to create and share beautiful images of your source code."
      />
      <meta name="application-name" content="Carbon" />
      <meta name="twitter:title" content="Carbon" />
      <meta
        name="twitter:description"
        content="Carbon is the easiest way to create and share beautiful images of your source code."
      />
      <meta name="og:title" content="Carbon" />
      <meta
        name="og:description"
        content="Carbon is the easiest way to create and share beautiful images of your source code."
      />
      <meta name="og:image" content="/static/banner.png" />
      <meta name="theme-color" content="#121212" />
      <title>Carbon</title>
      <link rel="shortcut icon" href="/static/favicon.ico" />
      <link
        rel="stylesheet"
        href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.26.0/codemirror.min.css"
      />
      <link
        rel="stylesheet"
        href={`//cdnjs.cloudflare.com/ajax/libs/codemirror/5.30.0/theme/solarized.min.css`}
      />
      {THEMES.filter(t => t.hasStylesheet !== false).map((theme, i) => (
        <link
          key={i}
          rel="stylesheet"
          href={
            theme.link ||
            `//cdnjs.cloudflare.com/ajax/libs/codemirror/5.34.0/theme/${theme.id}.min.css`
          }
        />
      ))}
      <link rel="stylesheet" type="text/css" href="/static/react-spinner.css" />
      <link rel="stylesheet" type="text/css" href="/static/react-crop.css" />

    <Reset />

    <Typography />

)
<|endoftext|>


<commit_before>// Copyright 2015 Yahoo! Inc.
// Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms.

var units = require('../utils/constants').physicalUnits;

/**
 * @class sCAL
 * @module PNG
 * @submodule PNGChunks
 */
module.exports = {

	/**
	 * Gets the sequence
	 *
	 * @method getSequence
	 * @return {int}
	 */
	getSequence: function () {
		return 142;
	},

	/**
	 * Gets the physical width per pixel
	 *
	 * @method getPixelWidth
	 * @return {float}
	 */
	getPixelWidth: function () {
		return this._pixelWidth || 1;
	},

	/**
	 * Sets the physical width per pixel
	 *
	 * @method setPixelWidth
	 * @param {float} pixelWidth Physical width per pixel
	 */
	setPixelWidth: function (pixelWidth) {
		this._pixelWidth = pixelWidth;
	},

	/**
	 * Gets the physical height per pixel
	 *
	 * @method getPixelHeight
	 * @return {float}
	 */
	getPixelHeight: function () {
		return this._pixelHeight || 1;
	},

	/**
	 * Sets the physical height per pixel
	 *
	 * @method setPixelHeight
	 * @param {float} pixelHeight Physical height per pixel
	 */
	setPixelHeight: function (pixelHeight) {
		this._pixelHeight = pixelHeight;
	},

	/**
	 * Gets the unit identifier
	 *
	 * @method getUnit
	 * @return {int}
	 */
	getUnit: function () {
		return this._unit || 1;
	},

	/**
	 * Sets the unit identifier
	 *
	 * @method setUnit
	 * @param {int} unit Unit identifier
	 */
	setUnit: function (unit) {
		if ([units.METER, units.RADIAN].indexOf(unit) === -1) {
			throw new Error('Unit identifier ' + unit + ' is not valid.');
		}
		this._unit = unit;
	},

	/**
	 * Is unit in meter?
	 *
	 * @method isUnitInMeter
	 * @return {boolean}
	 */
	isUnitInMeter: function () {
		return (this._unit === units.METER);
	},

	/**
	 * Is unit in radian?
	 *
	 * @method isUnitInRadian
	 * @return {boolean}
	 */
	isUnitInRadian: function () {
		return (this._unit === units.RADIAN);
	},

	/**
	 * Parsing of chunk data
	 *
	 * Phase 1
	 *
	 * @method parse
	 * @param {BufferedStream} stream Data stream
	 * @param {int} length Length of chunk data
	 * @param {boolean} strict Should parsing be strict?
	 * @param {object} options Decoding options
	 */
	parse: function (stream, length, strict, options) {

		var i, len, foundIndex = null, buffer;

		// Validation
		if (strict && (this.getFirstChunk(this.getType(), false) !== null)) {
			throw new Error('Only one ' + this.getType() + ' is allowed in the data.');
		}

		this.setUnit(stream.readUInt8());

		// See where the null-character is
		buffer = stream.peekBuffer(length - 1);
		for (i = 0, len = buffer.length; i < len; i++) {
			if (buffer.readUInt8(i) === 0) {
				foundIndex = i;
				break;
			}
		}

		// Found a null-character?
		if (foundIndex === null) {
			throw new Error('Cannot find separator in ' + this.getType() + ' chunk.');
		}

		this.setPixelWidth(Number(stream.readString(foundIndex, 'ascii')));

		// Skip null
		stream.skip(1);

		this.setPixelHeight(Number(stream.readString(length - foundIndex - 1, 'ascii')));
	},

	/**
	 * Gathers chunk-data from decoded chunks
	 *
	 * Phase 5
	 *
	 * @static
	 * @method decodeData
	 * @param {object} data Data-object that will be used to export values
	 * @param {boolean} strict Should parsing be strict?
	 * @param {object} options Decoding options
	 */
	decodeData: function (data, strict, options) {

		var chunks = this.getChunksByType(this.getType());

		if (!chunks) {
			return ;
		}

		if (strict && (chunks.length !== 1)) {
			throw new Error('Not more than one chunk allowed for ' + this.getType() + '.');
		}

		data.volatile = data.volatile || {};
		data.volatile.physicalScale = {
			pixelWidth: chunks[0].getPixelWidth(),
			pixelHeight: chunks[0].getPixelHeight(),
			unit: chunks[0].getUnit()
		};
	},

	/**
	 * Returns a list of chunks to be added to the data-stream
	 *
	 * Phase 1
	 *
	 * @static
	 * @method encodeData
	 * @param {Buffer} image Image data
	 * @param {object} options Encoding options
	 * @return {Chunk[]} List of chunks to encode
	 */
	encodeData: function (image, options) {

		if (options.physicalScale) {

			var chunk = this.createChunk(this.getType(), this.getChunks());

			if (options.physicalScale.xPixelPerUnit !== undefined) {
				chunk.setPixelWidth(options.physicalScale.xPixelPerUnit);
			}
			if (options.physicalScale.yPixelPerUnit !== undefined) {
				chunk.setPixelHeight(options.physicalScale.yPixelPerUnit);
			}
			if (options.physicalScale.unit !== undefined) {
				chunk.setUnit(options.physicalScale.unit);
			}

			return [chunk];
		} else {
			return [];
		}
	},

	/**
	 * Composing of chunk data
	 *
	 * Phase 4
	 *
	 * @method compose
	 * @param {BufferedStream} stream Data stream
	 * @param {object} options Encoding options
	 */
	compose: function (stream, options) {
		stream.writeUInt8(this.getUnit());

		stream.writeASCIIString(String(this.getPixelWidth()));

		// Write null-character
		stream.writeUInt8(0);

		stream.writeASCIIString(String(this.getPixelHeight()));
	}
};
<commit_msg>Only accept positive scale for sCAL chunk<commit_after>// Copyright 2015 Yahoo! Inc.
// Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms.

var units = require('../utils/constants').physicalUnits;

/**
 * @class sCAL
 * @module PNG
 * @submodule PNGChunks
 */
module.exports = {

	/**
	 * Gets the sequence
	 *
	 * @method getSequence
	 * @return {int}
	 */
	getSequence: function () {
		return 142;
	},

	/**
	 * Gets the physical width per pixel
	 *
	 * @method getPixelWidth
	 * @return {float}
	 */
	getPixelWidth: function () {
		return this._pixelWidth || 1;
	},

	/**
	 * Sets the physical width per pixel
	 *
	 * @method setPixelWidth
	 * @param {float} pixelWidth Physical width per pixel
	 */
	setPixelWidth: function (pixelWidth) {
		if (pixelWidth <= 0) {
			throw new Error('Width should be greater than zero. It is ' + pixelWidth + '.');
		}
		this._pixelWidth = pixelWidth;
	},

	/**
	 * Gets the physical height per pixel
	 *
	 * @method getPixelHeight
	 * @return {float}
	 */
	getPixelHeight: function () {
		return this._pixelHeight || 1;
	},

	/**
	 * Sets the physical height per pixel
	 *
	 * @method setPixelHeight
	 * @param {float} pixelHeight Physical height per pixel
	 */
	setPixelHeight: function (pixelHeight) {
		if (pixelHeight <= 0) {
			throw new Error('Height should be greater than zero. It is ' + pixelHeight + '.');
		}
		this._pixelHeight = pixelHeight;
	},

	/**
	 * Gets the unit identifier
	 *
	 * @method getUnit
	 * @return {int}
	 */
	getUnit: function () {
		return this._unit || 1;
	},

	/**
	 * Sets the unit identifier
	 *
	 * @method setUnit
	 * @param {int} unit Unit identifier
	 */
	setUnit: function (unit) {
		if ([units.METER, units.RADIAN].indexOf(unit) === -1) {
			throw new Error('Unit identifier ' + unit + ' is not valid.');
		}
		this._unit = unit;
	},

	/**
	 * Is unit in meter?
	 *
	 * @method isUnitInMeter
	 * @return {boolean}
	 */
	isUnitInMeter: function () {
		return (this._unit === units.METER);
	},

	/**
	 * Is unit in radian?
	 *
	 * @method isUnitInRadian
	 * @return {boolean}
	 */
	isUnitInRadian: function () {
		return (this._unit === units.RADIAN);
	},

	/**
	 * Parsing of chunk data
	 *
	 * Phase 1
	 *
	 * @method parse
	 * @param {BufferedStream} stream Data stream
	 * @param {int} length Length of chunk data
	 * @param {boolean} strict Should parsing be strict?
	 * @param {object} options Decoding options
	 */
	parse: function (stream, length, strict, options) {

		var i, len, foundIndex = null, buffer;

		// Validation
		if (strict && (this.getFirstChunk(this.getType(), false) !== null)) {
			throw new Error('Only one ' + this.getType() + ' is allowed in the data.');
		}

		this.setUnit(stream.readUInt8());

		// See where the null-character is
		buffer = stream.peekBuffer(length - 1);
		for (i = 0, len = buffer.length; i < len; i++) {
			if (buffer.readUInt8(i) === 0) {
				foundIndex = i;
				break;
			}
		}

		// Found a null-character?
		if (foundIndex === null) {
			throw new Error('Cannot find separator in ' + this.getType() + ' chunk.');
		}

		this.setPixelWidth(Number(stream.readString(foundIndex, 'ascii')));

		// Skip null
		stream.skip(1);

		this.setPixelHeight(Number(stream.readString(length - foundIndex - 1, 'ascii')));
	},

	/**
	 * Gathers chunk-data from decoded chunks
	 *
	 * Phase 5
	 *
	 * @static
	 * @method decodeData
	 * @param {object} data Data-object that will be used to export values
	 * @param {boolean} strict Should parsing be strict?
	 * @param {object} options Decoding options
	 */
	decodeData: function (data, strict, options) {

		var chunks = this.getChunksByType(this.getType());

		if (!chunks) {
			return ;
		}

		if (strict && (chunks.length !== 1)) {
			throw new Error('Not more than one chunk allowed for ' + this.getType() + '.');
		}

		data.volatile = data.volatile || {};
		data.volatile.physicalScale = {
			pixelWidth: chunks[0].getPixelWidth(),
			pixelHeight: chunks[0].getPixelHeight(),
			unit: chunks[0].getUnit()
		};
	},

	/**
	 * Returns a list of chunks to be added to the data-stream
	 *
	 * Phase 1
	 *
	 * @static
	 * @method encodeData
	 * @param {Buffer} image Image data
	 * @param {object} options Encoding options
	 * @return {Chunk[]} List of chunks to encode
	 */
	encodeData: function (image, options) {

		if (options.physicalScale) {

			var chunk = this.createChunk(this.getType(), this.getChunks());

			if (options.physicalScale.xPixelPerUnit !== undefined) {
				chunk.setPixelWidth(options.physicalScale.xPixelPerUnit);
			}
			if (options.physicalScale.yPixelPerUnit !== undefined) {
				chunk.setPixelHeight(options.physicalScale.yPixelPerUnit);
			}
			if (options.physicalScale.unit !== undefined) {
				chunk.setUnit(options.physicalScale.unit);
			}

			return [chunk];
		} else {
			return [];
		}
	},

	/**
	 * Composing of chunk data
	 *
	 * Phase 4
	 *
	 * @method compose
	 * @param {BufferedStream} stream Data stream
	 * @param {object} options Encoding options
	 */
	compose: function (stream, options) {
		stream.writeUInt8(this.getUnit());

		stream.writeASCIIString(String(this.getPixelWidth()));

		// Write null-character
		stream.writeUInt8(0);

		stream.writeASCIIString(String(this.getPixelHeight()));
	}
};
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// LOAD AND CONFIGURE MODULE DEPENDENCIES
////////////////////////////////////////////////////////////////////////////////

// Load module dependencies
var express = require("express");
var logger = require("morgan");
var fs = require("fs");
var _ = require('underscore');
var marked = require('marked');
var highlight = require('highlight.js');
var CSON = require('cson');
// var jade = require("jade");

// Configure module dependencies
marked.setOptions({
	highlight: function (code) {
		return highlight.highlightAuto(code).value;
	}
});

////////////////////////////////////////////////////////////////////////////////
// SETTINGS
////////////////////////////////////////////////////////////////////////////////

// app settings
var jade_options = {pretty: true};
var app = express();
var valid_endings = ['.json', '.cson'];
//var router = new express.Router();

// directories
var propfile = 'prop.cson'; // describes categories
var itemfile = 'item.cson'; // describes items
var postfile = 'post.md'; // describes posts
var pub_dir = __dirname + '/public/';
var views_dir = pub_dir + 'views/';
var data_dir = pub_dir + 'data/';
var prj_dir = data_dir + 'projects/';
var research_dir = data_dir + 'research/';
var cat_dir = data_dir + 'categories/';
var prj_cat_file = cat_dir + 'project_categories.cson';
var research_cat_file = cat_dir + 'research_categories.cson';
var ex_md_text = marked(fs.readFileSync(prj_dir +
	'programming/maxwellforbes.com/post.md',
	{encoding: 'utf8'}));

////////////////////////////////////////////////////////////////////////////////
// CONFIGURE SERVER
////////////////////////////////////////////////////////////////////////////////

// Pimp my string (from
// http://stackoverflow.com/questions/280634/endswith-in-javascript)
String.prototype.endsWith = function(suffix) {
    return this.indexOf(suffix, this.length - suffix.length) !== -1;
};

// check if a file found is what we want (ignores .DS_Store, etc.)
var isLoadable = function(file_name) {
	for (var i = 0; i < valid_endings.length; i++) {
		if (file_name.endsWith(valid_endings[i])) {
			return true;
		}
	}
	return false;
};

var isDirectory = function(file_path) {
	return fs.statSync(file_path).isDirectory();
};

// Load a file (must have a valid suffix).
var loadFile = function(file_path) {
	if (file_path.endsWith('.json')) {
		return JSON.parse(fs.readFileSync(file_path));
	} else if (file_path.endsWith('.cson')) {
		var tmp = CSON.parseFileSync(file_path);
		return CSON.parseFileSync(file_path);
	} else {
		console.warn('File ending not implemented for: ' + file_path);
	}
};

// generate the project names / icons array by loading from the filesystem
var genCatsForDir = function(dirname) {
	var c1 = fs.readdirSync(dirname);
	var candidates = _.filter(fs.readdirSync(dirname),
		function(f) { return fs.statSync(dirname + f).isDirectory(); });
	var results = [];
	for (var i = 0; i < candidates.length; i++) {
		results.push({
			name: candidates[i],
			icon: loadFile(dirname + candidates[i] + '/' + propfile).icon
		});
	}
	return results;
};

// To avoid creating the function in a loop, we provide an isDirectory with
// context for a prefix.
var isDirectoryPrefix = function(file_path) {
	return isDirectory(this + file_path);
};

// Also avoiding creating a function in a loop, this just checks if the
// past argument is equal to this
var equalToThis = function(arg) {
	return arg == this;
};

// scanns all categories within basedir (either projects or research) and
// gets all item objects within directories it finds
var loadItems = function(basedir, section, cats) {
	var results = [];
	for (var i = 0; i < cats.length; i++) {
		var cat = cats[i];
		// find all possible project/research entries within a project /
		// research category
		var catdir = basedir + cat.name + '/';
		console.log('loading items for: ' + catdir);
		var candidates = _.filter(fs.readdirSync(catdir),
			isDirectoryPrefix,
			catdir);
		console.log('candidates: ' + candidates);
		for (var j = 0; j < candidates.length; j++) {
			// for each entry (candidate), see if it contains an item.cson file
			var candidatedir = catdir + candidates[j] + '/';
			var itemfiles = _.filter(fs.readdirSync(candidatedir), equalToThis,
				itemfile);
			//function(filename) { return filename ==  itemfile});
			console.log('candidatedir: ' + candidatedir);
			console.log('itemfiles: ' + itemfiles);
			if (itemfiles.length >= 1) {
				// we just use the first if there's more than one itemfile
				newitem = loadFile(candidatedir + itemfiles[0]);
				newitem.section = section;
				newitem.cat = cat.name;
				results.push(newitem);
			}
		}
	}
	return results;
};

////////////////////////////////////////////////////////////////////////////////
// LOAD ALL OF THE DATA
////////////////////////////////////////////////////////////////////////////////

// data
var prj_files = _.filter(fs.readdirSync(prj_dir), isLoadable);
var research_files = _.filter(fs.readdirSync(research_dir), isLoadable);
var prj_jsons = [];
var research_jsons = [];

// Grab all project and research categories
// var prj_cats = loadFile(prj_cat_file);
var prj_cats = genCatsForDir(prj_dir);
//var research_cats = loadFile(research_cat_file);
var research_cats = genCatsForDir(research_dir);

// Grab all projects and research(es...)
for (var i = 0; i < prj_files.length; i++) {
	var raw_json = loadFile(prj_dir + prj_files[i]);
	raw_json.section = 'project';
	prj_jsons.push(raw_json);
}
for (var i = 0; i < research_files.length; i++) {
	var raw_json = loadFile(research_dir + research_files[i]);
	raw_json.section = 'research';
	research_jsons.push(raw_json);
}

var prj_jsons = loadItems(prj_dir, 'project', prj_cats);
var research_jsons = loadItems(research_dir, 'research', research_cats);

var data = {
	"prj_cats": prj_cats,
	"research_cats": research_cats,
	"prj_jsons": prj_jsons,
	"research_jsons": research_jsons,
};

// For safety of matching OK categories
var ok_cats = [];
for (var i = 0; i < prj_cats.length; i++) {
	ok_cats.push(prj_cats[i].name);
}
for (var i = 0; i < research_cats.length; i++) {
	ok_cats.push(research_cats[i].name);
}

// Basic reporting for debugging.
console.log("num projects: " + prj_jsons.length);
console.log("num research: " + research_jsons.length);
console.log(data);

////////////////////////////////////////////////////////////////////////////////
// CONFIGURE SERVER
////////////////////////////////////////////////////////////////////////////////

app.use(logger());
app.use(express.static(pub_dir));
app.locals._ = require("underscore");

////////////////////////////////////////////////////////////////////////////////
// CONFIGURE ROUTING
////////////////////////////////////////////////////////////////////////////////

app.get('/item', function(request, response) {
	var locals = {"data": data, "mdtext": ex_md_text};
	response.render(views_dir + 'page_post.jade',
		_.extend({}, jade_options, locals));
});

app.get('/:cat', function(request, response) {
	var cat = request.params.cat;
	var activecat;
	if (ok_cats.indexOf(cat) > -1) {
		activecat = cat;
	}
	var locals = {"data": data, "activecat": activecat};
	response.render(views_dir + 'page_overview.jade',
		_.extend({}, jade_options, locals));
});

app.get('/:cat/:item', function(request, response) {
	var cat = request.params.cat;
	var activecat;
	if (ok_cats.indexOf(cat) > -1) {
		activecat = cat;
	}
	var locals = {"data": data, "activecat": activecat};
	response.render(views_dir + 'page_overview.jade',
		_.extend({}, jade_options, locals));
});

app.get('/', function(request, response) {
	var locals = {"data": data};
	response.render(views_dir + 'page_overview.jade',
		_.extend({}, jade_options, locals));
});

////////////////////////////////////////////////////////////////////////////////
// START SERVER
////////////////////////////////////////////////////////////////////////////////

var port = process.env.PORT || 5000;
app.listen(port, function() {
  console.log("Listening on " + port);
});<commit_msg>Add newline @ end of file.<commit_after>////////////////////////////////////////////////////////////////////////////////
// LOAD AND CONFIGURE MODULE DEPENDENCIES
////////////////////////////////////////////////////////////////////////////////

// Load module dependencies
var express = require("express");
var logger = require("morgan");
var fs = require("fs");
var _ = require('underscore');
var marked = require('marked');
var highlight = require('highlight.js');
var CSON = require('cson');
// var jade = require("jade");

// Configure module dependencies
marked.setOptions({
	highlight: function (code) {
		return highlight.highlightAuto(code).value;
	}
});

////////////////////////////////////////////////////////////////////////////////
// SETTINGS
////////////////////////////////////////////////////////////////////////////////

// app settings
var jade_options = {pretty: true};
var app = express();
var valid_endings = ['.json', '.cson'];
//var router = new express.Router();

// directories
var propfile = 'prop.cson'; // describes categories
var itemfile = 'item.cson'; // describes items
var postfile = 'post.md'; // describes posts
var pub_dir = __dirname + '/public/';
var views_dir = pub_dir + 'views/';
var data_dir = pub_dir + 'data/';
var prj_dir = data_dir + 'projects/';
var research_dir = data_dir + 'research/';
var cat_dir = data_dir + 'categories/';
var prj_cat_file = cat_dir + 'project_categories.cson';
var research_cat_file = cat_dir + 'research_categories.cson';
var ex_md_text = marked(fs.readFileSync(prj_dir +
	'programming/maxwellforbes.com/post.md',
	{encoding: 'utf8'}));

////////////////////////////////////////////////////////////////////////////////
// CONFIGURE SERVER
////////////////////////////////////////////////////////////////////////////////

// Pimp my string (from
// http://stackoverflow.com/questions/280634/endswith-in-javascript)
String.prototype.endsWith = function(suffix) {
    return this.indexOf(suffix, this.length - suffix.length) !== -1;
};

// check if a file found is what we want (ignores .DS_Store, etc.)
var isLoadable = function(file_name) {
	for (var i = 0; i < valid_endings.length; i++) {
		if (file_name.endsWith(valid_endings[i])) {
			return true;
		}
	}
	return false;
};

var isDirectory = function(file_path) {
	return fs.statSync(file_path).isDirectory();
};

// Load a file (must have a valid suffix).
var loadFile = function(file_path) {
	if (file_path.endsWith('.json')) {
		return JSON.parse(fs.readFileSync(file_path));
	} else if (file_path.endsWith('.cson')) {
		var tmp = CSON.parseFileSync(file_path);
		return CSON.parseFileSync(file_path);
	} else {
		console.warn('File ending not implemented for: ' + file_path);
	}
};

// generate the project names / icons array by loading from the filesystem
var genCatsForDir = function(dirname) {
	var c1 = fs.readdirSync(dirname);
	var candidates = _.filter(fs.readdirSync(dirname),
		function(f) { return fs.statSync(dirname + f).isDirectory(); });
	var results = [];
	for (var i = 0; i < candidates.length; i++) {
		results.push({
			name: candidates[i],
			icon: loadFile(dirname + candidates[i] + '/' + propfile).icon
		});
	}
	return results;
};

// To avoid creating the function in a loop, we provide an isDirectory with
// context for a prefix.
var isDirectoryPrefix = function(file_path) {
	return isDirectory(this + file_path);
};

// Also avoiding creating a function in a loop, this just checks if the
// past argument is equal to this
var equalToThis = function(arg) {
	return arg == this;
};

// scanns all categories within basedir (either projects or research) and
// gets all item objects within directories it finds
var loadItems = function(basedir, section, cats) {
	var results = [];
	for (var i = 0; i < cats.length; i++) {
		var cat = cats[i];
		// find all possible project/research entries within a project /
		// research category
		var catdir = basedir + cat.name + '/';
		console.log('loading items for: ' + catdir);
		var candidates = _.filter(fs.readdirSync(catdir),
			isDirectoryPrefix,
			catdir);
		console.log('candidates: ' + candidates);
		for (var j = 0; j < candidates.length; j++) {
			// for each entry (candidate), see if it contains an item.cson file
			var candidatedir = catdir + candidates[j] + '/';
			var itemfiles = _.filter(fs.readdirSync(candidatedir), equalToThis,
				itemfile);
			//function(filename) { return filename ==  itemfile});
			console.log('candidatedir: ' + candidatedir);
			console.log('itemfiles: ' + itemfiles);
			if (itemfiles.length >= 1) {
				// we just use the first if there's more than one itemfile
				newitem = loadFile(candidatedir + itemfiles[0]);
				newitem.section = section;
				newitem.cat = cat.name;
				results.push(newitem);
			}
		}
	}
	return results;
};

////////////////////////////////////////////////////////////////////////////////
// LOAD ALL OF THE DATA
////////////////////////////////////////////////////////////////////////////////

// data
var prj_files = _.filter(fs.readdirSync(prj_dir), isLoadable);
var research_files = _.filter(fs.readdirSync(research_dir), isLoadable);
var prj_jsons = [];
var research_jsons = [];

// Grab all project and research categories
// var prj_cats = loadFile(prj_cat_file);
var prj_cats = genCatsForDir(prj_dir);
//var research_cats = loadFile(research_cat_file);
var research_cats = genCatsForDir(research_dir);

// Grab all projects and research(es...)
for (var i = 0; i < prj_files.length; i++) {
	var raw_json = loadFile(prj_dir + prj_files[i]);
	raw_json.section = 'project';
	prj_jsons.push(raw_json);
}
for (var i = 0; i < research_files.length; i++) {
	var raw_json = loadFile(research_dir + research_files[i]);
	raw_json.section = 'research';
	research_jsons.push(raw_json);
}

var prj_jsons = loadItems(prj_dir, 'project', prj_cats);
var research_jsons = loadItems(research_dir, 'research', research_cats);

var data = {
	"prj_cats": prj_cats,
	"research_cats": research_cats,
	"prj_jsons": prj_jsons,
	"research_jsons": research_jsons,
};

// For safety of matching OK categories
var ok_cats = [];
for (var i = 0; i < prj_cats.length; i++) {
	ok_cats.push(prj_cats[i].name);
}
for (var i = 0; i < research_cats.length; i++) {
	ok_cats.push(research_cats[i].name);
}

// Basic reporting for debugging.
console.log("num projects: " + prj_jsons.length);
console.log("num research: " + research_jsons.length);
console.log(data);

////////////////////////////////////////////////////////////////////////////////
// CONFIGURE SERVER
////////////////////////////////////////////////////////////////////////////////

app.use(logger());
app.use(express.static(pub_dir));
app.locals._ = require("underscore");

////////////////////////////////////////////////////////////////////////////////
// CONFIGURE ROUTING
////////////////////////////////////////////////////////////////////////////////

app.get('/item', function(request, response) {
	var locals = {"data": data, "mdtext": ex_md_text};
	response.render(views_dir + 'page_post.jade',
		_.extend({}, jade_options, locals));
});

app.get('/:cat', function(request, response) {
	var cat = request.params.cat;
	var activecat;
	if (ok_cats.indexOf(cat) > -1) {
		activecat = cat;
	}
	var locals = {"data": data, "activecat": activecat};
	response.render(views_dir + 'page_overview.jade',
		_.extend({}, jade_options, locals));
});

app.get('/:cat/:item', function(request, response) {
	var cat = request.params.cat;
	var activecat;
	if (ok_cats.indexOf(cat) > -1) {
		activecat = cat;
	}
	var locals = {"data": data, "activecat": activecat};
	response.render(views_dir + 'page_overview.jade',
		_.extend({}, jade_options, locals));
});

app.get('/', function(request, response) {
	var locals = {"data": data};
	response.render(views_dir + 'page_overview.jade',
		_.extend({}, jade_options, locals));
});

////////////////////////////////////////////////////////////////////////////////
// START SERVER
////////////////////////////////////////////////////////////////////////////////

var port = process.env.PORT || 5000;
app.listen(port, function() {
  console.log("Listening on " + port);
});
<|endoftext|>


<commit_before>var commandLine = (function(cArgs) {
  var args = {};
  var params = [];

  for(var i=0, arg; arg = cArgs[i]; i++) {
    if( arg.indexOf( "--" ) == 0 ) {
      arg = arg.substring(2).split( "=" );
      args[arg.shift()] = (arg[0] == null) ? true : arg.join( "=" );
    } else {
      params.push(arg);
    }
  }

  return { options: args, params: params };
}(arguments));

function isLocalFile(file) {
  return !(file.match(/^https?:/));
}

function moduleSource() {
  var opts = {
    output: ''
  };

  runCommand('cat', libPath + '/lib/loadrunner.js', opts);

  return opts.output
}

function isModule(file) {
  return !!file.match(/^((\.|\.\.|[a-zA-Z0-9\-_]+)\/)*[a-zA-Z0-9\-_]+$/);
}

function getModulePath(file) {
  return [require.paths[0], file, '.js'].join('');
}

function bundle(files) {
  return files.map(function(file) {
    var path = isModule(file) ? getModulePath(file) : file;

    var opts = {
      output: ''
    }

    if (isLocalFile(path)) {
      runCommand('cat', path, opts);
    } else {
      runCommand('curl', '-s', path, opts);
    }

    return opts.output + "\nrequire.loaded.push('" + file + "');";
  }).join('\n');
}

var libPath = commandLine.params[0];
var sourceFile = commandLine.params[1];
var destFile = commandLine.params[2];
var minify = !commandLine.options['no-min'];
var bundleRunner = commandLine.options['with-loadrunner'];
var modulePath = (commandLine.options['modules'] && commandLine.options['modules'].replace(/([^\/])$/, '$1/')) || '';
var docRoot = (commandLine.options['docroot'] && commandLine.options['docroot'].replace(/([^\/])$/, '$1/')) || '';
var cwd = (commandLine.options['cwd'] && commandLine.options['cwd'].replace(/([^\/])$/, '$1/')) || '';

if (commandLine.options['help'] || !(sourceFile && destFile)) {
  print('loadrunner.js build tool (c) Dan Webb 2010 licensed under an MIT LICENSE');
  print('Usage: loadbuilder {options} {source_file_or_module} {destination_file}');
  print('       --no-min                      disable minification');
  print('       --with-loadrunner             embed loadrunner.js');
  print('       --modules=path                path to unbuilt modules');
  print('       --docRoot=path                path to / (your sites web root)');
  print('       --cwd=path                    path to current directory if you use relative paths');
  quit();
}

print('Build starting from ' + sourceFile + '...');

load(libPath + '/vendor/env.js');

Envjs({
    scriptTypes : {
        '': true,
        'text/javascript': true
    }
});

load(libPath + '/lib/loadrunner.js');

require.paths = [modulePath];
require.docRoot = docRoot;
require.cwd = cwd;

require(sourceFile, function() {
  var opts;

  var buildOrder = require.loaded.reverse();

  print('Build order:');
  for (var i=0, item; item = buildOrder[i]; i++) {
    print(item);
  }

  var source = bundleRunner ? moduleSource() : '';
  source += bundle(buildOrder);

  if (minify) {
    opts = {
      input: source,
      output: ''
    };

    runCommand('java', '-jar',
               libPath + '/vendor/yuicompressor-2.4.2.jar',
               '--type', 'js', opts);

    source = opts.output;
  }

  Envjs.writeToFile(source, Envjs.uri(destFile));
  print('Built file to ' + destFile);
});

<commit_msg>build order should not be reversed<commit_after>var commandLine = (function(cArgs) {
  var args = {};
  var params = [];

  for(var i=0, arg; arg = cArgs[i]; i++) {
    if( arg.indexOf( "--" ) == 0 ) {
      arg = arg.substring(2).split( "=" );
      args[arg.shift()] = (arg[0] == null) ? true : arg.join( "=" );
    } else {
      params.push(arg);
    }
  }

  return { options: args, params: params };
}(arguments));

function isLocalFile(file) {
  return !(file.match(/^https?:/));
}

function moduleSource() {
  var opts = {
    output: ''
  };

  runCommand('cat', libPath + '/lib/loadrunner.js', opts);

  return opts.output
}

function isModule(file) {
  return !!file.match(/^((\.|\.\.|[a-zA-Z0-9\-_]+)\/)*[a-zA-Z0-9\-_]+$/);
}

function getModulePath(file) {
  return [require.paths[0], file, '.js'].join('');
}

function bundle(files) {
  return files.map(function(file) {
    var path = isModule(file) ? getModulePath(file) : file;

    var opts = {
      output: ''
    }

    if (isLocalFile(path)) {
      runCommand('cat', path, opts);
    } else {
      runCommand('curl', '-s', path, opts);
    }

    return opts.output + "\nrequire.loaded.push('" + file + "');";
  }).join('\n');
}

var libPath = commandLine.params[0];
var sourceFile = commandLine.params[1];
var destFile = commandLine.params[2];
var minify = !commandLine.options['no-min'];
var bundleRunner = commandLine.options['with-loadrunner'];
var modulePath = (commandLine.options['modules'] && commandLine.options['modules'].replace(/([^\/])$/, '$1/')) || '';
var docRoot = (commandLine.options['docroot'] && commandLine.options['docroot'].replace(/([^\/])$/, '$1/')) || '';
var cwd = (commandLine.options['cwd'] && commandLine.options['cwd'].replace(/([^\/])$/, '$1/')) || '';

if (commandLine.options['help'] || !(sourceFile && destFile)) {
  print('loadrunner.js build tool (c) Dan Webb 2010 licensed under an MIT LICENSE');
  print('Usage: loadbuilder {options} {source_file_or_module} {destination_file}');
  print('       --no-min                      disable minification');
  print('       --with-loadrunner             embed loadrunner.js');
  print('       --modules=path                path to unbuilt modules');
  print('       --docRoot=path                path to / (your sites web root)');
  print('       --cwd=path                    path to current directory if you use relative paths');
  quit();
}

print('Build starting from ' + sourceFile + '...');

load(libPath + '/vendor/env.js');

Envjs({
    scriptTypes : {
        '': true,
        'text/javascript': true
    }
});

load(libPath + '/lib/loadrunner.js');

require.paths = [modulePath];
require.docRoot = docRoot;
require.cwd = cwd;

require(sourceFile, function() {
  var opts;

  var buildOrder = require.loaded;

  print('Build order:');
  for (var i=0, item; item = buildOrder[i]; i++) {
    print(item);
  }

  var source = bundleRunner ? moduleSource() : '';
  source += bundle(buildOrder);

  if (minify) {
    opts = {
      input: source,
      output: ''
    };

    runCommand('java', '-jar',
               libPath + '/vendor/yuicompressor-2.4.2.jar',
               '--type', 'js', opts);

    source = opts.output;
  }

  Envjs.writeToFile(source, Envjs.uri(destFile));
  print('Built file to ' + destFile);
});

<|endoftext|>


<commit_before>// @flow
import React, { Component } from 'react';
import electron from 'electron';
import jQuery from 'jquery';
// import PropTypes from 'prop-types';
//  import { Link } from 'react-router-dom';
//  import styles from './LoginForm.css';

const app = electron.remote;
const dialog = app.dialog;

class JobSpecification extends Component {
  constructor() {
    super();
    this.state = {
      metaDataFolder: 'heyhey',
      metaDataFolderSelected: false,
      jobFilepath: 'jobFilepath',
      jobFilepathSelected: false,
    };
  }
  selectJobSpecsFile = () => {
    console.log('called selectJobSpecsFile');
    const fileNames = dialog.showOpenDialog();
    if (fileNames === undefined) {
      console.log('No file selected');
      this.setState({ jobFilepathSelected: false });
      this.setState({ jobFilepath: '' });
    } else {
      // console.log('going to set the filename and boolean' + fileNames);
      this.setState({ jobFilepathSelected: true });
      this.setState({ jobFilepath: fileNames[0] });
    }
    // NOTE put this back in when the first part is working
    // persistData('jobFilePersistKey', fileNames[0]);
    console.log('end of selectJobSpecsFile');
  }
  selectMetaDataFile = () => {
    console.log('called selectMetaDataFile');
    const path = dialog.showOpenDialog(
      { title: 'Select a folder', properties: ['openDirectory'] }
    );
    console.log('called showOpenDialog and returned');
    console.log('path is =====>' + path);
    if (path === undefined) {
      console.log('No destination folder selected');
      this.setState({ metaDataFolderSelected: false });
      this.setState({ metaDataFolder: '' });
    } else {
      console.log('path returned from dialog.showOpenDialog select a folder is....> ' + path);
      // console.log('path[0] is....> ' + path[0]);
      this.setState({ metaDataFolderSelected: true });
      this.setState({ metaDataFolder: path[0] });
    }
    // NOTE put this back in when the first part is working
    // persistData('metaDataPersistKey', path[0]);
    console.log('end of selectMetaDataFile');
  }
  render() {
    /* if (this.state.jobFilepathSelected && this.state.metaDataFolderSelected) {
      jQuery('#sendButton').removeAttr('disabled');
    } else {
      jQuery('#sendButton').attr('disabled', 'true');
    }*/
    return (

Job

          <form className="form" onSubmit={this.localHandleSend}>

Job

              Metadata File

{this.state.metaDataFolder}

                  <button
                    type="button"
                    className="btn btn-primary"
                    onClick={this.selectMetaDataFile}
                  >Select metadata.xml</button>

              Job Spec

{this.state.jobFilepath}

                  <button
                    type="button"
                    className="btn btn-primary"
                    onClick={this.selectJobSpecsFile}
                  >Select job specification</button>

                <button
                  type="submit"
                  id="sendButton"
                  className="btn btn-primary pull-left"
                  disabled
                >Send</button>

          </form>

    );
  }
}

export default JobSpecification;
<commit_msg>enable disable send button checkin some code I would like to be able to return to<commit_after>// @flow
import React, { Component } from 'react';
import electron from 'electron';
import $ from 'jquery';
// import PropTypes from 'prop-types';
//  import { Link } from 'react-router-dom';
//  import styles from './LoginForm.css';

const app = electron.remote;
const dialog = app.dialog;

class JobSpecification extends Component {
  constructor() {
    super();
    this.state = {
      metaDataFolder: 'heyhey',
      metaDataFolderSelected: false,
      jobFilepath: 'jobFilepath',
      jobFilepathSelected: false,
      sendButtonDisabled: true
    };
  }
  enableDisableSendButton = () => {
    if (this.state.jobFilepathSelected && this.state.metaDataFolderSelected) {
      this.setState({ sendButtonDisabled: false });
    } else {
      this.setState({ sendButtonDisabled: true });
    }
  }
  selectJobSpecsFile = () => {
    console.log('called selectJobSpecsFile');
    const fileNames = dialog.showOpenDialog();
    if (fileNames === undefined) {
      console.log('No file selected');
      this.setState({ jobFilepathSelected: false });
      this.setState({ jobFilepath: '' });
    } else {
      // console.log('going to set the filename and boolean' + fileNames);
      if (this.state.metaDataFolderSelected) {
        this.setState({ sendButtonDisabled: false });
      } else {
        this.setState({ sendButtonDisabled: true });
      }
      this.setState({ jobFilepathSelected: true });
      this.setState({ jobFilepath: fileNames[0] });
    }
    // this.enableDisableSendButton();
    // NOTE put this back in when the first part is working
    // persistData('jobFilePersistKey', fileNames[0]);
    console.log('end of selectJobSpecsFile');
  }
  selectMetaDataFile = () => {
    console.log('called selectMetaDataFile');
    const path = dialog.showOpenDialog(
      { title: 'Select a folder', properties: ['openDirectory'] }
    );
    console.log('called showOpenDialog and returned');
    console.log('path is =====>' + path);
    if (path === undefined) {
      console.log('No destination folder selected');
      this.setState({ metaDataFolderSelected: false });
      this.setState({ metaDataFolder: '' });
    } else {
      console.log('path returned from dialog.showOpenDialog select a folder is....> ' + path);
      // console.log('path[0] is....> ' + path[0]);
      if (this.state.jobFilepathSelected) {
        this.setState({ sendButtonDisabled: false });
      } else {
        this.setState({ sendButtonDisabled: true });
      }
      this.setState({ metaDataFolderSelected: true });
      this.setState({ metaDataFolder: path[0] });
    }
    // this.enableDisableSendButton();
    // NOTE put this back in when the first part is working
    // persistData('metaDataPersistKey', path[0]);
    console.log('end of selectMetaDataFile');
  }
  render() {
    /*
    if (this.state.jobFilepathSelected && this.state.metaDataFolderSelected) {
      $('#sendButton').removeAttr('disabled');
    } else {
      $('#sendButton').attr('disabled', 'true');
    }
    */
    let itemButton;
    if (this.state.jobFilepathSelected && this.state.metaDataFolderSelected) {
      itemButton = <button type="submit" id="sendButton" className="btn btn-primary pull-left">Send</button>
    } else {
      itemButton = <button disabled="true" type="submit" id="sendButton" className="btn btn-primary pull-left">Send</button>
    }
    return (

Job

          <form className="form" onSubmit={this.localHandleSend}>

Job

              Metadata File

{this.state.metaDataFolder}

                  <button
                    type="button"
                    className="btn btn-primary"
                    onClick={this.selectMetaDataFile}
                  >Select metadata.xml</button>

              Job Spec

{this.state.jobFilepath}

                  <button
                    type="button"
                    className="btn btn-primary"
                    onClick={this.selectJobSpecsFile}
                  >Select job specification</button>

                {itemButton}

          </form>

    );
  }
}

export default JobSpecification;
<|endoftext|>


<commit_before>/* eslint-disable camelcase */
import React from 'react'
import { difference, first, includes, map, some, sortBy, reject } from 'lodash'
import { find, filter, get } from 'lodash/fp'
const { array, bool, func, object, string } = React.PropTypes
import cx from 'classnames'
import cheerio from 'cheerio'
import decode from 'ent/decode'
import {
  humanDate, nonbreaking, present, textLength, truncate, appendInP
} from '../util/text'
import { sanitize } from 'hylo-utils/text'
import { linkifyHashtags } from '../util/linkify'
import { tagUrl } from '../routes'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { isMobile } from '../client/util'
import { same } from '../models'
import { denormalizedPost, getComments, isPinned } from '../models/post'
import { canEditPost, canModerate, hasFeature } from '../models/currentUser'
import { getCurrentCommunity } from '../models/community'
import { navigate, showModal, typeahead } from '../actions'
import {
  completePost, followPost, removePost, startPostEdit, voteOnPost, pinPost
} from '../actions/posts'
import { CONTRIBUTORS } from '../config/featureFlags'
import CommentSection from './CommentSection'
import TagInput from './TagInput'
import LinkedPersonSentence from './LinkedPersonSentence'
import LinkPreview from './LinkPreview'
import A from './A'
import Avatar from './Avatar'
import Dropdown from './Dropdown'
import Attachments from './Attachments'
import LazyLoader from './LazyLoader'
import Icon from './Icon'
import { ClickCatchingSpan } from './ClickCatcher'
import { handleMouseOver } from './Popover'

const spacer =   •

export const presentDescription = (post, community, opts = {}) =>
  present(sanitize(post.description), {slug: get('slug', community), ...opts})

export class Post extends React.Component {
  constructor (props, context) {
    super(props, context)
    this.state = {
      requestCompleting: false,
      contributors: []
    }
  }

  static propTypes = {
    post: object,
    community: object,
    comments: array,
    dispatch: func,
    expanded: bool,
    onExpand: func,
    commentId: string
  }

  static contextTypes = {currentUser: object}

  static childContextTypes = {post: object}

  getChildContext () {
    return {post: this.props.post}
  }

  render () {
    let { post } = this.props
    const { expanded, onExpand, community, dispatch, contributorChoices } = this.props
    var comments = this.props.comments
    if (post.project) {
      comments = comments.slice(-1)
    }
    const { contributors, requestCompleting } = this.state
    const { currentUser } = this.context
    const { communities, tag, media, linkPreview, project } = post
    const image = find(m => m.type === 'image', media)
    const classes = cx('post', tag, {image, expanded})
    const title = linkifyHashtags(decode(sanitize(post.name || '')), get('slug', community))

    const canEdit = canEditPost(currentUser, post)

    const isRequest = tag === 'request'
    const isCompleteRequest = isRequest && post.fulfilled_at
    const isIncompleteRequest = isRequest && !post.fulfilled_at && canEdit
    const addContributor = (person) => {
      this.setState({contributors: [...contributors, person]})
    }
    const removeContributor = (person) => {
      this.setState({contributors: reject(contributors, {id: person.id})})
    }
    const handleContributorInput = (term) => {
      dispatch(typeahead(term, 'contributors', {
        type: 'people', communityIds: post.community_ids }
      ))
    }
    const toggleRequestCompleting = () => this.setState({requestCompleting: !requestCompleting})
    const toggleRequestComplete = () => {
      if (isCompleteRequest) {
        if (window.confirm('This will mark this request as Incomplete. Are you sure?')) {
          dispatch(completePost(post.id))
        }
      } else {
        if (contributors.length > 0) this.setState({contributors: []})
        toggleRequestCompleting()
        dispatch(completePost(post.id, contributors))
      }
    }

    return

      <a name={`post-${post.id}`} />

      {image && <LazyLoader>

      </LazyLoader>}

      {linkPreview && <LinkPreview {...{linkPreview}} />}

        <VoteButton />
        <Voters />

      <Attachments />
      {hasFeature(currentUser, CONTRIBUTORS) && isCompleteRequest &&

              <input className='toggle'
                type='checkbox'
                checked={!!post.fulfilled_at}
                readOnly={!canEdit}
                onChange={toggleRequestComplete} />
              <RequestContributorsSentence post={post} />

      }
      <CommentSection {...{post, expanded, onExpand, comments}} />
      {hasFeature(currentUser, CONTRIBUTORS) && isIncompleteRequest &&

              <input type='checkbox'
                className='toggle'
                checked={requestCompleting}
                onChange={toggleRequestCompleting} />

                {requestCompleting
                  ? 'Awesome! Who helped you?'
                  : 'Click the checkmark if this request has been completed!'}

          {requestCompleting &&

              <TagInput className='request-complete-people-input'
                choices={contributorChoices}
                handleInput={handleContributorInput}
                onSelect={addContributor}
                onRemove={removeContributor}
                tags={contributors} />

                Done

          }

      }

  }
}

export default compose(connect((state, {post}) => {
  return {
    comments: getComments(post, state),
    community: getCurrentCommunity(state),
    post: denormalizedPost(post, state),
    contributorChoices: reject(
      state.typeaheadMatches.invite, {id: post.user_id}
    )
  }
}))(Post)

export const UndecoratedPost = Post // for testing

export const Header = ({ communities, project, expanded }, { post, currentUser, dispatch }) => {
  const { tag, fulfilled_at } = post
  const person = tag === 'welcome' ? post.relatedUsers[0] : post.user
  const createdAt = new Date(post.created_at)
  const canEdit = canEditPost(currentUser, post)
  const showCheckbox = !hasFeature(currentUser, CONTRIBUTORS) &&
    post.tag === 'request' && (canEdit || fulfilled_at)

  return

    <Menu expanded={expanded} post={post} />
    <Avatar person={person} showPopover />
    {showCheckbox && <input type='checkbox'
      className='completion-toggle'
      checked={!!post.fulfilled_at}
      onChange={() => canEdit && dispatch(completePost(post.id))}
      readOnly={!canEdit} />}
    {tag === 'welcome'
      ? <WelcomePostHeader communities={communities} />
      :

          <A className='name' to={`/u/${person.id}`}>
            {person.name}

            <A to={`/p/${post.id}`} title={createdAt}>
              {nonbreaking(humanDate(createdAt))}

            {communities && <Communities communities={communities} project={project} />}
            {post.public && {spacer}Public}

}

}
Header.contextTypes = {post: object, currentUser: object, dispatch: func}

const Communities = ({ communities, project }, { community }) => {
  if (community) communities = sortBy(communities, c => c.id !== community.id)
  const { length } = communities
  if (communities.length === 0) return null

  const communityLink = community => <A to={`/c/${community.slug}`}>{community.name}
  const projectLink = project =>
    <A to={`/p/${project.id}`} className='project-link'>{project.name}
    {spacer}

  return
     in
    {project && projectLink(project)}
    {communityLink(communities[0])}
    {length > 1 &&  + }
    {length > 1 && <Dropdown className='post-communities-dropdown'
      toggleChildren={{length - 1} other{length > 2 ? 's' : ''}}>
      {communities.slice(1).map(c =>
{communityLink(c)}
)}
    </Dropdown>}

}
Communities.contextTypes = {community: object}

const getTags = text =>
  cheerio.load(text)('.hashtag').toArray().map(tag =>
    tag.children[0].data.replace(/^#/, ''))

const extractTags = (shortDesc, fullDesc, omitTag) => {
  const tags = filter(t => t !== omitTag, getTags(fullDesc))
  return tags.length === 0 ? [] : difference(tags, getTags(shortDesc))
}

const HashtagLink = ({ tag, slug }, { dispatch }) => {
  const onMouseOver = handleMouseOver(dispatch)
  return
    {`#${tag}`}

}
HashtagLink.contextTypes = {dispatch: func}

export const Details = ({ expanded, onExpand }, { post, community, dispatch }) => {
  const truncatedSize = 300
  const { tag } = post
  if (!community) community = post.communities[0]
  const slug = get('slug', community)
  let description = presentDescription(post, community)
  let extractedTags = []
  const truncated = !expanded && textLength(description) > truncatedSize
  if (truncated) {
    const orig = description
    description = truncate(description, truncatedSize)
    extractedTags = extractTags(description, orig, tag)
  }
  if (description) description = appendInP(description, ' ')

  return

    <ClickCatchingSpan dangerouslySetInnerHTML={{__html: description}} />
    {truncated &&
      <wbr />
       onExpand(null)} className='show-more'>Show more

    }
    {extractedTags.map(tag =>
      <wbr />
      <HashtagLink tag={tag} slug={slug} />

    )}
    {tag && <HashtagLink tag={tag} slug={slug} />}

}
Details.contextTypes = {post: object, community: object, dispatch: func}

const WelcomePostHeader = ({ communities }, { post }) => {
  let person = post.relatedUsers[0]
  let community = communities[0]
  return

    <A to={`/u/${person.id}`}>{person.name} joined

    {community
      ?
          <A to={`/c/${community.slug}`}>{community.name}.

            Welcome them!

      :
          a community that is no longer active.
        }

}
WelcomePostHeader.contextTypes = {post: object}

export const Menu = ({ expanded, post }, { dispatch, currentUser, community }) => {
  const canEdit = canEditPost(currentUser, post)
  const following = some(post.follower_ids, id => id === get('id', currentUser))
  const pinned = isPinned(post, community)
  const edit = () => isMobile()
    ? dispatch(navigate(`/p/${post.id}/edit`))
    : dispatch(startPostEdit(post)) &&
      expanded && dispatch(showModal('post-editor', {post}))
  const remove = () => window.confirm('Are you sure? This cannot be undone.') &&
    dispatch(removePost(post.id))
  const pin = () => dispatch(pinPost(get('slug', community), post.id))

  const toggleChildren = pinned
    ? Pinned
    :

  return <Dropdown className='post-menu' alignRight {...{toggleChildren}}>
    {canModerate(currentUser, community) &&

      {pinned ? 'Unpin post' : 'Pin post'}

}
    {canEdit &&
Edit
}
    {canEdit &&
Remove
}

       dispatch(followPost(post.id, currentUser))}>
        Turn {following ? 'off' : 'on'} notifications for this post

    {/*

       window.alert('TODO')}>Report objectionable content

 */}
  </Dropdown>
}
Menu.contextTypes = {currentUser: object, dispatch: func, community: object}

export const VoteButton = (props, { post, currentUser, dispatch }) => {
  let vote = () => dispatch(voteOnPost(post, currentUser))
  let myVote = includes(map(post.voters, 'id'), (currentUser || {}).id)
  return
    {myVote ? <Icon name='Heart2' /> : <Icon name='Heart' />}
    {myVote ? 'Liked' : 'Like'}

}
VoteButton.contextTypes = {post: object, currentUser: object, dispatch: func}

export const Voters = (props, { post, currentUser }) => {
  const voters = post.voters || []

  let onlyAuthorIsVoting = voters.length === 1 && same('id', first(voters), post.user)
  return voters.length > 0 && !onlyAuthorIsVoting
    ? <LinkedPersonSentence people={voters} className='voters meta'>
        liked this.
      </LinkedPersonSentence>
    :
}
Voters.contextTypes = {post: object, currentUser: object}

export const RequestContributorsSentence = ({ post }) => {
  const contributors = post.contributors || []
  const onlyAuthorIsContributing = contributors.length === 1 && same('id', first(contributors), post.user)
  return contributors.length > 0 && !onlyAuthorIsContributing
    ? <LinkedPersonSentence people={contributors} className='contributors'>
        helped complete this request!
      </LinkedPersonSentence>
    :
Request has been completed

}
<commit_msg>move variable<commit_after>/* eslint-disable camelcase */
import React from 'react'
import { difference, first, includes, map, some, sortBy, reject } from 'lodash'
import { find, filter, get } from 'lodash/fp'
const { array, bool, func, object, string } = React.PropTypes
import cx from 'classnames'
import cheerio from 'cheerio'
import decode from 'ent/decode'
import {
  humanDate, nonbreaking, present, textLength, truncate, appendInP
} from '../util/text'
import { sanitize } from 'hylo-utils/text'
import { linkifyHashtags } from '../util/linkify'
import { tagUrl } from '../routes'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { isMobile } from '../client/util'
import { same } from '../models'
import { denormalizedPost, getComments, isPinned } from '../models/post'
import { canEditPost, canModerate, hasFeature } from '../models/currentUser'
import { getCurrentCommunity } from '../models/community'
import { navigate, showModal, typeahead } from '../actions'
import {
  completePost, followPost, removePost, startPostEdit, voteOnPost, pinPost
} from '../actions/posts'
import { CONTRIBUTORS } from '../config/featureFlags'
import CommentSection from './CommentSection'
import TagInput from './TagInput'
import LinkedPersonSentence from './LinkedPersonSentence'
import LinkPreview from './LinkPreview'
import A from './A'
import Avatar from './Avatar'
import Dropdown from './Dropdown'
import Attachments from './Attachments'
import LazyLoader from './LazyLoader'
import Icon from './Icon'
import { ClickCatchingSpan } from './ClickCatcher'
import { handleMouseOver } from './Popover'

const spacer =   •

export const presentDescription = (post, community, opts = {}) =>
  present(sanitize(post.description), {slug: get('slug', community), ...opts})

export class Post extends React.Component {
  constructor (props, context) {
    super(props, context)
    this.state = {
      requestCompleting: false,
      contributors: []
    }
  }

  static propTypes = {
    post: object,
    community: object,
    comments: array,
    dispatch: func,
    expanded: bool,
    onExpand: func,
    commentId: string
  }

  static contextTypes = {currentUser: object}

  static childContextTypes = {post: object}

  getChildContext () {
    return {post: this.props.post}
  }

  render () {
    let { post, comments } = this.props
    const { expanded, onExpand, community, dispatch, contributorChoices } = this.props
    if (post.project) {
      comments = comments.slice(-1)
    }
    const { contributors, requestCompleting } = this.state
    const { currentUser } = this.context
    const { communities, tag, media, linkPreview, project } = post
    const image = find(m => m.type === 'image', media)
    const classes = cx('post', tag, {image, expanded})
    const title = linkifyHashtags(decode(sanitize(post.name || '')), get('slug', community))

    const canEdit = canEditPost(currentUser, post)

    const isRequest = tag === 'request'
    const isCompleteRequest = isRequest && post.fulfilled_at
    const isIncompleteRequest = isRequest && !post.fulfilled_at && canEdit
    const addContributor = (person) => {
      this.setState({contributors: [...contributors, person]})
    }
    const removeContributor = (person) => {
      this.setState({contributors: reject(contributors, {id: person.id})})
    }
    const handleContributorInput = (term) => {
      dispatch(typeahead(term, 'contributors', {
        type: 'people', communityIds: post.community_ids }
      ))
    }
    const toggleRequestCompleting = () => this.setState({requestCompleting: !requestCompleting})
    const toggleRequestComplete = () => {
      if (isCompleteRequest) {
        if (window.confirm('This will mark this request as Incomplete. Are you sure?')) {
          dispatch(completePost(post.id))
        }
      } else {
        if (contributors.length > 0) this.setState({contributors: []})
        toggleRequestCompleting()
        dispatch(completePost(post.id, contributors))
      }
    }

    return

      <a name={`post-${post.id}`} />

      {image && <LazyLoader>

      </LazyLoader>}

      {linkPreview && <LinkPreview {...{linkPreview}} />}

        <VoteButton />
        <Voters />

      <Attachments />
      {hasFeature(currentUser, CONTRIBUTORS) && isCompleteRequest &&

              <input className='toggle'
                type='checkbox'
                checked={!!post.fulfilled_at}
                readOnly={!canEdit}
                onChange={toggleRequestComplete} />
              <RequestContributorsSentence post={post} />

      }
      <CommentSection {...{post, expanded, onExpand, comments}} />
      {hasFeature(currentUser, CONTRIBUTORS) && isIncompleteRequest &&

              <input type='checkbox'
                className='toggle'
                checked={requestCompleting}
                onChange={toggleRequestCompleting} />

                {requestCompleting
                  ? 'Awesome! Who helped you?'
                  : 'Click the checkmark if this request has been completed!'}

          {requestCompleting &&

              <TagInput className='request-complete-people-input'
                choices={contributorChoices}
                handleInput={handleContributorInput}
                onSelect={addContributor}
                onRemove={removeContributor}
                tags={contributors} />

                Done

          }

      }

  }
}

export default compose(connect((state, {post}) => {
  return {
    comments: getComments(post, state),
    community: getCurrentCommunity(state),
    post: denormalizedPost(post, state),
    contributorChoices: reject(
      state.typeaheadMatches.invite, {id: post.user_id}
    )
  }
}))(Post)

export const UndecoratedPost = Post // for testing

export const Header = ({ communities, project, expanded }, { post, currentUser, dispatch }) => {
  const { tag, fulfilled_at } = post
  const person = tag === 'welcome' ? post.relatedUsers[0] : post.user
  const createdAt = new Date(post.created_at)
  const canEdit = canEditPost(currentUser, post)
  const showCheckbox = !hasFeature(currentUser, CONTRIBUTORS) &&
    post.tag === 'request' && (canEdit || fulfilled_at)

  return

    <Menu expanded={expanded} post={post} />
    <Avatar person={person} showPopover />
    {showCheckbox && <input type='checkbox'
      className='completion-toggle'
      checked={!!post.fulfilled_at}
      onChange={() => canEdit && dispatch(completePost(post.id))}
      readOnly={!canEdit} />}
    {tag === 'welcome'
      ? <WelcomePostHeader communities={communities} />
      :

          <A className='name' to={`/u/${person.id}`}>
            {person.name}

            <A to={`/p/${post.id}`} title={createdAt}>
              {nonbreaking(humanDate(createdAt))}

            {communities && <Communities communities={communities} project={project} />}
            {post.public && {spacer}Public}

}

}
Header.contextTypes = {post: object, currentUser: object, dispatch: func}

const Communities = ({ communities, project }, { community }) => {
  if (community) communities = sortBy(communities, c => c.id !== community.id)
  const { length } = communities
  if (communities.length === 0) return null

  const communityLink = community => <A to={`/c/${community.slug}`}>{community.name}
  const projectLink = project =>
    <A to={`/p/${project.id}`} className='project-link'>{project.name}
    {spacer}

  return
     in
    {project && projectLink(project)}
    {communityLink(communities[0])}
    {length > 1 &&  + }
    {length > 1 && <Dropdown className='post-communities-dropdown'
      toggleChildren={{length - 1} other{length > 2 ? 's' : ''}}>
      {communities.slice(1).map(c =>
{communityLink(c)}
)}
    </Dropdown>}

}
Communities.contextTypes = {community: object}

const getTags = text =>
  cheerio.load(text)('.hashtag').toArray().map(tag =>
    tag.children[0].data.replace(/^#/, ''))

const extractTags = (shortDesc, fullDesc, omitTag) => {
  const tags = filter(t => t !== omitTag, getTags(fullDesc))
  return tags.length === 0 ? [] : difference(tags, getTags(shortDesc))
}

const HashtagLink = ({ tag, slug }, { dispatch }) => {
  const onMouseOver = handleMouseOver(dispatch)
  return
    {`#${tag}`}

}
HashtagLink.contextTypes = {dispatch: func}

export const Details = ({ expanded, onExpand }, { post, community, dispatch }) => {
  const truncatedSize = 300
  const { tag } = post
  if (!community) community = post.communities[0]
  const slug = get('slug', community)
  let description = presentDescription(post, community)
  let extractedTags = []
  const truncated = !expanded && textLength(description) > truncatedSize
  if (truncated) {
    const orig = description
    description = truncate(description, truncatedSize)
    extractedTags = extractTags(description, orig, tag)
  }
  if (description) description = appendInP(description, ' ')

  return

    <ClickCatchingSpan dangerouslySetInnerHTML={{__html: description}} />
    {truncated &&
      <wbr />
       onExpand(null)} className='show-more'>Show more

    }
    {extractedTags.map(tag =>
      <wbr />
      <HashtagLink tag={tag} slug={slug} />

    )}
    {tag && <HashtagLink tag={tag} slug={slug} />}

}
Details.contextTypes = {post: object, community: object, dispatch: func}

const WelcomePostHeader = ({ communities }, { post }) => {
  let person = post.relatedUsers[0]
  let community = communities[0]
  return

    <A to={`/u/${person.id}`}>{person.name} joined

    {community
      ?
          <A to={`/c/${community.slug}`}>{community.name}.

            Welcome them!

      :
          a community that is no longer active.
        }

}
WelcomePostHeader.contextTypes = {post: object}

export const Menu = ({ expanded, post }, { dispatch, currentUser, community }) => {
  const canEdit = canEditPost(currentUser, post)
  const following = some(post.follower_ids, id => id === get('id', currentUser))
  const pinned = isPinned(post, community)
  const edit = () => isMobile()
    ? dispatch(navigate(`/p/${post.id}/edit`))
    : dispatch(startPostEdit(post)) &&
      expanded && dispatch(showModal('post-editor', {post}))
  const remove = () => window.confirm('Are you sure? This cannot be undone.') &&
    dispatch(removePost(post.id))
  const pin = () => dispatch(pinPost(get('slug', community), post.id))

  const toggleChildren = pinned
    ? Pinned
    :

  return <Dropdown className='post-menu' alignRight {...{toggleChildren}}>
    {canModerate(currentUser, community) &&

      {pinned ? 'Unpin post' : 'Pin post'}

}
    {canEdit &&
Edit
}
    {canEdit &&
Remove
}

       dispatch(followPost(post.id, currentUser))}>
        Turn {following ? 'off' : 'on'} notifications for this post

    {/*

       window.alert('TODO')}>Report objectionable content

 */}
  </Dropdown>
}
Menu.contextTypes = {currentUser: object, dispatch: func, community: object}

export const VoteButton = (props, { post, currentUser, dispatch }) => {
  let vote = () => dispatch(voteOnPost(post, currentUser))
  let myVote = includes(map(post.voters, 'id'), (currentUser || {}).id)
  return
    {myVote ? <Icon name='Heart2' /> : <Icon name='Heart' />}
    {myVote ? 'Liked' : 'Like'}

}
VoteButton.contextTypes = {post: object, currentUser: object, dispatch: func}

export const Voters = (props, { post, currentUser }) => {
  const voters = post.voters || []

  let onlyAuthorIsVoting = voters.length === 1 && same('id', first(voters), post.user)
  return voters.length > 0 && !onlyAuthorIsVoting
    ? <LinkedPersonSentence people={voters} className='voters meta'>
        liked this.
      </LinkedPersonSentence>
    :
}
Voters.contextTypes = {post: object, currentUser: object}

export const RequestContributorsSentence = ({ post }) => {
  const contributors = post.contributors || []
  const onlyAuthorIsContributing = contributors.length === 1 && same('id', first(contributors), post.user)
  return contributors.length > 0 && !onlyAuthorIsContributing
    ? <LinkedPersonSentence people={contributors} className='contributors'>
        helped complete this request!
      </LinkedPersonSentence>
    :
Request has been completed

}
<|endoftext|>


<commit_before><commit_msg>changed stuff<commit_after>var port = Number(process.env.PORT || 8000);

var express = require('express'); var app = express();

app.use(express.static(__dirname + '/app'));
var server = app.listen(port, function() {
	console.log('Listening on port %d', server.address().port);
});<|endoftext|>


<commit_before>/**
 * Process Manager
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * This file abstract out multiprocess logic involved in several tasks.
 *
 * Child processes can be queried.
 *
 * @license MIT
 */

'use strict';

const path = require('path');
const child_process = require('child_process');
const Streams = require('./streams');
const ROOT_DIR = path.resolve(__dirname, '..');

class SubprocessStream extends Streams.ObjectReadWriteStream {
	/**
	 * @param {ChildProcess} process
	 * @param {number} taskId
	 */
	constructor(process, taskId) {
		super();
		this.process = process;
		this.taskId = taskId;
		this.process.send(`${taskId}\nNEW`);
	}
	_write(/** @type {string} */ message) {
		if (!this.process.connected) return;
		this.process.send(`${this.taskId}\nWRITE\n${message}`);
		// responses are handled in ProcessWrapper
	}
	_destroy() {
		if (!this.process.connected) return;
		this.process.send(`${this.taskId}\nDESTROY`);
	}
}

/**
 * @typedef {Object} ProcessWrapper
 * @property {number} load
 * @property {() => Promise<void>} release
 */

/**
 * Wraps the process object in the PARENT process
 */
class QueryProcessWrapper {
	constructor(/** @type {string} */ file) {
		this.process = child_process.fork(file, [], {cwd: ROOT_DIR});
		this.taskId = 0;
		/** @type {Map<number, (resp: string) => void>} */
		this.pendingTasks = new Map();
		/** @type {Promise<void>?} */
		this.pendingRelease = null;
		/** @type {(() => void)?} */
		this.resolveRelease = null;

		this.process.on('message', /** @param {string} message */ message => {
			const nlLoc = message.indexOf('\n');
			if (nlLoc <= 0) throw new Error(`Invalid response ${message}`);
			const taskId = parseInt(message.slice(0, nlLoc));
			const resolve = this.pendingTasks.get(taskId);
			if (!resolve) throw new Error(`Invalid taskId ${message.slice(0, nlLoc)}`);
			this.pendingTasks.delete(taskId);
			resolve(JSON.parse(message.slice(nlLoc + 1)));

			if (this.resolveRelease && !this.load) this.destroy();
		});
		this.process.on('disconnect', () => {
			this.destroy();
		});
	}

	get load() {
		return this.pendingTasks.size;
	}

	/**
	 * @param {any} input
	 * @return {Promise<any>}
	 */
	query(input) {
		this.taskId++;
		const taskId = this.taskId;
		this.process.send(`${taskId}\n${JSON.stringify(input)}`);
		return new Promise(resolve => {
			this.pendingTasks.set(taskId, resolve);
		});
	}

	/**
	 * @return {Promise<void>}
	 */
	release() {
		if (this.pendingRelease) return this.pendingRelease;
		if (!this.load) {
			this.destroy();
		} else {
			this.pendingRelease = new Promise(resolve => {
				this.resolveRelease = resolve;
			});
		}
		return /** @type {Promise<void>} */ (this.pendingRelease);
	}

	destroy() {
		if (this.pendingRelease && !this.resolveRelease) {
			// already destroyed
			return;
		}
		this.process.disconnect();
		for (const resolver of this.pendingTasks.values()) {
			// maybe we should track reject functions too...
			resolver('');
		}
		this.pendingTasks.clear();
		if (this.resolveRelease) {
			this.resolveRelease();
			this.resolveRelease = null;
		} else if (!this.pendingRelease) {
			this.pendingRelease = Promise.resolve();
		}
	}
}

/**
 * Wraps the process object in the PARENT process
 */
class StreamProcessWrapper {
	constructor(/** @type {string} */ file) {
		this.process = child_process.fork(file, [], {cwd: ROOT_DIR});
		this.taskId = 0;
		/** @type {Map<number, SubprocessStream>} */
		this.activeStreams = new Map();
		/** @type {Promise<void>?} */
		this.pendingRelease = null;
		/** @type {(() => void)?} */
		this.resolveRelease = null;

		this.process.on('message', /** @param {string} message */ message => {
			let nlLoc = message.indexOf('\n');
			if (nlLoc <= 0) throw new Error(`Invalid response ${message}`);
			const taskId = parseInt(message.slice(0, nlLoc));
			const stream = this.activeStreams.get(taskId);
			if (!stream) throw new Error(`Invalid taskId ${message.slice(0, nlLoc)}`);

			message = message.slice(nlLoc + 1);
			nlLoc = message.indexOf('\n');
			if (nlLoc < 0) nlLoc = message.length;
			const messageType = message.slice(0, nlLoc);
			message = message.slice(nlLoc + 1);

			if (messageType === 'END') {
				stream.end();
				this.deleteStream(taskId);
			} else if (messageType === 'PUSH') {
				stream.push(message);
			} else {
				throw new Error(`Unrecognized messageType ${messageType}`);
			}
		});
	}

	deleteStream(/** @type {number} */ taskId) {
		this.activeStreams.delete(taskId);
		// try to release
		if (this.resolveRelease && !this.load) this.destroy();
	}

	get load() {
		return this.activeStreams.size;
	}

	/**
	 * @return {SubprocessStream}
	 */
	createStream() {
		this.taskId++;
		const taskId = this.taskId;
		const stream = new SubprocessStream(this.process, taskId);
		this.activeStreams.set(taskId, stream);
		return stream;
	}

	/**
	 * @return {Promise<void>}
	 */
	release() {
		if (this.pendingRelease) return this.pendingRelease;
		if (!this.load) {
			this.destroy();
		} else {
			this.pendingRelease = new Promise(resolve => {
				this.resolveRelease = resolve;
			});
		}
		return /** @type {Promise<void>} */ (this.pendingRelease);
	}

	destroy() {
		if (this.pendingRelease && !this.resolveRelease) {
			// already destroyed
			return;
		}
		this.process.disconnect();
		for (const stream of this.activeStreams.values()) {
			stream.destroy();
		}
		this.activeStreams.clear();
		if (this.resolveRelease) {
			this.resolveRelease();
			this.resolveRelease = null;
		} else if (!this.pendingRelease) {
			this.pendingRelease = Promise.resolve();
		}
	}
}

/**
 * A ProcessManager wraps a query function: A function that takes a
 * string and returns a string or Promise<string>.
 */
class ProcessManager {
	/**
	 * @param {NodeJS.Module} module
	 */
	constructor(module) {
		/** @type {ProcessWrapper[]} */
		this.processes = [];
		/** @type {ProcessWrapper[]} */
		this.releasingProcesses = [];
		this.module = module;
		this.filename = module.filename;
		this.basename = path.basename(module.filename);

		this.isParentProcess = (process.mainModule !== module || !process.send);

		this.listen();
	}
	acquire() {
		if (!this.processes.length) {
			return null;
		}
		let lowestLoad = this.processes[0];
		for (const process of this.processes) {
			if (process.load < lowestLoad.load) {
				lowestLoad = process;
			}
		}
		return lowestLoad;
	}
	unspawn() {
		for (const process of this.processes) {
			process.release().then(() => {
				const index = this.releasingProcesses.indexOf(process);
				if (index >= 0) {
					this.releasingProcesses.splice(index, 1);
				}
			});
		}
		this.releasingProcesses = this.releasingProcesses.concat(this.processes);
		this.processes = [];
	}
	spawn(count = 1) {
		if (!this.isParentProcess) return;
		if (PMLib.disabled) return;
		while (this.processes.length < count) {
			this.processes.push(this.createProcess());
		}
	}
	respawn(/** @type {number?} */ count = null) {
		if (count === null) count = this.processes.length;
		this.unspawn();
		this.spawn(count);
	}
	/**
	 * @return {ProcessWrapper}
	 */
	createProcess() {
		throw new Error(`implemented by subclass`);
	}
	listen() {
		throw new Error(`implemented by subclass`);
	}
	destroy() {
		const index = PMLib.processManagers.indexOf(this);
		if (index) PMLib.processManagers.splice(index, 1);
		this.unspawn();
	}
}

class QueryProcessManager extends ProcessManager {
	/**
	 * @param {NodeJS.Module} module
	 * @param {(input: any) => any} query
	 */
	constructor(module, query) {
		super(module);
		this._query = query;

		PMLib.processManagers.push(this);
	}
	/**
	 * @param {any} input
	 */
	query(input) {
		const process = /** @type {QueryProcessWrapper} */ (this.acquire());
		if (!process) return Promise.resolve(this._query(input));
		return process.query(input);
	}
	createProcess() {
		return new QueryProcessWrapper(this.filename);
	}
	listen() {
		if (this.isParentProcess) return;
		// child process
		process.on('message', async (/** @type {string} */ message) => {
			let nlLoc = message.indexOf('\n');
			if (nlLoc <= 0) throw new Error(`Invalid response ${message}`);
			const taskId = message.slice(0, nlLoc);
			message = message.slice(nlLoc + 1);

			if (taskId.startsWith('EVAL')) {
				// @ts-ignore guaranteed to be defined here
				process.send(`${taskId}\n` + eval(message));
				return;
			}

			const response = await this._query(JSON.parse(message));
			// @ts-ignore guaranteed to be defined here
			process.send(`${taskId}\n${JSON.stringify(response)}`);
		});
		process.on('disconnect', () => {
			process.exit();
		});
	}
}
class StreamProcessManager extends ProcessManager {
	/**
	 * @param {NodeJS.Module} module
	 * @param {() => ObjectReadWriteStream} createStream
	 */
	constructor(module, createStream) {
		super(module);
		/** @type {Map<string, ObjectReadWriteStream>} taskid: stream used only in child process */
		this.activeStreams = new Map();
		this._createStream = createStream;

		PMLib.processManagers.push(this);
	}
	createStream() {
		const process = /** @type {StreamProcessWrapper} */ (this.acquire());
		if (!process) return this._createStream();
		return process.createStream();
	}
	createProcess() {
		return new StreamProcessWrapper(this.filename);
	}
	/**
	 * @param {string} taskId
	 * @param {ObjectReadStream} stream
	 */
	async pipeStream(taskId, stream) {
		let value, done;
		while (({value, done} = await stream.next(), !done)) {
			// @ts-ignore Guaranteed to be a child process
			process.send(`${taskId}\nPUSH\n${value}`);
		}
		// @ts-ignore Guaranteed to be a child process
		process.send(`${taskId}\nEND`);
		this.activeStreams.delete(taskId);
	}
	listen() {
		if (this.isParentProcess) return;
		// child process
		process.on('message', async (/** @type {string} */ message) => {
			let nlLoc = message.indexOf('\n');
			if (nlLoc <= 0) throw new Error(`Invalid request ${message}`);
			const taskId = message.slice(0, nlLoc);
			const stream = this.activeStreams.get(taskId);

			message = message.slice(nlLoc + 1);
			nlLoc = message.indexOf('\n');
			if (nlLoc < 0) nlLoc = message.length;
			const messageType = message.slice(0, nlLoc);
			message = message.slice(nlLoc + 1);

			if (taskId.startsWith('EVAL')) {
				// @ts-ignore guaranteed to be a child process
				process.send(`${taskId}\n` + eval(message));
				return;
			}

			if (messageType === 'NEW') {
				if (stream) throw new Error(`NEW: taskId ${taskId} already exists`);
				const newStream = this._createStream();
				this.activeStreams.set(taskId, newStream);
				this.pipeStream(taskId, newStream);
			} else if (messageType === 'DESTROY') {
				if (!stream) throw new Error(`DESTROY: Invalid taskId ${taskId}`);
				stream.destroy();
				this.activeStreams.delete(taskId);
			} else if (messageType === 'WRITE') {
				if (!stream) throw new Error(`WRITE: Invalid taskId ${taskId}`);
				stream.write(message);
			} else {
				throw new Error(`Unrecognized messageType ${messageType}`);
			}
		});
		process.on('disconnect', () => {
			process.exit();
		});
	}
}

const PMLib = {
	QueryProcessWrapper,
	StreamProcessWrapper,
	ProcessManager,
	QueryProcessManager,
	StreamProcessManager,

	/** @type {ProcessManager[]} */
	processManagers: [],
	disabled: false,
};
// @ts-ignore Typescript bug
module.exports = PMLib;
<commit_msg>Make ProcessManager slightly safer<commit_after>/**
 * Process Manager
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * This file abstract out multiprocess logic involved in several tasks.
 *
 * Child processes can be queried.
 *
 * @license MIT
 */

'use strict';

const path = require('path');
const child_process = require('child_process');
const Streams = require('./streams');
const ROOT_DIR = path.resolve(__dirname, '..');

class SubprocessStream extends Streams.ObjectReadWriteStream {
	/**
	 * @param {ChildProcess} process
	 * @param {number} taskId
	 */
	constructor(process, taskId) {
		super();
		this.process = process;
		this.taskId = taskId;
		this.process.send(`${taskId}\nNEW`);
	}
	_write(/** @type {string} */ message) {
		if (!this.process.connected) return;
		this.process.send(`${this.taskId}\nWRITE\n${message}`);
		// responses are handled in ProcessWrapper
	}
	_destroy() {
		if (!this.process.connected) return;
		this.process.send(`${this.taskId}\nDESTROY`);
	}
}

/**
 * @typedef {Object} ProcessWrapper
 * @property {number} load
 * @property {() => Promise<void>} release
 */

/**
 * Wraps the process object in the PARENT process
 */
class QueryProcessWrapper {
	constructor(/** @type {string} */ file) {
		this.process = child_process.fork(file, [], {cwd: ROOT_DIR});
		this.taskId = 0;
		/** @type {Map<number, (resp: string) => void>} */
		this.pendingTasks = new Map();
		/** @type {Promise<void>?} */
		this.pendingRelease = null;
		/** @type {(() => void)?} */
		this.resolveRelease = null;

		this.process.on('message', /** @param {string} message */ message => {
			const nlLoc = message.indexOf('\n');
			if (nlLoc <= 0) throw new Error(`Invalid response ${message}`);
			const taskId = parseInt(message.slice(0, nlLoc));
			const resolve = this.pendingTasks.get(taskId);
			if (!resolve) throw new Error(`Invalid taskId ${message.slice(0, nlLoc)}`);
			this.pendingTasks.delete(taskId);
			resolve(JSON.parse(message.slice(nlLoc + 1)));

			if (this.resolveRelease && !this.load) this.destroy();
		});
		this.process.on('disconnect', () => {
			this.destroy();
		});
	}

	get load() {
		return this.pendingTasks.size;
	}

	/**
	 * @param {any} input
	 * @return {Promise<any>}
	 */
	query(input) {
		this.taskId++;
		const taskId = this.taskId;
		this.process.send(`${taskId}\n${JSON.stringify(input)}`);
		return new Promise(resolve => {
			this.pendingTasks.set(taskId, resolve);
		});
	}

	/**
	 * @return {Promise<void>}
	 */
	release() {
		if (this.pendingRelease) return this.pendingRelease;
		if (!this.load) {
			this.destroy();
		} else {
			this.pendingRelease = new Promise(resolve => {
				this.resolveRelease = resolve;
			});
		}
		return /** @type {Promise<void>} */ (this.pendingRelease);
	}

	destroy() {
		if (this.pendingRelease && !this.resolveRelease) {
			// already destroyed
			return;
		}
		this.process.disconnect();
		for (const resolver of this.pendingTasks.values()) {
			// maybe we should track reject functions too...
			resolver('');
		}
		this.pendingTasks.clear();
		if (this.resolveRelease) {
			this.resolveRelease();
			this.resolveRelease = null;
		} else if (!this.pendingRelease) {
			this.pendingRelease = Promise.resolve();
		}
	}
}

/**
 * Wraps the process object in the PARENT process
 */
class StreamProcessWrapper {
	constructor(/** @type {string} */ file) {
		this.process = child_process.fork(file, [], {cwd: ROOT_DIR});
		this.taskId = 0;
		/** @type {Map<number, SubprocessStream>} */
		this.activeStreams = new Map();
		/** @type {Promise<void>?} */
		this.pendingRelease = null;
		/** @type {(() => void)?} */
		this.resolveRelease = null;

		this.process.on('message', /** @param {string} message */ message => {
			let nlLoc = message.indexOf('\n');
			if (nlLoc <= 0) throw new Error(`Invalid response ${message}`);
			const taskId = parseInt(message.slice(0, nlLoc));
			const stream = this.activeStreams.get(taskId);
			if (!stream) throw new Error(`Invalid taskId ${message.slice(0, nlLoc)}`);

			message = message.slice(nlLoc + 1);
			nlLoc = message.indexOf('\n');
			if (nlLoc < 0) nlLoc = message.length;
			const messageType = message.slice(0, nlLoc);
			message = message.slice(nlLoc + 1);

			if (messageType === 'END') {
				stream.end();
				this.deleteStream(taskId);
			} else if (messageType === 'PUSH') {
				stream.push(message);
			} else {
				throw new Error(`Unrecognized messageType ${messageType}`);
			}
		});
		this.process.on('disconnect', () => {
			this.destroy();
		});
	}

	deleteStream(/** @type {number} */ taskId) {
		this.activeStreams.delete(taskId);
		// try to release
		if (this.resolveRelease && !this.load) this.destroy();
	}

	get load() {
		return this.activeStreams.size;
	}

	/**
	 * @return {SubprocessStream}
	 */
	createStream() {
		this.taskId++;
		const taskId = this.taskId;
		const stream = new SubprocessStream(this.process, taskId);
		this.activeStreams.set(taskId, stream);
		return stream;
	}

	/**
	 * @return {Promise<void>}
	 */
	release() {
		if (this.pendingRelease) return this.pendingRelease;
		if (!this.load) {
			this.destroy();
		} else {
			this.pendingRelease = new Promise(resolve => {
				this.resolveRelease = resolve;
			});
		}
		return /** @type {Promise<void>} */ (this.pendingRelease);
	}

	destroy() {
		if (this.pendingRelease && !this.resolveRelease) {
			// already destroyed
			return;
		}
		this.process.disconnect();
		for (const stream of this.activeStreams.values()) {
			stream.destroy();
		}
		this.activeStreams.clear();
		if (this.resolveRelease) {
			this.resolveRelease();
			this.resolveRelease = null;
		} else if (!this.pendingRelease) {
			this.pendingRelease = Promise.resolve();
		}
	}
}

/**
 * A ProcessManager wraps a query function: A function that takes a
 * string and returns a string or Promise<string>.
 */
class ProcessManager {
	/**
	 * @param {NodeJS.Module} module
	 */
	constructor(module) {
		/** @type {ProcessWrapper[]} */
		this.processes = [];
		/** @type {ProcessWrapper[]} */
		this.releasingProcesses = [];
		this.module = module;
		this.filename = module.filename;
		this.basename = path.basename(module.filename);

		this.isParentProcess = (process.mainModule !== module || !process.send);

		this.listen();
	}
	acquire() {
		if (!this.processes.length) {
			return null;
		}
		let lowestLoad = this.processes[0];
		for (const process of this.processes) {
			if (process.load < lowestLoad.load) {
				lowestLoad = process;
			}
		}
		return lowestLoad;
	}
	unspawn() {
		for (const process of this.processes) {
			process.release().then(() => {
				const index = this.releasingProcesses.indexOf(process);
				if (index >= 0) {
					this.releasingProcesses.splice(index, 1);
				}
			});
		}
		this.releasingProcesses = this.releasingProcesses.concat(this.processes);
		this.processes = [];
	}
	spawn(count = 1) {
		if (!this.isParentProcess) return;
		if (PMLib.disabled) return;
		while (this.processes.length < count) {
			this.processes.push(this.createProcess());
		}
	}
	respawn(/** @type {number?} */ count = null) {
		if (count === null) count = this.processes.length;
		this.unspawn();
		this.spawn(count);
	}
	/**
	 * @return {ProcessWrapper}
	 */
	createProcess() {
		throw new Error(`implemented by subclass`);
	}
	listen() {
		throw new Error(`implemented by subclass`);
	}
	destroy() {
		const index = PMLib.processManagers.indexOf(this);
		if (index) PMLib.processManagers.splice(index, 1);
		this.unspawn();
	}
}

class QueryProcessManager extends ProcessManager {
	/**
	 * @param {NodeJS.Module} module
	 * @param {(input: any) => any} query
	 */
	constructor(module, query) {
		super(module);
		this._query = query;

		PMLib.processManagers.push(this);
	}
	/**
	 * @param {any} input
	 */
	query(input) {
		const process = /** @type {QueryProcessWrapper} */ (this.acquire());
		if (!process) return Promise.resolve(this._query(input));
		return process.query(input);
	}
	createProcess() {
		return new QueryProcessWrapper(this.filename);
	}
	listen() {
		if (this.isParentProcess) return;
		// child process
		process.on('message', async (/** @type {string} */ message) => {
			let nlLoc = message.indexOf('\n');
			if (nlLoc <= 0) throw new Error(`Invalid response ${message}`);
			const taskId = message.slice(0, nlLoc);
			message = message.slice(nlLoc + 1);

			if (taskId.startsWith('EVAL')) {
				// @ts-ignore guaranteed to be defined here
				process.send(`${taskId}\n` + eval(message));
				return;
			}

			const response = await this._query(JSON.parse(message));
			// @ts-ignore guaranteed to be defined here
			process.send(`${taskId}\n${JSON.stringify(response)}`);
		});
		process.on('disconnect', () => {
			process.exit();
		});
	}
}
class StreamProcessManager extends ProcessManager {
	/**
	 * @param {NodeJS.Module} module
	 * @param {() => ObjectReadWriteStream} createStream
	 */
	constructor(module, createStream) {
		super(module);
		/** @type {Map<string, ObjectReadWriteStream>} taskid: stream used only in child process */
		this.activeStreams = new Map();
		this._createStream = createStream;

		PMLib.processManagers.push(this);
	}
	createStream() {
		const process = /** @type {StreamProcessWrapper} */ (this.acquire());
		if (!process) return this._createStream();
		return process.createStream();
	}
	createProcess() {
		return new StreamProcessWrapper(this.filename);
	}
	/**
	 * @param {string} taskId
	 * @param {ObjectReadStream} stream
	 */
	async pipeStream(taskId, stream) {
		let value, done;
		while (({value, done} = await stream.next(), !done)) {
			// @ts-ignore Guaranteed to be a child process
			process.send(`${taskId}\nPUSH\n${value}`);
		}
		// @ts-ignore Guaranteed to be a child process
		process.send(`${taskId}\nEND`);
		this.activeStreams.delete(taskId);
	}
	listen() {
		if (this.isParentProcess) return;
		// child process
		process.on('message', async (/** @type {string} */ message) => {
			let nlLoc = message.indexOf('\n');
			if (nlLoc <= 0) throw new Error(`Invalid request ${message}`);
			const taskId = message.slice(0, nlLoc);
			const stream = this.activeStreams.get(taskId);

			message = message.slice(nlLoc + 1);
			nlLoc = message.indexOf('\n');
			if (nlLoc < 0) nlLoc = message.length;
			const messageType = message.slice(0, nlLoc);
			message = message.slice(nlLoc + 1);

			if (taskId.startsWith('EVAL')) {
				// @ts-ignore guaranteed to be a child process
				process.send(`${taskId}\n` + eval(message));
				return;
			}

			if (messageType === 'NEW') {
				if (stream) throw new Error(`NEW: taskId ${taskId} already exists`);
				const newStream = this._createStream();
				this.activeStreams.set(taskId, newStream);
				this.pipeStream(taskId, newStream);
			} else if (messageType === 'DESTROY') {
				if (!stream) throw new Error(`DESTROY: Invalid taskId ${taskId}`);
				stream.destroy();
				this.activeStreams.delete(taskId);
			} else if (messageType === 'WRITE') {
				if (!stream) throw new Error(`WRITE: Invalid taskId ${taskId}`);
				stream.write(message);
			} else {
				throw new Error(`Unrecognized messageType ${messageType}`);
			}
		});
		process.on('disconnect', () => {
			process.exit();
		});
	}
}

const PMLib = {
	QueryProcessWrapper,
	StreamProcessWrapper,
	ProcessManager,
	QueryProcessManager,
	StreamProcessManager,

	/** @type {ProcessManager[]} */
	processManagers: [],
	disabled: false,
};
// @ts-ignore Typescript bug
module.exports = PMLib;
<|endoftext|>


<commit_before>// Heavily inspired by https://github.com/jungledre/react-snow-effect
import React, { Component } from 'react'

class Snow extends Component {
  constructor(props) {
    super(props)

    const width = window ? window.innerWidth : props.width
    const height = window ? window.innerHeight : props.height

    this.state = {
      intervalTracker: null,
      canvasCtx: null,
      height,
      width
    }

    this.canvas = React.createRef()
  }

  static defaultProps = {
    width: 1024,
    height: 768
  }

  componentDidMount() {
    // Canvas init
    const canvas = this.canvas.current
    const canvasCtx = canvas.getContext('2d')

    this.setState({ canvasCtx })
    const { width: W, height: H } = this.state

    // Particles
    const max = 32
    const particles = []
    for (let i = 0; i < max; i++) {
      particles.push({
        x: Math.random() * W, // x-coordinate
        y: Math.random() * H, // y-coordinate
        r: Math.random() * 4 + 1, // radius
        d: Math.random() * max // density
      })
    }

    // Draw the flakes
    const draw = () => {
      canvasCtx.clearRect(0, 0, W, H)

      canvasCtx.fillStyle = 'rgba(255, 255, 255, 0.75)'
      canvasCtx.beginPath()
      for (let i = 0; i < max; i++) {
        const p = particles[i]
        canvasCtx.moveTo(p.x, p.y)
        canvasCtx.arc(p.x, p.y, p.r, 0, Math.PI * 2, true)
      }
      canvasCtx.fill()
      update()
    }

    // Function to move the snowflakes
    // angle will be an ongoing incremental flag. Sin and Cos functions will be applied to it to create vertical and horizontal movements of the flakes
    let angle = 0

    const update = () => {
      angle += 0.01
      for (let i = 0; i < max; i++) {
        const p = particles[i]
        // Updating X and Y coordinates
        // Adding 1 to cos to prevent negative values (which would move flakes upwards)
        // Every particle has its own density to make the downward movement different for each flake
        // Make it more random by adding in the radius
        p.y += Math.cos(angle + p.d) + 1 + p.r / 2
        p.x += Math.sin(angle) * 2

        // Sending flakes back from the top when it exits
        // Make it more organic with flakes entering from both sides
        if (p.x > W + 5 || p.x < -5 || p.y > H) {
          if (i % 3 > 0) {
            // 2/3 of the flakes
            particles[i] = { x: Math.random() * W, y: -10, r: p.r, d: p.d }
          } else {
            // If the flake is exitting from the right
            if (Math.sin(angle) > 0) {
              // Enter from the left
              particles[i] = { x: -5, y: Math.random() * H, r: p.r, d: p.d }
            } else {
              // Enter from the right
              particles[i] = { x: W + 5, y: Math.random() * H, r: p.r, d: p.d }
            }
          }
        }
      }
    }

    this.setState({ intervalTracker: setInterval(draw, 32) })

    // Animation loop
    this.state.intervalTracker
  }

  componentWillUnmount() {
    this.state.canvasCtx.clearRect(0, 0, this.state.width, this.state.height)
    clearInterval(this.state.intervalTracker)
  }

  render() {
    const style = {
      margin: 0,
      padding: 0,
      pointerEvents: 'none',
      position: 'fixed',
      top: 0,
      zIndex: 1
    }

    return (
      <canvas
        ref={this.canvas}
        style={style}
        width={this.state.width}
        height={this.state.height}
      />
    )
  }
}

export default Snow
<commit_msg>Fix Snow, finally<commit_after>// Heavily inspired by https://github.com/jungledre/react-snow-effect
import React, { Component } from 'react'

class Snow extends Component {
  constructor(props) {
    super(props)

    const browser = typeof window === 'object'
    const width = browser ? window.innerWidth : props.width
    const height = browser ? window.innerHeight : props.height

    this.state = {
      intervalTracker: null,
      canvasCtx: null,
      height,
      width
    }

    this.canvas = React.createRef()
  }

  static defaultProps = {
    width: 1024,
    height: 768
  }

  componentDidMount() {
    // Canvas init
    const canvas = this.canvas.current
    const canvasCtx = canvas.getContext('2d')

    this.setState({ canvasCtx })
    const { width: W, height: H } = this.state

    // Particles
    const max = 32
    const particles = []
    for (let i = 0; i < max; i++) {
      particles.push({
        x: Math.random() * W, // x-coordinate
        y: Math.random() * H, // y-coordinate
        r: Math.random() * 4 + 1, // radius
        d: Math.random() * max // density
      })
    }

    // Draw the flakes
    const draw = () => {
      canvasCtx.clearRect(0, 0, W, H)

      canvasCtx.fillStyle = 'rgba(255, 255, 255, 0.75)'
      canvasCtx.beginPath()
      for (let i = 0; i < max; i++) {
        const p = particles[i]
        canvasCtx.moveTo(p.x, p.y)
        canvasCtx.arc(p.x, p.y, p.r, 0, Math.PI * 2, true)
      }
      canvasCtx.fill()
      update()
    }

    // Function to move the snowflakes
    // angle will be an ongoing incremental flag. Sin and Cos functions will be applied to it to create vertical and horizontal movements of the flakes
    let angle = 0

    const update = () => {
      angle += 0.01
      for (let i = 0; i < max; i++) {
        const p = particles[i]
        // Updating X and Y coordinates
        // Adding 1 to cos to prevent negative values (which would move flakes upwards)
        // Every particle has its own density to make the downward movement different for each flake
        // Make it more random by adding in the radius
        p.y += Math.cos(angle + p.d) + 1 + p.r / 2
        p.x += Math.sin(angle) * 2

        // Sending flakes back from the top when it exits
        // Make it more organic with flakes entering from both sides
        if (p.x > W + 5 || p.x < -5 || p.y > H) {
          if (i % 3 > 0) {
            // 2/3 of the flakes
            particles[i] = { x: Math.random() * W, y: -10, r: p.r, d: p.d }
          } else {
            // If the flake is exitting from the right
            if (Math.sin(angle) > 0) {
              // Enter from the left
              particles[i] = { x: -5, y: Math.random() * H, r: p.r, d: p.d }
            } else {
              // Enter from the right
              particles[i] = { x: W + 5, y: Math.random() * H, r: p.r, d: p.d }
            }
          }
        }
      }
    }

    this.setState({ intervalTracker: setInterval(draw, 32) })

    // Animation loop
    this.state.intervalTracker
  }

  componentWillUnmount() {
    this.state.canvasCtx.clearRect(0, 0, this.state.width, this.state.height)
    clearInterval(this.state.intervalTracker)
  }

  render() {
    const style = {
      margin: 0,
      padding: 0,
      pointerEvents: 'none',
      position: 'fixed',
      top: 0,
      zIndex: 1
    }

    return (
      <canvas
        ref={this.canvas}
        style={style}
        width={this.state.width}
        height={this.state.height}
      />
    )
  }
}

export default Snow
<|endoftext|>