From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

// <nowiki>

/*

	Adds buttons for admins to respond to {{Copyvio-revdel}} requests;

	useful with [[User:Enterprisey/url-select-revdel]].



	Full documentation is available at: [[User:The Earwig/revdel-responder]].



	Install by adding:

		importScript('User:The Earwig/revdel-responder.js'); // [[User:The Earwig/revdel-responder.js]]

	to your [[Special:MyPage/common.js]].

*/



function revdelResponder(mboxes) {

	this.mboxes = mboxes;

	this.ui = [];

	this.document = null;

	this.templates = null;

	this.etag = null;

}



revdelResponder.SCRIPT_NAME = 'User:The Earwig/revdel-responder';

revdelResponder.MBOX_SELECTOR = '.box-Copyvio-revdel';



revdelResponder.prototype.getUrl = function() {

	return mw.util.getUrl(revdelResponder.SCRIPT_NAME);

};



revdelResponder.prototype.notifyDisabled = function() {

	mw.notify($('<span>')

		.append('You have the ')

		.append($('<a>', {href: this.getUrl()}).text('revdel-responder'))

		.append(' script loaded, but you are not an administrator, ' +

				'so it cannot be used.'));

};



revdelResponder.prototype.parseContent = function(raw) {

	const parser = new DOMParser();

	this.document = parser.parseFromString(raw, 'text/html');

	const mboxes = this.document.querySelectorAll(revdelResponder.MBOX_SELECTOR);



	const cleanWikitext = function(wt) {

		// This can be more robust

		return wt.replace(/<!--.*?-->/g, '').trim();

	};



	this.templates = Array.from(mboxes).map(function(e) {

		e = e.closest("[about]");

		if (

			e === null ||

			e.getAttribute("typeof") !== "mw:Transclusion" ||

			e.dataset.mw === undefined

		) {

			return null;

		}

		try {

			const info = JSON.parse(e.dataset.mw);

			const tmpl = info.parts0].template;

			Object.keys(tmpl.params).forEach(function(k) {

				tmpl.paramsk = cleanWikitext(tmpl.paramsk].wt);

			});

			return tmpl.params;

		} catch (err) {

			return null;

		}

	});

};



revdelResponder.prototype.withParsedContent = function(callback) {

	const url = '/api/rest_v1/page/html/' +

		mw.util.rawurlencode(mw.config.get('wgPageName')) + '/' +

		mw.util.rawurlencode(mw.config.get('wgRevisionId')) + '?stash=true';

	const raw = $.ajax({

		url: url,

		context: this,

		dataType: 'html',

	}).done(function(raw, status, xhr) {

		this.etag = xhr.getResponseHeader('ETag');

		this.parseContent(raw);

		callback();

	}).fail(function(xhr) {

		mw.log.error('Error while parsing page content:', xhr);

		mw.notify($('<span>')

			.append('Sorry! ')

			.append($('<a>', {href: this.getUrl()}).text('revdel-responder'))

			.append(' failed to parse the page content. ' +

					'Check the console for more info.'));

	});

};



revdelResponder.prototype.getRevIds = function(i) {

	const tmpl = this.templatesi];

	if (!tmpl) {

		return [];

	}

	const ranges = [];

	let idx = 1, start = tmpl.start || tmpl.start1, end = tmpl.end || tmpl.end1;

	while (start) {

		ranges.push(end ? start, end : start]);

		idx++;

		start = tmpl'start' + idx];

		end = tmpl'end' + idx];

	}

	return ranges;

};



revdelResponder.prototype.getSourceUrl = function(i) {

	const tmpl = this.templatesi];

	if (!tmpl) {

		return null;

	}

	return tmpl.url;

};



revdelResponder.prototype.getSourceUrls = function(i) {

	const tmpl = this.templatesi];

	if (!tmpl) {

		return null;

	}

	const maxLen = 256;

	const urls = [];

	let idx = 1, url = tmpl.url, curLen = -2;

	while (url && curLen + url.length + 2 <= maxLen) {

		urls.push(url);

		idx++;

		curLen += url.length + 2;

		url = tmpl'url' + idx];

	}

	return urls.join(', ');

};



