<commit_before>var _ = require('underscore'),
	async = require('async'),
	config = require('../config'),
	crypto = require('crypto'),
	exec = require('child_process').exec,
	fs = require('fs'),
	hooks = require('../hooks'),
	imager = require('../imager/config'),
	path = require('path'),
	report = require('../report/config'),
	vm = require('vm');

_.templateSettings = {
	interpolate: /\{\{(.+?)\}\}/g
};

exports.emitter = new (require('events').EventEmitter);

exports.dbCache = {
	OPs: {},
	opTags: {},
	threadSubs: {},
	YAKUMAN: 0,
	funThread: 0,
	addresses: {},
	ranges: {},
};

var HOT = exports.hot = {};
var RES = exports.resources = {};
exports.clientConfig = [];
exports.clientConfigHash = '';
exports.clients = {};
exports.clientsByIP = {};

function reload_hot_config(cb) {
	fs.readFile('hot.js', 'UTF-8', function (err, js) {
		if (err)
			cb(err);
		var hot = {};
		try {
			vm.runInNewContext(js, hot);
		}
		catch (e) {
			return cb(e);
		}
		if (!hot || !hot.hot)
			return cb('Bad hot config.');

		// Overwrite the original object just in case
		Object.keys(HOT).forEach(function (k) {
			delete HOT[k];
		});
		_.extend(HOT, hot.hot);

		// Pass some of the config variables to the client
		var clientHot = _.pick(HOT,
			'ILLYA_DANCE',
			'EIGHT_BALL',
			'THREADS_PER_PAGE',
			'ABBREVIATED_REPLIES',
			'SUBJECT_MAX_LENGTH',
			'EXCLUDE_REGEXP',
			'ADMIN_ALIAS',
			'MOD_ALIAS',
			'SAGE_ENABLED',
			'THREAD_LAST_N'
		);

		reloadCSS(clientHot, function(err) {
			if (err)
				return cb(err);
			HOT.CLIENT_CONFIG = JSON.stringify(clientConfig);
			HOT.CLIENT_IMAGER = JSON.stringify(clientImager);
			HOT.CLIENT_REPORT = JSON.stringify(clientReport);
			HOT.CLIENT_HOT = JSON.stringify(clientHot);
			var combined = exports.clientConfig = [
				clientConfig,
				clientImager,
				clientReport,
				clientHot
			];
			exports.clientConfigHash = HOT.CLIENT_CONFIG_HASH = crypto
				.createHash('MD5')
				.update(JSON.stringify(combined))
				.digest('hex');

			read_exits('exits.txt', function() {
				hooks.trigger('reloadHot', HOT, cb);
			});
		});
	});
}

var clientConfig = _.pick(config,
	'IP_MNEMONIC',
	'USE_WEBSOCKETS',
	'SOCKET_PATH',
	'DEBUG',
	'READ_ONLY',
	'API_URL',
	'IP_TAGGING',
	'RADIO',
	'PYU',
	'BOARDS'
);
var clientImager = _.pick(imager,
	'WEBM',
	'UPLOAD_URL',
	'MEDIA_URL',
	'THUMB_DIMENSIONS',
	'PINKY_DIMENSIONS',
	'SPOILER_IMAGES',
	'IMAGE_HATS'
);
var clientReport = _.pick(report, 'RECAPTCHA_PUBLIC_KEY');

function reload_scripts(cb) {
	async.mapSeries(['client', 'vendor', 'mod'], getRevision,
		function(err, js) {
			if (err)
				return cb(err);
			HOT.CLIENT_JS = js[0].client;
			HOT.VENDOR_JS = js[1].vendor;
			// Read moderator js file
			fs.readFile(path.join('state', js[2].mod), 'UTF-8',
				function (err, modSrc) {
					if (err)
						return cb(err);
					RES.modJs = modSrc;
					cb(null);
				}
			);
		}
	);
}

// Read JSON files in ./state, generated by grunt-rev
function getRevision(name, cb) {
	fs.readFile(path.join('state', name + '.json'), function(err, json) {
		if (err)
			return cb(err);
		var files;
		try {
			files = JSON.parse(json);
		}
		catch(e) {
			return cb(e);
		}
		if (!files)
			return cb('Bad state/' + name + '.json');
		cb(null, files);
	});
}

