User:Evad37/Covery/sandbox.js

/* jshint esversion: 6, laxbreak: true, undef: true, eqnull: true, maxerr: 999 */ /* globals console, document, File, FileReader, fetch, window, $, mw, OO */ // var setupCovery = function setupCovery { var SCRIPT = { name: 'Covery', version: '1.5.0', ad: ' (using Covery)' };

function isSuitable { var config = mw.config.get(['wgAction', 'wgDiffOldId', 'wgNamespaceNumber', 'wgPageName']); if (			config.wgAction !== 'view' ||			config.wgNamespaceNumber !== 0 ||			config.wgDiffOldId !== null		) { return $.Deferred.reject; }		return config; }

var getLeadWikitext = function getLeadWikitext(api, pageName) { return api .get({				action: 'parse',				format: 'json',				page: pageName,				prop: 'wikitext',				section: '0'			}) .then(function(response) {				return response.parse.wikitext['*'];			}); };

/**	 *	 * @param {String} wikitext parameters section from a template, including pipes before each parameter name inside braces `` */	var getTemplateParameters = function getTemplateParameters(wikitext) { var params = {}; var unnamedParamCount = 0; var templateParamsPattern = /\|(?!(?:[^{]+}|[^\[]+]))(?:.|\s)*?(?=(?:\||$)(?!(?:[^{]+}|[^\[]+])))/g; var parts = wikitext.match(templateParamsPattern); return parts.map(function(part, position) {			var isEmptyParameter = part.trim === '|'; // i.e. first parameter of {{foo||bar}			if (isEmptyParameter) {				unnamedParamCount++;				return {					name: unnamedParamCount.toString,					value: '',					wikitext: {						name: '|',						value: part.slice(1)					}				};			}			var equalsIndex = part.indexOf('=');			var bracesIndex = part.indexOf('{{');

var hasNoEqualsSign = equalsIndex === -1; var firstEqualsSignWithinBraces = bracesIndex !== -1 && bracesIndex < equalsIndex; var isUnnamedParameter = hasNoEqualsSign || firstEqualsSignWithinBraces; if (isUnnamedParameter) { unnamedParamCount++; return { name: unnamedParamCount.toString, value: part.slice(1).trim, wikitext: { name: '|', value: part.slice(1) }				};			} else { return { name: part.slice(1, equalsIndex).trim, value: part.slice(equalsIndex + 1).trim, wikitext: { name: part.slice(0, equalsIndex + 1), value: part.slice(equalsIndex + 1) }				};			}		});	};

var getInfoboxTemplate = function getInfoboxTemplate(wikitext) { var infoboxPattern = /\{\{\s*(.*?[Ii]nfobox.*?|[Ii]OS App)\s*(\|(?:.|\n)*?(?:(?:\{\{(?:.|\n)*?(?:(?:\{\{(?:.|\n)*?\}\})(?:.|\n)*?)*?\}\})(?:.|\n)*?)*|)\}\}\n?/; var infoboxParts = infoboxPattern.exec(wikitext); if (!infoboxParts || !infoboxParts[0] || !infoboxParts[1]) { throw new Error('Unable to parse infobox from wikitext `' + wikitext + '`'); }		var name = infoboxParts[1]; var params = infoboxParts[2] ? getTemplateParameters(infoboxParts[2]) : []; return { name: name, params: params, wikitext: infoboxParts[0] };	};

var toSentanceCase = function toSentanceCase(text) { return text.slice(0, 1).toUpperCase + text.slice(1); };

var checkInfobox = function checkInfobox(infobox) { var videoGameInfoboxTemplates = [ 'Infobox video game', 'Infobox Arcade Game', 'Infobox Videogame', 'Infobox cvg', 'Infobox Arcade game', 'Infobox arcade', 'GameInfobox', 'Infobox CVG', 'Infobox vg', 'Infobox Video Game', 'Infobox VG', 'IOS App', 'Infobox video games', 'Vg infobox', 'Infobox videogame', 'Infobox console game', 'Infobox computer game', 'Videogame infobox', 'Video game infobox', 'Infobox video game series', 'Infobox VG series', 'Infobox video game franchise' ];		var infoboxName = toSentanceCase(infobox.name); if (!videoGameInfoboxTemplates.includes(infoboxName)) { throw new Error(' not found.'); }		var imageParam = infobox.params.find(paramByName('image')); if (imageParam && imageParam.value) { throw new Error('Infobox already has an image!'); }		return infobox; };

/**	 * @param {File} file source file * @param {Number} maxResolution maximum resolution in pixels * @returns {Promise} Promise of (1) a File with the given max resoltion, and (2) a data url of the resized image **/	var resizeImageFile = function resizeImageFile(file, maxResolution) { var resizeFilePromise = $.Deferred;

var origImg = document.createElement('img');

var reader = new FileReader; reader.onload = function(e) { origImg.addEventListener(				'load',				function {					var canvas = document.createElement('canvas');					var ctx = canvas.getContext('2d');					ctx.drawImage(origImg, 0, 0);					var resolution = origImg.width * origImg.height;					var scaleFactor =						resolution > maxResolution ? Math.sqrt(maxResolution / resolution) : 1;					var width = origImg.width * scaleFactor;					var height = origImg.height * scaleFactor;					canvas.width = width;					canvas.height = height;					ctx = canvas.getContext('2d');					ctx.drawImage(origImg, 0, 0, width, height);

var dataurl = canvas.toDataURL(file.type);

canvas.toBlob(function(blob) {						resizeFilePromise.resolve( new File([blob], file.name, { type: file.type }), dataurl );					}, file.type); },				false );			origImg.src = e.target.result;		};		reader.readAsDataURL(file);

return resizeFilePromise.promise; };

