Jump to content

User:Ahecht/Scripts/potd-helper.js

From Wikipedia, the free encyclopedia
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
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.
//jshint maxerr:512
// <nowiki>
mw.loader.using( [ 'mediawiki.util', 'oojs-ui' ], function () {
	var potdScriptLongTitle = "Picture of the Day Helper",
		potdScriptShortTitle = "POTDHelper",
		potdScriptLocation = "User:Ahecht/Scripts/potd-helper.js",
		potdFPCategory = 'Featured pictures';
		api = new mw.Api( { userAgent: potdScriptShortTitle + '/0.0.1' } );
		
	const urlParams = new URLSearchParams(window.location.search);
	const debug = urlParams.get('debug') || false;
	
	function POTDHelper() {
		var config = {
			fpCategory: potdFPCategory,
			fpcPrefix: 'Wikipedia:Featured picture candidates/',
			potdPrefix: 'Template:POTD/',
			editSummaryPattern: '[[File:%0]] scheduled for [[WP:POTD|POTD]] on [[Template:POTD/%1|%1]]',
			editSummarySuffix: ' ([[' + potdScriptLocation + '|' + potdScriptShortTitle + ']])',
			panoWidth: 500,
			defaultWidth: 350,
			landscapeWidth: 300,
			squareWidth: 275,
			portraitWidth: 250,
			tallWidth: 225,
			maxWidth: 650,
			suggestHeight: 350,
			maxHeight: 450,
			aspectRatio: 0,
			submitMessageHeader: '<strong>Submitting Picture of the Day:</strong><ul><li>',
			submitMessageFooter: '</li></ul>',
			submitMessages: [],
			query: {
				action: 'query',
				formatversion: '2'
			},
			revQuery: {
				prop: 'revisions',
				rvprop: 'content',
				rvslots: 'main',
				rvlimit: '1',
				rvsection: '0',
			},
			imageQuery: {
				prop: 'imageinfo|revisions|categories|fileusage',
				iiprop: 'user|dimensions',
				iilimit: 'max',
				cllimit: 'max',
				funamespace: '0',
				fulimit: 'max'
			},
			nomQuery: {
				rvprop: 'user',
				rvdir: 'newer'
			},
			titleQuery: {
				prop: 'revisions|pageimages|images',
				piprop: 'name',
				imlimit: 'max'
			},
			edit: {
				action: 'edit',
				watchlist: 'preferences',
				recreate: 1
			}
		};
		
		function wikiLink(message) {
			message = message.replace(/\[\[(.*?)\]\]/g, function(s,v) {
				v = v.split("|");
				var url = mw.config.get("wgServer") + mw.config.get("wgArticlePath").replace("$1", encodeURI(v[0]));
				return '<a href="'+ url + '">' + (v[1] || v[0]) + "</a>";
			} );
			return message;
		}
		
		function showImageMessage(label, type='error') {
			imageMessage.setType(type);
			imageMessage.setLabel(label);
			imageMessage.toggle(true);
			imageMessage.scrollElementIntoView();
		}

		function showImageError(error) {
			error = "The script " + error + " Check that the image name [[File:" + imageInput.value + "]] is correct.";
			showImageMessage(new OO.ui.HtmlSnippet(wikiLink(error)));
		}
		
		function showTitleError(error) {
			error = "The script " + error + " Check that the article name [[" + titleInput.value + "]] is correct.";
			titleMessage.setLabel(new OO.ui.HtmlSnippet(wikiLink(error)));
			titleMessage.toggle(true);
			titleMessage.scrollElementIntoView();
		}
		
		function showDateError(error) {
			dateMessage.setLabel(new OO.ui.HtmlSnippet(wikiLink(error)));
			dateMessage.toggle(true);
			dateMessage.scrollElementIntoView();
		}
		
		function showSubmitMessage(message, type) {
			type = type ? type : 'notice';
			config.submitMessages.push(wikiLink(message));
			var label = new OO.ui.HtmlSnippet( config.submitMessageHeader + config.submitMessages.join("</li><li>") + config.submitMessageFooter );
			submitMessage.setLabel(label);
			var types = ['notice', 'success', 'warning', 'error'];
			if (types.indexOf(type) > types.indexOf(submitMessage.type)) {
				submitMessage.setType(type);
			}
			submitMessage.toggle(true);
			submitMessage.scrollElementIntoView();
		}
		
		function fetchImageData(doneFunction = function() {return;}, errorFunction = function() {return;}){
			var imageName = 'File:' + imageInput.value;
			if (imageName != "File:" && typeof config.imageData === 'undefined') {
				var fn = "fetchImageData("+doneFunction+","+errorFunction;
				console.log('Fetching image data...');
				api.get(
					Object.assign( {titles: imageName}, config.query, config.revQuery, config.imageQuery )
				).fail(function(code, error) {
					console.warn(fn+") encountered an API error retrieving info for "+imageName+":");
					console.warn(error);
					showImageError("got an API error '" + code + "' when querying image [[" + imageName + "]]: " + error.error.info);
					errorFunction();
				}).done( function(data) {
					if (data && data.query && data.query.pages && data.query.pages[0]) {
						if (data.query.pages[0].missing) {
							if (data.query.pages[0].known) {
								errorMsg = "was unable find a local page on Wikipedia for this image (although one exists on Commons), which means it is likely not a Wikipedia Featured Picture.";
							} else {
								errorMsg = "was unable to find the image.";
							}
							showImageError(errorMsg);
							console.warn(fn+") did not find a page for "+imageName+".");
							console.log(data);
							errorFunction();
						} else {
							console.log('Caching image data...');
							config.imageData = data.query.pages[0];
							doneFunction();
						}
					} else {
						console.warn(fn+") encountered an error parsing the API respose for "+imageName+":");
						console.log(data);
						showImageError("was unable to parse the API response when trying get info on the image.");
						errorFunction();
					}
				} );
			} else {
				console.log('Using cached image data...');
				doneFunction();
			}
		}
		
		function fillImage(titleData, imageInput) {
			if (titleData.images && titleData.images.length) {
				var options = [];
				titleData.images.forEach( function(item) {return options.push({data: item.title.replace(/^File:/i, '')});} );
				if (options.length) {
					imageInput.setOptions(options);
				} else {
					console.log("No images found on " + titleData.title);
					console.log(imageData);
				}
			} else {
				console.warn("Images not found in API response:");
				console.log(imageData);
			}
			if (titleData.pageimage) {
				if (!imageInput.value.length) {
					imageInput.setValue(titleData.pageimage);
					checkSizeInput();
				} else {
					console.log("Page image " + titleData.pageimage + " found, but imageInput is not empty.");
				}
			} else {
				console.warn("Page Image not found for article " + titleData.title);
				console.log(titleData);
			}
		}
		
		function fillUsage(imageData, titleInput) {
			if (imageData.fileusage && imageData.fileusage.length) {
				var options = [];
				imageData.fileusage.forEach( function(item) {return options.push({data: item.title});} );
				if (options.length) {
					titleInput.setOptions(options);
				} else {
					console.log("No usage of " + imageData.title + " found.");
					console.log(imageData);
				}
			} else {
				console.warn("Fileusage not found in API response:");
				console.log(imageData);
			}
		}
			
		function getImageInfo() {
			function getNominator(nom) {
				if (nom && nom.query && nom.query.pages && nom.query.pages[0] && nom.query.pages[0].revisions) {
					if (nom.query.pages[0].revisions[0] && nom.query.pages[0].revisions[0].user && nom.query.pages[0].revisions[0].user.length) {
						nominatorInput.setValue("User:" + nom.query.pages[0].revisions[0].user);
						console.log("Nominated by " + nominatorInput.value);
						if (nominatorInput.value == uploaderInput.value) {
							nominatorInput.setValue();
						}
					} else {
						showImageError("was unable to parse API data on nomination page [[" + nomination + "]].");
					}
				} else {
					showImageError("was unable to find the nomination page [[" + nomination + "]].");
				}
			}
			
			function getNomination(imageData) {
				if (imageData.revisions && imageData.revisions.length && imageData.categories && imageData.categories.length) {
					var cats=[];
					imageData.categories.forEach( function(cat) {return cats.push(cat.title);} );
					if (cats.includes('Category:' + config.fpCategory)) {
						if (imageData.revisions[0] && imageData.revisions[0].slots && imageData.revisions[0].slots.main && imageData.revisions[0].slots.main.content) {
								var content = imageData.revisions[0].slots.main.content,
									nominations = [];
								try {
									nominations = XRegExp.matchRecursive(content, "\\{\\{featured\\s*picture\\s*\\|", "\\}}", 'i');
								} catch(error) {
									console.warn(error + " when parsing local file description.");
									console.log(content);
									showImageError("was unable to parse the {{Featured picture}} template on [[" + imageInput.value + "]]: " + error + ".");
								}
								if (nominations.length && nominations[0].length) {
									var nomination = config.fpcPrefix + nominations[0];
									console.log("Nominated at " + nomination);
									api.get(
											Object.assign( {titles: nomination}, config.query, config.revQuery, config.nomQuery )
										).fail(function(code, error) {
											console.warn("API error retrieving nomination:");
											console.warn(error);
											showImageError("got an API error '" + code + "' when retrieving the nomination page [[" + nomination + "]]: " + error.error.info);
										}).done( getNominator );
								} else {
									showImageError("was unable to find the featured picture template in the local description. Are you sure this is a featured image on the English Wikipedia?");
								}
						} else {
							showImageError("was unable to parse API data on the image.");
						}
					} else {
						showImageError("was unable to find [[Category:" + config.fpCategory + "]] on that image. Are you sure this is a featured image on the English Wikipedia?");
					}
				} else {
					showImageError("was unable to find a local file description. Are you sure this is a featured image on the English Wikipedia?");
				}
			}
			
			function getUploader(imageData) {
				if (imageData.imageinfo && imageData.imageinfo.length) {
					info = imageData.imageinfo;
					info = info[info.length - 1];
					if (info.user) {
						uploaderInput.setValue("User:" + info.user);
						console.log("Uploaded by " + uploaderInput.value);
						getNomination(imageData);
					} else {
						console.warn("Error parsing API respose for image uploader:");
						console.log(data);
						showImageError("was unable to parse the API response when trying to determine who uploaded the image.");
					}
				} else {
					console.warn("Error parsing API respose for image uploader:");
					console.log(data);
					showImageError("was unable to parse the API response when trying to determine who uploaded the image.");
				}
			}
			
			imageMessage.toggle(false);
			fetchImageData(function() {
				getUploader(config.imageData);
				fillUsage(config.imageData, titleInput);
				checkSizeInput();
			} );
		}
		
		function getCaption() {
			function extractCaption(titleData) {
				var extract = "";
				
				if (titleData.revisions && titleData.revisions[0] && titleData.revisions[0].slots &&
					titleData.revisions[0].slots.main && titleData.revisions[0].slots.main.content) {
					var content = titleData.revisions[0].slots.main.content,
						xValues = [];
					
					content = content.replace(/(\[\[File:.*\]\][\s\n]*)*/ig, '');
					
					try {
						xValues = XRegExp.matchRecursive(content, "\\{{", "\\}}[\s\n]*", 'gi', {valueNames: ['b', 'l', 'm', 'r']});
					} catch(error) {
						console.warn(error + " when parsing linked article.");
						console.log(content);
						showTitleError("encountered an error when parsing the article's lead section: " + error + ".");
					}
					if (xValues.length) {
						var startSub = 0;

						for(var i = 0; i < xValues.length; i++){
    						var x = xValues[i];
							if (x.name && x.name=='b' && (typeof x.start === 'number')) {
								startSub=x.start;
								break;
							}
						}

						extract = content.substr(startSub).split('\n\n')[0];
						
						if (extract && extract.length) {
							extract = extract.replace(/<ref.*?>(.*?)<\/ref>/g, "");
							extract = extract.replace(/<ref.*?\/>/g, "");
							re = new RegExp(/'''('')?(.*?)\1''' /);
							titleMatch=extract.match(re);
							if (titleMatch) {
								if (titleMatch[2]==titleInput.value) {
									captionInput.setValue(extract.replace(re, "'''$1[[$2]]$1''' "));
								} else {
									captionInput.setValue(extract.replace(re, "'''[["+titleInput.value+"|$1$2$1]]''' "));
								}
							} else {
								captionInput.setValue("'''[["+titleInput.value+"]]:''' "+extract);
							}
						} else {
							showTitleError("was unable to find non-template content in lead section.");
							extract = "";
						}
					} else {
						showTitleError("was unable to parse the article's lead section.");
					}
				} else if (titleData.missing) {
					console.warn("Could not find revisions for Title:");
					console.log(titleData);
					showTitleError("was unable to find an article with that title.");
				} else {
					console.warn("Error parsing API respose when fetching lead section:");
					console.log(titleData);
					showTitleError("encountered an error parsing the API response when fetching the article's lead section.");
				}
			}
			
			titleMessage.toggle(false);
			if (typeof config.titleData === 'undefined') {
				console.log('Fetching title data...');
				api.get(
					Object.assign( {titles: titleInput.value}, config.query, config.revQuery, config.titleQuery )
				).fail(function(code, error) {
					console.warn("API error retrieving linked article:");
					console.warn(error);
					showTitleError("encountered an API error '" + code + "' when retrieving the article: " + error.error.info);
				} ).done( function(data) {
					if (data && data.query && data.query.pages && data.query.pages[0]) {
						console.log('Caching title data...');
						config.titleData = data.query.pages[0];
						extractCaption(config.titleData);
						fillImage(config.titleData, imageInput);
					} else {
						console.warn("Error parsing API respose when fetching lead section:");
						console.log(data);
						showTitleError("encountered an error parsing the API response when fetching the article's lead section.");
					}
				} );
			} else {
				console.log('Using cached title data...');
				extractCaption(config.titleData);
				fillImage(config.titleData, imageInput);
			}
		}
		
		function finishSubmit(done) {
			done = done ? done : false;
			if (done) {showSubmitMessage("Done!", 'success');}
			while (closeButton.isPending()) {closeButton.popPending();}
			closeButton.toggleFramed(true).setDisabled(false);
			while (submitButton.isPending()) {submitButton.popPending();}
			submitButton.toggleFramed(true);
			checkButtons();
		}
		
		function writeUserTalk(user) {
			var templateText = "\n{{subst:" + "NotifyPOTD|1=" + 
				"|2=File:" + imageInput.value +
				"|3=" + dateInput.value +
				(commentsInput.value.length ? ("|4=" + commentsInput.value) : ""),
				userTalk = "";
			
			if (user == 'uploader') {
				userTalk = uploaderInput.value;
				templateText += "|action=uploaded}}";
			} else if ((user == 'nominator') && (nominatorInput.value != uploaderInput.value)) {
				userTalk = nominatorInput.value;
				templateText += "|action=nominated}}";
			}
			
			if (userTalk.search(/^User:./i) == 0) { //Value starts with "User:"
				console.log(templateText);
				userTalk = userTalk.replace(/^User:/i, 'User talk:');
				var params = Object.assign( {
					title: userTalk,
					appendtext: templateText,
					summary: config.editSummary,
					redirect: 1,
					nocreate: 1
				}, config.edit );
				
				if (debug) {
					console.log(params);
					showSubmitMessage("Simulated message to [[" + userTalk + "]].");
					if (user == 'uploader') {
						writeUserTalk('nominator');
					} else {
						finishSubmit(true);
					}
				} else {
					api.postWithEditToken(params).fail( function(code, error) {
						console.error("API error when creating article talk page message: ");
						console.error(error);
						showSubmitMessage("ERROR '" + code + "' when creating message on [[" + userTalk + "]]: " + error.error.info, 'warning');
						if (user == 'uploader') {
							writeUserTalk('nominator');
						} else {
							finishSubmit(true);
						}
					} ).done( function() {
						showSubmitMessage("Added message to [[" + userTalk + "]].");
						if (user == 'uploader') {
							writeUserTalk('nominator');
						} else {
							finishSubmit(true);
						}
					} );
				}
			} else if (user == 'uploader') {
				writeUserTalk('nominator');
			} else {
				finishSubmit(true);
			}
		}
		
		function writeArticleTalk(){
			var templateText = "\n{{subst:" + "UpcomingPOTD" + 
				"|1=File:" + imageInput.value +
				"|2=" + dateInput.value +
				(commentsInput.value.length ? ("|3=" + commentsInput.value) : "") +
				"}}",
				titleTalk = 'Talk:' + titleInput.value;
			
			console.log(templateText);
			
			var params = Object.assign( {
				title: titleTalk,
				appendtext: templateText,
				summary: config.editSummary,
				redirect: 1
			}, config.edit );
			
			if (debug) {
				console.log(params);
				showSubmitMessage("Simulated message to [[" + titleTalk + "]].");
				writeUserTalk('uploader');
			} else {
				api.postWithEditToken(params).fail( function(code, error) {
					console.error("API error when creating article talk page message: ");
					console.error(error);
					showSubmitMessage("ERROR '" + code + "' when creating message on [[" + titleTalk + "]]: " + error.error.info, 'warning');
					writeUserTalk('uploader');
				} ).done( function() {
					showSubmitMessage("Added message to [[" + titleTalk + "]].");
					writeUserTalk('uploader');
				} );
			}
		}
		
		function checkSize() {
			config.wide = false;
			config.tall = false;
			if (config.imageData && config.imageData.imageinfo && config.imageData.imageinfo[0] && config.imageData.imageinfo[0].width > 0) {
				var imageWidth = config.imageData.imageinfo[0].width;
				var imageHeight = config.imageData.imageinfo[0].height || 0;
				config.aspectRatio = imageWidth / imageHeight;
				console.log("Original image dimensions: " + imageWidth + "×" + imageHeight + " (" + config.aspectRatio + ":1).");
				
				if (!sizeInput.value.length) {
					width =	config.aspectRatio > 2	? config.panoWidth
						: config.aspectRatio > 1.1 ? config.landscapeWidth
						: config.aspectRatio >= 0.9 ? config.squareWidth
						: config.aspectRatio >= 0.5 ? config.portraitWidth
						: config.aspectRatio > 0 ? config.tallWidth
						: config.defaultWidth;
				} else {
					width = sizeInput.value;
				}
				if (width > config.maxWidth) {
					console.log("Wide image detected.");
					config.wide = true;
				}
				
				var height = width * imageHeight / imageWidth;
				console.log("Template image dimensions: " + width + "×" + height);
				if (!config.wide && height > config.maxHeight) {
					console.log("Tall image detected.");
					if (!sizeInput.value.length) {
						width = Math.floor(config.suggestHeight*config.aspectRatio / 10) * 10;
						height = width * imageHeight / imageWidth;
						console.log("Changing default value to: " + width + "×" + height);
					} else {
						config.tall = true;
					}
				}
				if (!sizeInput.value.length) {
					sizeInput.setValue(width);
				}
			} else {
				console.warn("Error reading or parsing image data when trying to determine dimensions.");
				console.log(config.imageData);
			}
		}
		
		function writePOTDTemplate() {
			var templateName = config.potdPrefix + dateInput.value;
			if (!texttitleInput.value.length) {
				texttitleInput.setValue(titleInput.value);
			}
			if (!sizeInput.value.length) {
				sizeInput.setValue(config.defaultWidth);
			}
			checkSize();
			if (!captionInput.value.length) {
				captionInput.setValue("'''[[<!--article-->]]'''");
			}
			var templateText = "{{POTD {{{1|{{{style|default}}}}}}" +
				"\n|image=" + imageInput.value +
				"\n|size=" + sizeInput.value +
				( (config.wide && config.aspectRatio >= 1.5) ? "\n|wide=yes" : "" ) +
				( (config.tall && config.aspectRatio < 0.67) ? "\n|tall=yes" : "" ) +
				"\n|title=[[" + titleInput.value + "]]" +
				"\n|texttitle=" + texttitleInput.value +
				( alttextInput.value.length ? ("\n|alttext=" + alttextInput.value) : "" ) +
				"\n|caption=\n\n" + captionInput.value +
				"\n\n|credit=" + creditInput.value + ( secondaryCreditInput.value.length ? ("; " + secondaryCreditInput.value) : "" ) +
				"\n}}<noinclude>[[Category:Wikipedia Picture of the day {{#time:F Y|{{SUBPAGENAME}}}}]]" + 
				"\n\n== See also ==" + 
				"\n* [[Template:POTD{{#ifeq:{{BASEPAGENAME}}|POTD protected||&#32;protected}}/{{SUBPAGENAME}}]]</noinclude>";
			
			console.log(templateText);
			
			var params = Object.assign( {
				createonly: 1,
				title: templateName,
				text: templateText,
				summary: 'Creating a [[WP:POTD|POTD]] template for [[File:' + imageInput.value + ']]' + config.editSummarySuffix
			}, config.edit );
			
			if (debug) {
				console.log(params);
				showSubmitMessage("Simulated [[" + templateName + "]].");
				writeArticleTalk();
			} else {
				api.postWithEditToken(params).fail( function(code, error) {
					console.error("API error when creating template: ");
					console.error(error);
					showSubmitMessage("ERROR '" + code + "' when creating template [[" + templateName + "]]: " + error.error.info, 'error');
					finishSubmit(true);
				} ).done( function() {
					showSubmitMessage("Created [[" + templateName + "]].");
					writeArticleTalk();
				} );
			}
		}
		
		function writeLocalDesc() {
			var params = Object.assign( {
				title: 'File:' + imageInput.value,
				appendtext: '\n{{Picture of the day|' + dateInput.value + '}}',
				summary: config.editSummary,
				redirect: 1,
				nocreate: 1
			}, config.edit );
			
			if (debug) {
				console.log(params);
				showSubmitMessage("Simulated message to [[File:" + imageInput.value + "]].");
				writePOTDTemplate();
			} else {
				api.postWithEditToken(params).fail( function(code, error) {
						console.error("API error when adding template to local file description: ");
						console.error(error);
						showSubmitMessage("ERROR '" + code + "' when adding template to [[File:" + imageInput.value + "]]: " + error.error.info, 'warning');
						finishSubmit(true);
					} ).done( function() {
						showSubmitMessage("Added message to [[File:" + imageInput.value + "]].");
						writePOTDTemplate();
					} );
			}
		}
		
		function checkButtons() {
			if (!titleInput.value.length) {
				autofillTitleButton.setDisabled(true);
			} else {
				autofillTitleButton.setDisabled(false);
			}
			
			if (!imageInput.value.length) {
				autofillImageButton.setDisabled(true);
			} else {
				autofillImageButton.setDisabled(false);
			}
			
			if (titleInput.value.length && imageInput.value.length && dateInput.value.length && creditInput.value.length && !submitButton.isPending()) {
				submitButton.setDisabled(false);
			} else {
				submitButton.setDisabled(true);
			}
		}
		
		function preCheck() {
			var imageData = config.imageData;
			if (imageData.categories && imageData.categories.length){
				var cats=[];
				imageData.categories.forEach( function(cat) {return cats.push(cat.title);} );
				if (cats.includes('Category:' + config.fpCategory)) {
					var templateName = config.potdPrefix + dateInput.value;
					api.get(
						Object.assign( {titles: templateName}, config.query )
					).fail(function(code, error) {
						console.warn("API error checking if POTD template exists:");
						console.warn(error);
						showDateError("got an API error '" + code + "' when checking if [[" + templateName + "]] exists: " + error.error.info);
						finishSubmit();
					}).done( function(data) {
						if (data && data.query && data.query.pages && data.query.pages[0]) {
							if (data.query.pages[0].missing) {
								console.log(templateName + " does not yet exist.");
								if (typeof config.titleData === 'undefined') {
									console.log('Fetching title data...');
									api.get(
										Object.assign( {titles: titleInput.value}, config.query, config.revQuery, config.titleQuery )
									).fail(function(code, error) {
										console.warn("API error checking if article exists:");
										console.warn(error);
										showDateError("got an API error '" + code + "' when checking if [[" + titleInput.value + "]] exists: " + error.error.info);
										finishSubmit();
									}).done( function(data) {
										if (data && data.query && data.query.pages && data.query.pages[0]) {
											if (data.query.pages[0].missing) {
												showTitleError("was unable to find that article.");
											} else {
												console.log('Caching title data...');
												config.titleData = data.query.pages[0];
												writeLocalDesc();
											}
										} else {
											console.warn("Error parsing API respose for article:");
											console.log(data);
											showTitleError("was unable to parse the API response when trying get info on [[" + titleInput.value + "]].");
											finishSubmit();
										}
									});
								} else {
									console.log('Using cached title data...');
									if (config.titleData.missing) {
										showTitleError("was unable to find that article.");
									} else {
										writeLocalDesc();
									}
								}
							} else {
								showDateError("A Picture of the Day template already exists at [[" + templateName + "]].");
								finishSubmit();
							}
						} else {
							console.warn("Error parsing API respose for template:");
							console.log(data);
							showDateError("The script was unable to parse the API response when trying get info on the [[" + templateName + "]] template.");
							finishSubmit();
						}
					} );
				} else {
					showImageError("was unable to find [[Category:" + config.fpCategory + "]] on that image.");
					finishSubmit();
				}
			} else {
				showImageError("was unable to find any categories on that image.");
				finishSubmit();
			}
		}
		
		function submitClick() {
			titleMessage.toggle(false);
			imageMessage.toggle(false);
			dateMessage.toggle(false);
			submitMessage.toggle(false);
			if (uploaderInput.value != "" && uploaderInput.value.search(/^User:/i) == -1) {
				showImageMessage("Uploader name must begin with \"User:\".");
				uploaderInput.select();
			} else if (nominatorInput.value != "" && nominatorInput.value.search(/^User:/i) == -1) {
				showImageMessage("Nominator name must begin with \"User:\".");
				nominatorInput.select();
			} else if (creditInput.value.includes(creditDefault) || creditInput.value.includes(nameDefault)) {
				showImageMessage("Please enter a real image credit.");
				creditInput.select();
			} else if (secondaryCreditInput.value.includes(secondaryCreditDefault) || secondaryCreditInput.value.includes(nameDefault) ) {
				showImageMessage("Please enter a real secondary credit or leave it blank.");
				secondaryCreditInput.select();
			} else if (isNaN(Date.parse(dateInput.value))) {
				showDateError("Invalid date.");
				dateInput.select();
			} else {
				submitButton.setDisabled(true).pushPending().toggleFramed(false);
				closeButton.setDisabled(true).pushPending().toggleFramed(false);
				
				dateInput.setValue(new Date(dateInput.value).toISOString().split('T')[0]);
				config.editSummary = config.editSummaryPattern.replace(/%(\d+)/g, function(_, n) {return [imageInput.value, dateInput.value][n];}) + config.editSummarySuffix;
				
				fetchImageData(preCheck, finishSubmit);
			}
		}
		
		function filePrefill(imageInput, titleInput) {
			imageInput.setValue(mw.config.get('wgTitle'));
			fetchImageData(function() {
				fillUsage(config.imageData, titleInput);
				if (!uploaderInput.value.length && !nominatorInput.value.length) { getImageInfo(); }
			} );
		}
		
		function articlePrefill(imageInput, titleInput) {
			titleInput.setValue(mw.config.get('wgTitle'));
			if (typeof config.titleData === 'undefined') {
				console.log('Fetching title data...');
				api.get(
					Object.assign( {titles: titleInput.value}, config.query, config.revQuery, config.titleQuery )
				).fail(function(code, error) {
					console.warn("API error getting Page Image:");
					console.warn(error);
				}).done( function(data) {
					if (data && data.query && data.query.pages && data.query.pages[0]) {
						console.log('Caching title data...');
						config.titleData = data.query.pages[0];
						fillImage(config.titleData, imageInput);
						if (!captionInput.value.length) { getCaption(); }
					} else {
						console.warn("Error parsing API respose for title:");
						console.log(data);
					}
				} );
			} else {
				console.log('Using cached title data...');
				fillImage(config.titleData, imageInput);
				if (!captionInput.value.length) { getCaption(); }
			}
		}
		
		function checkSizeInput() {
			fetchImageData(function() {
				checkSize();
				if (config.wide) {
					showImageMessage("Image will be wider than "+config.maxWidth+"px. "
					+(config.aspectRatio >= 1.5 
						? "The wide=yes parameter will be set in the POTD template unless the size is reduced" 
						: "Consider reducing the size")
					+" to "+config.maxWidth+" or less.", "warning");
				} else if (config.tall) {
					var maxSizeMsg = (config.aspectRatio ?  " to "+Math.floor(config.maxHeight*config.aspectRatio)+" or less ("+Math.floor(config.suggestHeight*config.aspectRatio)+" for an image height of "+config.suggestHeight+"px)" : "");
					showImageMessage("Image will be taller than "+config.maxHeight+"px. "
					+(config.aspectRatio < 0.67
						? "The tall=yes parameter will be set in the POTD template unless the size is reduced"
						: "Consider reducing the size")
					+maxSizeMsg+".", "warning");
				} else if (typeof imageMessage.getLabel() == 'string' && imageMessage.getLabel().startsWith("Image will be")) {
					imageMessage.toggle(false);
				}
			} );
		}
		
		if (typeof POTDFieldset === 'undefined') {
			var creditDefault = 'Photograph/Painting/etc.',
				secondaryCreditDefault = 'restored/photographed/etc.',
				nameDefault = '[[<!--name-->]]';
			
			var titleMessage = new OO.ui.MessageWidget( {type: 'error', showClose: true} ),
				titleInput = new OO.ui.ComboBoxInputWidget( { placeholder: 'Article the image represents', indicator: 'required', validate: 'non-empty'} ),
				autofillTitleButton = new OO.ui.ButtonWidget( { label: 'Autofill caption', icon: 'search', flags: ['progressive'] } ),
				captionInput = new OO.ui.MultilineTextInputWidget({placeholder: '\'\'\'[[Article title]]\'\'\' is...', autosize: true, }),
				texttitleInput = new OO.ui.TextInputWidget( { placeholder: 'Short caption. Leave blank to use Title' } ),
				alttextInput = new OO.ui.TextInputWidget( { placeholder: 'Leave blank to use Text Title' } ),
				creditInput = new OO.ui.TextInputWidget( { value: creditDefault + ' credit: ' + nameDefault, indicator: 'required', validate: function(v) {return (v != "" && !v.includes(creditDefault) && !v.includes(nameDefault));} } ),
				secondaryCreditInput = new OO.ui.TextInputWidget( { value: secondaryCreditDefault + ' by ' + nameDefault, validate: function(v) {return (!v.includes(secondaryCreditDefault) && !v.includes(nameDefault));} } ),
				sizeInput = new OO.ui.TextInputWidget( { placeholder: 'Size in pixels (defaults to '+config.defaultWidth.toString()+')', type: 'number' } ),
				imageMessage = new OO.ui.MessageWidget( {type: 'error', showClose: true} ),
				imageInput = new OO.ui.ComboBoxInputWidget( { placeholder: 'File name without "File:"', indicator: 'required', validate: 'non-empty' } ),
				autofillImageButton = new OO.ui.ButtonWidget( { label: 'Autofill image details', icon: 'search', flags: ['progressive'] } ),
				uploaderInput = new OO.ui.TextInputWidget( { placeholder: 'User:', validate: function(v){return (v == "" || v.search(/^User:/i) != -1); } } ),
				nominatorInput = new OO.ui.TextInputWidget( { placeholder: 'User:', validate: function(v){return (v == "" || v.search(/^User:/i) != -1); } } ),
				dateMessage = new OO.ui.MessageWidget( {type: 'error', showClose: true} ),
				dateInput = new OO.ui.TextInputWidget( { placeholder: 'YYYY-MM-DD', indicator: 'required', validate: function(v) {return !isNaN(Date.parse(v));} } ),
				commentsInput = new OO.ui.TextInputWidget( { placeholder: '[optional]' } ),
				submitMessage = new OO.ui.MessageWidget( {type: 'notice', showClose: true} ),
				submitButton = new OO.ui.ActionWidget( { label: 'Submit'+(debug ? " (debug)" : ""), disabled: true, framed: true, flags: ['primary','progressive']} ),
				closeButton = new OO.ui.ActionWidget( { label: 'Close', framed: true, flags: ['primary','destructive']} );
				
			var POTDFieldset = new OO.ui.FieldsetLayout( { label: potdScriptLongTitle, classes: [ 'container' ] } ),
				titleFieldset = new OO.ui.FieldsetLayout( { label: 'Linked article', classes: [ 'container' ] } ),
				imageFieldset = new OO.ui.FieldsetLayout( { label: 'Image information', classes: [ 'container' ] } ),
				templateFieldset = new OO.ui.FieldsetLayout( { label: 'Template information', classes: [ 'container' ] } ),
				submitFieldset = new OO.ui.FieldsetLayout( { classes: [ 'container' ] } );
			
			titleFieldset.addItems( [
				titleMessage,
				new OO.ui.ActionFieldLayout(titleInput, autofillTitleButton, {align: 'top', label: 'Title'}),
				new OO.ui.FieldLayout(captionInput, {align: 'top', label: 'Caption'}),
				new OO.ui.FieldLayout(texttitleInput, {align: 'top', label: 'Text title'})
			] );
			
			imageFieldset.addItems( [
				imageMessage,
				new OO.ui.ActionFieldLayout(imageInput, autofillImageButton, {align: 'top', label: 'Image'}),
				new OO.ui.FieldLayout(uploaderInput, {align: 'top', label: 'Original uploader'}),
				new OO.ui.FieldLayout(nominatorInput, {align: 'top', label: 'Featured Picture nominator'}),
				new OO.ui.FieldLayout(creditInput, {align: 'top', label: 'Credit (use full name, not user name, if known)'}),
				new OO.ui.FieldLayout(secondaryCreditInput, {align: 'top', label: 'Secondary credit (may be blank)'}),
				new OO.ui.FieldLayout(alttextInput, {align: 'top', label: 'Alt text'}),
				new OO.ui.FieldLayout(sizeInput, {align: 'top', label: 'Size'})
			] );
				
			templateFieldset.addItems( [
				dateMessage,
				new OO.ui.FieldLayout(dateInput, {align: 'top', label: 'Date'}),
				new OO.ui.FieldLayout(commentsInput, {align: 'top', label: 'Additional comments for talk page messages'}),
			] );
			
			submitFieldset.addItems( [
				new OO.ui.FieldLayout(submitMessage, {}),
				new OO.ui.FieldLayout(new OO.ui.ButtonGroupWidget( { items: [ submitButton, closeButton] } ))
			] );
			
			POTDFieldset.addItems( [ 
				titleFieldset,
				imageFieldset,
				templateFieldset,
				submitFieldset
			] );
			
			if (mw.config.get('wgNamespaceNumber') == 6) {
				filePrefill(imageInput, titleInput);
			} else if (mw.config.get('wgNamespaceNumber') == 0) {
				articlePrefill(imageInput, titleInput);
			}
			
			checkButtons();

			titleInput.inputFilter = function(value){return value.replace(/^\[\[/, '').replace(/\]\]$/, '').replace('_', ' ');};
			imageInput.inputFilter = function(value){return value.replace(/^File:/, '').replace('_', ' ');};
			creditInput.on( 'change', checkButtons );
			titleInput.on( 'change', function() {checkButtons();delete config.titleData;});
			titleInput.on( 'enter', getCaption);
			dateInput.on( 'change', checkButtons);
			imageInput.on( 'change', function() {checkButtons();delete config.imageData;fetchImageData(function(){imageMessage.toggle(false);checkSizeInput();});});
			imageInput.on( 'enter', getImageInfo );
			autofillTitleButton.on( 'click', getCaption);
			autofillImageButton.on( 'click', getImageInfo);
			sizeInput.on ( 'change', checkSizeInput);
			submitButton.on( 'click', submitClick);
			closeButton.on( 'click', function() {POTDFieldset.toggle(false);} );
			submitMessage.on( 'close', function() {
					config.submitMessages = [];
					submitMessage.setType('notice');
				} ).on( 'toggle', function() {
					if (!submitMessage.isVisible()) {
						config.submitMessages = [];
						submitMessage.setType('notice');
					}
				} );

			titleMessage.toggle(false);
			imageMessage.toggle(false);
			dateMessage.toggle(false);
			submitMessage.toggle(false);
			
			$( POTDFieldset.$element ).insertBefore( "#mw-content-text" );
		} else {
			POTDFieldset.toggle(true);
		}
	}
	
	function loadPOTDHelper() {
		mw.loader.getScript(
			'https://tools-static.wmflabs.org/cdnjs/ajax/libs/xregexp/3.2.0/xregexp-all.js'
		).then( function () {
			console.log('XRegExp loaded.');
			POTDHelper();
		}, function ( e ) {
			errorMessage = "Error: Cannot load XRegExp: " + e.message;
			console.error( errorMessage );
			mw.notify( errorMessage, {type: 'error'} );
		} );
	}
	
	if ((mw.config.get('wgNamespaceNumber') == 6 && mw.config.get('wgCategories').find( function(cat) {return cat == potdFPCategory;} )) || mw.config.get('wgNamespaceNumber') == 0) {
		var portletLink = mw.util.addPortletLink("p-cactions", "#", potdScriptShortTitle,
		"ca-potdhelper", "Make image a Picture of the Day");
		$( portletLink ).click(function(e) {
			e.preventDefault();
			return loadPOTDHelper();
		} );
	} else if (mw.config.get('wgNamespaceNumber') == -1 && mw.config.get('wgTitle').toLowerCase() === "potdhelper") {
		document.title = "Special:" + potdScriptShortTitle + ' - Wikipedia';
		document.getElementsByTagName("h1")[0].textContent = "Special:" + potdScriptShortTitle;
		document.getElementById("mw-content-text").innerHTML="";
		loadPOTDHelper();
	}
} );
// </nowiki>