function reloadCSS(hot, cb) {
	getRevision('css', function(err, files) {
		if (err)
			return cb(err);
		// Only the curfew template is statically assigned a CSS file. The rest
		// are inserted per request in server/render.js and www/js/setup.js
		HOT.CURFEW_CSS = files['curfew.css'];
		// Export to these modules and client
		HOT.css = hot.css = files;
		cb(null);
	});
}

function reload_resources(cb) {
	read_templates(function (err, tmpls) {
		if (err)
			return cb(err);

		_.extend(RES, expand_templates(tmpls));

		hooks.trigger('reloadResources', RES, cb);
	});
}

function read_templates(cb) {
	function read(dir, file) {
		return fs.readFile.bind(fs, path.join(dir, file), 'UTF-8');
	}

	async.parallel({
		index: read('tmpl', 'index.html'),
		filter: read('tmpl', 'filter.html'),
		login: read('tmpl', 'login.html'),
		curfew: read('tmpl', 'curfew.html'),
		suspension: read('tmpl', 'suspension.html'),
		aLookup: read('tmpl', 'alookup.html'),
		notFound: read('www', '404.html'),
		serverError: read('www', '50x.html'),
	}, cb);
}

function expand_templates(res) {
	var templateVars = _.clone(HOT);
	_.extend(templateVars, imager);
	_.extend(templateVars, config);
	_.extend(templateVars, make_navigation_html());

	templateVars.SCHEDULE = build_schedule(templateVars.SCHEDULE);
	templateVars.FAQ = build_FAQ(templateVars.FAQ);
	// Format info banner
	if (templateVars.BANNERINFO)
		templateVars.BANNERINFO = '&nbsp;&nbsp;&nbsp;['
				+ templateVars.BANNERINFO + ']';

	function tmpl(data) {
		var expanded = _.template(data)(templateVars);
		return {tmpl: expanded.split(/\$[A-Z]+/),
			src: expanded};
	}

	var ex = {
		filterTmpl: tmpl(res.filter).tmpl,
		curfewTmpl: tmpl(res.curfew).tmpl,
		suspensionTmpl: tmpl(res.suspension).tmpl,
		loginTmpl: tmpl(res.login).tmpl,
		aLookupHtml: res.aLookup,
		notFoundHtml: res.notFound,
		serverErrorHtml: res.serverError,
	};

	var index = tmpl(res.index);
	ex.indexTmpl = index.tmpl;
	var hash = crypto.createHash('md5').update(index.src);
	ex.indexHash = hash.digest('hex').slice(0, 8);

	return ex;
}

function build_schedule(schedule){
	var filler = ['drink & fap', 'fap & drink', 'tea & keiki'];
	var table = ['<table>'];
	for (var day in schedule){
		var plans = schedule[day].plans;
		var time = schedule[day].time;
		// Fill empty slots
		if (plans == '')
			plans = filler[Math.floor(Math.random() * filler.length)];
		if (time == '')
			time = 'all day';
		table.push('<tr><td><b>[', day + ']&nbsp;&nbsp;', '</b></td><td>',
				plans + '&nbsp;&nbsp;', '</td><td>', time, '</td></tr>');
	}
	table.push('</table>');
	return table.join('');
}

function build_FAQ(faq){
	if (faq.length > 0){
		var list = ['<ul>'];
		faq.forEach(function(entry){
			list.push('<li>' + entry + '</li>');
		});
		list.push('<ul>');
		return list.join('');
	}
}

function buildClient(cb){
	exec('./node_modules/gulp/bin/gulp.js client mod vendor css',
		function(err, stdout, stderr){
			if (err)
				return console.error(
					'Error: Failed to build client:',
					err, stderr
				);
			cb();
		}
	);
}