/**	 *	 * @param {String} articleTitle * @param {String} developer * @param {String} publisher * @param {String[]} platforms */	var makeDescriptionText = function makeDescriptionText(		articleTitle,		developer,		publisher,		platforms	) { var platformsParams = platforms.reduce(function(params, platform) {			return params + '|' + platform;		}, ''); return (			'==Summary==\n\n' +			'==Licensing==\n'		); };

/**	 * @param {Object} api * @param {File} file * @param {String} text wikitext for the file description page * @param {Object} title mw.Title object * @returns {Promise} Promise of result object, or an error code and a jqxhr object */	var uploadFile = function uploadFile(api, file, text, title) { var filename = title.getMain; return api .postWithToken(				'csrf',				{					action: 'upload',					format: 'json',					filename: filename,					comment: 'Upload cover image' + SCRIPT.ad,					text: text,					file: file				},				{ contentType: 'multipart/form-data' }			) .then(function(response) {				/* on success, will get an object like:           { upload:                filename: "Image_page_sandbox_1000x596.png",                imageinfo: {                    bitdepth: 8,                    canonicaltitle: "File:Image page sandbox 1000x596.png",                    ...                },                result: "Success"            }            */				if (response && response.upload && response.upload.result === 'Success') {					return true;				}				return $.Deferred.reject('API failed to upload file');			}); };

var createFileTalkpage = function(api, fileTitle) { return api.postWithToken('csrf', {			action: 'edit',			format: 'json',			title: fileTitle.getTalkPage.toString,			text: '',			summary: 'WikiProject tagging (using Covery)',			createonly: true		}); };

/**	 * @param {String} pageTitle * @returns {Promise} {wikitext: {String} Revision wikitext, timestamp: {String} last edit timestamp} */	var getRevisionWikitext = function getRevisionWikitext(api, pageTitle) { return api .get({				action: 'query',				format: 'json',				prop: 'revisions',				titles: pageTitle,				rvprop: 'timestamp|content',				rvslots: 'main'			}) .then(function(response) {				return $.map(response.query.pages, function(page) { return { wikitext: page.revisions[0].slots.main['*'], timestamp: page.revisions[0].timestamp };				})[0];			});	};

var paramByName = function paramByName(name) { return function(param) { return param.name === name; };	};

var makeInfoboxWikitext = function makeInfoboxWikitext(originalInfobox, newParameters) { var updatedParametersWikitext = originalInfobox.params.map(function(param) {			var updatedParam = newParameters.find(paramByName(param.name));			return ( param.wikitext.name + (updatedParam ? ' ' + updatedParam.value + '\n' : param.wikitext.value) );		});		var originalParametersList = originalInfobox.params.map(function(param) {			return param.name;		}); var parametersToAddWikitext = newParameters .filter(function(param) {				return !originalParametersList.includes(param.name);			}) .map(function(param) {				return '|' + param.name + ' = ' + param.value + '\n';			});

return (			''		); };

var updateWikitext = function(revisionWikitext, infobox, updatedParams) { if (revisionWikitext.indexOf(infobox.wikitext) === -1) { return $.Deferred.reject('Edit conflict'); }		var newInfobox = makeInfoboxWikitext(infobox, updatedParams); return revisionWikitext.replace(infobox.wikitext, newInfobox); };

var editPage = function(api, pageTitle, wikitext, timestamp) { return api.postWithToken('csrf', {			action: 'edit',			title: pageTitle,			text: wikitext,			summary: 'Added cover image (using Covery)',			basetimestamp: timestamp,			nocreate: true		}); };

var updatePage = function updatePage(api, page, infobox, updatedParams) { return getRevisionWikitext(api, page) .then(function(revision) {				return $.when( updateWikitext(revision.wikitext, infobox, updatedParams), revision.timestamp );			})			.then(function(updatedWikitext, timestamp) {				return editPage(api, page, updatedWikitext, timestamp);			}); };

var updateTalkpageWikitext = function updateTalkpageWikitext(revisionWikitext) { /* Redirects to : //   Template:Cvgproj', //   Template:WikiProject Video Games', //   Template:WPVG', //   Template:Vgproj', //   Template:Wpvg', //   Template:WP video games', //   Template:WP cvg', //   Template:WikiProject Rockstar Games', //   Template:WGVG', //   Template:WP Video games', //   Template:WikiProject VG', //   Template:WikiProject video games (redirect page) */		var bannerPattern = /\{\{\s*([Ww](?:P|p|G|ikiProject) ?c?[Vv](?:ideo )?[Gg](?:ames)?|[Cc]?[Vv]gproj|[Ww]ikiProject Rockstar Games)\s*(\|(?:.|\n)*?(?:(?:\{\{(?:.|\n)*?(?:(?:\{\{(?:.|\n)*?\}\})(?:.|\n)*?)*?\}\})(?:.|\n)*?)*|)\}\}\n?/; var banner = bannerPattern.exec(revisionWikitext); var noBannerPrersent = !banner || !banner[0]; if (noBannerPrersent) { return '\n' + revisionWikitext; }		var noParamsInBanner = !banner[2]; if (noParamsInBanner) { return false; }		var params = getTemplateParameters(banner[2]); var coverParam = getTemplateParameters(banner[2]).find(paramByName('cover')); if (!coverParam) { return false; }		var updatedBannerWikitext = banner[0].replace(			coverParam.wikitext.name + coverParam.wikitext.value,			''		); return revisionWikitext.replace(banner[0], updatedBannerWikitext); };

