Користувач:Base/ajaxQuickDelete.js

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

Увага: Після публікування слід очистити кеш браузера, щоб побачити зміни.

  • Firefox / Safari: тримайте Shift, коли натискаєте Оновити, або натисніть Ctrl-F5 чи Ctrl-Shift-R (⌘-R на Apple Mac)
  • Google Chrome: натисніть Ctrl-Shift-R (⌘-Shift-R на Apple Mac)
  • Internet Explorer / Edge: тримайте Ctrl, коли натискаєте Оновити, або натисніть Ctrl-F5
  • Opera: натисніть Ctrl-F5
// Original code written by [[User:Ilmari Karonen]]
// Rewritten & extended by [[User:DieBuche]]. Botdetection and encoding fixer by [[User:Lupo]]
// Translated into Ukrainian (partly) by [[User:Base]]
// Modified & optimized for uk.wiki by [[User:Andriy.v]]
//<nowiki>
if ( typeof( AjaxQuickDelete ) === 'undefined' ) {

window.AjaxQuickDelete = {

	/**
	 ** Set up the AjaxQuickDelete object and add the toolbox link.  Called via $( document ).ready() during page loading.
	 **/
	install: function ( ) {
		this.insertDSTagButton = [{
			label: this.i18n.toolboxLinkDeleteSlow,
			tag: '{\{subst:ds}}',
			summary: 'Номінація статті на відстрочене вилучення',	
		}];
		this.insertTagButtons = [{
				label: this.i18n.toolboxLinkAuthor,
				tag: '{\{subst:nld}}',
				talkTag: '{\{subst:Nostatus ask|1=%FILE%}}',
				imgSummary: 'не вказано автора файлу',
				talkSummary: 'автосповіщення про відсутність автора для %FILE%'
			}, {
				label: this.i18n.toolboxLinkSource,
				tag: '{\{subst:nsd}}',
				talkTag: '{\{subst:Nostatus ask|1=%FILE%}}',
				imgSummary: 'не вказано джерело файлу',
				talkSummary: 'автосповіщення про відсутність джерела для %FILE%'
			}, {
				label: this.i18n.toolboxLinkLicense,
				tag: '{\{subst:nld}}',
				talkTag: '{\{subst:Nostatus ask|1=%FILE%}}',
				imgSummary: 'не вказано ліцензію файлу',
				talkSummary: 'автосповіщення про відсутність ліцензії для %FILE%'
			}, {
				label: this.i18n.toolboxLinkPermission,
				tag: '{\{subst:npd}}',
				talkTag: '{\{subst:Image permission|1=%FILE%}}',
				imgSummary: 'відсутній дозвіл на використання файлу',
				talkSummary: 'автосповіщення про відсутність дозволу на використання %FILE%'
			},/* {
				label: this.i18n.toolboxLinkDisputed,
				tag: '{\{subst:dd}}',
				talkTag: '{\{subst:Запрос о статусе файла|1=%FILE%}}',
				imgSummary: 'сомнительный статус файла',
				talkSummary: '[[ВП:ППФ|автоматическое]] уведомление о сомнительном статусе %FILE%'
			},*/ {
				label: this.i18n.toolboxLinkDisputedFairuse,
				tag: '{\{subst:dfud|.%FUC1%%FUC2%%FUC3%%FUC11%%FUC4%%FUC5%%FUC6%%FUC7%%FUC8%%FUC9%%FUC10%}}',
				talkTag: '{\{subst:Disputed fair-use ask|1=%FILE%}}',
				imgSummary: 'файл не відповідає [[ВП:КДВ]]',
				talkSummary: 'автосповіщення про невідповідність [[ВП:КДВ]] %FILE%',
				promptText: {
					title: this.i18n.reasonForDisputedFairuse,
					questions: [{
						message: '1 — можлива вільна заміна;',
						type: 'checkbox',
						prefill: '|1',
						returnvalue: 'FUC1'
					}, {
						message: '2 — зменшує комерційну цінність початкового твору;',
						type: 'checkbox',
						prefill: '|2',
						returnvalue: 'FUC2'
					}, {
						message: '3а — не відповідає критерію мінімального використання;',
						type: 'checkbox',
						prefill: '|3',
						returnvalue: 'FUC3'
					}, {
              			message: '3б — не відповідає критерію роздільності/якості відтворення;',
                  		type: 'checkbox',
                  		prefill: '|3(б)',
                		returnvalue: 'FUC11'
           			}, {
                	 	message: '4 — не опубліковано раніше ніде;',
                		type: 'checkbox',
                  		prefill: '|4',
                	 	returnvalue: 'FUC4'
            		}, {
                	 	message: '5 — не має енциклопедичної значимості;',
                		type: 'checkbox',
                  		prefill: '|5',
                	 	returnvalue: 'FUC5'
               		}, {
                  		message: '6 — не відповідає критеріям використання цього типу файлів;',
                	 	type: 'checkbox',
                	 	prefill: '|6',
                	 	returnvalue: 'FUC6'
               		}, {
                	 	message: '7 — не використовується у статтях;',
                	 	type: 'checkbox',
                  		prefill: '|7',
                  		returnvalue: 'FUC7'
               		}, {
                	 	message: '8 — не має значущості для використання/декоративне використання;',
                  		type: 'checkbox',
                	 	prefill: '|8',
                		returnvalue: 'FUC8'
               		}, {
                  		message: '9 — не використовується у статтях;',
                  		type: 'checkbox',
                  		prefill: '|9',
                  		returnvalue: 'FUC9'
               		}, {
                		message: '10 — нема повноти опису/ОДВ.',
                  		type: 'checkbox',
                  		prefill: '|10',
	        			returnvalue: 'FUC10'
					}]
				}
			}, {
				label: this.i18n.toolboxLinkOrphanedFairuse,
				tag: '{\{subst:ofud}}',
				talkTag: '{\{subst:Orphaned-fairuse-notice|1=%FILE%}}',
				imgSummary: 'невикористовуваний невільний',
				talkSummary: 'автосповіщення про невідповідність [[ВП:КДВ]] %FILE%',
			}, {
				label: this.i18n.toolboxLinkRFU,
				tag: '{\{Db-rfu|%PARAMETER%}}',
				imgSummary: 'є вільна заміна на Вікісховищі ([[:File:%PARAMETER%]])',
				promptText: this.i18n.labelForRFU
			}, {				
            	label: this.i18n.toolboxLinkNoFoP,
            	tag: '{\{NoCommons|nofop}}',
        		imgSummary: 'nofop',
         	}, {
            	label: this.i18n.toolboxLinkNotHost,
            	tag: '{\{subst:nothost}}',
            	imgSummary: 'невикористовуваний файл, що не має енциклопедичної значимості',
         	}, {
            	label: this.i18n.toolboxLinkLivingPeople,
            	tag: '{\{subst:dfud}}{{LivingPeople}}',
            	talkTag: '{\{subst:Disputed fair-use ask2|1=%FILE%}}',
            	imgSummary: 'невільний файл, що зображує нині живу особу',
            	talkSummary: 'автосповіщення про те, що %FILE% — невільний файл нині живої особи'
         	}, {
            	label: this.i18n.toolboxLinkNowCommons,
        	 	tag: '{\{NowCommons|%PARAMETER%}}',
        	 	imgSummary: 'є у Вікісховищі ([[:File:%PARAMETER%]])',
        		promptText: this.i18n.labelForNowCommons
         	}, {				
            	label: this.i18n.toolboxLinkDbCommons,
            	tag: '{\{Db-commons}}',
        		imgSummary: 'є у Вікісховищі під такою самою назвою.',        		
         	},{
            	label: this.i18n.toolboxLinkMoveToCommons,
            	tag: '{\{Move to Commons|%PARAMETER%}}',
            	imgSummary: 'до перенесення на Вікісховище',
            	promptText: this.i18n.labelForMoveToCommons
			}];
		if ( typeof window.AjaxDeleteExtraButtons !== 'undefined' ) {
			this.insertTagButtons = this.insertTagButtons.concat( window.AjaxDeleteExtraButtons );
		}

		// Define optional buttons
		if ( mw.config.get( 'wgNamespaceNumber' ) === 6 && $( '#shared-image-desc' ).length === 0 ) {
			$.each( this.insertTagButtons, function ( k, v ) {
				mw.util.addPortletLink( 'p-tb', 'javascript:AjaxQuickDelete.insertTagOnPage("' + v.tag + '","'
				  + v.imgSummary + '","' + v.talkTag + '","' + v.talkSummary + '","'
				  + ( typeof v.promptText !== 'undefined' ? JSON.stringify( v.promptText ).replace( /"/g, '\\"' ) : v.promptText )
				  + '");', v.label );
			} );
		}
		if ( mw.config.get( 'wgNamespaceNumber' ) === 0 ) {
			$.each(this.insertDSTagButton, function ( k, v ) {
				mw.util.addPortletLink( 'p-tb', 'javascript:AjaxQuickDelete.insertTagOnPage("' + v.tag + '","'
				  + v.summary + '","' + v.talkTag + '","' + v.talkSummary + '","'
				  + ( typeof v.promptText !== 'undefined' ? JSON.stringify( v.promptText ).replace( /"/g, '\\"' ) : v.promptText )
				  + '");', v.label );
			});
		}
	},

	insertTagOnPage: function ( tag, imgSummary, talkTag, talkSummary, promptText, page ) {
		this.pageName = ( page === undefined ) ? mw.config.get( 'wgPageName' ).replace( /_/g, ' ' ) : page.replace( /_/g, ' ' );
		this.tag = tag + '\n';
		this.imgSummary = imgSummary;

		// first schedule some API queries to fetch the info we need...
		this.tasks = [];

		// get token
		this.addTask( 'findCreator' );

		this.addTask( 'prependTemplate' );

		if ( talkTag !== 'undefined' ) {
			this.talkTag = talkTag.replace( '%FILE%', this.pageName );
			this.talkSummary = talkSummary.replace( '%FILE%', '[[:' + this.pageName + ']]' );

			this.usersNeeded = true;
			this.addTask( 'notifyUploaders' );
		}
		this.addTask( 'reloadPage' );

		if ( promptText !== 'undefined' ) {
			promptText = JSON.parse( promptText );
			if ( typeof promptText === 'string' ) {
				this.prompt( [{
					message: '',
					prefill: '',
					returnvalue: 'reason',
					cleanUp: true,
					noEmpty: true
				}], promptText || this.i18n.reasonForDeletion );
			} else {
				this.prompt( promptText.questions, promptText.title );
			}
		} else {
			this.nextTask();
		}
	},

	nominateForDeletion: function ( page ) {
		this.pageName = ( page === undefined ) ? mw.config.get( 'wgPageName' ) : page;
		this.startDate = new Date();

		// set up some page names we'll need later
		this.requestPage = this.requestPagePrefix + this.pageName;
		this.dailyLogPage = this.requestPagePrefix + this.formatDate( 'YYYY/MM/DD' );

		this.tag = '{{delete|reason=%PARAMETER%|subpage=' + this.pageName + this.formatDate( '|year=YYYY|month=MON|day=DAY}}\n' );
		// On templates: Wrap inside <noinclude>s. Thanks Rillke
		if ( mw.config.get( 'wgNamespaceNumber' ) === 10 ) {
		  this.tag = '<noinclude>' + this.tag + '</noinclude>';
		}
		this.imgSummary = 'Nominating for deletion';
		this.talkTag = '{\{subst:idw|' + this.pageName + '}}';
		this.talkSummary = '[[:' + this.pageName + ']] has been nominated for deletion';
		this.subpageSummary = 'Starting deletion request';

		// first schedule some API queries to fetch the info we need...
		this.tasks = []; // reset task list in case an earlier error left it non-empty
		this.addTask( 'findCreator' );

		// ...then schedule the actual edits
		this.addTask( 'prependTemplate' );
		this.addTask( 'createRequestSubpage' );
		this.addTask( 'listRequestSubpage' );
		this.addTask( 'notifyUploaders' );

		// finally reload the page to show the deletion tag
		this.addTask( 'reloadPage' );

		this.prompt( [{
			message: '',
			prefill: '',
			returnvalue: 'reason',
			cleanUp: true,
			noEmpty: true
		}], this.i18n.reasonForDeletion );
	},

	/**
	 ** Edit the current page to add the specified tag.  Assumes that the page hasn't
	 ** been tagged yet; if it is, a duplicate tag will be added.
	 **/
	prependTemplate: function () {
		var page = [];
		page.title = this.pageName;
		page.text = this.tag;
		page.editType = 'prependtext';
		if ( window.AjaxDeleteWatchFile ) {
			page.watchlist = 'watch';
		}

		this.showProgress( this.i18n.addingAnyTemplate );
		this.savePage( page, this.imgSummary, 'nextTask' );
	},

	/**
	 ** Create the DR subpage (or append a new request to an existing subpage).
	 ** The request page will always be watchlisted.
	 **/
	createRequestSubpage: function () {
		this.templateAdded = true; // we've got this far; if something fails, user can follow instructions on template to finish
		var page = [];
		page.title = this.requestPage;
		page.text = '\n\n=== [[:' + this.pageName + ']] ===\n' + this.reason + ' ~~' + '~~\n';
		page.watchlist = 'watch';
		page.editType = 'appendtext';

		this.showProgress( this.i18n.creatingNomination );

		this.savePage( page, this.subpageSummary, 'nextTask' );
	},

	/**
	 ** Transclude the nomination page onto today's DR log page, creating it if necessary.
	 ** The log page will never be watchlisted (unless the user is already watching it).
	 **/
	listRequestSubpage: function () {
		var page = [];
		page.title = this.dailyLogPage;

		// Impossible when using appendtext. Shouldn't not be severe though, since DRBot creates those pages before they are needed.
		// if (!page.text) page.text = '{{'+'subst:' + this.requestPagePrefix + 'newday}}';  // add header to new log pages
		page.text = '\n{{' + this.requestPage + '}}\n';
		page.watchlist = 'nochange';
		page.editType = 'appendtext';

		this.showProgress( this.i18n.listingNomination );

		this.savePage( page, 'Listing [[' + this.requestPage + ']]', 'nextTask' );
	},

	/**
	 ** Notify any uploaders/creators of this page using {{idw}}.
	 **/
	notifyUploaders: function () {
		this.uploadersToNotify = 0;
		for ( var user in this.uploaders ) {
			if ( user === mw.config.get( 'wgUserName' ) || user == 'LRBot' || user == 'ImageResizerBot' ) {
				// notifying yourself is pointless
				continue;
			}
			var page = [];
			page.title = this.userTalkPrefix + user;
			page.text = '\n' + this.talkTag + ' ~~' + '~~\n';
			page.editType = 'appendtext';
			page.redirect = true;
			if ( window.AjaxDeleteWatchUserTalk ) {
				page.watchlist = 'watch';
			}
			this.savePage( page, this.talkSummary, 'uploaderNotified' );

			this.showProgress( this.i18n.notifyingUploader.replace( '%USER%', user ) );

			this.uploadersToNotify++;
		}
		if ( this.uploadersToNotify === 0 ) {
			this.nextTask();
		}
	},

	uploaderNotified: function () {
		this.uploadersToNotify--;
		if ( this.uploadersToNotify === 0 ) {
			this.nextTask();
		}
	},

	/**
	 ** Compile a list of uploaders to notify.  Users who have only reverted the file to an
	 ** earlier version will not be notified.
	 ** DONE: notify creator of non-file pages
	 **/
	findCreator: function () {
		var query;
		if ( mw.config.get( 'wgNamespaceNumber' ) === 6 ) {
			query = {
				action: 'query',
				prop: 'imageinfo|revisions|info',
				rvprop: 'content|timestamp',
				meta: 'tokens',
				type: 'csrf',
				iiprop: 'user|sha1|comment',
				iilimit: 50,
				titles: this.pageName
			};

		} else {
			query = {
				action: 'query',
				prop: 'info|revisions',
				rvprop: 'user|timestamp',
				rvlimit: 1,
				rvdir: 'newer',
				meta: 'tokens',
				type: 'csrf',
				titles: this.pageName
			};
		}
		this.showProgress();
		this.doAPICall( query, 'findCreatorCB' );
	},
	findCreatorCB: function ( result ) {
		this.uploaders = {};
		var pages = result.query.pages;
		for ( var id in pages ) { // there should be only one, but we don't know its ID
			// The edittoken only changes between logins
			this.edittoken = pages[id].edittoken;

			//First handle non-file pages
			if ( mw.config.get( 'wgNamespaceNumber' ) !== 6 ) {

				this.pageCreator = pages[id].revisions[0].user;
				this.starttimestamp = pages[id].starttimestamp;
				this.timestamp = pages[id].revisions[0].timestamp;

				this.uploaders[this.pageCreator] = true;

			} else {
				var info = pages[id].imageinfo;

				var content = pages[id].revisions[0]['*'];

				var seenHashes = {};
				for ( var i = info.length - 1; i >= 0; i-- ) { // iterate in reverse order
					if ( info[i].sha1 && seenHashes[info[i].sha1] ) {
						// skip reverts
						continue;
					}
					seenHashes[info[i].sha1] = true;
					// Now exclude bots which only reupload a new version:
					this.excludedBots = 'FlickreviewR, Rotatebot, Cropbot, Picasa Review Bot';
					if ( this.excludedBots.indexOf( info[i].user ) !== -1 ) {
						continue;
					}

					// Handle some special cases, most of the code by [[User:Lupo]]
					var match;
					if ( info[i].user === 'File Upload Bot (Magnus Manske)' ) {
						// CommonsHelper
						match = /transferred to Commons by \[\[User:([^\]\|]*)(\|([^\]]*))?\]\] using/.exec( info[i].comment );

						// geograph_org2commons, regex accounts for typo ("transferd") and it's possible future correction
						if ( !match ) {
						  match = /geograph.org.uk\]; transferr?e?d by \[\[User:([^\]\|]*)(\|([^\]]*))?\]\] using/.exec( info[i].comment );
						}

						// flickr2commons
						if ( !match ) {
							match = /\* Uploaded by \[\[User:([^\]\|]*)(\|([^\]]*))?\]\]/.exec( content );
						}

						if ( match ) {
							match = match[1];
						}
						// Really necessary?
						match = this.fixDoubleEncoding( match );
					} else if ( info[i].user === 'FlickrLickr' ) {
						match = /\n\|reviewer=\s*(.*)\n/.exec( content );
						if ( match ) {
							match = match[1];
						}
					} else if ( info[i].user === 'Flickr upload bot' ) {
						// Check for the bot's upload template
						match = /\{\{User:Flickr upload bot\/upload(\|[^\|\}]*)?\|reviewer=([^\}]*)\}\}/.exec( content );
						if ( match ) {
							match = match[2];
						}
					} else {
						// No special case applies, just continue;
						this.uploaders[info[i].user] = true;
						continue;
					}
					if ( match ) {
						// Make sure the username is in canonical form
						match = match
							.replace( /^[\s_]+/, '' )
							.replace( /[\s_]+$/, '' )
							.replace( /[\s_]+/g, ' ' );
						match = match.substr( 0, 1 ).toUpperCase() + match.substr( 1 );
						this.uploaders[match] = true;
					}
				}
			}
		}
		this.nextTask();
	},

	removeTemplate: function () {
		var page = [];
		page.title = ( this.destination || mw.config.get( 'wgPageName' ) );
		page.text = this.pageContent.replace( /\{\{(rename|rename media|move)\|.*?\}\}/i, '' );
		page.editType = 'text';
		page.starttimestamp = this.starttimestamp;
		page.timestamp = this.timestamp;

		this.showProgress( this.i18n.removingTemplate );
		this.savePage( page, ( this.declineReason || this.i18n.renameDone ), 'nextTask' );
	},

	redirectPage: function () {
		var page = [];
		page.title = mw.config.get( 'wgPageName' );
		page.text = '#REDIRECT [[' + this.destination + ']]';
		page.editType = 'text';

		this.showProgress( this.i18n.redirectingFile );
		this.savePage( page, 'Redirecting to duplicate file', 'nextTask' );
	},
	saveDescription: function () {
		var page = [];
		page.title = this.destination;
		page.text = this.newPageText;
		page.editType = 'text';

		this.showProgress( this.i18n.savingDescription );
		this.savePage( page, 'Merging details from duplicate', 'nextTask' );
	},


	/**
	 ** Pseudo-Modal JS windows.
	 **/
	prompt: function ( questions, title, width ) {
		var dlgButtons = {};
		dlgButtons[this.i18n.submitButtonLabel] = function () {
			$.each( questions, function ( i, v ) {
				var response = $( '#AjaxQuestion' + i ).val();
				if ( v.type === 'checkbox' ) {
					if ( response === 'on' ) {
						// no value
						response = $( '#AjaxQuestion' + i ).prop( 'checked' );
					}
					else if ( !$( '#AjaxQuestion' + i ).prop( 'checked' ) ) {
						// unchecked
						response = '';
					}
				}
				if ( v.cleanUp ) {
					if ( v.returnvalue === 'reason' ) {
						response = AjaxQuickDelete.cleanReason( response );
					}
					if ( v.returnvalue === 'destination' ) {
						response = AjaxQuickDelete.cleanFileName( response );
					}
				}
				AjaxQuickDelete[v.returnvalue] = response;
				if ( v.returnvalue === 'reason' && AjaxQuickDelete.tag ) {
					AjaxQuickDelete.tag = AjaxQuickDelete.tag.replace( '%PARAMETER%', response );
					AjaxQuickDelete.imgSummary = AjaxQuickDelete.imgSummary.replace( '%PARAMETER%', response );
					AjaxQuickDelete.imgSummary = AjaxQuickDelete.imgSummary.replace( '%PARAMETER-LINKED%', '[[:' + response + ']]' );
				}
				var param = '%' + v.returnvalue.toUpperCase() + '%';
				AjaxQuickDelete.tag = AjaxQuickDelete.tag.replace( param, response );
				AjaxQuickDelete.imgSummary = AjaxQuickDelete.imgSummary.replace( param, response );
			} );
			$( this ).dialog( 'close' );
			AjaxQuickDelete.nextTask();
		};
		dlgButtons[this.i18n.cancelButtonLabel] = function () {
			$( this ).dialog( 'close' );
		};

		var $dialog =$( '<div>' )
			.html( $('<div>').attr('id', 'AjaxDeleteContainer') )
			.dialog( {
				width: ( width || 600 ),
				modal: true,
				title: title,
				draggable: false,
				dialogClass: 'wikiEditor-toolbar-dialog',
				close: function () {
					$( this ).dialog( 'destroy' );
					$( this ).remove();
				},
				buttons: dlgButtons
			} );
		var submitButton = $( '.ui-dialog-buttonpane button:first' );

		$.each( questions, function ( i, v ) {
			if ( v.type === 'textarea' ) {
				$( '#AjaxDeleteContainer' )
					.append( '<label for="AjaxQuestion' + i + '">' + v.message + '</label>' )
					.append( '<textarea rows=20 id="AjaxQuestion' + i + '"><br/><br/>' );
			} else {
				$( '#AjaxDeleteContainer' )
					.append( '<label for="AjaxQuestion' + i + '">' + v.message + '</label>' )
					.append( '<input type="' + ( v.type || 'text' ) + '" id="AjaxQuestion' + i + '" style="width: 98%;"><br/><br/>' );
			}
			var curQuestion = $( '#AjaxQuestion' + i );
			curQuestion.keyup( function ( event ) {
				if ( v.noEmpty ) {
					if ( $( this ).val().length < ( v.minLength || 4 ) ) {
						submitButton.addClass( 'ui-state-disabled' );
					} else {
						submitButton.removeClass( 'ui-state-disabled' );
					}
				}
				if ( event.keyCode === '13' && v.enterToSubmit !== false ) {
					submitButton.click();
				}
			} );
			curQuestion.val( v.prefill );
			if ( v.type === 'checkbox' ) {
				$( '#AjaxQuestion' + i )
					.prop( 'checked', ( typeof v.checked !== 'undefined' ? v.checked : false ) )
					.attr( 'style', 'float:left; margin-right:5px' );
			}
			curQuestion.keyup();
		} );
		$( '#AjaxQuestion0' ).focus();
		if(AjaxQuickDelete.imgSummary == 'до перенесення на Вікісховище') {
      		mw.loader.using('ext.gadget.Suggestions', function() {
      			$('#AjaxQuestion0').suggest('//commons.wikimedia.org/w/api.php', 14, 'jsonp', 'Category:');
      		});
		}
		if(AjaxQuickDelete.imgSummary == 'є у Вікісховищі ([[:File:%PARAMETER%]])') {
      		mw.loader.using('ext.gadget.Suggestions', function() {
      			$('#AjaxQuestion0').suggest('//commons.wikimedia.org/w/api.php', 6, 'jsonp', 'File:');
      		});
		}
		if(AjaxQuickDelete.imgSummary == 'є вільна заміна на Вікісховищі ([[:File:%PARAMETER%]])') {
      		mw.loader.using('ext.gadget.Suggestions', function() {
      			$('#AjaxQuestion0').suggest('//commons.wikimedia.org/w/api.php', 6, 'jsonp', 'File:');
      		});
		}	
	},

	/**
	 ** Pseudo-Modal JS windows.
	 **/
	compareDetails: function () {
		var d = this.details[0];
		var f = this.details[1];
		document.body.style.cursor = 'default';
		this.progressDialog.remove();
		if ( d.sha1 === f.sha1 ) {
			this.exactDupes = true;
			this.nextTask();
			return;
		}
		var dlgButtons = {};
		dlgButtons[this.i18n.submitButtonLabel] = function () {

			$( this ).dialog( 'close' );
			AjaxQuickDelete.nextTask();
		};
		dlgButtons[this.i18n.cancelButtonLabel] = function () {
			$( this ).dialog( 'close' );
		};
		var $dialog =$( '<div>' )
			.html( $('<div>').attr('id', 'AjaxDeleteContainer') )
			.dialog( {
				width: 800,
				modal: true,
				title: 'title',
				draggable: false,
				dialogClass: 'wikiEditor-toolbar-dialog',
				close: function () {
					$( this ).dialog( 'destroy' );
					$( this ).remove();
				},
				buttons: dlgButtons
			} );
		$( '#AjaxDupeContainer' )
			.append( '<div><img src="' + d.thumburl + '"></div>' )
			.append( '<div><img src="' + f.thumburl + '"></div>' )
			.append( '<div>' + Math.round( d.size / 1000 ) + ' KB <br/>' + d.width + 'x' + d.height + '</div>' )
			.append( '<div>' + Math.round( f.size / 1000 ) + ' KB <br/>' + f.width + 'x' + f.height + '</div>' );
	},

	/**
	 ** Double encoding fixer by Lupo. This is necessary for some older uploads of Magnus' bot.
	 **/
	fixDoubleEncoding: function ( match ) {
		if ( !match ) {
			return match;
		}
		var utf8 = /[u00C2-u00F4][u0080-u00BF][u0080-u00BF]?[u0080-u00BF]?/g;
		if ( !utf8.test( match ) ) {
			return match;
		}
		// Looks like we have a double encoding. At least it contains character
		// sequences that might be legal UTF-8 encodings. Translate them into %-
		// syntax and try to decode again.
		var temp = '',
			curr = 0,
			m,
			hexDigit = '0123456789ABCDEF';
		var str = match.replace( /%/g, '%25' );
		utf8.lastIndex = 0;
		// Reset regexp to beginning of string
		try {
			while ( ( m = utf8.exec( str ) ) !== null ) {
				temp += str.substring( curr, m.index );
				m = m[0];
				for ( var i = 0; i < m.length; i++ ) {
					temp += '%' + hexDigit.charAt( m.charCodeAt( i ) / 16 ) + hexDigit.charAt( m.charCodeAt( i ) % 16 );
				}
				curr = utf8.lastIndex;
			}
			if ( curr < str.length ) {
				temp += str.substring( curr );
			}
			temp = decodeURIComponent( temp );
			return temp;
		} catch ( e ) {}
		return match;
	},

	cleanFileName: function ( uncleanName ) {
		uncleanName = uncleanName
			.replace( /^Image:/i, 'File:' )
			.replace( /\.jpe*g$/i, '.jpg' )
			.replace( /\.png$/i, '.png' )
			.replace( /\.gif$/i, '.gif' );

		var currentExtension = mw.config.get( 'wgPageName' ).toLowerCase().replace( /.*?\.(\w{3,4})$/, '$1' ).replace( 'jpeg', 'jpg' );

		// If new file name is without extension, add the one from the old name
		if ( uncleanName.toLowerCase().indexOf( currentExtension ) === -1 ) {
			uncleanName += '.' + currentExtension;
		}
		if ( uncleanName.indexOf( 'File:' ) === -1 ) {
			uncleanName = 'File:' + uncleanName;
		}
		return uncleanName;
	},
	cleanReason: function ( uncleanReason ) {
		// trim whitespace
		uncleanReason = uncleanReason.replace( /^\s*(.+)\s*$/, '$1' );
		// remove signature
		uncleanReason = uncleanReason.replace( /(.+)(--)?~{3,5}$/, '$1' );
		return uncleanReason;
	},

	/**
	 ** For display of progress messages.
	 **/
	showProgress: function ( message ) {
		if ( $( '#feedbackContainer' ).length ) {
			$( '#feedbackContainer' ).html( message );
		} else {
			document.body.style.cursor = 'wait';

			this.progressDialog = $( '<div>' )
				.html( '<div id="feedbackContainer">' + ( message || this.i18n.preparingToEdit ) + '</div>' )
				.dialog( {
					width: 450,
					height: 90,
					minHeight: 90,
					modal: true,
					resizable: false,
					draggable: false,
					closeOnEscape: false,
					dialogClass: 'ajaxDeleteFeedback'
				} );
			$( '.ui-dialog-titlebar' ).hide();
		}

	},
	/**
	 ** Submit an edited page.
	 **/
	savePage: function ( page, summary, callback ) {
		var edit = {
			action: 'edit',
			summary: summary,
			watchlist: ( page.watchlist || 'preferences' ),
			title: page.title,
		};
		if ( page.redirect ) {
			edit.redirect = '';
		}
		edit[page.editType] = page.text;
		this.doAPICall( edit, callback );
	},

	/**
	 ** Does a MediaWiki API request and passes the result to the supplied callback (method name).
	 ** Uses POST requests for everything for simplicity.
	 **/
	doAPICall: function ( params, callback ) {
		var o = this;
		api = new mw.Api();
		api.postWithToken( 'csrf', params ).done(function(result){
			if ( !result ) {
					return o.fail( 'Receive empty API response:\n' + x.responseText );
				}

				// In case we get the mysterious 231 unknown error, just try again
				if ( result.error && result.error.info.indexOf( '231' ) !== -1 ) {
					return setTimeout( function () {
						o.doAPICall( params, callback );
					}, 500 );
				}
				if ( result.error ) {
					return o.fail( 'API request failed (' + result.error.code + '): ' + result.error.info );
				}
				if ( result.edit && result.edit.spamblacklist ) {
					  return o.fail( 'The edit failed because ' + result.edit.spamblacklist + ' is on the Spam Blacklist' );
				}
				try {
					o[callback]( result );
				} catch ( e ) {
					return o.fail( e );
				}
		}).fail(function(error){
			return o.fail( 'API request returned code ' + x.status + ' ' + status + 'Error code is ' + error );
		});
	},

	/**
	 ** Simple task queue.  addTask() adds a new task to the queue, nextTask() executes
	 ** the next scheduled task.  Tasks are specified as method names to call.
	 **/
	tasks: [],
	// list of pending tasks
	currentTask: '',
	// current task, for error reporting
	addTask: function ( task ) {
		this.tasks.push( task );
	},
	nextTask: function () {
		var task = this.currentTask = this.tasks.shift();
		try {
			this[task]();
		} catch ( e ) {
			this.fail( e );
		}
	},

	/**
	 ** Once we're all done, reload the page.
	 **/
	reloadPage: function () {
		this.progressDialog.remove();
		if ( this.pageName && this.pageName.replace( / /g, '_' ) !== mw.config.get( 'wgPageName' ) ) {
			return;
		}
		var encTitle = ( this.destination || mw.config.get( 'wgPageName' ) );
		encTitle = encodeURIComponent( encTitle.replace( / /g, '_' ) ).replace( /%2F/ig, '/' ).replace( /%3A/ig, ':' );
		location.href = mw.config.get( 'wgServer' ) + mw.config.get( 'wgArticlePath' ).replace( '$1', encTitle );
	},

	/**
	 ** Crude error handler. Just throws an alert at the user and
	 ** (if we managed to add the { { delete } } tag) reloads the page.
	 **/
	 fail: function ( err ) {
		if ( 'object' === typeof err ) {
			var stErr = err.message + '<br/>' + err.name;
			if ( err.lineNumber ) {
				stErr += ' @line' + err.lineNumber;
			}
			err = stErr;
		}

		document.body.style.cursor = 'default';
		var msg = this.i18n.taskFailure[this.currentTask] || this.i18n.genericFailure;

		//TODO: Needs cleanup
		var fix = '';
		if ( this.imgSummary === 'Nominating for deletion' ) {
			fix = ( this.templateAdded ? this.i18n.completeRequestByHand : this.i18n.addTemplateByHand );
		}

		$( '#feedbackContainer' ).html( msg + ' ' + fix + '<br/>' + this.i18n.errorDetails + '<br/>' + err
			+ '<br/><a href=' + mw.config.get( 'wgServer' ) + '/wiki/MediaWiki_talk:AjaxQuickDelete.js>' + this.i18n.errorReport + '</a>' );
		$( '.ui-dialog-content' ).height( 'auto' );
		$( '.ui-dialog' ).addClass( 'ajaxDeleteError' );
		// Allow some time to read the message
		if ( this.templateAdded ) {
			setTimeout( this.reloadPage(), 5000 );
		}
	},

	/**
	 ** Very simple date formatter.  Replaces the substrings 'YYYY', 'MM' and 'DD' in a
	 ** given string with the UTC year, month and day numbers respectively.
	 ** Also replaces 'MON' with the English full month name and 'DAY' with the unpadded day.
	 **/
	formatDate: function ( fmt, date ) {
		var pad0 = function ( s ) {
			s = '' + s;
			return ( s.length > 1 ? s : '0' + s );
		}; // zero-pad to two digits
		if ( !date ) {
			date = this.startDate;
		}
		fmt = fmt.replace( /YYYY/g, date.getUTCFullYear() );
		fmt = fmt.replace( /MM/g, pad0( date.getUTCMonth() + 1 ) );
		fmt = fmt.replace( /DD/g, pad0( date.getUTCDate() ) );
		fmt = fmt.replace( /MON/g, this.months[date.getUTCMonth()] );
		fmt = fmt.replace( /DAY/g, date.getUTCDate() );
		return fmt;
	},
	months: 'January February March April May June July August September October November December'.split( ' ' ),

	// Constants
	// DR subpage prefix
	requestPagePrefix: 'Commons:Deletion requests/',
	// user talk page prefix
	userTalkPrefix: mw.config.get( 'wgFormattedNamespaces' )[3] + ':',
	// MediaWiki API script URL
	apiURL: ( /^\/\//.test( mw.config.get( 'wgServer' ) ) ? document.location.protocol : '' ) + mw.config.get( 'wgServer' ) + mw.util.wikiScript( 'api' ),


	// Translatable strings
	i18n: {
		toolboxLinkDelete: 'Nominate for deletion',
		toolboxLinkDiscuss: 'Nominate category for discussion',

		// GUI reason prompt form
		reasonForDeletion: 'Why should this file be deleted?',
		reasonForDiscussion: 'Why does this category need discussion?',
		reasonForDisputedFairuse: 'Яким критеріям не відповідає файл?',
		submitButtonLabel: 'Підтвердити',
		cancelButtonLabel: 'Скасувати',

		// GUI progress messages
      	preparingToEdit: "Підготовка до редагування сторінок… ",
      	creatingNomination: "Creating nomination page... ",
      	listingNomination: "Adding nomination page to daily list... ",
      	addingAnyTemplate: "Додання шаблону на сторінку" + (mw.config.get('wgNamespaceNumber') == 6 ? " файла" : "") + "… ",
      	notifyingUploader: "Сповіщення користувача %USER%… ",

		// Extended version
    	toolboxLinkAuthor: "нема автора",
      	toolboxLinkLicense: "нема ліцензії",
      	toolboxLinkSource: "нема джерела",
      	toolboxLinkPermission: "нема дозволу",
      	toolboxLinkDisputed: "сумнівний",
      	toolboxLinkDisputedFairuse: "не відповідає КДВ",
      	toolboxLinkOrphanedFairuse: "невик. невільний",
    	toolboxLinkNoFoP: "nofop",
      	toolboxLinkNotHost: "не хостинг",
    	toolboxLinkLivingPeople: "ФЮ живої людини",
    	toolboxLinkRFU: "є вільна заміна на Вікісховищі",
    	toolboxLinkNowCommons: "є у Вікісховищі під іншою назвою.",
    	toolboxLinkDbCommons: "є у Вікісховищі під такою самою назвою.",
    	toolboxLinkMoveToCommons: "на Вікісховище",
    	toolboxLinkCopyvio: "Report copyright violation",
    	reasonForCopyvio: "Why is this file a copyright violation?",
    	titleForRFU: "Вкажіть ім'я файлу на Вікісховищі",
    	labelForRFU: "Вкажіть ім'я файлу на Вікісховищі",
      	titleForMoveToCommons: "Введіть назву категорії на Вікісховищі, в яку слід помістити файл, без префіксу Category.\<br\/>Якщо доречних категорій кілька, то їх можна вказати розділивши символом \<code>\|\<\/code>",
      	labelForMoveToCommons: "У яку категорію помістити файл?",
      	titleForNowCommons: "Вкажіть ім'я файлу на Вікісховищі",
      	labelForNowCommons: "Вкажіть ім'я файлу на Вікісховищі",
      	toolboxLinkDeleteSlow: "deleteSlow",

		// For moving files
		renameDone: 'Removing template; rename done',
		removingTemplate: 'Removing rename template',
		notAllowed: 'You do not have the neccessary rights to move files',
		reasonForMove: 'Why do you want to move this file?',
		moveDestination: 'What should be the new file name?',
		movingFile: 'Moving file',
		replacingUsage: 'Ordering CommonsDelinker to replace all usage',
		declineMove: 'Why do you want to decline the request?',
		leaveRedirect: 'Leave a redirect behind:',

		//For Duplicates
		deletingFile: 'Deleting file',
		mergeDescription: 'Please now merge the file descriptions',
		redirectingFile: 'Redirecting file',
		savingDescription: 'Saving new details',

		// Errors
		genericFailure: 'An error occurred while trying to do the requested action. ',
		taskFailure: {
			listUploaders: 'An error occurred while determining the '
				+ ( 6 === mw.config.get( 'wgNamespaceNumber' ) ? ' uploader(s) of this file' : 'creator of this page' ) + '.',
			loadPages: 'An error occurred while preparing to nominate this '
				+ mw.config.get( 'wgCanonicalNamespace' ).toLowerCase() + ' for deletion.',
			prependDeletionTemplate: 'An error occurred while adding the {{delete}} template to this '
				+ mw.config.get( 'wgCanonicalNamespace' ).toLowerCase() + '.',
			createRequestSubpage: 'An error occurred while creating the request subpage.',
			listRequestSubpage: 'An error occurred while adding the deletion request to today\'s log.',
			notifyUploaders: 'An error occurred while notifying the '
				+ ( 6 === mw.config.get( 'wgNamespaceNumber' ) ? ' uploader(s) of this file' : 'creator of this page' ) + '.'
		},
		addTemplateByHand: 'To nominate this ' + mw.config.get( 'wgCanonicalNamespace' ).toLowerCase()
					+ ' for deletion, please edit the page to add the {{delete}} template and follow the instructions shown on it.',
		completeRequestByHand: 'Please follow the instructions on the deletion notice to complete the request.',
		errorDetails: 'A detailed description of the error is shown below:',
		errorReport: 'Report the error here'
	}
};

mediaWiki.loader.using( 'jquery.ui', function () {
	$( document ).ready( function () {
		AjaxQuickDelete.install();
	} );
} );


} // end if (guard)
// </nowiki>