exports.reload_hot_resources = function (cb) {
	buildClient(function(){<commit_msg>Notify about client compilation start<commit_after>var _ = require('underscore'),
	async = require('async'),
	config = require('../config'),
	crypto = require('crypto'),
	exec = require('child_process').exec,
	fs = require('fs'),
	hooks = require('../hooks'),
	imager = require('../imager/config'),
	path = require('path'),
	report = require('../report/config'),
	vm = require('vm'),
	winston = require('winston');

_.templateSettings = {
	interpolate: /\{\{(.+?)\}\}/g
};

exports.emitter = new (require('events').EventEmitter);

exports.dbCache = {
	OPs: {},
	opTags: {},
	threadSubs: {},
	YAKUMAN: 0,
	funThread: 0,
	addresses: {},
	ranges: {},
};

var HOT = exports.hot = {};
var RES = exports.resources = {};
exports.clientConfig = [];
exports.clientConfigHash = '';
exports.clients = {};
exports.clientsByIP = {};

function reload_hot_config(cb) {
	fs.readFile('hot.js', 'UTF-8', function (err, js) {
		if (err)
			cb(err);
		var hot = {};
		try {
			vm.runInNewContext(js, hot);
		}
		catch (e) {
			return cb(e);
		}
		if (!hot || !hot.hot)
			return cb('Bad hot config.');

		// Overwrite the original object just in case
		Object.keys(HOT).forEach(function (k) {
			delete HOT[k];
		});
		_.extend(HOT, hot.hot);

		// Pass some of the config variables to the client
		var clientHot = _.pick(HOT,
			'ILLYA_DANCE',
			'EIGHT_BALL',
			'THREADS_PER_PAGE',
			'ABBREVIATED_REPLIES',
			'SUBJECT_MAX_LENGTH',
			'EXCLUDE_REGEXP',
			'ADMIN_ALIAS',
			'MOD_ALIAS',
			'SAGE_ENABLED',
			'THREAD_LAST_N'
		);

		reloadCSS(clientHot, function(err) {
			if (err)
				return cb(err);
			HOT.CLIENT_CONFIG = JSON.stringify(clientConfig);
			HOT.CLIENT_IMAGER = JSON.stringify(clientImager);
			HOT.CLIENT_REPORT = JSON.stringify(clientReport);
			HOT.CLIENT_HOT = JSON.stringify(clientHot);
			var combined = exports.clientConfig = [
				clientConfig,
				clientImager,
				clientReport,
				clientHot
			];
			exports.clientConfigHash = HOT.CLIENT_CONFIG_HASH = crypto
				.createHash('MD5')
				.update(JSON.stringify(combined))
				.digest('hex');

			read_exits('exits.txt', function() {
				hooks.trigger('reloadHot', HOT, cb);
			});
		});
	});
}

var clientConfig = _.pick(config,
	'IP_MNEMONIC',
	'USE_WEBSOCKETS',
	'SOCKET_PATH',
	'DEBUG',
	'READ_ONLY',
	'API_URL',
	'IP_TAGGING',
	'RADIO',
	'PYU',
	'BOARDS'
);
var clientImager = _.pick(imager,
	'WEBM',
	'UPLOAD_URL',
	'MEDIA_URL',
	'THUMB_DIMENSIONS',
	'PINKY_DIMENSIONS',
	'SPOILER_IMAGES',
	'IMAGE_HATS'
);
var clientReport = _.pick(report, 'RECAPTCHA_PUBLIC_KEY');

function reload_scripts(cb) {
	async.mapSeries(['client', 'vendor', 'mod'], getRevision,
		function(err, js) {
			if (err)
				return cb(err);
			HOT.CLIENT_JS = js[0].client;
			HOT.VENDOR_JS = js[1].vendor;
			// Read moderator js file
			fs.readFile(path.join('state', js[2].mod), 'UTF-8',
				function (err, modSrc) {
					if (err)
						return cb(err);
					RES.modJs = modSrc;
					cb(null);
				}
			);
		}
	);
}

// Read JSON files in ./state, generated by grunt-rev
function getRevision(name, cb) {
	fs.readFile(path.join('state', name + '.json'), function(err, json) {
		if (err)
			return cb(err);
		var files;
		try {
			files = JSON.parse(json);
		}
		catch(e) {
			return cb(e);
		}
		if (!files)
			return cb('Bad state/' + name + '.json');
		cb(null, files);
	});
}

function reloadCSS(hot, cb) {
	getRevision('css', function(err, files) {
		if (err)
			return cb(err);
		// Only the curfew template is statically assigned a CSS file. The rest
		// are inserted per request in server/render.js and www/js/setup.js
		HOT.CURFEW_CSS = files['curfew.css'];
		// Export to these modules and client
		HOT.css = hot.css = files;
		cb(null);
	});
}

function reload_resources(cb) {
	read_templates(function (err, tmpls) {
		if (err)
			return cb(err);

		_.extend(RES, expand_templates(tmpls));

		hooks.trigger('reloadResources', RES, cb);
	});
}

function read_templates(cb) {
	function read(dir, file) {
		return fs.readFile.bind(fs, path.join(dir, file), 'UTF-8');
	}

	async.parallel({
		index: read('tmpl', 'index.html'),
		filter: read('tmpl', 'filter.html'),
		login: read('tmpl', 'login.html'),
		curfew: read('tmpl', 'curfew.html'),
		suspension: read('tmpl', 'suspension.html'),
		aLookup: read('tmpl', 'alookup.html'),
		notFound: read('www', '404.html'),
		serverError: read('www', '50x.html'),
	}, cb);
}

function expand_templates(res) {
	var templateVars = _.clone(HOT);
	_.extend(templateVars, imager);
	_.extend(templateVars, config);
	_.extend(templateVars, make_navigation_html());

	templateVars.SCHEDULE = build_schedule(templateVars.SCHEDULE);
	templateVars.FAQ = build_FAQ(templateVars.FAQ);
	// Format info banner
	if (templateVars.BANNERINFO)
		templateVars.BANNERINFO = '&nbsp;&nbsp;&nbsp;['
				+ templateVars.BANNERINFO + ']';

	function tmpl(data) {
		var expanded = _.template(data)(templateVars);
		return {tmpl: expanded.split(/\$[A-Z]+/),
			src: expanded};
	}

	var ex = {
		filterTmpl: tmpl(res.filter).tmpl,
		curfewTmpl: tmpl(res.curfew).tmpl,
		suspensionTmpl: tmpl(res.suspension).tmpl,
		loginTmpl: tmpl(res.login).tmpl,
		aLookupHtml: res.aLookup,
		notFoundHtml: res.notFound,
		serverErrorHtml: res.serverError,
	};

	var index = tmpl(res.index);
	ex.indexTmpl = index.tmpl;
	var hash = crypto.createHash('md5').update(index.src);
	ex.indexHash = hash.digest('hex').slice(0, 8);

	return ex;
}

function build_schedule(schedule){
	var filler = ['drink & fap', 'fap & drink', 'tea & keiki'];
	var table = ['<table>'];
	for (var day in schedule){
		var plans = schedule[day].plans;
		var time = schedule[day].time;
		// Fill empty slots
		if (plans == '')
			plans = filler[Math.floor(Math.random() * filler.length)];
		if (time == '')
			time = 'all day';
		table.push('<tr><td><b>[', day + ']&nbsp;&nbsp;', '</b></td><td>',
				plans + '&nbsp;&nbsp;', '</td><td>', time, '</td></tr>');
	}
	table.push('</table>');
	return table.join('');
}

function build_FAQ(faq){
	if (faq.length > 0){
		var list = ['<ul>'];
		faq.forEach(function(entry){
			list.push('<li>' + entry + '</li>');
		});
		list.push('<ul>');
		return list.join('');
	}
}

function buildClient(cb){
	winston.info('(Re)compiling client JS and CSS.');
	exec('./node_modules/gulp/bin/gulp.js client mod vendor css',
		function(err, stdout, stderr){
			if (err)
				return console.error(
					'Error: Failed to build client:',
					err, stderr
				);
			cb();
		}
	);
}

exports.reload_hot_resources = function (cb) {
	buildClient(function(){
{'fraction_non_alphanumeric': 0.1860627719080174, 'fraction_numerical': 0.0027967681789931634, 'mean_word_length': 3.896538607835679, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1405396', 'n_tokens_mistral': 5419, 'n_tokens_neox': 4892, 'n_words': 974}