var updateTalkpage = function updateTalkpage(api, page) { var talkpageTitle = mw.Title.newFromText(page).getTalkPage; var talkpage = talkpageTitle && talkpageTitle.toString; return getRevisionWikitext(api, talkpage) .then(function(revision) {				return $.when(updateTalkpageWikitext(revision.wikitext), revision.timestamp);			}) .then(function(updatedWikitext, timestamp) {				if (!updatedWikitext) {					return 'Done';				}				return editPage(api, talkpage, updatedWikitext, timestamp);			}); };

var CoveryDialog = function CoveryDialog(config) { CoveryDialog.super.call(this, config); };	OO.inheritClass(CoveryDialog, OO.ui.ProcessDialog);

CoveryDialog.static.name = 'coveryDialog'; CoveryDialog.static.title = 'Covery'; CoveryDialog.static.size = 'large'; CoveryDialog.static.actions = [ { flags: ['primary', 'progressive'], label: 'Upload', action: 'upload' }, { flags: 'safe', label: 'Cancel' } ];

// Customize the initialize function to add content and layouts: CoveryDialog.prototype.initialize = function { CoveryDialog.super.prototype.initialize.call(this);

this.panel = new OO.ui.PanelLayout({ padded: true, expanded: false });

/* Form content: */ this.content = new OO.ui.FieldsetLayout;