revdelResponder.prototype.doHistory = function(i) {

	const revIds = this.getRevIds(i).map(function(r) {

		return r.length === 1 ? r0 : r0 + '..' + r1];

	}).join('|');

	const url = mw.config.get('wgScript') + '?action=history&title=' +

		mw.util.wikiUrlencode(mw.config.get('wgPageName')) + '&revdel_select=' +

		mw.util.rawurlencode(revIds) + '&revdel_urls=' +

		mw.util.rawurlencode(this.getSourceUrls(i));

	window.open(url, '_blank');

};



revdelResponder.prototype.doCompare = function(i) {

	const revIds = this.getRevIds(i);

	const revId = (revIds.length > 0) ? revIds0][0 : mw.config.get('wgRevisionId');

	const url = 'https://copyvios.toolforge.org/?' + $.param({

		lang: mw.config.get('wgContentLanguage'),

		project: mw.config.get('wgSiteName').toLowerCase(),

		title: mw.config.get('wgPageName'),

		oldid: revId,

		action: 'compare',

		url: this.getSourceUrl(i) || '',

	});

	window.open(url, '_blank');

};



revdelResponder.prototype.removeTemplate = function(i) {

	let mbox = this.document.querySelectorAll(revdelResponder.MBOX_SELECTOR)[i];

	if (mbox !== undefined) {

		mbox = mbox.closest("[about]");

	}

	if (!mbox) {

		mw.notify('Error: Couldn\'t find the template in the page source?');

		return;

	}

	// Remove by transclusion ID, otherwise we might leave the category behind

	const about = mbox.getAttribute('about');

	this.document.querySelectorAll('[about="' + about + '"]').forEach(function(el) {

		// Need to remove a single newline if immediately following this element

		const next = el.nextSibling;

		if (next !== null && next.nodeType === Node.TEXT_NODE &&

			next.textContent.startsWith('\n')) {

			next.textContent = next.textContent.substr(1);

		}

		el.remove();

	});

};



revdelResponder.prototype.transformWikicode = function(callback) {

	const url = '/api/rest_v1/transform/html/to/wikitext/' +

		mw.util.rawurlencode(mw.config.get('wgPageName')) + '/' +

		mw.util.rawurlencode(mw.config.get('wgRevisionId'));

	const payload = this.document.documentElement.outerHTML;

	const raw = $.ajax({

		url: url,

		context: this,

		method: 'POST',

		data: {html: payload},

		dataType: 'html',

		headers: {'If-Match': this.etag},

	}).done(callback)

	.fail(function(xhr) {

		mw.log.error('Error while transforming wikicode:', xhr);

		mw.notify('Error: Couldn\'t transform wikicode. ' +

			'Check the console for more info.');

	});

};



revdelResponder.prototype.savePage = function(text, summary, callback) {

	new mw.Api().postWithEditToken({

		action: 'edit',

		title: mw.config.get('wgPageName'),

		text: text,

		summary: summary + ' ([[' + revdelResponder.SCRIPT_NAME + '|RR]])',

		formatversion: '2',

		baserevid: mw.config.get('wgRevisionId'),

		nocreate: true,

		assert: 'user',

	}).done(callback)

	.fail(function(code, result) {

		const errcode = result.error && result.error.code;

		const errinfo = result.error && result.error.info || 'Check the console for more info.';

		mw.log.error('Error while saving:', result);

		mw.notify('Error: Couldn\'t save the page: ' + errinfo);

	});

};



revdelResponder.prototype.indicateRefresh = function(i) {

	const ui = this.uii];

	ui.buttons.forEach(function(button) {

		button.$element.remove();

	});

	ui.elem.append($('<span>', {addClass: 'revdel-responder-status'}).text('Page saved!'));

	ui.elem.append(new OO.ui.ButtonWidget({

		label: 'Refresh',

		icon: 'reload',

		title: 'Reload the page',

	}).on('click', function() {

		window.location.reload();

	}).$element);

};



