MediaWiki:Gadget-LicenseReview.js

Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
// Original Flickrreview script written by [[User:Patstuart]]
// Rewritten and extended by [[User:ZooFari]]
// Special thanks to [[User:Krinkle]] and [[User:DieBuche]] for assistance
// Large parts rewritten by [[User:Rillke]]
// Taken over by Majora in June 2018
// Moved to the MediaWiki namespace in March 2020
// See talk page for documentation
// <nowiki>
/* jshint multistr:true */
/* global AjaxQuickDelete, licenseReviewer */

$(function () {
'use strict';
var conf = mw.config.get(),
	userName = conf.wgUserName;

if (conf.wgNamespaceNumber !== 6) return;

var cleanUp = function (input) {
		return $.trim( input	
			.replace( /== ?Summary ?==/, '=={{int:filedesc}}==' )
			.replace( /== ?Licensing ?==/, '=={{int:license-header}}==' )
			// Remove problem since tags (X-To-DR string)
			.replace( /\{\{\s*[Nn]o[ _](source|(OTRS[ _])?permission|license)([ _]since)?\s*(\|[^}\n]+)?\}\}\s?/g, '' )
			// Remove Copyvio (X-To-DR string)
			.replace( /\{\{\s*(Copyviol?|Copyright(?:ed)?|Screenshot|Icon|Logo|Logo-Germany|(?:Non-free video |DVD )?Cover|Db-f9|db-copyvio|Vio(?:lation)?)\s*(\|[^\n]+|[^}\n]*)\}\}\s?/i, '' )
			// Remove Speedy (X-To-DR string)
			.replace( /\{\{\s*(Speedy(?:[ -]?delet(?:e|ion))?|Speedilydelete|Noncommercial|Nonderivative|löschen|db|spd|qd|Sdelete|SLA|Spdel|Ek|Destruir)\s*(\|[^\n]+|[^}\n]*)\}\}\s?/i, '' )
			.replace( /\{\{\s*SD\s*(\|[^\n]+|[^}\n]*)\}\}\s?/, '' ) ); // Remove SD (not Sd)
	},

	thxPatterns = { flickr: 'Thanks for licensing this image using a free license! \
	Your choice of a free license has allowed us to add your image to <a href="http://commons.wikimedia.org/">Wikimedia Commons</a>. \
	The image can now be used to illustrate <a href="http://commons.wikimedia.org/wiki/Special:GlobalUsage/%FILE%">pages</a> \
	on <a href="http://en.wikipedia.org/">Wikipedia</a> and other free knowledge projects.' },

	linkSanitizer = [
		// Google+
		// $1: userID; $2: albumID; $3: photoID
		{
			match: /\/\/plus\.google\.[^\/]+\/photos\/(\d+)\/albums\/(\d+)\/(\d+)/g, replace: '//picasaweb.google.com/lh/sredir?uname=$1&target=PHOTO&id=$3&noredirect=1'
		}

	],

	sanitizeLinks = function (textIn) {
		textIn = textIn || '';
		$.each(linkSanitizer, function (i, el) {
			textIn = textIn.replace(el.match, el.replace);
		});
		return textIn;
	};

$.extend(thxPatterns, window.LRThxPatterns);