this.fileSelect = new OO.ui.SelectFileWidget({			droppable: true,			showDropTarget: true,			// thumbnailSizeLimit: 0,			$element: $(" ")		}); // this.fileSelect.$element // 	.find(".oo-ui-selectFileWidget-dropTarget").css({"height":"auto"}); // this.fileSelect.$element // 	.find(".oo-ui-selectFileWidget-thumbnail").css({"display":"none"}); // this.fileSelect.$element // 	.find(".oo-ui-selectFileInputWidget-info").css({"margin":"0"}); this.urlInput = new OO.ui.TextInputWidget({			type: 'url',			placeholder: 'http://'		}); this.imagePreview = new OO.ui.LabelWidget({ label: '...' }); this.titleInput = new OO.ui.TextInputWidget({ required: true }); this.captionInput = new OO.ui.TextInputWidget; this.altTextInput = new OO.ui.TextInputWidget; this.developerInput = new OO.ui.TextInputWidget({ required: true }); this.publisherInput = new OO.ui.TextInputWidget({ required: true }); this.platformInput = new OO.ui.MenuTagMultiselectWidget({			inputPosition: 'inline',			allowDisplayInvalidTags: true,			allowArbitrary: true		});

this.fileSelectField = new OO.ui.FieldLayout(this.fileSelect, {			label: 'Upload a file...',			align: 'top'		}); this.fileSelect.field = this.fileSelectField; this.urlInputField = new OO.ui.FieldLayout(this.urlInput, {			label: '...or enter a URL',			align: 'left'		}); this.urlInput.field = this.urlInputField; this.imagePreviewField = new OO.ui.FieldLayout(this.imagePreview, {			label: 'Preview:',			align: 'top'		}); this.titleInputField = new OO.ui.FieldLayout(this.titleInput, {			label: 'File name',			align: 'top'		}); this.titleInputField.$element .find(".oo-ui-fieldLayout-messages").css({"margin-top":"2em"}); // prevent errors overlapping input this.captionInputField = new OO.ui.FieldLayout(this.captionInput, {			label: 'Caption',			align: 'left'		}); this.altTextInputField = new OO.ui.FieldLayout(this.altTextInput, {			label: 'Alt text',			align: 'left'		}); this.developerInputField = new OO.ui.FieldLayout(this.developerInput, {			label: 'Developer',			align: 'left'		}); this.publisherInputField = new OO.ui.FieldLayout(this.publisherInput, {			label: 'Publisher',			align: 'left'		}); this.platformInputField = new OO.ui.FieldLayout(this.platformInput, {			label: 'Platform(s)',			align: 'left'		});

this.content.addItems([			this.fileSelectField,			// this.urlInputField, 			this.titleInputField,			//this.imagePreviewField,			this.captionInputField,			this.altTextInputField,			this.developerInputField,			this.publisherInputField,			this.platformInputField		]);

/* Progress status content: */ this.progressStatusContent = new OO.ui.FieldsetLayout({			label: 'Status'		}); this.progressBar = new OO.ui.ProgressBarWidget({			progress: 0		}); this.progressField = new OO.ui.FieldLayout(this.progressBar, {			label: '',			align: 'below'		}); this.progressStatusContent.addItems([this.progressField]); this.progressStatusContent.toggle(false); //hide

this.panel.$element.append([this.content.$element, this.progressStatusContent.$element]); this.$body.append(this.panel.$element);

this.fileSelect.connect(			this,			{ change: 'onFileSelectChange' }		); this.urlInput.connect(			this,			{ change: 'onUrlInputChange' }		); this.titleInput.connect(			this,			{ flag: 'onTitleInputFlag' }		); this.developerInput.connect(			this,			{ change: 'onRequiredInputChange' }		); this.publisherInput.connect(			this,			{ change: 'onRequiredInputChange' }		); this.platformInput.connect(			this,			{ add: 'onPlatformInputAdd' }		); (function(self) {			self.platformInput.$element.find('input').on('blur', function { self.onPlatformInputBlur.call(self); });		})(this); };

CoveryDialog.prototype.onFileChosen = function(filePromise, fileName, widgetUsed, otherWidget) { widgetUsed.pushPending; widgetUsed.field.setErrors([]); otherWidget.setDisabled(true); var self = this; $.when(filePromise) .then(function(file) {				return resizeImageFile(file, 100000);			}) .then(				function(resizedFile, resizedDataURL) {					self.resizedFile = resizedFile;					self.imagePreview.$element						.empty						.show						.append($(' ').attr('src', resizedDataURL));					self.updateSize;					widgetUsed.popPending;					if (widgetUsed.setIndicator) {						widgetUsed.setIndicator('required');					}					otherWidget.setDisabled(false);					if (otherWidget.setIndicator) {						otherWidget.setIndicator(null);					}					self.titleInput.setValue(fileName);					self.onRequiredInputChange;				},				function(code) {					var errorMessage = code						? 'An error occured: ' + code						: 'An unexpected error occured';					self.resizedFile = null;					widgetUsed.popPending;					if (widgetUsed.setIndicator) {						widgetUsed.setIndicator('clear');					}					widgetUsed.field.setErrors([errorMessage]);					otherWidget.setDisabled(false);					if (otherWidget.setIndicator) { otherWidget.setIndicator(null); }					self.onRequiredInputChange; }			);	};

CoveryDialog.prototype.onFileSelectChange = function(files) { var file = files && files[0]; if (!file || !file.name) { return; }		this.onFileChosen(file, file.name, this.fileSelect, this.urlInput); };

CoveryDialog.prototype.onUrlInputChange = function(value) { if (!value) { this.urlInput.setIcon(null); return; }		var hasImageExtension = /\.(?:gif|png|jpe?g|svg|tiff?)$/i.test(value); if (!hasImageExtension) { this.urlInput.setIcon('ellipsis'); return; }		var filePromise = fetch(value, {mode: 'no-cors'}).then(function(result) {			return result.blob;		}); var fileName = value.replace(/^.*\//, ''); this.onFileChosen(filePromise, fileName, this.urlInput, this.fileSelect); };

CoveryDialog.prototype.onTitleInputFlag = function(flag) { if (flag.invalid === true) { if (this.titleInput.getValue.length) { this.titleInputField.setErrors(['Invalid file name']); }			this.actions.setAbilities({				upload: false			}); } else { this.onRequiredInputChange; }	};

CoveryDialog.prototype.checkMimes = function { var mimeLookup = { '.bmp': 'image/bmp', '.gif': 'image/gif', '.jpeg': 'image/jpeg', '.jpg': 'image/jpeg', '.png': 'image/png', '.svg': 'image/svg+xml', '.tif': 'image/tiff', '.tiff': 'image/tiff' };		var fileMime = (this.resizedFile && this.resizedFile.type) || ''; var titleParts = this.titleInput .getValue .toLowerCase .match(/.*(\..*)$/, '$1'); var titleExtension = titleParts && titleParts[1]; var impliedTitleMime = mimeLookup[titleExtension] || ''; return fileMime === impliedTitleMime; };

// Only allow uploading if requirements are met CoveryDialog.prototype.onRequiredInputChange = function(change) { var self = this; $.when((change && change.titleIsValid) || this.titleInput.getValidity).then(			function {				// remove any old title input errors				self.titleInputField.setErrors([]);				// check file mime matches title mime				var titleHasCorrectExtension = self.checkMimes;				if (!titleHasCorrectExtension && self.resizedFile) {					self.titleInputField.setErrors([ 'Invalid file extension (file is a ' +							self.resizedFile.type								.replace('image/', )								.replace(/\+.*$/, )								.toUpperCase +							' image)' ]);				}

var requirementsMet = !self.fileSelect.isPending && !self.urlInput.isPending && !!self.resizedFile && !!self.titleInput.getValue.length && titleHasCorrectExtension && !!self.developerInput.getValue.length && !!self.publisherInput.getValue.length; self.actions.setAbilities({					upload: requirementsMet				}); },			function { if (self.titleInput.getValue.length) { self.titleInputField.setErrors(['Invalid file name']); }				self.actions.setAbilities({					upload: false				}); }		);	};

CoveryDialog.prototype.onPlatformInputAdd = function(item) { this.api .get({				action: 'query',				format: 'json',				titles: 'Category:' + item.data + ' game covers'			}) .then(function(response) {				return $.map(response.query.pages, function(page) { return page.missing !== ''; })[0];			})			.then(function(isValid) {				item.toggleValid(isValid);			}); };

CoveryDialog.prototype.onPlatformInputBlur = function { this.platformInput.doInputEnter; };

// Specify the dialog height (or don't to use the automatically generated height). CoveryDialog.prototype.getBodyHeight = function { return this.panel.$element.outerHeight(true); };

// Set up the window with data passed to it at the time of opening. CoveryDialog.prototype.getSetupProcess = function(data) { data = data || {}; return CoveryDialog.super.prototype.getSetupProcess.call(this, data).next(function {			this.uploaded = false;			this.createdFileTalkpage = false;			this.updatedArticle = false;

this.api = data.api; this.infobox = data.infobox; this.pageName = data.pageName; var developerParam = data.infobox.params.find(paramByName('developer')); var publisherParam = data.infobox.params.find(paramByName('publisher')); this.developerInput.setValue((developerParam && developerParam.value) || ''); this.publisherInput.setValue((publisherParam && publisherParam.value) || ''); this.titleInput.setValidation(function(value) {				var title = mw.Title.newFromFileName(value);				if (title === null) {					return false;				}				return data.api					.get({ action: 'query', format: 'json', prop: 'imageinfo', titles: title.toString, iiprop: '' })					.then(function(response) { return $.map(response.query.pages, function(page) {							return page.missing ===  && page.imagerepository === ;						})[0]; });			});			var self = this; data.api .get({					action: 'query',					format: 'json',					list: 'categorymembers',					cmtitle: 'Category:Video game covers',					cmprop: 'title',					cmtype: 'subcat',					cmlimit: 'max'				}) .then(function(response) {					return response.query.categorymembers						.map(function(category) { return category.title; })						.map(function(categoryTitle) { return { data: categoryTitle.replace(/Category\:(.+) game covers/, '$1') };						});				})				.then(function(platforms) {					self.platformInput.addOptions(platforms);				}); }, this);	};

