<commit_before>/* NOTE: This file is processed by server/state.js
         and served by server/server.js (to auth'd users only) */

yepnope(mediaURL + 'css/mod-v1.css');

var $selectButton, $controls;
window.loggedInUser = IDENT.email;
window.x_csrf = IDENT.csrf;

function show_toolbox() {
	var specs = [
		{name: 'Lewd', kind: 7},
		{name: 'Porn', kind: 8},
		{name: 'Delete', kind: 9},
		{name: 'Lock', kind: 11},
	];
	if (IDENT.auth == 'Admin')
		specs.push({name: 'Panel', kind: 'panel'});
	var $toolbox = $('<div/>', {id: 'toolbox', "class": 'mod'});

	$selectButton = $('<input />', {
		type: 'button', val: 'Select',
		click: function (e) { toggle_multi_selecting(); },
	});
	$toolbox.append($selectButton, ' ');

	$controls = $('<span></span>').hide();
	_.each(specs, function (spec) {
		$controls.append($('<input />', {
			type: 'button',
			val: spec.name,
			data: {kind: spec.kind},
		}), ' ');
	});
	$controls.on('click', 'input[type=button]', tool_action);

	_.each(delayNames, function (when, i) {
		var id = 'delay-' + when;
		var $label = $('<label/>', {text: when, 'for': id});
		var $radio = $('<input/>', {
			id: id, type: 'radio', val: when, name: 'delay',
		});
		if (i == 0)
			$radio.prop('checked', true);
		$controls.append($radio, $label, ' ');
	});

	$toolbox.append($controls).insertBefore(THREAD ? 'hr:last' : $ceiling);
}

function tool_action(event) {
	var ids = [];
	var $sel = $('.selected');
	$sel.each(function () {
		var id = extract_num(parent_post($(this)));
		if (id)
			ids.push(id);
	});
	var $button = $(this);
	var kind = $button.data('kind');
	if (kind == 'panel')
		return toggle_panel();

	/* On a thread page there's only one thread to lock, so... */
	if (kind == 11 && THREAD && !ids.length)
		ids = [THREAD];

	if (ids.length) {
		var when = $('input:radio[name=delay]:checked').val();
		ids.unshift(parseInt(kind, 10), {when: when});
		send(ids);
		$sel.removeClass('selected');
	}
	else {
		var orig = $button.val();
		var caption = _.bind($button.val, $button);
		caption('Nope.');
		if (orig != 'Nope.')
			_.delay(caption, 2000, orig);
	}
}

readOnly.push('graveyard');
menuOptions.unshift('Select');

var multiSelecting = false;

function toggle_multi_selecting($post) {
	var oldTarget;
	if ($post) {
		oldTarget = lockTarget;
		set_lock_target(extract_num($post));
	}
	with_dom(function () {
	multiSelecting = !multiSelecting;
	if (multiSelecting) {
		$('body').addClass('multi-select');
		make_selection_handle().prependTo('article');
		make_selection_handle().prependTo('section > header');
		if ($post)
			select_post($post);
		$controls.show();
		$selectButton.val('X');
	}
	else {
		$('body').removeClass('multi-select');
		$('.select-handle').remove();
		$controls.hide();
		$selectButton.val('Select');
	}
	});
	if ($post)
		set_lock_target(oldTarget);
}

menuHandlers.Select = toggle_multi_selecting;

function enable_multi_selecting() {
	if (!multiSelecting)
		toggle_multi_selecting();
}

function select_post($post) {
	$post.find('.select-handle:first').addClass('selected');
}

function make_selection_handle() {
	return $('<a class="select-handle" href="#"/>');
}

window.fun = function () {
	send([33, THREAD]);
};

override(ComposerView.prototype, 'make_alloc_request',
			function (orig, text, img) {
	var msg = orig.call(this, text, img);
	if ($('#authname').is(':checked'))
		msg.auth = IDENT.auth;
	return msg;
});