revdelResponder.prototype.transformAndSave = function(i, summary) {

	this.transformWikicode(function(text) {

		this.savePage(text, summary, this.indicateRefresh.bind(this, i));

	});

};



revdelResponder.prototype.doCompleteReal = function(i) {

	this.removeTemplate(i);

	this.transformAndSave(i, 'Copyvio revdel completed');

};



revdelResponder.prototype.doWarnComplete = function(i) {

	const that = this;

	const prompt = 'The requested revisions have not been deleted. Still remove the template?';

	OO.ui.confirm(prompt).done(function(confirmed) {

		if (confirmed) {

			that.doCompleteReal(i);

		} else {

			that.enableInterface();

		}

	});

};



revdelResponder.prototype.doComplete = function(i) {

	this.disableInterface();



	var newest = null, oldest = null;

	this.getRevIds(i).forEach(function(revs) {

		revs.forEach(function(revId) {

			if (newest === null || revId > newest) {

				newest = revId;

			}

			if (oldest === null || revId < oldest) {

				oldest = revId;

			}

		});

	});



	const that = this;

	const api = new mw.Api();

	const params = {

		action: 'query',

		prop: 'revisions',

		pageids: mw.config.get('wgArticleId'),

		rvprop: 'sha1',

		rvdir: 'older',

		rvlimit: 500,

		formatversion: '2',

	};

	if (newest !== null && oldest !== null) {

		params.rvstartid = newest;

		params.rvendid = oldest;

	}

	api.get(params).done(function(result) {

		const page = result && result.query && result.query.pages && result.query.pages0];

		const revs = page && page.revisions || [];

		if (revs.length === 0 || revs.some(function(rev) { return rev.sha1hidden; })) {

			that.doCompleteReal(i);

		} else {

			that.doWarnComplete(i);

		}

	}).fail(function(xhr) {

		mw.log.error('Error while verifying redacted revisions:', xhr);

		mw.notify('Error: Couldn\'t verify redacted revisions. ' +

			'Check the console for more info.');

	});

};



revdelResponder.prototype.doDeclineSave = function(i, reason) {

	const ui = this.uii];

	this.disableInterface();

	this.removeTemplate(i);

	var summary = 'Copyvio revdel declined';

	if (reason) summary += ': ' + reason;

	this.transformAndSave(i, summary);

};



revdelResponder.prototype.doDecline = function(i) {

	const that = this;

	OO.ui.prompt('Enter a decline reason:', {

		size: 'medium',

		textInput: {placeholder: 'Reason'},

	}).done(function(reason) {

		if (reason !== null) {

			that.doDeclineSave(i, reason);

		}

	});

};



revdelResponder.prototype.doDelete = function(i) {

	const reason = '[[WP:CSD#G12|G12]]: Unambiguous [[WP:CV|copyright infringement]]';

	const url = mw.config.get('wgScript') + '?action=delete&title=' +

		mw.util.wikiUrlencode(mw.config.get('wgPageName')) + '&wpDeleteReasonList=' +

		mw.util.rawurlencode(reason) + '&wpReason=' +

		mw.util.rawurlencode(this.getSourceUrls(i));

	window.location.href = url;

};



revdelResponder.prototype.setupInterface = function() {

	const ui = $('<div>', {addClass: 'revdel-responder-ui'})

		.append($('<i>').append($('<a>', {href: this.getUrl()}).text('RR')).append(': '));

	ui.append($('<span>', {

		addClass: 'revdel-responder-loading revdel-responder-status',

	}).text('Loading...'));

	return {

		elem: ui,

		buttons: null,

	};

};



revdelResponder.prototype.disableInterface = function() {

	this.ui.forEach(function(ui) {

		ui.buttons.forEach(function(button) {

			button.setDisabled(true);

		});

	});

};



revdelResponder.prototype.enableInterface = function() {

	this.ui.forEach(function(ui) {

		ui.buttons.forEach(function(button) {

			button.setDisabled(false);

		});

	});

};