CoveryDialog.prototype.setProgressStatus = function(label, progress) { this.progressBar.setProgress(progress); this.progressField.setLabel(label); };

CoveryDialog.prototype.setProgressError = function(label, progress) { this.getActions.forEach(null, function(actionWidget) {			if (actionWidget.getAction === 'upload') {				actionWidget.setLabel('Retry');			}			actionWidget.setDisabled(false);		}); this.setProgressStatus(label, progress); };

// Specify processes to handle the actions. CoveryDialog.prototype.getActionProcess = function(action) { if (action === 'upload') { this.getActions.forEach(null, function(actionWidget) {				actionWidget.setDisabled(true);			}); this.content.toggle(false); // hide this.progressStatusContent.toggle(true); // show this.setProgressStatus('Uploading...', 1); var self = this;

var fileTitle = mw.Title.newFromFileName(this.titleInput.getValue); return new OO.ui.Process(function {				var platformValues = this.platformInput.getItems.map(function(item) { return item.getData; });				return ( this.uploaded || uploadFile(						this.api,						this.resizedFile,						makeDescriptionText( new mw.Title(this.pageName), this.developerInput.getValue, this.publisherInput.getValue, platformValues ),						fileTitle					).then(						function {							return true;						},						function(errorCode) {							self.setProgressError.call(self, 'Failed', 1);							return $.Deferred.reject( new OO.ui.Error('Error uploading: ' + errorCode) );						}					)				);			}, this) .next(function {					this.uploaded = true;					this.setProgressStatus('Uploaded file!', 25);				}, this) .next(function {					this.setProgressStatus('Uploaded file! Creating file talk page...', 26);					return ( this.createdFileTalkpage || createFileTalkpage(this.api, fileTitle).then(							function {								return true;							},							function(errorCode) {								self.setProgressError.call( self, 'Uploaded file! Failed to create file talk page.', 26								);								return $.Deferred.reject( new OO.ui.Error('Error creating file talk page: ' + errorCode) );							}						)					);				}, this) .next(function {					this.createdFileTalkpage = true;					this.setProgressStatus('Uploaded file! Created file talk page!', 50);				}, this) .next(function {					this.setProgressStatus( 'Uploaded file! Created file talk page! Updating article...', 51					);					var updatedParams = [						{ name: 'image', value: fileTitle.getMainText },						{ name: 'caption', value: this.captionInput.getValue },						{ name: 'alt', value: this.altTextInput.getValue },						{ name: 'publisher', value: this.publisherInput.getValue },						{ name: 'developer', value: this.developerInput.getValue }					];					return ( this.updatedArticle || updatePage(this.api, this.pageName, this.infobox, updatedParams).then(							function {								return true;							},							function(errorCode) {								self.setProgressError.call( self, 'Uploaded file! Created file talk page! Failed to update article.', 51								);								return $.Deferred.reject( new OO.ui.Error('Error editing article: ' + errorCode) );							}						)					);				}, this) .next(function {					this.updatedArticle = true;					this.setProgressStatus( 'Uploaded file! Created file talk page! Updated article!', 75					);				}, this) .next(function {					this.setProgressStatus( 'Uploaded file! Created file talk page! Updated article! Updating article talk page...', 76					);					return updateTalkpage(this.api, this.pageName).then( function { return true; },						function(errorCode) { self.setProgressError.call(								self,								'Uploaded file! Created file talk page! Updated article! Failed to update article talk page.',								76							); return $.Deferred.reject(								new OO.ui.Error('Error editing article talk page: ' + errorCode)							); }					);				}, this) .next(function {					this.setProgressStatus('All done! Reloading article...', 100);					return 1200;				}, this) .next(function {					return this.close({ sucess: true });				}, this); } else if (action === 'cancel') { return new OO.ui.Process(function {				return this.close;			}, this); }		// Fallback to parent handler return CoveryDialog.super.prototype.getActionProcess.call(this, action); };

// Use the getTeardownProcess method to perform actions whenever the dialog is closed. // This method provides access to data passed into the window's close method // or the window manager's closeWindow method. CoveryDialog.prototype.getTeardownProcess = function(data) { return CoveryDialog.super.prototype.getTeardownProcess.call(this, data).first(function {			// Perform any cleanup as needed		}, this); };

var showDialog = function showDialog(data) { var coveryWindowFactory = new OO.Factory; coveryWindowFactory.register(CoveryDialog); var mainWindowManager = new OO.ui.WindowManager({			factory: coveryWindowFactory		}); $('body').append(mainWindowManager.$element); var instance = mainWindowManager.openWindow('coveryDialog', data); return instance.closed; };