$DOC.on('click', '.select-handle', function (event) {
	event.preventDefault();
	$(event.target).toggleClass('selected');
});

with_dom(function () {
	$('h1').text('Moderation - ' + $('h1').text());
	var $authname = $('<input>', {type: 'checkbox', id: 'authname'});
	var $label = $('<label/>', {text: ' '+IDENT.auth}).prepend($authname);
	$name.after(' ', $label);

	/* really ought to be done with model observation! */
	$authname.change(function () {
		if (postForm)
			postForm.propagate_ident();
	});
	oneeSama.hook('fillMyName', function ($el) {
		var auth = $('#authname').is(':checked');
		$el.toggleClass(IDENT.auth.toLowerCase(), auth);
		if (auth)
			$el.append(' ## ' + IDENT.auth)
	});

	oneeSama.hook('afterInsert', function (target) {
		if (multiSelecting)
			make_selection_handle().prependTo(target);
	});
	show_toolbox();
});

var Address = Backbone.Model.extend({
	idAttribute: 'ip',
	defaults: {
		count: 0,
	},
});

var AddressView = Backbone.View.extend({
	className: 'mod address',
	events: {
		'keydown .name': 'entered_name',
		'click .sel-all': 'select_all',
	},

	initialize: function () {
		var $el = this.$el;
		$('<span/>', {"class": 'ip'}).appendTo($el);
		if (CurThread) {
			$el.append(' &nbsp; ', $('<input/>', {
				"class": 'sel-all',
				type: 'button',
				val: 'Sel All'
			}));
		}
		$el.append(
			'<br>',
			$('<input>', {"class": 'name', placeholder: 'Name'})
		);
		this.listenTo(this.model, 'change', this.render);
	},

	render: function () {
		var attrs = this.model.attributes;
		if (attrs.shallow) {
			this.$('.ip').text('Loading...');
			return this;
		}
		this.$('.ip').text(attrs.ip);
		var $name = this.$('.name');
		if (!this.focusedName) {
			_.defer(function () {
				$name.focus().prop({
					selectionStart: 0,
					selectionEnd: $name.val().length,
				});
			});
			this.focusedName = true;
		}
		if (attrs.name != $name.val()) {
			$name.val(attrs.name);
		}
		return this;
	},

	entered_name: function (event) {
		if (event.which != 13)
			return;
		event.preventDefault();
		var name = this.$('.name').val().trim();
		send([102, this.model.id, name]);
		this.remove();
	},

	remove: function () {
		this.trigger('preremove');
		Backbone.View.prototype.remove.call(this);
	},

	select_all: function () {
		var models = CurThread.where({ip: this.model.get('ip')});
		if (!models.length)
			return;
		enable_multi_selecting();
		$.each(models, function () {
			select_post($('#' + this.id));
		});
	},
});

// basically just a link
var AddrView = Backbone.View.extend({
	tagName: 'a',
	className: 'mod addr',

	events: {
		click: 'toggle_expansion',
	},

	initialize: function () {
		this.$el.attr('href', '#');
		this.listenTo(this.model, 'change:name', this.render);
	},

	render: function () {
		var attrs = this.model.attributes;
		var text = ip_mnemonic(attrs.ip);
		if (attrs.name)
			text += ' "' + attrs.name + '"';
		this.$el.attr('title', attrs.ip).text(text);
		return this;
	},

	toggle_expansion: function (event) {
		if (event.target !== this.el)
			return;
		event.preventDefault();

		if (this.expansion)
			return this.expansion.remove();

		this.expansion = new AddressView({model: this.model});
		this.$el.after(this.expansion.render().el);
		this.listenTo(this.expansion, 'preremove',
				this.expansion_removed);

		if (this.model.get('shallow'))
			send([101, this.model.id]);
	},

	remove: function () {
		if (this.expansion)
			this.expansion.remove();
		Backbone.View.prototype.remove.call(this);
	},

	expansion_removed: function () {
		this.expansion = null;
	},
});