revdelResponder.prototype.buildInterface = function(ui, i) {

	ui.elem.find('.revdel-responder-loading').remove();

	ui.buttons = 

		new OO.ui.ButtonWidget({

			label: 'History',

			icon: 'history',

			title: 'View page history, with revisions highlighted',

		}).on('click', this.doHistory.bind(this, i)),

		new OO.ui.ButtonWidget({

			label: 'Compare',

			icon: 'search',

			title: 'Compare oldest revision with first source URL using Earwig\'s Copyvio Detector',

		}).on('click', this.doCompare.bind(this, i)),

		new OO.ui.ButtonWidget({

			label: 'Complete',

			flags: 'progressive'],

			icon: 'check',

			title: 'Remove the template after completing the revdel request',

		}).on('click', this.doComplete.bind(this, i)),

		new OO.ui.ButtonWidget({

			label: 'Decline',

			flags: 'destructive'],

			icon: 'cancel',

			title: 'Decline the revdel request',

		}).on('click', this.doDecline.bind(this, i)),

		new OO.ui.ButtonWidget({

			label: 'Delete',

			flags: 'destructive'],

			icon: 'trash',

			title: 'Delete the page',

		}).on('click', this.doDelete.bind(this, i)),

	];

	ui.buttons.forEach(function(button) {

		ui.elem.append(button.$element);

	})

};



revdelResponder.prototype.render = function() {

	const that = this;

	mw.util.addCSS(

		'.revdel-responder-ui { min-height: 32px; }' +

		'.revdel-responder-ui > * { vertical-align: middle; }' +

		'.revdel-responder-status { font-style: italic; margin-right: 1em; }'

	);

	this.mboxes.find('.mbox-text').each(function(i, e) {

		const ui = that.setupInterface();

		that.ui.push(ui);

		$(e).append(ui.elem);

	});



	mw.loader.using([

		'mediawiki.api',

		'oojs-ui-core',

		'oojs-ui.styles.icons-content',

		'oojs-ui.styles.icons-interactions',

		'oojs-ui.styles.icons-moderation',

		'oojs-ui-windows',

	], function() {

		that.withParsedContent(function() {

			that.ui.forEach(function(e, i) {

				that.buildInterface(e, i);

			});

		});

	});

};



revdelResponder.prototype.setupHistory = function(urls) {

	$('#mw-history-revisionactions').append($('<input>', {

		type: 'hidden',

		name: 'wpRevDeleteReasonList',

		value: '[[WP:RD1|RD1]]: Violations of [[Wikipedia:Copyright violations|copyright policy]]',

	})).append($('<input>', {

		type: 'hidden',

		name: 'wpReason',

		value: urls,

	})).append($('<input>', {

		type: 'hidden',

		name: 'wpHidePrimary',

		value: '1',

	}));

};



revdelResponder.prototype.setupRevdel = function(hidePrimary) {

	$('input[name="wpHidePrimary"]').filter(function(i, e) {

		return $(e).prop('value') === hidePrimary;

	}).prop('checked', true);

};



$.when(mw.loader.using('mediawiki.util'), $.ready).then(function() {

	if (mw.config.get('wgAction') === 'view') {

		if (mw.util.getParamValue('action') === 'revisiondelete') {

			const hidePrimary = mw.util.getParamValue('wpHidePrimary');

			if (hidePrimary !== null) {

				new revdelResponder().setupRevdel(hidePrimary);

			}

			return;

		}

		if (mw.config.get('wgRevisionId') !== mw.config.get('wgCurRevisionId')) {

			return;

		}

		const mboxes = $(revdelResponder.MBOX_SELECTOR);

		if (mboxes.length > 0) {

			const rr = new revdelResponder(mboxes);

			const groups = mw.config.get('wgUserGroups');

			if (groups === null || !groups.includes('sysop')) {

				rr.notifyDisabled();

				return;

			}

			rr.render();

		}

	} else if (mw.config.get('wgAction') === 'history') {

		const urls = mw.util.getParamValue('revdel_urls');

		if (urls !== null) {

			new revdelResponder().setupHistory(urls);

		}

	}

});



// </nowiki>
From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