var startCovery = function startCovery(api, pageName) { return getLeadWikitext(api, pageName) .then(getInfoboxTemplate) .then(checkInfobox) .then(function(infobox) {				return showDialog({ api: api, pageName: pageName, infobox: infobox });			})			.then(				function(data) {					if (data && data.sucess) {						window.location.reload;					}				},				function(error) {					var errorIsString = error === error.toString;					var errorMessage = errorIsString ? 'Error: ' + error : error.toString;					OO.ui.alert(errorMessage);				}			); };

$.when(isSuitable, $.ready).then(function(config) {		var portletLink = mw.util.addPortletLink('p-tb', '#', 'Upload cover', 'tb-covery');		$(portletLink).click(function(e) { e.preventDefault; var api = new mw.Api({				ajax: {					headers: {						'Api-User-Agent':							SCRIPT.name +							'/' +							SCRIPT.version +							' ( https://en.wikipedia.org/wiki/User:Evad37/Covery )'					}				}			}); startCovery(api, config.wgPageName); });	});

/* ========== SANDBOX USAGE ONLY (remove when updating main script) ========== */ SCRIPT.version += '/sandbox'; SCRIPT.ad = ' (using Covery/sandbox)'; function setupForTesting { /*       window.QUnit = { config: { autoStart: false }       };        */		$(' ')			.attr('id', 'qunit') .insertBefore('#firstHeading'); return true; // mw.loader.using('jquery.qunit'); }	if (mw.config.get('wgNamespaceNumber') === 0) { $.when($.ready).then(function {			var FakeApi = function {				this.realApi = new mw.Api({ ajax: { headers: { 'Api-User-Agent': SCRIPT.name + '/' +								SCRIPT.version + ' ( https://en.wikipedia.org/wiki/User:Evad37/Covery )' }					}				});			};			FakeApi.prototype.get = function(query) {				return this.realApi.get(query);			};			FakeApi.prototype.postWithToken = function(token, params) {				console.log(params);				if (Math.random < 0.5) {					return $.Deferred.reject('Random error');				}				var response = {};				response[params.action] = { result: 'Success' };				return $.Deferred.resolve(response);			};

var portletLink = mw.util.addPortletLink('p-tb', '#', 'Test covery', 'tb-testcovery'); $(portletLink).click(function(e) {				e.preventDefault;				startCovery(new FakeApi, mw.config.get('wgPageName'));			}); });	} else if ( mw.config.get('wgPageName').includes('User:Evad37/Covery/') && mw.config.get('wgAction') === 'view' && mw.config.get('wgDiffOldId') === null ) {		$.when(setupForTesting, $.ready).then(function { mw.loader.load('https://en.wikipedia.org/w/index.php?title=User:Evad37/qunit-2.8.0.css&action=raw&ctype=text/css', 'text/css'); $.getScript('https://en.wikipedia.org/w/index.php?title=User:Evad37/qunit-2.8.0.js&action=raw&ctype=text/javascript', function {				/* globals QUnit */				QUnit.module('Get parameters from a template');				QUnit.test('Single unnamed param', function(assert) { var params1 = getTemplateParameters('|one'); var expected1 = [ {							name: '1', value: 'one', wikitext: { name: '|', value: 'one' }						}					];					var params2 = getTemplateParameters('| one '); var expected2 = [ {							name: '1', value: 'one', wikitext: { name: '|', value: ' one ' }						}					];					assert.deepEqual(params1, expected1, 'unspaced'); assert.deepEqual(params2, expected2, 'spaced'); });				QUnit.test('Two unnamed params', function(assert) { var params1 = getTemplateParameters('|one|two'); var expected1 = [ {							name: '1', value: 'one', wikitext: { name: '|', value: 'one' }						},						{							name: '2', value: 'two', wikitext: { name: '|', value: 'two' }						}					];					var params2 = getTemplateParameters('| one | two '); var expected2 = [ {							name: '1', value: 'one', wikitext: { name: '|', value: ' one ' }						},						{							name: '2', value: 'two', wikitext: { name: '|', value: ' two ' }						}					];					assert.deepEqual(params1, expected1, 'unspaced'); assert.deepEqual(params2, expected2, 'spaced'); });				QUnit.test('Single named param', function(assert) { var params1 = getTemplateParameters('|a=apple'); var expected1 = [ {							name: 'a', value: 'apple', wikitext: { name: '|a=', value: 'apple' }						}					];					var params2 = getTemplateParameters('| a = apple '); var expected2 = [ {							name: 'a', value: 'apple', wikitext: { name: '| a =', value: ' apple ' }						}					];					assert.deepEqual(params1, expected1, 'unspaced'); assert.deepEqual(params2, expected2, 'spaced'); });				QUnit.test('Two named params', function(assert) { var params1 = getTemplateParameters('|a=apple|b=bannana'); var expected1 = [ {							name: 'a', value: 'apple', wikitext: { name: '|a=', value: 'apple' }						},						{							name: 'b', value: 'bannana', wikitext: { name: '|b=', value: 'bannana' }						}					];					var params2 = getTemplateParameters('| a = apple | b = bannana '); var expected2 = [ {							name: 'a', value: 'apple', wikitext: { name: '| a =', value: ' apple ' }						},						{							name: 'b', value: 'bannana', wikitext: { name: '| b =', value: ' bannana ' }						}					];					assert.deepEqual(params1, expected1, 'unspaced'); assert.deepEqual(params2, expected2, 'spaced'); });				QUnit.test('Named and unnamed params', function(assert) { var params1 = getTemplateParameters('|one|a=apple|two|b=bannana'); var expected1 = [ {							name: '1', value: 'one', wikitext: { name: '|', value: 'one' }						},						{							name: 'a', value: 'apple', wikitext: { name: '|a=', value: 'apple' }						},						{							name: '2', value: 'two', wikitext: { name: '|', value: 'two' }						},						{							name: 'b', value: 'bannana', wikitext: { name: '|b=', value: 'bannana' }						}					];					var params2 = getTemplateParameters('| one | a = apple | two | b = bannana '); var expected2 = [ {							name: '1', value: 'one', wikitext: { name: '|', value: ' one ' }						},						{							name: 'a', value: 'apple', wikitext: { name: '| a =', value: ' apple ' }						},						{							name: '2', value: 'two', wikitext: { name: '|', value: ' two ' }						},						{							name: 'b', value: 'bannana', wikitext: { name: '| b =', value: ' bannana ' }						}					];					assert.deepEqual(params1, expected1, 'unspaced'); assert.deepEqual(params2, expected2, 'spaced'); });				QUnit.test('Empty unnamed param in middle', function(assert) { var params1 = getTemplateParameters('|one||three'); var expected1 = [ {							name: '1', value: 'one', wikitext: { name: '|', value: 'one' }						},						{							name: '2', value: '', wikitext: { name: '|', value: '' }						},						{							name: '3', value: 'three', wikitext: { name: '|', value: 'three' }						}					];					var params2 = getTemplateParameters('||two|three'); var expected2 = [ {							name: '1', value: '', wikitext: { name: '|', value: '' }						},						{							name: '2', value: 'two', wikitext: { name: '|', value: 'two' }						},						{							name: '3', value: 'three', wikitext: { name: '|', value: 'three' }						}					];					var params3 = getTemplateParameters('|one|two|'); var expected3 = [ {							name: '1', value: 'one', wikitext: { name: '|', value: 'one' }						},						{							name: '2', value: 'two', wikitext: { name: '|', value: 'two' }						},						{							name: '3', value: '', wikitext: { name: '|', value: '' }						}					];					assert.deepEqual(params1, expected1, 'middle'); assert.deepEqual(params2, expected2, 'start'); assert.deepEqual(params3, expected3, 'end'); });

QUnit.module('Get infobox from wikitext'); QUnit.test('Simple infobox', function(assert) {					var wikitext1 = ;					var infobox1 = getInfoboxTemplate(wikitext1);					var expected = {						name: 'Infobox Video Game',						params: [							{								name: 'publisher',								value: 'PUB',								wikitext: {									name: '|publisher=',									value: 'PUB\n'								}							},							{								name: 'developer',								value: 'DEV',								wikitext: {									name: '|developer=',									value: 'DEV\n'								}							}						],						wikitext: 					};					var wikitext2 =						'Lorem Ipsum Dorem Sum';					var infobox2 = getInfoboxTemplate(wikitext2);					// expect same output

assert.deepEqual(infobox1, expected, 'Just the infobox'); assert.deepEqual(infobox2, expected, 'Inside other text'); });				QUnit.test('Empty infobox', function(assert) { var wikitext1 = ''; var infobox1 = getInfoboxTemplate(wikitext1); var expected = { name: 'Infobox Video Game', params: [], wikitext: '' };					var wikitext2 = 'Lorem Ipsum Dorem Sum'; var infobox2 = getInfoboxTemplate(wikitext2); // expect same output

assert.deepEqual(infobox1, expected, 'Just the infobox'); assert.deepEqual(infobox2, expected, 'Inside other text'); });				QUnit.test('Real infobox', function(assert) { var wikitext = '\n' + '\n' + "Attack Force is a 1980 video game developed by Big Five Software for the TRS-80 16K."; var infobox = getInfoboxTemplate(wikitext); var expected = { name: 'Infobox video game', params: [ {								name: 'collapsible', value: '', wikitext: { name: '| collapsible =', value: ' \n' } },							{								name: 'state', value: '', wikitext: { name: '| state =', value: ' \n' } },							{								name: 'italic title', value: '', wikitext: { name: '| italic title =', value: ' \n' } },							{								name: 'title', value: 'Attack Force', wikitext: { name: '| title =', value: ' Attack Force\n' } },							{								name: 'image', value: '', wikitext: { name: '| image =', value: ' \n' } },							{								name: 'alt', value: '', wikitext: { name: '| alt =', value: ' \n' } },							{								name: 'caption', value: '', wikitext: { name: '| caption =', value: ' \n' } },							{								name: 'developer', value: 'Big Five Software', wikitext: { name: '| developer =', value: ' Big Five Software\n' }							},							{								name: 'publisher', value: 'Big Five Software', wikitext: { name: '| publisher =', value: ' Big Five Software\n' }							},							{								name: 'designer', value: 'Bill Hogue Jeff Konyu ', wikitext: { name: '| designer =', value: ' Bill Hogue Jeff Konyu \n' }							},							{								name: 'series', value: '', wikitext: { name: '| series =', value: ' \n' } },							{								name: 'platforms', value: 'TRS-80', wikitext: { name: '| platforms =', value: ' TRS-80\n' } },							{								name: 'released', value: ' WW: 1980 ',								wikitext: { name: '| released =', value: ' WW: 1980 \n' }							},							{								name: 'genre', value: '', wikitext: { name: '| genre =', value: ' \n' } },							{								name: 'modes', value: '', wikitext: { name: '| modes =', value: ' \n' } }						],						wikitext: '\n' };					assert.deepEqual(infobox, expected, 'Attack Force (video game)'); });

QUnit.module('toSentanceCase'); QUnit.test('Starting with letters', function(assert) {					var sentanceCased1 = toSentanceCase('one');					var expected1 = 'One';					var sentanceCased2 = toSentanceCase('Two');					var expected2 = 'Two';					var sentanceCased3 = toSentanceCase('thRee');					var expected3 = 'ThRee';					var sentanceCased4 = toSentanceCase('FOUR');					var expected4 = 'FOUR';					assert.equal(sentanceCased1, expected1, 'From lowercase');					assert.equal(sentanceCased2, expected2, 'From sentance case');					assert.equal(sentanceCased3, expected3, 'From capital in middle');					assert.equal(sentanceCased4, expected4, 'From upper case');				}); QUnit.test('Not starting with letters', function(assert) {					var sentanceCased1 = toSentanceCase('1 is one');					var expected1 = '1 is one';					var sentanceCased2 = toSentanceCase();					var expected2 = ;					var sentanceCased3 = toSentanceCase('_e');					var expected3 = '_e';					var sentanceCased4 = toSentanceCase('§₳♥');					var expected4 = '§₳♥';					assert.equal(sentanceCased1, expected1, 'Starting with number');					assert.equal(sentanceCased2, expected2, 'Starting with brace');					assert.equal(sentanceCased3, expected3, 'Starting with underscore');					assert.equal(sentanceCased4, expected4, 'All symbols');				}); //checkInfobox QUnit.module('Infobox check'); var infoboxesExpectedToPass = [ ,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,					,									];				infoboxesExpectedToPass.forEach(infobox => {					QUnit.test(infobox, function(assert) { let infoboxObject = getInfoboxTemplate(infobox); let checkedInfobox = checkInfobox(infoboxObject); assert.deepEqual(checkedInfobox, infoboxObject, infobox + ' passes check'); });				});				QUnit.module('Infobox check (expected fail)'); var infoboxesExpectedToFail = [ ,					,					,					,					,									];				infoboxesExpectedToFail.forEach(infobox => {					QUnit.test(infobox, function(assert) { let infoboxObject = getInfoboxTemplate(infobox); assert.throws(function {							return checkInfobox(infoboxObject);						}, infobox + ' throws an error'); });				});

QUnit.module('Make infobox wikitext'); QUnit.test('Add a param', function(assert) {					var newParams = [{ name: 'image', value: 'Foo.png' }];					var infobox1 = getInfoboxTemplate();					var wikitext1 = makeInfoboxWikitext(infobox1, newParams);					var expected1 = ;					var infobox2 = getInfoboxTemplate();					var wikitext2 = makeInfoboxWikitext(infobox2, newParams);					var expected2 = ;					assert.equal(wikitext1, expected1, 'Empty infobox');					assert.equal(wikitext2, expected2, 'With one other param');				}); QUnit.test('Override empty param', function(assert) {					var newParams = [{ name: 'image', value: 'Foo.png' }];					var infobox1 = getInfoboxTemplate();					var wikitext1 = makeInfoboxWikitext(infobox1, newParams);					var expected1 = ;					var infobox2 = getInfoboxTemplate( 					);					var wikitext2 = makeInfoboxWikitext(infobox2, newParams);					var expected2 =						;					assert.equal(wikitext1, expected1, 'Otherwise empty infobox');					assert.equal(wikitext2, expected2, 'With one other param');				}); QUnit.test('Override 1 empty param, 1 non-empty param', function(assert) {					var newParams = [						{ name: 'image', value: 'Foo.png' },						{ name: 'developer', value: 'DEV' }					];					var infobox1 = getInfoboxTemplate( 					);					var wikitext1 = makeInfoboxWikitext(infobox1, newParams);					var expected1 =						;					var infobox2 = getInfoboxTemplate( 					);					var wikitext2 = makeInfoboxWikitext(infobox2, newParams);					var expected2 =						;					assert.equal(wikitext1, expected1, 'No other params');					assert.equal(wikitext2, expected2, 'With one other param');				});

QUnit.module('Make description text'); QUnit.test('No platforms', function(assert) {					var description = makeDescriptionText(new mw.Title('title'), 'dev', 'pub', []);					var expected =						'==Summary==\n\n' +						'==Licensing==\n';					assert.equal(description, expected, 'No platforms');				}); QUnit.test('One platform', function(assert) {					var description = makeDescriptionText(new mw.Title('title'), 'dev', 'pub', [ 'Platform' ]);					var expected =						'==Summary==\n\n' +						'==Licensing==\n';					assert.equal(description, expected, 'One platform');				}); QUnit.test('Two platforms', function(assert) {					var description = makeDescriptionText(new mw.Title('title'), 'dev', 'pub', [ 'Platform1', 'Platform2' ]);					var expected =						'==Summary==\n\n' +						'==Licensing==\n';					assert.equal(description, expected, 'Two platforms');				}); }); // end of "get script QUnit"		}); // end of "when set up for testing" callback } // end of "else if a covery test page" /* ========== end of SANDBOX USAGE ONLY (remove when updating main script) ========== */ }; // end of main wrapper function

mw.loader .using([		'mediawiki.util',		'mediawiki.api',		'oojs-ui-core',		'oojs-ui-widgets',		'oojs-ui-windows'	]) .then(setupCovery);

/*); //

/*   QUnit.module("Name for group of tests"); QUnit.test("Some test", function( assert ) {       assert.ok(someCondition, "Description");        assert.notOk(someCondition, "Description");        assert.equal(firstVar, secondVar, "Description")        assert.deepEqual(firstObject, secondObject, "Description")    }); QUnit.test("Some async test", function( assert ) {       assert.expect;        var done = assert.async;        $.when( // Some async code... ).then(function { // asserts go here, then... done; })   });