var licenseReviewMaybeSave = function (val, summary, minor, justsave, oldtext) {
		document.editform.wpSummary.value = summary + ' ([[MediaWiki talk:Gadget-LicenseReview.js|script]])';
		document.editform.wpMinoredit.checked = minor;

		if (justsave) {
			document.editform.submit();
			return true;
		}
		if (!oldtext) oldtext = val;

		// First Flickr-check
		var gotResult,
			$submitButton,
			thxPattern,
			match = oldtext.match(/\d{1,11}@\D\d{2}/);
		if (!match) match = oldtext.match(/flickr\.com\/people\/(\S+?)\/? /);
		if (!match) match = oldtext.match(/flickr\.com\/people\/(\S+)/);
		if (!match) match = oldtext.match(/flickr\.com\/photos\/(\S+?)\//);
		if (!match) match = oldtext.match(/flickr\.com\/photos\/(\S+)/);
		if (match)
			thxPattern = thxPatterns.flickr;

		var checkBlacklistCB = function (isBad) {
			if (!isBad) {
				$submitButton.prop('disabled', false).removeClass('ui-state-disabled');
				gotResult = true;
			}
		};

		if (match) {
			match = (match[1] || match[0]);
			ajaxIsBadAuthor(match, checkBlacklistCB);
			if (thxPattern) {
				$('body').append('<div id="mw-licensereview-add" style="display:none" title="You may use this thank-you text">\
			<textarea id="ProposalThankyoumessage" readonly="readonly" style="width:400px;height:130px;" rows="5" cols="50" onclick="select()">' +
			mw.html.escape(thxPattern.replace(/\%FILE\%/g, conf.wgPageName.replace('File:', ''))) +
			'</textarea>\
			</div>'
				);
				$('#mw-licensereview-add').dialog({
					buttons: { Ok: function () {
						if (gotResult) document.editform.submit();
					} },
					modal: true,
					width: 430
				});
				$submitButton = $('.ui-dialog-buttonpane').find('button:first');
				$submitButton.prop('disabled', true).addClass('ui-state-disabled');
				$('#ProposalThankyoumessage').keyup(function (e) {
					if (((e.keyCode - 0) === 13)) $submitButton.click();
				});
			}
			return;
		} else {
			document.editform.submit();
			return true;
		}
	},

	getSource = function (val) {
		var ret,
			r = val.match(/\|\s*Source\s*=(.+)/i);
		if (r && r[1])
			ret = $.trim(r[1]);

		if (!ret) {
			r = val.match(/http:\/\/photozou.jp[^\s\]\|]+/i);
			if (r)
				ret = r[0];

		}
		return ((ret || 'http://'));
	},

	lrw = window.licenseReviewer = {

		promptForSource: function (prefill) {
			return prompt('Link:', getSource(prefill));
		},

		// LRP - License review passed
		LicenseReview_P: function () {
			var tb1 = document.editform.wpTextbox1,
				noChanges = tb1.value;
			tb1.value = tb1.value.replace(/\{\{!}}/g, '&#124;');
			if (/\{\{Licen[cs]e ?review.*\}\}/i.test(noChanges)) {
				var LRsite = lrw.promptForSource(tb1.value);
				if (LRsite === null) return mw.notify('License-review: Aborted');
				tb1.value = tb1.value.replace(/\{\{[Ll]icen[cs]e ?[Rr]eview ?(\|.*= ?|\| ?)*\}\}/g, '{{LicenseReview|site=' + LRsite + '|user=' + userName + '|date={{subst:#time:Y-m-d}}}}');
				tb1.value = tb1.value.replace(/\[\[Category:((Tasnim|Moj|Fars|Mehr|Nasim)news|SNN|CSS|Khamenei\.ir) review needed\]\]/g, '');
				tb1.value = tb1.value.replace(/\{\{ArrangedLicenseReview.*?\}\}/g, ''); 
				if (noChanges === tb1.value) alert('RegExp did not match. Likely no changes. Please report to [[MediaWiki talk:Gadget-LicenseReview.js]]');
				tb1.value = cleanUp(tb1.value);

				// submit
				licenseReviewMaybeSave(tb1.value, '[[Commons:License review|License review]] passed', true, false, noChanges);
			} else if (/\{\{(F|f)inna(R|r)eview\}\}/i.test(noChanges)) {
				var LRsite2 = lrw.promptForSource(tb1.value);
				if (LRsite2 === null) return mw.notify('License-review: Aborted');
				tb1.value = tb1.value.replace(/\{\{(F|f)inna(R|r)eview\}\}/g, '{{FinnaReview|site=' + LRsite2 + '|user=' + userName + '|date={{subst:#time:Y-m-d}}}}');
				if (noChanges === tb1.value) alert('RegExp did not match. Likely no changes. Please report to [[MediaWiki talk:Gadget-LicenseReview.js]]');
				tb1.value = cleanUp(tb1.value);

				// submit
				licenseReviewMaybeSave(tb1.value, '[[Commons:License review|License review]] passed', true, false, noChanges);
			} else if (/\{\{GODL-India/i.test(noChanges)) {
				var index = noChanges.search('GODL-India'),
					substring1 = noChanges.substring(index + 10),
					index2 = substring1.search('}}'),
					GODLparams = substring1.substring(0, index2);
				tb1.value = tb1.value.replace(/\{\{GODL-India(.+?|)}}/g, '{{GODL-India|status=confirmed|reviewer=~~~|date={{subst:#time:Y-m-d}}' + GODLparams + '}}');
				if (noChanges === tb1.value) alert('RegExp did not match. Likely no changes. Please report to [[MediaWiki talk:Gadget-LicenseReview.js]]');
				tb1.value = cleanUp(tb1.value);

				// submit
				licenseReviewMaybeSave(tb1.value, '[[Commons:License review|License review]] passed', true, false, noChanges);
			} else if (/\{\{(I|i)(n|N)aturalist(R|r)eview\}\}/i.test(noChanges)) {
				var LRsite4 = lrw.promptForSource(tb1.value);
				if (LRsite4 === null) return mw.notify('License-review: Aborted');
				tb1.value = tb1.value.replace(/\{\{(I|i)(n|N)aturalist(R|r)eview\}\}/g, '{{subst:Inrw |site=' + LRsite4 + '|license= |author=}}');
				if (noChanges === tb1.value) alert('RegExp did not match. Likely no changes. Please report to [[MediaWiki talk:Gadget-LicenseReview.js]]');
				tb1.value = cleanUp(tb1.value);

				// submit
				licenseReviewMaybeSave(tb1.value, '[[Commons:License review|License review]] passed', true, false, noChanges);
			} else if (/\{\{[Pp]ixabay.*\}\}/i.test(noChanges)) {
				var pbid;
				// First attempt: get id from {{pixabay|xxx}}
				var pbTemplateID = noChanges.match(/\{\{[Pp]ixabay.*[|=](\d+)}}/);
				if (pbTemplateID) {
					pbid = pbTemplateID[1];
				} else {
					var tryPbid = noChanges.match(/pixabay.com.*-(\d+)\/?/i);
					if(tryPbid) pbid = tryPbid[1];
					else pbid = lrw.promptForSource(tb1.value);
				}
				if (pbid === null) return mw.notify('License-review: Aborted');
				var urlRemoved = pbid.match(/(\d+)\/?$/)[1];
				tb1.value = tb1.value.replace(/\{\{[Pp]ixabay.*}}/, '{{subst:pblr|1=' + pbid +'}}');
				if (noChanges === tb1.value) alert('RegExp did not match. Likely no changes. Please report to [[MediaWiki talk:Gadget-LicenseReview.js]]');
				tb1.value = cleanUp(tb1.value);

				// submit
				licenseReviewMaybeSave(tb1.value, '[[Commons:License review|License review]] passed', true, false, noChanges);
			} else if (/\{\{YouTubeReview/i.test(noChanges)) {
				var lineIndex = noChanges.search('YouTubeReview'),
					youtubeSubstring = noChanges.substring(lineIndex + 13),
					lineIndex2 = youtubeSubstring.search('}}'),
					YouTubeparams = youtubeSubstring.substring(0,lineIndex2);
				if (tb1.value.includes('|id=')) {
					tb1.value = tb1.value.replace(/\{\{YouTubeReview(.*?|)}}/ig, '{{YouTubeReview' + YouTubeparams + '|reviewer=' + userName + '}}');
					if (noChanges === tb1.value) alert('RegExp did not match. Likely no changes. Please report to [[MediaWiki talk:Gadget-LicenseReview.js]]');
					tb1.value = cleanUp(tb1.value);
					licenseReviewMaybeSave(tb1.value, '[[Commons:License review|License review]] passed', true, false, noChanges);
				}
				else {
					var LRsite3 = tb1.value;
					if (LRsite3.includes('From YouTube')) {
						var id;
						var indexstart = LRsite3.substring(LRsite3.search('From YouTube'));
						var index3 = indexstart.search('1=');
						var substring2 = indexstart.substring(index3 + 2);
						var index4 = substring2.search('2=');
							if (index4 === -1) {
								index4 = substring2.search('}');
							}
						id = substring2.substring(0, index4);
					}
					else {
						var index5 = LRsite3.search('v=');
						var idtemp = LRsite3.substring(index5 + 2);
						var index6 = idtemp.indexOf('\n');
						id = idtemp.substring(0, index6);
					}
					 tb1.value = tb1.value.replace(/\{\{YouTubeReview(.*?|)}}/ig, '{{YouTubeReview|id=' + id + YouTubeparams + '|date={{subst:#time:Y-m-d}}|reviewer=' + userName + '}}');
					 if (noChanges === tb1.value) alert('RegExp did not match. Likely no changes. Please report to [[MediaWiki talk:Gadget-LicenseReview.js]]');
					 tb1.value = cleanUp(tb1.value);
					 licenseReviewMaybeSave(tb1.value, '[[Commons:License review|License review]] passed', true, false, noChanges);
				}
			}
				else {
				tb1.value = tb1.value
					.replace(/({{User:Flickr Review Bot.*?}})|({{(F|f)lickr(R|r)?eview.*?}})|({{(F|f)lickr no source.*?}})|({{User:FlickreviewR.*?}})/g, '{{Flickrreview|' + userName + '|{{subst:#time:Y-m-d}}}}')
					.replace(/({{User:Picasa Review Bot.*?}})|({{(P|p)icasar?eview}})/g, '{{Picasareview|' + userName + '|{{subst:#time:Y-m-d}}}}').replace(/{{(([^\}]*?))(P|p)icasareview}}/g, '{{$1}}{{Picasareview|' + userName + '|{{subst:#time:Y-m-d}}}}')
					.replace(/({{\s*(?:Template:)?((C|c)c-by-3.0-(IndiaFM|BollywoodHungama)|(B|b)ollywoodHungama)}})/g, '{{Cc-by-3.0-BollywoodHungama|status=confirmed|reviewer=~~~~}}')
					.replace(/({{\s*(?:Template:)?(C|c)c-by(?:-sa)?-3.0-FilmiTadka}})/g, '{{Cc-by-sa-3.0-FilmiTadka|passed|~~~~}}')
					.replace(/({{\s*(?:Template:)?(I|i)pernity(R|r)eview}})/g, '{{Ipernityreview|' + userName + '|{{subst:#time:Y-m-d}}}}')
					.replace(/({{\s*(?:Template:)?(I|i)ndafotó review}})/g, '{{Indafotó review|site=[http://indafoto.hu Indafotó]|user=' + userName + '|date={{subst:#time:Y-m-d}}}}')
					.replace(/({{\s*(?:Template:)?(N|n)ROER}})/g, '{{NROER|status=confirmed|reviewer=' + userName + '|date={{subst:#time:Y-m-d}}}}')
					.replace(/(\[\[(C|c)ategory:(P|d)D files for review\]\])/g, '');

				// if no replacements
				if (noChanges === tb1.value) {
					$('body').append('<div id="mw-licensereview-add" style="display:none" title="No template detected">\
					<label for="mw-licensereview-pick">No template found. Add template:</label>\
					<select id="mw-licensereview-pick" name="mw-licensereview-pick">\
					<option value="LR">LicenseReview</option>\
					<option value="PIX">Pixabayreview</option>\
					<option value="FR">Flickrreview</option>\
					<option value="PICR">Picasareview</option>\
					<option value="IR">Ipernityreview</option>\
					<option value="INR">iNaturalistReview</option>\
					</select>\
					</div>');
					$('#mw-licensereview-add').dialog({
						buttons: { Ok: function () {
							licenseReviewer.LicenseReview_A($('#mw-licensereview-pick').val());
							$(this).dialog('close');
						} },
						modal: true
					});
				} else {
				// if replacements
					tb1.value = cleanUp(tb1.value);

					// submit
					licenseReviewMaybeSave(tb1.value, '[[Commons:License review|License review]] passed', true, false, noChanges);
				}
			}
		},

		// Add a License-review template
		LicenseReview_A: function (pick) {
			var tb1 = document.editform.wpTextbox1;
			if (pick) {
				switch (pick) {
					case 'FR' : tb1.value += '{{Flickrreview|' + userName + '|{{subst:#time:Y-m-d}}}}';
						tb1.value = cleanUp(tb1.value);
						// submit
						licenseReviewMaybeSave(tb1.value, '[[Commons:Flickr files|Flickr review]]', false);
						break;
					case 'PICR' : tb1.value += '{{Picasareview|' + userName + '|{{subst:#time:Y-m-d}}}}';
						tb1.value = cleanUp(tb1.value);
						// submit
						licenseReviewMaybeSave(tb1.value, '[[Commons:License review|Picasa review]]', false);
						break;
					case 'IR' : tb1.value += '{{Ipernityreview|' + userName + '|{{subst:#time:Y-m-d}}}}';
						tb1.value = cleanUp(tb1.value);
						// submit
						licenseReviewMaybeSave(tb1.value, '[[Commons:License review|Ipernity review]]', false);
						break;
					case 'PIX' :
						tb1.value = cleanUp(tb1.value);
						var pixabayID = tb1.value.match(/pixabay.com.*-(\d+)\/?/i)[1];
						if(!pixabayID) return mw.notify('License-review: Aborted, could not find ID');
						tb1.value = tb1.value.replace(/\{\{[Pp]ixabay.*}}/, '{{subst:pblr|1=' + pixabayID +'}}');
						licenseReviewMaybeSave(tb1.value, '[[Commons:License review|Pixabay review]]', false);
						break;
					case 'INR' : 
						var site2 = lrw.promptForSource(tb1.value);
						tb1.value += '{{subst:inrw|site=' + site2 + '}}';
						tb1.value = cleanUp(tb1.value);
						// submit
						licenseReviewMaybeSave(tb1.value, '[[Commons:License review|iNaturalist review]]', false);
						break;
					case 'LR' : var site = lrw.promptForSource(tb1.value);
						if (site === null) return mw.notify('License-review: Aborted');
						tb1.value += '{{LicenseReview|site=' + site + '|user=' + userName + '|date={{subst:#time:Y-m-d}}}}';
						tb1.value = cleanUp(tb1.value);
						// submit
						licenseReviewMaybeSave(tb1.value, '[[Commons:License review|License review]]', false);
						break;
				}
			}
		},

		// LRF - License review failed
		LicenseReview_F: function () {
			var tb1 = document.editform.wpTextbox1,
				isBollyWood = /({{\s*(?:Template:)?((C|c)c-by-3.0-(IndiaFM|BollywoodHungama)|(B|b)ollywoodHungama)}})/.test(tb1.value),
				isFlickr = /[Ff]lickr/.test(tb1.value),
				isPixabay = /[Pp]ixabay/.test(tb1.value),
				options;

			options = '<option value="NS">-- No source indicated --</option>';
			if (isBollyWood) {
				options += '<option value="BOLLY">Screenshot of Bollywood image</option>';
			} else {
				options += '<option value="ARR">All rights reserved</option>\
			<option value="NC">Non-commercial</option>\
			<option value="ND">Non-derivative</option>\
			<option value="NCD">Non-commercial + Non-derivative</option>\
			<option value="NCS">Non-commercial-ShareAlike</option>\
			<option value="PICASA?">Picasa review unnecessary</option>';
			}
			if (isFlickr) {
				options += '<option value="ARR?">ARR (Puf)(Flickr-only)</option>\
			<option value="NC?">NC (Puf)(Flickr-only)</option>\
			<option value="ND?">ND (Puf)(Flickr-only)</option>\
			<option value="NCD?">NC+D (Puf)(Flickr-only)</option>\
			<option value="NCS?">NC-SA (Puf)(Flickr-only)</option>\
			<option value="PDMARK">PD-Mark</option>';
			}
			if (isPixabay) {
				options += '<option value="pixabay19">Pixabay upload after 2019-01-09</option>';
			}

			$('body').append('<div id="mw-licensereview-selector" style="display:none" title="Pick a license">\
			<label for="mw-licensereview-pick">Which License?</label>\
			<select id="mw-licensereview-pick" name="mw-licensereview-pick">' +
			options +
			'</select>\
			</div>');
			$('#mw-licensereview-selector').dialog({
				buttons: { Ok: function () {
					licenseReviewer.LicenseReview_C($('#mw-licensereview-pick').val());
					$(this).dialog('close');
				} },
				modal: true
			});
		},

		// License review failed, got reason, processing
		LicenseReview_C: function (pick) {
			var tb1 = document.editform.wpTextbox1,
				isFlickr = /[Ff]lickr/.test(tb1.value),
				reFlickr = /({{User:Flickr Review Bot.*?}})|({{(F|f)lickrr?eview.*?}})|({{User:FlickreviewR.*?}})/g,
				rePicasa = /({{User:Picasa Review Bot.*?}})|({{(P|p)icasar?eview}})/g,
				rePicasa2 = /{{(([^\}]*?))(P|p)icasareview}}/g,
				reFlickr2 = /{{(([^\}]*?))(F|f)lickrreview}}/g,
				reInat = /\{\{(I|i)(n|N)aturalist(R|r)eview\}\}/g,
				rePixabay = /\{\{[Pp]ixabay.*}}/g,
				reLR = /{{(L|l)icen[cs]e[ _]?(R|r)eview}}/g,
				reLicenses = /\{\{(?:Template:)?(cc-by(?:-sa)?-\d\.\d|pd-self)\}\}/gi,
				summary = '',
				minor = false,
				userTag = ''; // You can create new templates if you want and add them to the switch in order to notify the user with a default template
			// First changing/ adding review-tags
			if (pick) {
				switch (pick) {
					case 'ARR' :
						if (isFlickr) {
							tb1.value = '{{subst:Uffd|2=ARR}} \n' + tb1.value.replace(reFlickr, '');
							userTag = '{{subst:unfreeflickrnote|1=%FILE%}}';
						} else {
							tb1.value = String(tb1.value.replace(reFlickr, '{{subst:Uffd|2=ARR}}')
								.replace(rePicasa, '{{picasareview|' + userName + '|{{subst:#time:Y-m-d}}|All Rights Reserved}}')
								.replace(rePicasa2, '{{$1}}{{picasareview|' + userName + '|{{subst:#time:Y-m-d}}|All Rights Reserved}}')
								.replace(reLR, '{{copyvio|1=Posted to source as All Rights Reserved. Only free files are allowed on Commons.}}')
								.replace(reFlickr2, '{{$1}}{{subst:Uffd|2=ARR}}')
								.replace(reInat, '{{subst:inrw|license=arr}}')
								);
						}
						break;
					case 'pixabay19':
						tb1.value = tb1.value.replace(rePixabay, '{{copyvio|1=Posted on Pixabay after January 9, 2019, which is not under free license.}}');
						userTag = '{{subst:image source|1=%FILE%}}';
						break;
					case 'NC' :
						if (isFlickr) {
							tb1.value = '{{subst:Uffd|2=NC}} \n' + tb1.value.replace(reFlickr, '');
							userTag = '{{subst:unfreeflickrnote|1=%FILE%}}';
						} else {
							tb1.value = String(tb1.value.replace(reFlickr, '{{subst:Uffd|2=NC}}')
								.replace(rePicasa, '{{picasareview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nc-3.0}}')
								.replace(rePicasa2, '{{$1}}{{picasareview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nc-3.0}}')
								.replace(reLR, '{{copyvio|1=Posted to source as cc-by-nc. Non-commercial restriction is not allowed on Commons.}}')
								.replace(reFlickr2, '{{$1}}{{subst:Uffd|2=NC}}')
								.replace(reInat, '{{subst:inrw|license=cc-by-nc-4.0}}')
								);
						}
						break;
					case 'ND' :
						if (isFlickr) {
							tb1.value = '{{subst:Uffd|2=ND}} \n' + tb1.value.replace(reFlickr, '');
							userTag = '{{subst:unfreeflickrnote|1=%FILE%}}';
						} else {
							tb1.value = String(tb1.value.replace(reFlickr, '{{subst:Uffd|2=ND}}')
								.replace(rePicasa, '{{picasareview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nd-3.0}}')
								.replace(rePicasa2, '{{$1}}{{picasareview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nd-3.0}}')
								.replace(reLR, '{{copyvio|1=Posted to source as cc-by-nd. Non-derivative restriction is not allowed on Commons.}}')
								.replace(reFlickr2, '{{$1}}{{subst:Uffd|2=ND}}')
								.replace(reInat, '{{subst:inrw|license=cc-by-nd-4.0}}')
								);
						}
						break;
					case 'NCD' :
						if (isFlickr) {
							tb1.value = '{{subst:Uffd|2=NCD}} \n' + tb1.value.replace(reFlickr, '');
							userTag = '{{subst:unfreeflickrnote|1=%FILE%}}';
						} else {
							tb1.value = String(tb1.value.replace(reFlickr, '{{subst:Uffd|2=NCD}}')
								.replace(rePicasa, '{{picasareview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nc-nd-3.0}}')
								.replace(rePicasa2, '{{$1}}{{picasareview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nc-nd-3.0}}')
								.replace(reLR, '{{copyvio|1=Posted to source as cc-by-nc-nd. Non-derivative and non-commercial restriction are not allowed on Commons.}}')
								.replace(reFlickr2, '{{$1}}{{subst:Uffd|2=NCD}}')
								.replace(reInat, '{{subst:inrw|license=cc-by-nc-nd-4.0}}')
								);
						}
						break;
					case 'NCS' :
						if (isFlickr) {
							tb1.value = '{{subst:Uffd|2=NCS}} \n' + tb1.value.replace(reFlickr, '');
							userTag = '{{subst:unfreeflickrnote|1=%FILE%}}';
						} else {
							tb1.value = String(tb1.value.replace(reFlickr, '{{subst:Uffd|2=NCS}}')
								.replace(rePicasa, '{{picasareview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nc-sa-3.0}}')
								.replace(rePicasa2, '{{$1}}{{picasareview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nc-sa-3.0}}')
								.replace(reLR, '{{copyvio|1=Posted to source as cc-by-nc-sa. Non-commercial restriction is not allowed on Commons.}}')
								.replace(reFlickr2, '{{$1}}{{subst:Uffd|2=NCS}}')
								.replace(reInat, '{{subst:inrw|license=cc-by-nc-sa-4.0}}')
								);
						}
						break;
					case 'NS' :
						tb1.value = '{{subst:nsd}} \n' + tb1.value
							.replace(reFlickr, '{{Flickr no source|' + userName + '|{{subst:#time:Y-m-d}}}}')
							.replace(rePicasa, '{{$1}}{{Picasa no source|' + userName + '|{{subst:#time:Y-m-d}}}}')
							.replace(reLR, '{{LicenseReview|site=?}}')
							.replace(reFlickr2, '{{$1}}{{Flickr no source|' + userName + '|{{subst:#time:Y-m-d}}}}');
						userTag = '{{subst:image source|1=%FILE%}}';
						break;
					case 'PICASA' : tb1.value = String(tb1.value.replace(/({{User:Picasa Review Bot.*?}})|({{(P|p)icasar?eview}})/g, '{{Picasareviewunnecessary}}'));
						break;
					case 'ARR?' : tb1.value = String(tb1.value.replace(reFlickr, '{{flickrreview|' + userName + '|{{subst:#time:Y-m-d}}|All rights reserved}}').replace(/{{(([^\}]*?))(F|f)lickrreview}}/g, '{{$1}}{{Flickrreview|' + userName + '|{{subst:#time:Y-m-d}}|All Rights Reserved}}'));
						break;
					case 'NC?' : tb1.value = String(tb1.value.replace(reFlickr, '{{flickrreview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nc-2.0}}').replace(/{{(([^\}]*?))(F|f)lickrreview}}/g, '{{$1}}{{Flickrreview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nc-2.0}}'));
						break;
					case 'ND?' : tb1.value = String(tb1.value.replace(reFlickr, '{{flickrreview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nd-2.0}}').replace(/{{(([^\}]*?))(F|f)lickrreview}}/g, '{{$1}}{{Flickrreview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nd-2.0}}'));
						break;
					case 'NCD?' : tb1.value = String(tb1.value.replace(reFlickr, '{{flickrreview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nc-nd-2.0}}').replace(/{{(([^\}]*?))(F|f)lickrreview}}/g, '{{$1}}{{Flickrreview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nc-nd-2.0}}'));
						break;
					case 'NCS?' : tb1.value = String(tb1.value.replace(reFlickr, '{{flickrreview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nc-sa-2.0}}').replace(/{{(([^\}]*?))(F|f)lickrreview}}/g, '{{$1}}{{Flickrreview|' + userName + '|{{subst:#time:Y-m-d}}|cc-by-nc-sa-2.0}}'));
						break;
					case 'BOLLY': tb1.value = '{{Copyvio|1=[[Commons:License review|License review]] of Bollywood Hungama failed: Image is a screenshot, wallpaper or promotional poster. See [[Template:Cc-by-3.0-BollywoodHungama]]. ~~~~}}' + tb1.value.replace(/({{\s*(?:Template:)?((C|c)c-by-3.0-(IndiaFM|BollywoodHungama)|(B|b)ollywoodHungama)}})/ig, '{{Cc-by-3.0-BollywoodHungama}}');
						break;
					case 'PDMARK':
						tb1.value = '{{subst:Flickr-public domain mark/subst}}\n' + tb1.value
							.replace(reFlickr, '')
							.replace(reLR, '{{LicenseReview|site=?}}')
							.replace(reFlickr2, '{{$1}}')
							.replace(reLicenses, '');
						break;
				}
			}
			// Then, adding edit summary
			if (pick) {
				switch (pick) {
					case 'NS' :
						summary = '[[Commons:License review|License review]] failed: Source not found';
						break;
					case 'PICASA' :
						summary = '[[Commons:Picasa Web Albums files|Picasa review]] not required';
						break;
					case 'PDMARK' :
						summary = '[[Commons:License review|License review]]: Provide a specific reason for the public domain status, please.';
						break;
					default :
						summary = '[[Commons:License review|License review]] failed';
				}
			}
			if (userTag) {
				ajaxNotifyUploader(userTag, licenseReviewMaybeSave, [tb1.value, summary, minor, true]);
			} else {
			// Finally, submit
				licenseReviewMaybeSave(tb1.value, summary, minor, true);
			}
		},

		// Change license
		LicenseReviewChange: function () {
			$('body').append('<div id="mw-licensereview-changer" style="display:none" title="Pick a license">\
			<label for="mw-licensereviewc-pick">Change to:</label><br>\
			<select id="mw-licensereviewc-pick" name="mw-licensereviewc-pick">\
			<option value="BY2">cc-by-2.0</option>\
			<option value="BY21jp">cc-by-2.1-jp</option>\
			<option value="SA2">cc-by-sa-2.0</option>\
			<option value="PD">No known restrictions (Flickr only)</option>\
			<option value="PDMo">PDMark owner (Flickr only)</option>\
			<option value="CCz">Cc-zero</option>\
			<option value="BY3">cc-by-3.0</option>\
			<option value="SA3">cc-by-sa-3.0</option>\
			<option value="BY4">cc-by-4.0</option>\
			<option value="SA4">cc-by-sa-4.0</option>\
			</select>\
			</div>');
			$('#mw-licensereview-changer').dialog({
				buttons: { Ok: function () {
					licenseReviewer.LicenseReview_L($('#mw-licensereviewc-pick').val());
					$(this).dialog('close');
				} },
				modal: true
			});
		},

		LicenseReview_L: function (pick) {
			var tb1 = document.editform.wpTextbox1;
			if (pick) {
				var pattern = /({{(C|c)c-by-(sa-|)(2|3|4)\.\d.*?}})|({{(C|c)c-zero}})|({{(F|f)lickr-no known copyright restrictions}})|({{(R|r)emove this line and insert a public domain copyright tag instead}})/g,
					doReplacement = function (replace, summaryText) {
						tb1.value = String(tb1.value.replace(pattern, replace));
						document.editform.wpSummary.value = '[[Commons:License review|License review]]: Change to ' + summaryText + ' ([[MediaWiki talk:Gadget-LicenseReview.js|script]])';
						document.editform.wpMinoredit.checked = false;
					};
				switch (pick) {
					case 'BY2' :
						doReplacement('{{cc-by-2.0}}', 'CC-BY-2.0');
						break;
					case 'BY21jp':
						doReplacement('{{cc-by-2.1-jp}}', 'CC-BY-2.1-JP');
						break;
					case 'SA2' :
						doReplacement('{{cc-by-sa-2.0}}', 'CC-BY-SA-2.0');
						break;
					case 'PD' :
						doReplacement('{{Flickr-no known copyright restrictions}}', 'Flickr-no known copyright restrictions');
						break;
					case 'PDMo' :
						doReplacement('{{PDMark-owner}}', 'Flickr-PDMark owner');
						break;
					case 'CCz' :
						doReplacement('{{Cc-zero}}', 'Cc-zero');
						break;
					case 'BY3' :
						doReplacement('{{cc-by-3.0}}', 'CC-BY-3.0');
						break;
					case 'SA3' :
						doReplacement('{{cc-by-sa-3.0}}', 'CC-BY-SA-3.0');
						break;
					case 'BY4' :
						doReplacement('{{cc-by-4.0}}', 'CC-BY-4.0');
						break;
					case 'SA4' :
						doReplacement('{{cc-by-sa-4.0}}', 'CC-BY-SA-4.0');
						break;
				}
			}
		},
	// License migration code (part 1) (GFDL --> Cc-by-sa-3.0) 
		installMig: function ($node) {
			var $migDiv = $('div.LMR'),
				$migIntro = $('<label>', { text: 'Migration Review:' }).attr({ 'for': 'lrwMigSel' }).appendTo($migDiv),
				$migSelect = $('<select>').attr({
					id: 'lrwMigSel', size: 1, style: 'vertical-align:middle'
				}).appendTo($migDiv),
				$migOptEmpty = $('<option>', {
					text: ' ', value: ''
				}).appendTo($migSelect),
				$migOptRelicense = $('<option>', {
					text: 'relicense', value: 'relicense'
				}).appendTo($migSelect),
				$migOptRedundant = $('<option>', {
					text: 'redundant', value: 'redundant'
				}).appendTo($migSelect),
				$migOptInelibible = $('<option>', {
					text: 'not eligible', value: 'not-eligible'
				}).appendTo($migSelect),
				$migOptReviewNeeded = $('<option>', {
					text: 'needs review', value: 'needs-review'
				}).appendTo($migSelect),
				$migOptOut = $('<option>', {
					text: 'opt-out', value: 'opt-out'
				}).appendTo($migSelect),
				$migReason = $('<input>').attr({
					type: 'text', size: 60, value: '[[Template:License migration|License migration]]: '
				}).appendTo($migDiv),
				$saveButton = $('<button>', { text: 'Save' }).attr({
					type: 'button', role: 'button'
				}).button({ icons: { primary: 'ui-icon-disk' } }).appendTo($migDiv),
				$editButton = $('<button>', { text: 'Edit' }).attr({
					type: 'button', role: 'button'
				}).button({ icons: { primary: 'ui-icon-pencil' } }).appendTo($migDiv);

			$migSelect.change(function () {
				$migReason.val('[[Template:License migration|License migration]]: ' + $migSelect.val());
			});
			var $nodeChild = $node.children('tbody');
			if ($nodeChild.length)
				$nodeChild.append($('<tr>').append($('<td>').attr({ colspan: 3 }).append($migDiv)));
			else
				$migDiv.prependTo($node);

			var migReplace = function (wikiText, cb) {
				mw.loader.using('ext.gadget.libWikiDOM', function () {
					var nwe = mw.libs.wikiDOM.nowikiEscaper(wikiText),
						nt;

					// First try to replace the migration parameter
					nwe.secureReplace(/\|\s*[Mm]igration\s*=\s*(?:needs[\- _]review|review)?\s*([\|\}])/, '|migration=' + $migSelect.val() + '$1');
					nt = nwe.getText();
					if (nt !== wikiText) return cb(nt);

					// If nothing was replaced, then replace templates without params
					nwe.secureReplace(/\{\{\s*(Bild\-GFDL|GFDL|GFDL\-1.3|Gfdl|GNU FDL|Eigenwerk|Gdfl|GFDL\-no\-disclaimers|GNU|ГЛСД\-без\-одрицања|CopyrightGFDL|مجوز گنو|Јд\-јас|ГЛСД|ГЛСД\-без одрицања|GFDL\-lastno|GFDl|GFDL\-Self|GFDL\-user|GFDL\-user\-w|GNU[ _]Free[ _]Documentation[ _]License|GFDL-\D\D\D?|GFDL-user-\D\D\D?|YAM|OsborneFossils)\s*\}\}/i, '{{$1|migration=' + $migSelect.val() + '}}');
					nt = nwe.getText();
					if (nt !== wikiText) return cb(nt);

					// If nothing was replaced, try with params
					nwe.secureReplace(/\{\{\s*(Bild\-GFDL|GFDL|GFDL\-1.3|Gfdl|GNU FDL|Eigenwerk|Gdfl|GFDL\-no\-disclaimers|GNU|ГЛСД\-без\-одрицања|CopyrightGFDL|مجوز گنو|Јд\-јас|ГЛСД|ГЛСД\-без одрицања|GFDL\-lastno|GFDl|GFDL\-Self|GFDL\-user|GFDL\-user\-w|GNU[ _]Free[ _]Documentation[ _]License|GFDL-\D\D\D?|GFDL-user-\D\D\D?)\s*(\|[^\}]*)?\}\}/i, '{{$1$2|migration=' + $migSelect.val() + '}}');
					nt = nwe.getText();
					if (nt !== wikiText) return cb(nt);

					// If nothing was replaced, try {{self}}
					nwe.secureReplace(/\{\{\s*self2?\s*\|(.*?\|?)(?:GFDL|GNU[ _]FDL|GNU[ _]Free[ _]Documentation[ _]License)(\s*\|.*?)?(.*)}}/i, '{{self|$1GFDL$2$3|migration=' + $migSelect.val() + '}}');
					nt = nwe.getText();
					if (nt !== wikiText) return cb(nt);

					throw new Error('Could not migrate license because the template for license migration was not detected.');
				});
			};

			$editButton.click(function (/* e*/) {
				var onMigEdit = function () {
					var $tb1 = $(document.editform.wpTextbox1),
						$sum = $(document.editform.wpSummary);

					migReplace($tb1.val(), function (nt) {
						$tb1.val(nt);
						$sum.val($migReason.val());
					});
				};
				mw.loader.load('ext.gadget.libWikiDOM');
				execEdit(onMigEdit);
			});

			$saveButton.click(function (/* e*/) {
				mw.loader.using(['ext.gadget.AjaxQuickDelete', 'ext.gadget.libWikiDOM'], function () {
					var aqd = AjaxQuickDelete;
					aqd.initialize();
					aqd.addTask('getMoveToken');
					aqd.addTask('lrwTextModifyProxy');
					aqd.addTask('reloadPage');
					aqd.lrwTextModifyProxy = function () {
						migReplace(aqd.pageContent, function (nc) {
							var page = {
								title: conf.wgPageName.replace(/_/g, ' '),
								text: nc,
								starttimestamp: aqd.starttimestamp,
								timestamp: aqd.timestamp,
								editType: 'text',
								watchlist: 'nochange'
							};
							aqd.showProgress('Changing template parameter to ' + $migSelect.val());
							aqd.savePage(page, $migReason.val(), 'nextTask');
						});
					};
					aqd.nextTask();
				});
			});
		},
           // End license migration code (part 1)
		//

		installLLinks: function (undef) {
			var $passHref = $(mw.libs.commons.ui.addEditLink('#', 'license +', 'lPlus', 'License-review: Pass file.', '+', undef, 'p-cactions')).find('a'),
				$failHref = $(mw.libs.commons.ui.addEditLink('#', 'license -', 'lMinus', 'License-review: Review failed.', '-', undef, 'p-cactions')).find('a');

			$passHref.click(function (e) {
				e.preventDefault();
				execEdit(licenseReviewer.LicenseReview_P);
			});
			$failHref.click(function (e) {
				e.preventDefault();
				execEdit(licenseReviewer.LicenseReview_F);
			});
			var $changeHref,
				$lrNode = $('div.LR');
			if ($lrNode.length) {
				$lrNode.css('text-align', 'center');
				$changeHref = $('<a>', {
					href: '#', text: 'change license'
				});
				$changeHref.click(function (e) {
					e.preventDefault();
					execEdit(licenseReviewer.LicenseReviewChange);
				});
				$lrNode.append($('<span>', { id: 'lChange' }).append(' [', $changeHref, '] '));
				$lrNode.append($('<span>', { id: 'lPlus2' }).append(' [', $passHref.clone(true).removeAttr('id'), '] '));
				$lrNode.append($('<span>', { id: 'lMinus2' }).append(' [', $failHref.clone(true).removeAttr('id'), '] '));

				// Sanitizing Google+ Links to PicasaWeb because no license info is displayed on G+
				$('.hproduct').find('a[href^="http"]').each(function (i, el) {
					var $el = $(el),
						href = $el.attr('href');
					$el.attr('href', sanitizeLinks(href));
				});
			}
			if (document.editform && document.editform.wpTextbox1) document.editform.wpTextbox1.value = sanitizeLinks(document.editform.wpTextbox1.value);

			// License migration review (part 2)
			var $migImage = $('img[alt="Licensing update unknown"]'),
				hasMigImage = !!$migImage.length;
			if (hasMigImage) {
				$('span.licensetpl_short').each(function (i, el) {
					var $el = $(el);
					if (['GFDL', 'GFDL 1.3'].indexOf($el.text()) > -1 && $('img[alt="Licensing update unknown"]').length) {
						licenseReviewer.installMig($migImage.parents('.licensetpl'));
						return false;
					}
				});
			}
		}   	// End of license migration review (part 2)
	}, // End of Object (licenseReviewer)

	execEdit = function (cb) {
		if (['edit', 'submit'].indexOf(conf.wgAction) < 0) {
			// scroll to top
			$('html,body').animate({ scrollTop: 0 }, 1000);
			// switch to pseudo-edit-mode
			var gotEditHTML = function (html) {
				$('#bodyContent').replaceWith($(html).find('#bodyContent'));
				document.editform.wpTextbox1.value = sanitizeLinks(document.editform.wpTextbox1.value);
				mw.config.set('wgAction', 'edit');
				document.title = 'Editing ' + document.title;
				$('#ca-edit').addClass('selected');
				cb();
			};
			$('#bodyContent').prepend($('<h2>', { text: 'Loading ...' }));
			$.get(mw.util.wikiScript('index'), {
				action: 'edit', title: conf.wgPageName.replace(/ /g, '_')
			}, gotEditHTML);
		} else {
			// execute
			cb();
		}
	},

	/**
* Notify the initial uploader about a non-free image upload.
*
* @example
*      ajaxNotifyUploader('{{subst:unfreeflickrnote|%FILE%}}', someFunction, arguments to pass along);
*
* @param tag {String} Tag to add to the user's talk page
* @param callback {Function} Pointer to a callback function on ready or error.
*
* @context {window}
* @return n/d
*/
	ajaxNotifyUploader = function (tag, callback, args) {
		if (!tag) return;
		var errDisplay = function (err) {
				alert(err);
				callback.apply(this, args);
			},
			didEdit = function (result) {
				try {
					if (result.edit.spamblacklist)
						throw new Error('Spamblacklisted. Sorry.');
					else if (result.error)
						throw new Error('API returned error: ' + result.error.code + '\n' + result.error.info);

					alert('User notified.');
					callback.apply(this, args);
					return;
				} catch (err) {
					errDisplay('Something went wrong while notifying the user.');
					return;
				}
			},
			doEdit = function (user) {
				if (/(:?^[Bb]ot)|(:?[Bb]ot$)/.test(user)) {
					errDisplay('User who created this page is very likely a bot and is NOT notified.');
					return;
				}
				var page = {
					action: 'edit',
					format: 'json',
					title: conf.wgFormattedNamespaces[3] + ':' + user,
					summary: 'Notification of possible unfree file. ([[MediaWiki talk:Gadget-LicenseReview.js|script]])',
					appendtext: '\n' + tag.replace('%FILE%', conf.wgPageName.replace(/_/g, ' ')) + ' ~~' + '~~',
					token: mw.user.tokens.get('csrfToken')
				};
				jQuery.post(mw.util.wikiScript('api'), page, didEdit);
			},
			gotUser = function (result) {
				try {
					for (var id in result.query.pages) {
						var pg = result.query.pages[id];
						doEdit(pg.revisions[0].user);
					}
				} catch (err) {
					errDisplay('Error notifying user. Do it yourself.');
					return;
				}
			},
			// query first revision (in Lupo-Style :D)
			query = {
				action: 'query',
				format: 'json',
				prop: 'revisions',
				rvprop: 'user',
				rvdir: 'newer',
				rvlimit: 1,
				titles: conf.wgPageName.replace(/_/g, ' ')
			};
		$.getJSON(mw.util.wikiScript('api'), query, gotUser);
	},

	/**
* Determine whether the flickr-user is a "bad-author".
*
* @example
*      ajaxIsBadAuthor('11121189@N04', someFunction);
*
* @param author {String} The flckr-user's ID
* @param callback {Function} Pointer to a callback function on ready or error. Should take one argument.
*
* @context {window}
* @return n/d
*/
	ajaxIsBadAuthor = function (author, callback) {
		var gotList = function (result, useRx) {
				pendingCalls--;
				if (result) {
					var rx = useRx ? (new RegExp('\\|\\s*' + mw.RegExp.escape(author) + '\\s*\\|', '')) :
						(new RegExp(mw.RegExp.escape(author), ''));
					if (rx.test(result)) {
						isBad = isBad || true;
						if (pendingCalls) return;
						if (isBad) alert('Warning! The Flickr-Uploader is blacklisted.');
						callback(isBad);
						return;
					} else {
						if (pendingCalls) return;
						if (isBad) alert('Warning! The Flickr-Uploader is blacklisted.');
						callback(isBad);
						return;
					}
				} else {
					alert('Error retrieving bad-author-list from server.');
				}
			},
			pendingCalls = 0,
			isBad = false,
			doCall = function (page, useRx) {
				pendingCalls++;
				$.ajax({
					url: mw.util.wikiScript('index'),
					data: {
						title: page,
						action: 'raw',
						dataType: 'text',
						// Allow caching for 1/4h
						maxage: 900,
						smaxage: 900
					},
					cache: true,
					type: 'GET',
					success: function (r) {
						gotList(r, useRx);
					},
					error: function () {
						gotList();
					}
				});
			};
		doCall('Commons:Questionable_Flickr_images/Users', true);
		doCall('User:FlickreviewR/bad-authors', false);
	};

// Install
mw.loader.using(['jquery.ui', 'ext.gadget.editDropdown'], function () {
	licenseReviewer.installLLinks();
	mw.loader.load(['mediawiki.user', 'mediawiki.util']);
});
});
// </nowiki>