<commit_before>var common = require('./common'),
	config = require('./config'),
	fs = require('fs'),
	io = require('socket.io'),
	http = require('http'),
	pix = require('./pix'),
	db = require('./db'),
	Template = require('./lib/json-template').Template,
	tripcode = require('./tripcode'),
	util = require('util');

var clients = {};
var dispatcher = {};

function multisend(client, msgs) {
	client.socket.send(JSON.stringify(msgs));
}

function broadcast(msg, origin) {
	var thread_num = post.op || post.num;
	++syncNumber;
	msg = JSON.stringify(msg);
	var payload = '[' + msg + ']';
	for (id in clients) {
		var client = clients[id];
		if (!client.synced)
			continue;
		if (client.watching && client.watching != thread_num) {
			/* Client isn't in this thread so let them fall
			 * out of sync until something relevant comes up */
			client.defer_sync = syncNumber;
			continue;
		}
		if (id == origin) {
			/* Client won't increment SYNC since they won't
			 * receive the broadcasted message, so do manually */
			multisend(client, [[common.SYNCHRONIZE, syncNumber]]);
		}
		else if (client.defer_sync) {
			/* First catch them up, then send the new message */
			client.socket.send('[[' + common.SYNCHRONIZE + ',' +
					client.defer_sync + '],' + msg + ']');
		}
		else {
			/* Client is already in sync */
			client.socket.send(payload);
		}
		/* At this point the client must be caught up */
		client.defer_sync = null;
	}
	var now = new Date().getTime();
	backlog.push({when: now, msg: msg, thread: thread_num});
	cleanup_backlog(now);
}

function cleanup_backlog(now) {
	var limit = now - config.BACKLOG_PERIOD;
	/* binary search would be nice */
	while (backlog.length && backlog[0].when < limit) {
		backlog.shift();
		backlogLastDropped++;
	}
}

dispatcher[common.SYNCHRONIZE] = function (msg, client) {
	if (msg.length != 2)
		return false;
	var sync = msg[0], watching = msg[1];
	if (sync.constructor != Number)
		return false;
	if (watching) {
		var post = posts[watching];
		if (post && !post.op)
			client.watching = watching;
		else
			return false;
	}
	if (sync == syncNumber) {
		multisend(client, [[common.SYNCHRONIZE, syncNumber]]);
		client.synced = true;
		return true; /* already synchronized */
	}
	if (sync > syncNumber)
		return false; /* client in the future? */
	if (sync < backlogLastDropped)
		return false; /* client took too long */
	var logs = [];
	for (var i = sync - backlogLastDropped; i < backlog.length; i++) {
		var log = backlog[i];
		if (!watching || log.thread == watching)
			logs.push(log.msg);
	}
	logs.push('[' + common.SYNCHRONIZE + ',' + syncNumber + ']');
	client.socket.send('[' + logs.join() + ']');
	client.synced = true;
	return true;
}

var oneeSama = new common.OneeSama(function (num) {
	var post = posts[num];
	if (post)
		this.callback(common.safe('<a href="'
				+ common.post_url(post, false)
				+ '">&gt;&gt;' + num + '</a>'));
	else
		this.callback('>>' + num);
});
oneeSama.image_view = pix.get_image_view;
oneeSama.dirs = {src_url: config.IMAGE_URL, thumb_url: config.THUMB_URL};

function write_thread_html(reader, response, full_thread) {
	reader.on('thread', function (op_post, omit, image_omit) {
		oneeSama.full = full_thread;
		var first = oneeSama.monomono(op_post);
		first.pop();
		response.write(first.join(''));
		if (omit)
			response.write('\t<span class="omit">' +
				common.abbrev_msg(omit, image_omit) +
				'</span>\n');
	});
	reader.on('post', function (post, has_next) {
		oneeSama.full = full_thread;
		response.write(oneeSama.mono(post));
		if (!has_next)
			response.write('</section><hr>\n');
	});
}

var indexTmpl = Template(fs.readFileSync('index.html', 'UTF-8'),
		{meta: '{{}}'}).expand(config).split(/\$[A-Z]+/);