// <nowiki>

/*

	Adds buttons for admins to respond to {{Copyvio-revdel}} requests;

	useful with [[User:Enterprisey/url-select-revdel]].



	Full documentation is available at: [[User:The Earwig/revdel-responder]].



	Install by adding:

		importScript('User:The Earwig/revdel-responder.js'); // [[User:The Earwig/revdel-responder.js]]

	to your [[Special:MyPage/common.js]].

*/



function revdelResponder(mboxes) {

	this.mboxes = mboxes;

	this.ui = [];

	this.document = null;

	this.templates = null;

	this.etag = null;

}



revdelResponder.SCRIPT_NAME = 'User:The Earwig/revdel-responder';

revdelResponder.MBOX_SELECTOR = '.box-Copyvio-revdel';



revdelResponder.prototype.getUrl = function() {

	return mw.util.getUrl(revdelResponder.SCRIPT_NAME);

};



revdelResponder.prototype.notifyDisabled = function() {

	mw.notify($('<span>')

		.append('You have the ')

		.append($('<a>', {href: this.getUrl()}).text('revdel-responder'))

		.append(' script loaded, but you are not an administrator, ' +

				'so it cannot be used.'));

};



revdelResponder.prototype.parseContent = function(raw) {

	const parser = new DOMParser();

	this.document = parser.parseFromString(raw, 'text/html');

	const mboxes = this.document.querySelectorAll(revdelResponder.MBOX_SELECTOR);



	const cleanWikitext = function(wt) {

		// This can be more robust

		return wt.replace(/<!--.*?-->/g, '').trim();

	};



	this.templates = Array.from(mboxes).map(function(e) {

		e = e.closest("[about]");

		if (

			e === null ||

			e.getAttribute("typeof") !== "mw:Transclusion" ||

			e.dataset.mw === undefined

		) {

			return null;

		}

		try {

			const info = JSON.parse(e.dataset.mw);

			const tmpl = info.parts0].template;

			Object.keys(tmpl.params).forEach(function(k) {

				tmpl.paramsk = cleanWikitext(tmpl.paramsk].wt);

			});

			return tmpl.params;

		} catch (err) {

			return null;

		}

	});

};



revdelResponder.prototype.withParsedContent = function(callback) {

	const url = '/api/rest_v1/page/html/' +

		mw.util.rawurlencode(mw.config.get('wgPageName')) + '/' +

		mw.util.rawurlencode(mw.config.get('wgRevisionId')) + '?stash=true';

	const raw = $.ajax({

		url: url,

		context: this,

		dataType: 'html',

	}).done(function(raw, status, xhr) {

		this.etag = xhr.getResponseHeader('ETag');

		this.parseContent(raw);

		callback();

	}).fail(function(xhr) {

		mw.log.error('Error while parsing page content:', xhr);

		mw.notify($('<span>')

			.append('Sorry! ')

			.append($('<a>', {href: this.getUrl()}).text('revdel-responder'))

			.append(' failed to parse the page content. ' +

					'Check the console for more info.'));

	});

};



revdelResponder.prototype.getRevIds = function(i) {

	const tmpl = this.templatesi];

	if (!tmpl) {

		return [];

	}

	const ranges = [];

	let idx = 1, start = tmpl.start || tmpl.start1, end = tmpl.end || tmpl.end1;

	while (start) {

		ranges.push(end ? start, end : start]);

		idx++;

		start = tmpl'start' + idx];

		end = tmpl'end' + idx];

	}

	return ranges;

};



revdelResponder.prototype.getSourceUrl = function(i) {

	const tmpl = this.templatesi];

	if (!tmpl) {

		return null;

	}

	return tmpl.url;

};



revdelResponder.prototype.getSourceUrls = function(i) {

	const tmpl = this.templatesi];

	if (!tmpl) {

		return null;

	}

	const maxLen = 256;

	const urls = [];

	let idx = 1, url = tmpl.url, curLen = -2;

	while (url && curLen + url.length + 2 <= maxLen) {

		urls.push(url);

		idx++;

		curLen += url.length + 2;

		url = tmpl'url' + idx];

	}

	return urls.join(', ');

};