var Addresses = Backbone.Collection.extend({
	model: Address,
	comparator: function (a) { return ip_mnemonic(a.ip); },
});

window.addrs = new Addresses;

function hook_up_address($post) {
	var $a = $post.find('a.mod.addr');
	if (!$a.length)
		return;
	var ip = $a.prop('title') || $a.text();
	var givenName;
	var m = $a.text().match(/^([\w'.]+) "(.+)"$/);
	if (m) {
		if (is_valid_ip(m[1]))
			ip = m[1];
		givenName = m[2];
	}
	if (!is_valid_ip(ip))
		return;

	/* Activate this address link */
	var address = window.addrs.get(ip);
	if (!address) {
		address = new Address({ip: ip});
		address.set(givenName ? {name: givenName} : {shallow: true});
		window.addrs.add(address);
	}
	var view = new AddrView({model: address, el: $a[0]});
	if (address.get('name'))
		view.render();

	/* Augment post with IP */
	if (CurThread) {
		var post = CurThread.get($post.attr('id'));
		if (post && !post.has('ip'))
			post.set('ip', ip);
	}
}
oneeSama.hook('afterInsert', hook_up_address);

with_dom(function () {
	$('article').each(function () {
		hook_up_address($(this));
	});
});

window.adminState = new Backbone.Model({
});

var PanelView = Backbone.View.extend({
	id: 'panel',

	initialize: function () {
		this.listenTo(this.model, 'change:visible', this.renderVis);
		this.listenTo(window.addrs, 'add change:count reset',
				this.renderIPs);
		this.listenTo(this.model, 'change:memoryUsage',
				this.renderMemory);
		this.listenTo(this.model, 'change:addrs change:bans',
				this.renderCounts);<commit_msg>Install addrs on OPs too<commit_after>/* NOTE: This file is processed by server/state.js
         and served by server/server.js (to auth'd users only) */

yepnope(mediaURL + 'css/mod-v1.css');

var $selectButton, $controls;
window.loggedInUser = IDENT.email;
window.x_csrf = IDENT.csrf;

function show_toolbox() {
	var specs = [
		{name: 'Lewd', kind: 7},
		{name: 'Porn', kind: 8},
		{name: 'Delete', kind: 9},
		{name: 'Lock', kind: 11},
	];
	if (IDENT.auth == 'Admin')
		specs.push({name: 'Panel', kind: 'panel'});
	var $toolbox = $('<div/>', {id: 'toolbox', "class": 'mod'});

	$selectButton = $('<input />', {
		type: 'button', val: 'Select',
		click: function (e) { toggle_multi_selecting(); },
	});
	$toolbox.append($selectButton, ' ');

	$controls = $('<span></span>').hide();
	_.each(specs, function (spec) {
		$controls.append($('<input />', {
			type: 'button',
			val: spec.name,
			data: {kind: spec.kind},
		}), ' ');
	});
	$controls.on('click', 'input[type=button]', tool_action);

	_.each(delayNames, function (when, i) {
		var id = 'delay-' + when;
		var $label = $('<label/>', {text: when, 'for': id});
		var $radio = $('<input/>', {
			id: id, type: 'radio', val: when, name: 'delay',
		});
		if (i == 0)
			$radio.prop('checked', true);
		$controls.append($radio, $label, ' ');
	});

	$toolbox.append($controls).insertBefore(THREAD ? 'hr:last' : $ceiling);
}

function tool_action(event) {
	var ids = [];
	var $sel = $('.selected');
	$sel.each(function () {
		var id = extract_num(parent_post($(this)));
		if (id)
			ids.push(id);
	});
	var $button = $(this);
	var kind = $button.data('kind');
	if (kind == 'panel')
		return toggle_panel();

	/* On a thread page there's only one thread to lock, so... */
	if (kind == 11 && THREAD && !ids.length)
		ids = [THREAD];

	if (ids.length) {
		var when = $('input:radio[name=delay]:checked').val();
		ids.unshift(parseInt(kind, 10), {when: when});
		send(ids);
		$sel.removeClass('selected');
	}
	else {
		var orig = $button.val();
		var caption = _.bind($button.val, $button);
		caption('Nope.');
		if (orig != 'Nope.')
			_.delay(caption, 2000, orig);
	}
}

readOnly.push('graveyard');
menuOptions.unshift('Select');

var multiSelecting = false;

function toggle_multi_selecting($post) {
	var oldTarget;
	if ($post) {
		oldTarget = lockTarget;
		set_lock_target(extract_num($post));
	}
	with_dom(function () {
	multiSelecting = !multiSelecting;
	if (multiSelecting) {
		$('body').addClass('multi-select');
		make_selection_handle().prependTo('article');
		make_selection_handle().prependTo('section > header');
		if ($post)
			select_post($post);
		$controls.show();
		$selectButton.val('X');
	}
	else {
		$('body').removeClass('multi-select');
		$('.select-handle').remove();
		$controls.hide();
		$selectButton.val('Select');
	}
	});
	if ($post)
		set_lock_target(oldTarget);
}

menuHandlers.Select = toggle_multi_selecting;

function enable_multi_selecting() {
	if (!multiSelecting)
		toggle_multi_selecting();
}

function select_post($post) {
	$post.find('.select-handle:first').addClass('selected');
}

function make_selection_handle() {
	return $('<a class="select-handle" href="#"/>');
}

window.fun = function () {
	send([33, THREAD]);
};

override(ComposerView.prototype, 'make_alloc_request',
			function (orig, text, img) {
	var msg = orig.call(this, text, img);
	if ($('#authname').is(':checked'))
		msg.auth = IDENT.auth;
	return msg;
});

$DOC.on('click', '.select-handle', function (event) {
	event.preventDefault();
	$(event.target).toggleClass('selected');
});

with_dom(function () {
	$('h1').text('Moderation - ' + $('h1').text());
	var $authname = $('<input>', {type: 'checkbox', id: 'authname'});
	var $label = $('<label/>', {text: ' '+IDENT.auth}).prepend($authname);
	$name.after(' ', $label);

	/* really ought to be done with model observation! */
	$authname.change(function () {
		if (postForm)
			postForm.propagate_ident();
	});
	oneeSama.hook('fillMyName', function ($el) {
		var auth = $('#authname').is(':checked');
		$el.toggleClass(IDENT.auth.toLowerCase(), auth);
		if (auth)
			$el.append(' ## ' + IDENT.auth)
	});

	oneeSama.hook('afterInsert', function (target) {
		if (multiSelecting)
			make_selection_handle().prependTo(target);
	});
	show_toolbox();
});

var Address = Backbone.Model.extend({
	idAttribute: 'ip',
	defaults: {
		count: 0,
	},
});

var AddressView = Backbone.View.extend({
	className: 'mod address',
	events: {
		'keydown .name': 'entered_name',
		'click .sel-all': 'select_all',
	},

	initialize: function () {
		var $el = this.$el;
		$('<span/>', {"class": 'ip'}).appendTo($el);
		if (CurThread) {
			$el.append(' &nbsp; ', $('<input/>', {
				"class": 'sel-all',
				type: 'button',
				val: 'Sel All'
			}));
		}
		$el.append(
			'<br>',
			$('<input>', {"class": 'name', placeholder: 'Name'})
		);
		this.listenTo(this.model, 'change', this.render);
	},

	render: function () {
		var attrs = this.model.attributes;
		if (attrs.shallow) {
			this.$('.ip').text('Loading...');
			return this;
		}
		this.$('.ip').text(attrs.ip);
		var $name = this.$('.name');
		if (!this.focusedName) {
			_.defer(function () {
				$name.focus().prop({
					selectionStart: 0,
					selectionEnd: $name.val().length,
				});
			});
			this.focusedName = true;
		}
		if (attrs.name != $name.val()) {
			$name.val(attrs.name);
		}
		return this;
	},

	entered_name: function (event) {
		if (event.which != 13)
			return;
		event.preventDefault();
		var name = this.$('.name').val().trim();
		send([102, this.model.id, name]);
		this.remove();
	},

	remove: function () {
		this.trigger('preremove');
		Backbone.View.prototype.remove.call(this);
	},

	select_all: function () {
		var models = CurThread.where({ip: this.model.get('ip')});
		if (!models.length)
			return;
		enable_multi_selecting();
		$.each(models, function () {
			select_post($('#' + this.id));
		});
	},
});

// basically just a link
var AddrView = Backbone.View.extend({
	tagName: 'a',
	className: 'mod addr',

	events: {
		click: 'toggle_expansion',
	},

	initialize: function () {
		this.$el.attr('href', '#');
		this.listenTo(this.model, 'change:name', this.render);
	},

	render: function () {
		var attrs = this.model.attributes;
		var text = ip_mnemonic(attrs.ip);
		if (attrs.name)
			text += ' "' + attrs.name + '"';
		this.$el.attr('title', attrs.ip).text(text);
		return this;
	},

	toggle_expansion: function (event) {
		if (event.target !== this.el)
			return;
		event.preventDefault();

		if (this.expansion)
			return this.expansion.remove();

		this.expansion = new AddressView({model: this.model});
		this.$el.after(this.expansion.render().el);
		this.listenTo(this.expansion, 'preremove',
				this.expansion_removed);

		if (this.model.get('shallow'))
			send([101, this.model.id]);
	},

	remove: function () {
		if (this.expansion)
			this.expansion.remove();
		Backbone.View.prototype.remove.call(this);
	},

	expansion_removed: function () {
		this.expansion = null;
	},
});

var Addresses = Backbone.Collection.extend({
	model: Address,
	comparator: function (a) { return ip_mnemonic(a.ip); },
});

window.addrs = new Addresses;

function hook_up_address($post) {
	var $a = $post.find('a.mod.addr');
	if (!$a.length)
		return;
	var ip = $a.prop('title') || $a.text();
	var givenName;
	var m = $a.text().match(/^([\w'.]+) "(.+)"$/);
	if (m) {
		if (is_valid_ip(m[1]))
			ip = m[1];
		givenName = m[2];
	}
	if (!is_valid_ip(ip))
		return;

	/* Activate this address link */
	var address = window.addrs.get(ip);
	if (!address) {
		address = new Address({ip: ip});
		address.set(givenName ? {name: givenName} : {shallow: true});
		window.addrs.add(address);
	}
	var view = new AddrView({model: address, el: $a[0]});
	if (address.get('name'))
		view.render();

	/* Augment post with IP */
	if (CurThread) {
		var post = CurThread.get($post.attr('id'));
		if (post && !post.has('ip'))
			post.set('ip', ip);
	}
}
oneeSama.hook('afterInsert', hook_up_address);

with_dom(function () {
	$('article, section').each(function () {
		hook_up_address($(this));
	});
});

window.adminState = new Backbone.Model({
});

var PanelView = Backbone.View.extend({
	id: 'panel',

	initialize: function () {
		this.listenTo(this.model, 'change:visible', this.renderVis);
		this.listenTo(window.addrs, 'add change:count reset',
				this.renderIPs);
		this.listenTo(this.model, 'change:memoryUsage',
				this.renderMemory);
		this.listenTo(this.model, 'change:addrs change:bans',
				this.renderCounts);
{'fraction_non_alphanumeric': 0.215888954518606, 'fraction_numerical': 0.003898405197873597, 'mean_word_length': 4.081332533013205, 'pattern_counts': {'":': 8, '<': 31, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '2305270', 'n_tokens_mistral': 6751, 'n_tokens_neox': 6220, 'n_words': 1238}