var notFoundHtml = fs.readFileSync('../www/404.html');

function image_status(status) {
	multisend(this.client, [[common.IMAGE_STATUS, status]]);
}

function set_post_image(post, image, imgnm) {
	post.image = image;
	post.imgnm = imgnm;
	if (post.op)
		posts[post.op].thread.image_count++;
}

var httpHeaders = {'Content-Type': 'text/html; charset=UTF-8',
		'Expires': 'Thu, 01 Jan 1970 00:00:00 GMT, -1',
		'Cache-Control': 'no-cache'};
var server = http.createServer(function(req, resp) {
	if (req.method.toLowerCase() == 'post') {
		var upload = new pix.ImageUpload(clients, allocate_post,
				set_post_image, announce_image, image_status);
		upload.handle_request(req, resp);
		return;
	}
	if (req.url == '/' && render_index(req, resp))
		return;
	m = req.url.match(/^\/(\d+)$/);
	if (m && render_thread(req, resp, m[1]))
		return;
	resp.writeHead(404, httpHeaders);
	resp.end(notFoundHtml);
});

function render_index(req, resp) {
	var yaku = new db.Yakusoku();
	yaku.get_tag();
	yaku.on('begin', function () {
		resp.writeHead(200, httpHeaders);
		resp.write(indexTmpl[0]);
	});
	write_thread_html(yaku, resp, false);
	yaku.on('end', function () {
		resp.write(indexTmpl[1]);
		resp.write('0' /* XXX sync */);
		resp.end(indexTmpl[2]);
		yaku.disconnect();
	});<commit_msg>Serve static content in debug<commit_after>var common = require('./common'),
	config = require('./config'),
	fs = require('fs'),
	io = require('socket.io'),
	http = require('http'),
	pix = require('./pix'),
	db = require('./db'),
	Template = require('./lib/json-template').Template,
	tripcode = require('./tripcode'),
	util = require('util');

var clients = {};
var dispatcher = {};

function multisend(client, msgs) {
	client.socket.send(JSON.stringify(msgs));
}

function broadcast(msg, origin) {
	var thread_num = post.op || post.num;
	++syncNumber;
	msg = JSON.stringify(msg);
	var payload = '[' + msg + ']';
	for (id in clients) {
		var client = clients[id];
		if (!client.synced)
			continue;
		if (client.watching && client.watching != thread_num) {
			/* Client isn't in this thread so let them fall
			 * out of sync until something relevant comes up */
			client.defer_sync = syncNumber;
			continue;
		}
		if (id == origin) {
			/* Client won't increment SYNC since they won't
			 * receive the broadcasted message, so do manually */
			multisend(client, [[common.SYNCHRONIZE, syncNumber]]);
		}
		else if (client.defer_sync) {
			/* First catch them up, then send the new message */
			client.socket.send('[[' + common.SYNCHRONIZE + ',' +
					client.defer_sync + '],' + msg + ']');
		}
		else {
			/* Client is already in sync */
			client.socket.send(payload);
		}
		/* At this point the client must be caught up */
		client.defer_sync = null;
	}
	var now = new Date().getTime();
	backlog.push({when: now, msg: msg, thread: thread_num});
	cleanup_backlog(now);
}

function cleanup_backlog(now) {
	var limit = now - config.BACKLOG_PERIOD;
	/* binary search would be nice */
	while (backlog.length && backlog[0].when < limit) {
		backlog.shift();
		backlogLastDropped++;
	}
}

dispatcher[common.SYNCHRONIZE] = function (msg, client) {
	if (msg.length != 2)
		return false;
	var sync = msg[0], watching = msg[1];
	if (sync.constructor != Number)
		return false;
	if (watching) {
		var post = posts[watching];
		if (post && !post.op)
			client.watching = watching;
		else
			return false;
	}
	if (sync == syncNumber) {
		multisend(client, [[common.SYNCHRONIZE, syncNumber]]);
		client.synced = true;
		return true; /* already synchronized */
	}
	if (sync > syncNumber)
		return false; /* client in the future? */
	if (sync < backlogLastDropped)
		return false; /* client took too long */
	var logs = [];
	for (var i = sync - backlogLastDropped; i < backlog.length; i++) {
		var log = backlog[i];
		if (!watching || log.thread == watching)
			logs.push(log.msg);
	}
	logs.push('[' + common.SYNCHRONIZE + ',' + syncNumber + ']');
	client.socket.send('[' + logs.join() + ']');
	client.synced = true;
	return true;
}

var oneeSama = new common.OneeSama(function (num) {
	var post = posts[num];
	if (post)
		this.callback(common.safe('<a href="'
				+ common.post_url(post, false)
				+ '">&gt;&gt;' + num + '</a>'));
	else
		this.callback('>>' + num);
});
oneeSama.image_view = pix.get_image_view;
oneeSama.dirs = {src_url: config.IMAGE_URL, thumb_url: config.THUMB_URL};

function write_thread_html(reader, response, full_thread) {
	reader.on('thread', function (op_post, omit, image_omit) {
		oneeSama.full = full_thread;
		var first = oneeSama.monomono(op_post);
		first.pop();
		response.write(first.join(''));
		if (omit)
			response.write('\t<span class="omit">' +
				common.abbrev_msg(omit, image_omit) +
				'</span>\n');
	});
	reader.on('post', function (post, has_next) {
		oneeSama.full = full_thread;
		response.write(oneeSama.mono(post));
		if (!has_next)
			response.write('</section><hr>\n');
	});
}

var indexTmpl = Template(fs.readFileSync('index.html', 'UTF-8'),
		{meta: '{{}}'}).expand(config).split(/\$[A-Z]+/);
var notFoundHtml = fs.readFileSync('../www/404.html');

function image_status(status) {
	multisend(this.client, [[common.IMAGE_STATUS, status]]);
}

function set_post_image(post, image, imgnm) {
	post.image = image;
	post.imgnm = imgnm;
	if (post.op)
		posts[post.op].thread.image_count++;
}

var httpHeaders = {'Content-Type': 'text/html; charset=UTF-8',
		'Expires': 'Thu, 01 Jan 1970 00:00:00 GMT, -1',
		'Cache-Control': 'no-cache'};
var server = http.createServer(function(req, resp) {
	if (req.method.toLowerCase() == 'post') {
		var upload = new pix.ImageUpload(clients, allocate_post,
				set_post_image, announce_image, image_status);
		upload.handle_request(req, resp);
		return;
	}
	if (req.url == '/' && render_index(req, resp))
		return;
	m = req.url.match(/^\/(\d+)$/);
	if (m && render_thread(req, resp, m[1]))
		return;
	if (config.DEBUG) {
		/* Highly insecure! Abunai! */
		var path = '../www/' + req.url.replace(/\.\./g, '');
		fs.readFile(path, function (err, buf) {
			if (err) {
				resp.writeHead(404, httpHeaders);
				resp.end(notFoundHtml);
			}
			else {
				resp.writeHead(200, {});
				resp.end(buf);
			}
		});
		return;
	}
	resp.writeHead(404, httpHeaders);
	resp.end(notFoundHtml);
});

function render_index(req, resp) {
	var yaku = new db.Yakusoku();
	yaku.get_tag();
	yaku.on('begin', function () {
		resp.writeHead(200, httpHeaders);
		resp.write(indexTmpl[0]);
	});
	write_thread_html(yaku, resp, false);
	yaku.on('end', function () {
		resp.write(indexTmpl[1]);
		resp.write('0' /* XXX sync */);
		resp.end(indexTmpl[2]);
		yaku.disconnect();
	});
{'fraction_non_alphanumeric': 0.18321054672353626, 'fraction_numerical': 0.006979449398991857, 'mean_word_length': 3.934002869440459, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 3, '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': '6564889', 'n_tokens_mistral': 4097, 'n_tokens_neox': 3762, 'n_words': 836}