revdelResponder.prototype.doHistory = function(i) {

	const revIds = this.getRevIds(i).map(function(r) {

		return r.length === 1 ? r0 : r0 + '..' + r1];

	}).join('|');

	const url = mw.config.get('wgScript') + '?action=history&title=' +

		mw.util.wikiUrlencode(mw.config.get('wgPageName')) + '&revdel_select=' +

		mw.util.rawurlencode(revIds) + '&revdel_urls=' +

		mw.util.rawurlencode(this.getSourceUrls(i));

	window.open(url, '_blank');

};



revdelResponder.prototype.doCompare = function(i) {

	const revIds = this.getRevIds(i);

	const revId = (revIds.length > 0) ? revIds0][0 : mw.config.get('wgRevisionId');

	const url = 'https://copyvios.toolforge.org/?' + $.param({

		lang: mw.config.get('wgContentLanguage'),

		project: mw.config.get('wgSiteName').toLowerCase(),

		title: mw.config.get('wgPageName'),

		oldid: revId,

		action: 'compare',

		url: this.getSourceUrl(i) || '',

	});

	window.open(url, '_blank');

};



revdelResponder.prototype.removeTemplate = function(i) {

	let mbox = this.document.querySelectorAll(revdelResponder.MBOX_SELECTOR)[i];

	if (mbox !== undefined) {

		mbox = mbox.closest("[about]");

	}

	if (!mbox) {

		mw.notify('Error: Couldn\'t find the template in the page source?');

		return;

	}

	// Remove by transclusion ID, otherwise we might leave the category behind

	const about = mbox.getAttribute('about');

	this.document.querySelectorAll('[about="' + about + '"]').forEach(function(el) {

		// Need to remove a single newline if immediately following this element

		const next = el.nextSibling;

		if (next !== null && next.nodeType === Node.TEXT_NODE &&

			next.textContent.startsWith('\n')) {

			next.textContent = next.textContent.substr(1);

		}

		el.remove();

	});

};



revdelResponder.prototype.transformWikicode = function(callback) {

	const url = '/api/rest_v1/transform/html/to/wikitext/' +

		mw.util.rawurlencode(mw.config.get('wgPageName')) + '/' +

		mw.util.rawurlencode(mw.config.get('wgRevisionId'));

	const payload = this.document.documentElement.outerHTML;

	const raw = $.ajax({

		url: url,

		context: this,

		method: 'POST',

		data: {html: payload},

		dataType: 'html',

		headers: {'If-Match': this.etag},

	}).done(callback)

	.fail(function(xhr) {

		mw.log.error('Error while transforming wikicode:', xhr);

		mw.notify('Error: Couldn\'t transform wikicode. ' +

			'Check the console for more info.');

	});

};



revdelResponder.prototype.savePage = function(text, summary, callback) {

	new mw.Api().postWithEditToken({

		action: 'edit',

		title: mw.config.get('wgPageName'),

		text: text,

		summary: summary + ' ([[' + revdelResponder.SCRIPT_NAME + '|RR]])',

		formatversion: '2',

		baserevid: mw.config.get('wgRevisionId'),

		nocreate: true,

		assert: 'user',

	}).done(callback)

	.fail(function(code, result) {

		const errcode = result.error && result.error.code;

		const errinfo = result.error && result.error.info || 'Check the console for more info.';

		mw.log.error('Error while saving:', result);

		mw.notify('Error: Couldn\'t save the page: ' + errinfo);

	});

};



revdelResponder.prototype.indicateRefresh = function(i) {

	const ui = this.uii];

	ui.buttons.forEach(function(button) {

		button.$element.remove();

	});

	ui.elem.append($('<span>', {addClass: 'revdel-responder-status'}).text('Page saved!'));

	ui.elem.append(new OO.ui.ButtonWidget({

		label: 'Refresh',

		icon: 'reload',

		title: 'Reload the page',

	}).on('click', function() {

		window.location.reload();

	}).$element);

};



revdelResponder.prototype.transformAndSave = function(i, summary) {

	this.transformWikicode(function(text) {

		this.savePage(text, summary, this.indicateRefresh.bind(this, i));

	});

};



revdelResponder.prototype.doCompleteReal = function(i) {

	this.removeTemplate(i);

	this.transformAndSave(i, 'Copyvio revdel completed');

};



revdelResponder.prototype.doWarnComplete = function(i) {

	const that = this;

	const prompt = 'The requested revisions have not been deleted. Still remove the template?';

	OO.ui.confirm(prompt).done(function(confirmed) {

		if (confirmed) {

			that.doCompleteReal(i);

		} else {

			that.enableInterface();

		}

	});

};



revdelResponder.prototype.doComplete = function(i) {

	this.disableInterface();



	var newest = null, oldest = null;

	this.getRevIds(i).forEach(function(revs) {

		revs.forEach(function(revId) {

			if (newest === null || revId > newest) {

				newest = revId;

			}

			if (oldest === null || revId < oldest) {

				oldest = revId;

			}

		});

	});



	const that = this;

	const api = new mw.Api();

	const params = {

		action: 'query',

		prop: 'revisions',

		pageids: mw.config.get('wgArticleId'),

		rvprop: 'sha1',

		rvdir: 'older',

		rvlimit: 500,

		formatversion: '2',

	};

	if (newest !== null && oldest !== null) {

		params.rvstartid = newest;

		params.rvendid = oldest;

	}

	api.get(params).done(function(result) {

		const page = result && result.query && result.query.pages && result.query.pages0];

		const revs = page && page.revisions || [];

		if (revs.length === 0 || revs.some(function(rev) { return rev.sha1hidden; })) {

			that.doCompleteReal(i);

		} else {

			that.doWarnComplete(i);

		}

	}).fail(function(xhr) {

		mw.log.error('Error while verifying redacted revisions:', xhr);

		mw.notify('Error: Couldn\'t verify redacted revisions. ' +

			'Check the console for more info.');

	});

};



revdelResponder.prototype.doDeclineSave = function(i, reason) {

	const ui = this.uii];

	this.disableInterface();

	this.removeTemplate(i);

	var summary = 'Copyvio revdel declined';

	if (reason) summary += ': ' + reason;

	this.transformAndSave(i, summary);

};



revdelResponder.prototype.doDecline = function(i) {

	const that = this;

	OO.ui.prompt('Enter a decline reason:', {

		size: 'medium',

		textInput: {placeholder: 'Reason'},

	}).done(function(reason) {

		if (reason !== null) {

			that.doDeclineSave(i, reason);

		}

	});

};



revdelResponder.prototype.doDelete = function(i) {

	const reason = '[[WP:CSD#G12|G12]]: Unambiguous [[WP:CV|copyright infringement]]';

	const url = mw.config.get('wgScript') + '?action=delete&title=' +

		mw.util.wikiUrlencode(mw.config.get('wgPageName')) + '&wpDeleteReasonList=' +

		mw.util.rawurlencode(reason) + '&wpReason=' +

		mw.util.rawurlencode(this.getSourceUrls(i));

	window.location.href = url;

};



revdelResponder.prototype.setupInterface = function() {

	const ui = $('<div>', {addClass: 'revdel-responder-ui'})

		.append($('<i>').append($('<a>', {href: this.getUrl()}).text('RR')).append(': '));

	ui.append($('<span>', {

		addClass: 'revdel-responder-loading revdel-responder-status',

	}).text('Loading...'));

	return {

		elem: ui,

		buttons: null,

	};

};



revdelResponder.prototype.disableInterface = function() {

	this.ui.forEach(function(ui) {

		ui.buttons.forEach(function(button) {

			button.setDisabled(true);

		});

	});

};



revdelResponder.prototype.enableInterface = function() {

	this.ui.forEach(function(ui) {

		ui.buttons.forEach(function(button) {

			button.setDisabled(false);

		});

	});

};



revdelResponder.prototype.buildInterface = function(ui, i) {

	ui.elem.find('.revdel-responder-loading').remove();

	ui.buttons = 

		new OO.ui.ButtonWidget({

			label: 'History',

			icon: 'history',

			title: 'View page history, with revisions highlighted',

		}).on('click', this.doHistory.bind(this, i)),

		new OO.ui.ButtonWidget({

			label: 'Compare',

			icon: 'search',

			title: 'Compare oldest revision with first source URL using Earwig\'s Copyvio Detector',

		}).on('click', this.doCompare.bind(this, i)),

		new OO.ui.ButtonWidget({

			label: 'Complete',

			flags: 'progressive'],

			icon: 'check',

			title: 'Remove the template after completing the revdel request',

		}).on('click', this.doComplete.bind(this, i)),

		new OO.ui.ButtonWidget({

			label: 'Decline',

			flags: 'destructive'],

			icon: 'cancel',

			title: 'Decline the revdel request',

		}).on('click', this.doDecline.bind(this, i)),

		new OO.ui.ButtonWidget({

			label: 'Delete',

			flags: 'destructive'],

			icon: 'trash',

			title: 'Delete the page',

		}).on('click', this.doDelete.bind(this, i)),

	];

	ui.buttons.forEach(function(button) {

		ui.elem.append(button.$element);

	})

};



revdelResponder.prototype.render = function() {

	const that = this;

	mw.util.addCSS(

		'.revdel-responder-ui { min-height: 32px; }' +

		'.revdel-responder-ui > * { vertical-align: middle; }' +

		'.revdel-responder-status { font-style: italic; margin-right: 1em; }'

	);

	this.mboxes.find('.mbox-text').each(function(i, e) {

		const ui = that.setupInterface();

		that.ui.push(ui);

		$(e).append(ui.elem);

	});



	mw.loader.using([

		'mediawiki.api',

		'oojs-ui-core',

		'oojs-ui.styles.icons-content',

		'oojs-ui.styles.icons-interactions',

		'oojs-ui.styles.icons-moderation',

		'oojs-ui-windows',

	], function() {

		that.withParsedContent(function() {

			that.ui.forEach(function(e, i) {

				that.buildInterface(e, i);

			});

		});

	});

};



revdelResponder.prototype.setupHistory = function(urls) {

	$('#mw-history-revisionactions').append($('<input>', {

		type: 'hidden',

		name: 'wpRevDeleteReasonList',

		value: '[[WP:RD1|RD1]]: Violations of [[Wikipedia:Copyright violations|copyright policy]]',

	})).append($('<input>', {

		type: 'hidden',

		name: 'wpReason',

		value: urls,

	})).append($('<input>', {

		type: 'hidden',

		name: 'wpHidePrimary',

		value: '1',

	}));

};



revdelResponder.prototype.setupRevdel = function(hidePrimary) {

	$('input[name="wpHidePrimary"]').filter(function(i, e) {

		return $(e).prop('value') === hidePrimary;

	}).prop('checked', true);

};



$.when(mw.loader.using('mediawiki.util'), $.ready).then(function() {

	if (mw.config.get('wgAction') === 'view') {

		if (mw.util.getParamValue('action') === 'revisiondelete') {

			const hidePrimary = mw.util.getParamValue('wpHidePrimary');

			if (hidePrimary !== null) {

				new revdelResponder().setupRevdel(hidePrimary);

			}

			return;

		}

		if (mw.config.get('wgRevisionId') !== mw.config.get('wgCurRevisionId')) {

			return;

		}

		const mboxes = $(revdelResponder.MBOX_SELECTOR);

		if (mboxes.length > 0) {

			const rr = new revdelResponder(mboxes);

			const groups = mw.config.get('wgUserGroups');

			if (groups === null || !groups.includes('sysop')) {

				rr.notifyDisabled();

				return;

			}

			rr.render();

		}

	} else if (mw.config.get('wgAction') === 'history') {

		const urls = mw.util.getParamValue('revdel_urls');

		if (urls !== null) {

			new revdelResponder().setupHistory(urls);

		}

	}

});



// </nowiki>

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook