User:Fred the Oyster/common.js

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
//<nowiki>
/*global $:false, mw:false */
/*jshint curly:false, bitwise:false*/
(function() {
'use strict';

function ClsUPC($userInputField, $outputField, stCallBack, callingObject, CBValidChange) { // User Presence Check
	this.$userInputField = $userInputField;
	this.$outputField = $outputField;
	this.callingObject = callingObject;
	this.stCallBack = stCallBack;
	this.CBValidChange = CBValidChange;
	this.pendingCalls = 0;
	this.pendingSetTimeouts = 0;
	this.oldValue = '';
	this.userNameExists = 2;
 
	var o = this;
 
	$userInputField.keyup( function (event) { 
		var tmpUNE = o.userNameExists;
		if ( o.isValidIP($(this).val()) ) {
			o.setToIP(tmpUNE);
		} else {
			o.execApiCall(false, $(this).val());
		}
	});
	$userInputField.bind('input', function() { 
		$userInputField.keyup();
	});
	$userInputField.bind('selected', function() { 
		$userInputField.keyup();
	});
}

ClsUPC.prototype.isValidIP = function (username) {
	if ( mw.util.isIPv4Address(username) ) return true; //IP v.4
	return mw.util.isIPv6Address(username);  //IP v.6
}

ClsUPC.prototype.setToIP = function (tmpUNE) {
	var o = this;
	o.userNameExists = -1;
	if ('function' === typeof(o.CBValidChange) && tmpUNE !== o.userNameExists ) {
		o.$outputField.attr('src', o.callingObject.umImgUserIsIP);
		o.$outputField.attr('alt', 'IP');
		o.oldValue = o.callingObject.umCleanFileAndUser(o.$userInputField.val());
		o.CBValidChange(o, o.callingObject);
	}
}

ClsUPC.prototype.execApiCall = function (isTimeout, val) {
	if (isTimeout) this.pendingSetTimeouts--;
	if (this.oldValue !== this.callingObject.umCleanFileAndUser(val)) {
		if (this.pendingCalls > 0) {
			if (0 === this.pendingSetTimeouts) {
				this.pendingSetTimeouts++;
				var o = this;
				setTimeout( function () { 
					o.execApiCall(true, o.$userInputField.val());
				}, 1000); // do not use the old value, use the current instead
			}
			return;
		}
		var User = this.oldValue = this.callingObject.umCleanFileAndUser(val);
		var query = {
			action: 'query',
			list: 'allusers',
			aufrom: User,
			auto: User
		};
		this.callingObject.umCurrentUserQuery = this;
		this.pendingCalls++;
		this.callingObject.doAPICall(query, this.stCallBack);
	}
};
 
ClsUPC.prototype.evalAPICall = function(result) {
	this.pendingCalls--;
	var uifval = this.$userInputField.val();
	if (this.oldValue !== this.callingObject.umCleanFileAndUser(uifval)) {
		// Don't do anything if user updated the field in-between
		return;
	}
	if (this.isValidIP(uifval)) {
		return;
	}
	if ('object' === typeof(result.query) && 'object' === typeof(result.query.allusers)) {
		var tmpUNE = this.userNameExists;
		if (0 === result.query.allusers.length) {
			this.$outputField.attr('src', this.callingObject.umImgUserNotExists);
			this.$outputField.attr('alt', '!! invalid !!');
			this.userNameExists = false;
		} else {
			if (this.callingObject.umCleanFileAndUser(this.$userInputField.val()) === result.query.allusers[0].name) {
				this.$outputField.attr('src', this.callingObject.umImgUserExists);
				this.$outputField.attr('alt', 'OK');
				this.userNameExists = true;
			} else {
				if (0 === this.pendingSetTimeouts) { // Only overwrite if there is nothing pending
					this.$outputField.attr('src', this.callingObject.umImgUserUndefined);
					this.$outputField.attr('alt', '?');
					this.userNameExists = 2;
				}
			}
		}
		if ('function' === typeof(this.CBValidChange) && tmpUNE != this.userNameExists) this.CBValidChange(this, this.callingObject);
	}
};


var umsg = window.AxUserMsg = {
	umInstall: function () {
		if (0 === $('#t-AjaxUserMessage').length) {
			var _this = this;
			var pHref = mw.util.addPortletLink('p-tb', '#', "Notify this user", 't-AjaxUserMessage', null);
			$(pHref).click(function(e) {
				e.preventDefault();
				_this.umNotifyUser();
			});
		}
		window.AxUserMsgPreSelect = (window.AxUserMsgPreSelect || 42);
	},

	umInstallOldLinks: function () {
		// written for Herby, who needs this for working on Commons
		var _this = this;
		for (var id in this.umTemplate) {
			// Create portlet link
			var portletLink = mw.util.addPortletLink( 'p-tb', '#' + id, this.umTemplate[id][1], 't-gadgetUserMessage' + id, this.umTemplate[id][2]);
			// Bind click handler
			$(portletLink).click( function( e ) {
				e.preventDefault();
				window.AxUserMsgPreSelect = $(this).find('a').attr('href').split('#')[1];
				_this.umNotifyUser();
			});
		}
	},

	umNotifyUser: function () {
		mw.util.addCSS('a.new {color: #BA0000 !important;}');
		this.umUser = '';
		this.editToken = '';
		this.umDlgPresent = false;
		this.umExecuting = false;
		this.umPendingParser = 0;
		this.umPendingParserTimeouts = 0;
		this.umParserTimerID = 0;
		this.umDelay = (window.AxUserMsgDelay || 100);
		this.umUserCache = {};
		this.umFileCache = {};
		this.umParserCache = {};
		this.focusOwner = '';
		this.umObtainEditToken();
 
		switch (mw.config.get('wgNamespaceNumber')) {
			case 3: // User_talk
			case 2: // User
				this.umUser = mw.config.get('wgPageName').match(/.*?\:(.*?)(\/.*)*$/)[1];
				break;
			case -1: // Special pages
				try {
					if ( "Contributions" === mw.config.get('wgCanonicalSpecialPageName') ) {
						if ( -1 !== location.href.indexOf("\/Special:Contributions\/") ) {
							this.umUser = decodeURI(location.href.match(/Special\:Contributions\/(.*?)$/)[1]);
						} else if ( -1 !== location.href.indexOf(mw.config.get('wgScript')) ) {
							this.umUser = mw.util.getParamValue('target');
						}
					}
					if ( "Log" === mw.config.get('wgCanonicalSpecialPageName') ) {
							if (mw.util.getParamValue('user')) this.umUser = mw.util.getParamValue('user');
							if (mw.util.getParamValue('page')) { if (/User:+./.test(mw.util.getParamValue('page'))) {
								this.umUser = mw.util.getParamValue('page').replace("User:", "");
							}}
					}
				} catch (ex) { }
			break;
		}
		this.umDlg();
	},
 
	umFillSelect: function (caller, o) {
		var userstate = caller.userNameExists;
		for (var id in o.umTemplate) {
			var currentTag = o.umTemplate[id];
			if ( $('#umOpt' + id, o.$tagSelect).length === 0 ) { // check wether to add
				if ( (-1 === userstate && !(currentTag[3] & o.umFlagUM)) || (true === userstate && !(currentTag[3] & o.umFlagIP)) ) {
					o.$tagSelect.append( '<option id="umOpt' + id + '" value="' + id + '">' + currentTag[1] + ' - ' + currentTag[2] + '</option>' );
					continue;
				}
			} else { // check wether to remove
				if ( (-1 === userstate && (currentTag[3] & o.umFlagUM)) || (true === userstate && (currentTag[3] & o.umFlagIP)) ) {
					$('#umOpt' + id, o.$tagSelect).remove();
					continue;
				}
			}
		}
		if (-1 === userstate) { if (window.AxUserMsgPreSelectIP) { o.$tagSelect.val(window.AxUserMsgPreSelectIP); } 
		} else { if (window.AxUserMsgPreSelect) { o.$tagSelect.val(window.AxUserMsgPreSelect); } 
		}
		o.umValidateInput(o);
		o.$tagSelect.change();
	},
 
	umDlg: function () {
		var _this = this,
			dlgButtons = {};
		dlgButtons[this.i18n.submitButtonLabel] = function () {
			try {
				if (_this.umIsValid) _this.umNotifyUserExecute();
			} catch (ex) {
				_this.fail(ex);
			}
		};
		dlgButtons[this.i18n.cancelButtonLabel] = function () {
			$(this).dialog("close");
		};
		this.dlg = $('<div></div>').html('<div id="AjaxUmContainer"></div>').dialog({
			modal: true,
			closeOnEscape: true,
			position: [Math.round(($(window).width() - Math.min($(window).width(), 850)) / 2), 
						Math.round(($(window).height() - Math.min($(window).height(), 750)) / 2)],
			dialogClass: "wikiEditor-toolbar-dialog",
			title: 'User Messages-Script in Beta-Mode. ' + 
				'Report bugs and ideas to <a href="//commons.wikimedia.org/wiki/User:Rillke" target="_blank">Rillke</a>.',
			height: Math.min($(window).height(), 750),
			width: Math.min($(window).width(), 850),
			buttons: dlgButtons,
			close: function () {
				$(this).dialog("destroy");
				$(this).remove();
				_this.umDlgPresent = false;
			}
		});
		$('.wikiEditor-toolbar-dialog.ui-dialog').css({position:'fixed', top:Math.round(($(window).height() - Math.min($(window).height(), 700)) / 2) + 'px'});
		this.umDlgPresent = true;
 
		if (this.dlg) {
			$('#AjaxUmContainer').append('<label for="umUser">' + this.i18n.umFillInUser + '</label><br><input type="text" id="umUser" style="width: 95%;" value="' + this.umUser + '"/>' + 
				this.umInitImgUserExists.replace('%ID%', 'umUserExists') + '<br><br>');
 
			this.$tagSelect = $('<select>', {
				size : '1',
				id   : 'umTagToInsert',
				style: 'width: 99%;'
			} );
 
			$('#AjaxUmContainer').append('<label for="umTagToInsert">' + this.i18n.umSelectTag + '</label><br>');
			$('#AjaxUmContainer').append(this.$tagSelect).append('<br><br>');
			$('#AjaxUmContainer').append('<span id="umMediaWrapper"><label for="umMedia">' + this.i18n.umFillInMedia + '</label><br><input type="text" id="umMedia" style="width: 95%;" value="File:"/><br><br></span>');
			$('#AjaxUmContainer').append('<span id="umP2Wrapper"><label for="umP2">' + this.i18n.umFillInAdditional + '</label><br><input type="text" id="umP2" style="width: 95%;"/><br><br></span>');
			$('#AjaxUmContainer').append('<span id="umP3Wrapper"><label for="umP3">' + this.i18n.umFillInAdditional + '</label><br><input type="text" id="umP3" style="width: 95%;"/><br><br></span>');
			$('#AjaxUmContainer').append('<span id="umRelatedUserWrapper"><label for="umRelatedUser">' + this.i18n.umFillInRelUser + '</label><br><input type="text" id="umRelatedUser" style="width: 95%;" value="User:"/>' + this.umInitImgUserExists.replace('%ID%', 'umRelatedUserExists') + '<br><br></span>');
			$('#AjaxUmContainer').append('<label for="umAddText">' + this.i18n.umAddText + '</label><br><textarea id="umAddText" style="width: 95%; height: 5em;">' + (window.AxUserMsgCustomText || '') + '</textarea><br><br>');
 
			this.talkTag = '';
			var
				$umMedia       = $('#umMedia'),
				$umP2          = $('#umP2'),
				$umP3          = $('#umP3'),
				$umUser        = $('#umUser'),
				$umRelatedUser = $('#umRelatedUser'),
				$umAddText     = $('#umAddText');
 
			this.uUPC = new ClsUPC($umUser, $('#umUserExists'), 'umUserExistsCB', this, this.umFillSelect);
			this.ouUPC = new ClsUPC($umRelatedUser, $('#umRelatedUserExists'), 'umUserExistsCB', this, this.umUserValidChange);
 
			var submitButton = $('.ui-dialog-buttonpane button:first');
			this.$tagSelect.keyup( function(event) { 
				console.log('got event')
				if ('13' == event.keyCode) submitButton.click();
			});
 
			// guessing the related file thanks User:Platonides
			try {
				var umFile = document.referrer.match(/File:(.+)/);
				if (umFile) { if (/&.+=/.test(umFile[1])) $umMedia.val('File:' + decodeURI(umFile[1]).match(/^(.+)&/)[1]); else $umMedia.val('File:' + umFile[1]); }
			} catch (ex) {}
 
			$umUser.keyup( function(event) {
				$(this).val( $(this).val().replace(/<|>|\^/g, '') );
				if (event) if (event.keyCode) if ('13' == event.keyCode) submitButton.click();
			});
			$umUser.autocomplete({
				minLength: 1,
				source: function ( request, callback ) { _this.umSeekUsers( request, callback ); },
				close: function( event, ui ) { $umUser.triggerHandler('selected'); }
			});
			$umMedia.change( function() { 
				_this.umValidateInput(_this);
			});
			$umMedia.bind('input', function() { 
				$(this).val( $(this).val().replace(/<|>|\^/g, '') );
				_this.umValidateInput(_this); 
			});
			$umMedia.keyup( function(event) { 
				$(this).val( $(this).val().replace(/<|>|\^/g, '') );
				if ('13' == event.keyCode) submitButton.click();
			});
			$umMedia.autocomplete({
				minLength: 1,
				source: function ( request, callback ) { _this.umSeekFiles( request, callback ); },
				close: function( event, ui ) { $umMedia.triggerHandler('input'); }
			});
			$umRelatedUser.keyup( function(event) { 
				$(this).val( $(this).val().replace(/<|>|\^/g, '') );
				if (event) if (event.keyCode) if ('13' == event.keyCode) submitButton.click();
				_this.umValidateInput(_this);
			});
			$umRelatedUser.autocomplete({
				minLength: 1,
				source: function ( request, callback ) { _this.umSeekUsers( request, callback ); },
				close: function( event, ui ) { $umRelatedUser.triggerHandler('selected'); }
			});
			$umP2.change( function() { 
				_this.umValidateInput(_this);
			});			
			$umP2.bind('input', function() { 
				_this.umValidateInput(_this); 
			});
			$umP2.keyup( function(event) { 
				if ('13' == event.keyCode) submitButton.click();
			});
			$umP3.change( function() { 
				_this.umValidateInput(_this);
			});			
			$umP3.bind('input', function() { 
				_this.umValidateInput(_this); 
			});
			$umP3.keyup( function(event) { 
				if ('13' == event.keyCode) submitButton.click();
			});
			$umAddText.change( function() { 
				_this.umValidateInput(_this);
			});
			$umAddText.bind('input', function() { 
				_this.umValidateInput(_this);
			});
 
			submitButton.focus();
 
			$('#AjaxUmContainer').append(this.umInstPrevContainer.clone().text('Instant-preview container is empty.'));
			this.$tagSelect.change( function (event) {
				var umType = _this.umTemplate[$(this).val()][3];
 
				if (umType & _this.umFlagP2) {
					$('#umP2Wrapper').show();
					if (document.activeElement) if ($umUser.attr('id') !== $(document.activeElement).attr('id')) $('#umP2').select();
				} else {
					$('#umP2Wrapper').hide();
				}
				if (umType & _this.umFlagP3) {
					$('#umP3Wrapper').show();
					if (document.activeElement) if ($umUser.attr('id') !== $(document.activeElement).attr('id')) $('#umP3').select();
				} else {
					$('#umP3Wrapper').hide();
				}
				if (umType & _this.umFlagMQ) {
					$('#umMediaWrapper').show();
					if (document.activeElement) if ($umUser.attr('id') !== $(document.activeElement).attr('id')) $('#umMedia').select();
				} else {
					$('#umMediaWrapper').hide();
				}
				if (umType & _this.umFlagUQ) {
					$('#umRelatedUserWrapper').show();
					if (document.activeElement) if ($umUser.attr('id') !== $(document.activeElement).attr('id')) $('#umMedia').select();
				} else {
					$('#umRelatedUserWrapper').hide();
				}
				_this.umValidateInput(_this);
				_this.$tagSelect.combobox({'displaytext': _this.$tagSelect.val() ? _this.$tagSelect.children( ":selected" ).text() : ""});
			});
			if (window.AxUserMsgPreSelect) this.$tagSelect.val(window.AxUserMsgPreSelect);
			$('#umUser').keyup();
			$('#umTagToInsert').combobox();
		}
	},
 
	umSeekUsers: function ( request, pCallback ) {
		var query = {
			action: 'query',
			list: 'allusers',
			auprefix: request.term.replace(/^(?:User):/, "")
		};
		this.doGetApiCall(query, 'umSeekUsersCB', pCallback);
	},
 
	umSeekUsersCB: function ( result, pCallback ) {
		var searchTerms = [];
		for (var id in result) {
			searchTerms.push( { 'id': result[id].userid, 'value': result[id].name } );
		}
		if ('function' === typeof pCallback) pCallback(searchTerms);
	},
 
	umSeekFiles: function ( request, pCallback ) {
		var query = {
			action: 'query',
			list: 'allimages',
			aiprefix: request.term.replace(/^(?:File|Image):/, "")
		};
		this.doGetApiCall(query, 'umSeekFilesCB', pCallback);
	},
 
	umSeekFilesCB: function ( result, pCallback ) {
		var searchTerms = [];
		for (var id in result) {
			searchTerms.push( { 'id': result[id].timestamp, 'value': 'File:' + result[id].name } );
		}
		if ('function' === typeof pCallback) pCallback(searchTerms);
	},
 
	umUserExistsCB: function (result) {
		this.umCurrentUserQuery.evalAPICall(result);
	},
 
	umShowInfo: function(info, o) {
		$('#umInstantPreviewContainer').replaceWith(o.umInstPrevContainer.clone().append('<p align="center"><img src="' + o.umImgInfo + '" width="64" height="64"/><br/>' +
		info + '</p>'));
	},
 
	umValidateInput: function (o) {
		this.umIsValid = true;
		var umType = this.umTemplate[$('#umTagToInsert').val()][3];
		var submitButton = $('.ui-dialog-buttonpane button:first');
		var validRelatedUser = function() { 
			if (umType & o.umFlagUQ) { 
				if (o.umCleanFileAndUser($('#umRelatedUser').val()).length < 1 ) {
					o.umShowInfo('No related user specified.', o);
					return false;
				}
				if ( !o.ouUPC.userNameExists ) {
					o.umShowInfo('Related user does not exist.', o);
					return false;
				}
			} 
			return true; };
 
		var validMedia = function() { 
			if (umType & o.umFlagMQ) {
				if ( (o.umCleanFileAndUser($('#umMedia').val()).length < 5 ) && (umType & o.umFlagReq) ) {
					o.umShowInfo('No file specified. This is mandatory in this case.', o);
					return false;
				}
			}
			return true; };
 
		var validUser = function() {
			if ( o.umCleanFileAndUser($('#umUser').val()).length < 1 ) { 
				o.umShowInfo('No user specified.', o);
				return false;
			}
			if ( !o.uUPC.userNameExists ) { 
				o.umShowInfo('User does not exist.', o);
				return false;
			}
			return true; };
 
		this.umIsValid = this.umIsValid && validRelatedUser() && validMedia() && validUser();
 
		if (this.umIsValid) {
			submitButton.removeClass('ui-state-disabled');
			if (umType & this.umFlagMQ) {
				this.talkTag = '\n{{subst:' + this.umTemplate[$('#umTagToInsert').val()][0] + 
					('' == this.umCleanFileAndUser($('#umMedia').val()) ? '' : ('|1=' + ((umType & this.umFlagNS) ? ('File:' + this.umCleanFileAndUser($('#umMedia').val())) : $('#umMedia').val())) );
			} else if (umType & this.umFlagUQ) {
				this.talkTag = '\n{{subst:' + this.umTemplate[$('#umTagToInsert').val()][0] + '|1=' + this.umCleanFileAndUser($('#umRelatedUser').val());
			}
			else {
				this.talkTag = '\n{{subst:' + this.umTemplate[$('#umTagToInsert').val()][0];
			}
			var paramCount = ((umType & this.umFlagUQ) ? 1 : 0) + ((umType & this.umFlagMQ) ? 1 : 0);
			if (umType & this.umFlagP2) {
				paramCount++;
				this.talkTag += '|' + paramCount + '=' + $('#umP2').val();
			}
			if (umType & this.umFlagP3) {
				paramCount++;
				this.talkTag += '|' + paramCount + '=' + $('#umP3').val();
			}
			this.talkTag += '}}';
			if ('\n{{subst:}}' === this.talkTag) this.talkTag = '\n';
 
			this.talkTag += '\n' + $('#umAddText').val().replace(/~{3,5}$/, '') + '~~~~\n';
			this.umParseTemplate(false);
		} else {
			submitButton.addClass('ui-state-disabled');
		}
	},
 
	umUserValidChange: function (caller, o) {
		o.umValidateInput(o);
	},
 
	umCleanFileAndUser: function (input) {
		var output = '';
		if (input) {
			output = input.replace(/\_/g, " ").replace(/File\:/g, "").replace(/Image\:/g, "").replace(/User\:/g, "").replace(/^\s+|\s+$/g, '');
			output = output.substring(0, 1).toUpperCase() + output.substring(1);
		}
		return output;
	},
 
	umParseTemplate: function (viaSetTimeout) {
		if (window.AxUserMsgNoParse) return;
		var _this = this;
 
		if (viaSetTimeout) _this.umPendingParserTimeouts--;
 
		if (_this.umPendingParser > 0) {
			if (0 === _this.umPendingParserTimeouts) {
				// call me later
				var o = _this;
				_this.umPendingParserTimeouts++;
				setTimeout( function () { 
					o.umParseTemplate(true);
				}, 300);
			}
			return;
		}
 
		function maybeParse() { 
			_this.umPendingParser++;
			var action = {
				action: 'parse',
				uselang: mw.config.get('wgUserLanguage'),
				redirects: true,
				prop: 'text',
				pst: true,
				text: _this.talkTag
			};
			_this.umDelay = Math.min((_this.umDelay + 30), 1500); // Save server resources.
			_this.doAPICall(action, 'umParsedTemplate');
		}
 
		if (_this.umParserTimerID) {
			clearTimeout(_this.umParserTimerID);
		}
		_this.umParserTimerID = setTimeout( maybeParse, _this.umDelay );
	},
 
	umParsedTemplate: function(result) {
		this.umPendingParser--;
		if ( 'object' === typeof(result.parse) && ('object' === typeof(result.parse.text)) && this.umDlgPresent && (!this.umExecuting) && this.umIsValid )  {
			var $containerText = result.parse.text['*'].replace(' API', ' ' + this.umCleanFileAndUser($('#umUser').val())).replace('>API', '>' + this.umCleanFileAndUser($('#umUser').val()));
			$containerText = $($containerText);
			$('.editsection', $containerText).remove();
			$('a', $containerText).attr('target', '_blank');
			$('#umInstantPreviewContainer').replaceWith(this.umInstPrevContainer.clone().append($containerText));
		}
	},
 
	umObtainEditToken: function () {
		if (mw.user && mw.user.tokens && mw.user.tokens.get) this.editToken = mw.user.tokens.get( 'csrfToken' );
		this.editToken = (this.editToken || window['wikilove-edittoken'] || (mw.user.isAnon() ? '+\\' : '') );
		if (this.editToken) return;
 
		var query = {
			action: 'query',
			prop: 'info',
			intoken: 'edit',
			titles: 'FAQ'  // Random title
		};
		this.doAPICall(query, 'umObtainEditTokenCB');
	},
 
	umObtainEditTokenCB: function (result) {
		var pages = result.query.pages;
		for (var id in pages) { // there should be only one, but we don't know its ID
			this.editToken = pages[id].edittoken;
		}
	},
 
	umNotifyUserExecute: function () {
		this.pageName = this.umUserTalkPrefix + $('#umUser').val();
		this.talkSummary = this.umTemplate[$('#umTagToInsert').val()][4] ? this.umTemplate[$('#umTagToInsert').val()][4] : (this.umTemplate[$('#umTagToInsert').val()][2] + '.');
		this.appendTemplate();
	},
 
	appendTemplate: function () {
		var page = [];
		page.title = this.pageName;
		page.text = this.talkTag;
		page.editType = 'appendtext';
		page.redirect = true;
		if (window.AjaxDeleteWatchFile) page.watchlist = 'watch';
 
		this.umExecuting = true;
		$('#umInstantPreviewContainer').replaceWith(this.umInstPrevContainer.clone().append('<p align="center"><img src="//upload.wikimedia.org/wikipedia/commons/c/ce/RE_Ajax-Loader.gif"/></p>'));
		this.savePage(page, this.talkSummary, 'umNotifyUserExecuteCB');
	},
 
	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;
		edit.token = this.editToken;
		this.doAPICall(edit, callback);
	},
 
	umNotifyUserExecuteCB: function (result) {
		var encTitle = this.umUserTalkPrefix + $('#umUser').val();
		encTitle = encodeURIComponent(encTitle.replace(/ /g, '_')).replace(/%2F/ig, '/').replace(/%3A/ig, ':');
		location.href = mw.config.get('wgServer') + mw.config.get('wgArticlePath').replace("$1", encTitle);
	},
 
	doGetApiCall: function (params, callback, pCallback) {
		var o = this;
 
		// Local Cache
		if (params.list) { 
			if ("allusers" === params.list) {
				if (params.auprefix in o.umUserCache) {
					o[callback](o.umUserCache[params.auprefix], pCallback);
					return;
				}
			} else if ("allimages" === params.list) {
				if (params.aiprefix in o.umFileCache) {
					o[callback](o.umFileCache[params.aiprefix], pCallback);
					return;
				}
			}
		}
 
		params.format = 'json';
		$.ajax({
			url: this.apiURL,
			cache: true,
			dataType: 'json',
			data: params,
			type: 'GET',
			async: true,
			success: function (result, status, x) {
				if (!result) { if ('function' === typeof pCallback) pCallback(); return; }
				try {
					if (params.list) if ("allusers" === params.list) {
						// cache the result
						o.umUserCache[ params.auprefix ] = result.query.allusers;
						o[callback](result.query.allusers, pCallback);
						return;
					}
					if (params.list) if ("allimages" === params.list) {
						// cache the result
						o.umFileCache[ params.aiprefix ] = result.query.allimages;
						o[callback](result.query.allimages, pCallback);
						return;
					}
					// This is a "must", the doc sais
					if ('function' === typeof pCallback) pCallback();
					o[callback](result);
				} catch (e) {
					return o.fail(e);
				}
			},
			error: function () {
				// This is a "must", the doc sais
				if ('function' === typeof pCallback) pCallback();
			}
		});
	},
 
	doAPICall: function (params, callback) {
		var o = this;
 
		if (params.action) if ("parse" === params.action) {
			if (params.text in o.umParserCache) {
				o[callback](o.umParserCache[params.text]);
				return;
			}
		}
 
		params.format = 'json';
		$.ajax({
			url: this.apiURL,
			cache: false,
			dataType: 'json',
			data: params,
			type: 'POST',
			success: function (result, status, x) {
				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");
				}
				if (params.action) if ("parse" === params.action) {
					o.umParserCache[params.text] = result;
				}
				try {
					o[callback](result);
				} catch (e) {
					return o.fail(e);
				}
			},
			error: function (x, status, error) {
				return o.fail("API request returned code " + x.status + " " + status + "Error code is " + error);
			}
		});
	},
 
	fail: function (err) {
		if ('object' === typeof err) {
			var stErr = mw.html.escape(err.message) + '<br/>' + err.name;
			if (err.lineNumber) stErr += ' @line' + err.lineNumber;
			if (err.line) stErr += ' @line' + err.line;
			err = stErr;
		} else {
			err = mw.html.escape(err.toString());
		}
		if (this.umDlgPresent) {
			$('#umInstantPreviewContainer').replaceWith(this.umInstPrevContainer.clone().append('<p align="center"><img src="' + this.umImgErr + '" width="64" height="64"/></p><br/>' +
			"During the execution of AxUserMsg, the following error occured:<br/>" + err.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/&/g, '&amp;')));
		} else {
			mw.notify("During the execution of AxUserMsg, the following error occured: " + err);
		}
 
	},
 
	i18n: {
		umFillInUser: "Please fill in the user to notify:",
		umSelectTag: "Select the template to be added to the user's page:",
		umFillInMedia: "Please specify the Media-file this message is about (namespace will be auto-detected):",
		umFillInAdditional: "The template has an additional parameter. You can fill it in here.",
		umFillInRelUser: "Who is the user related to this message?",
		umAddText: "Fill in additional text to place on the user's discussion page, please:",
		submitButtonLabel: "Add template",
		cancelButtonLabel: "Cancel"
	},
 
	umInstPrevContainer: $('<div>', { id: 'umInstantPreviewContainer', style: 'background-color:#EFD;height:380px;overflow:scroll;vertical-align:middle;' }),
	umInitImgUserExists: '<img id="%ID%" src="//upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no.svg/20px-P_no.svg.png" alt="?"/>',
	umImgUserUndefined: '//upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no.svg/20px-P_no.svg.png',
	umImgUserNotExists: '//upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no_red.svg/20px-P_no_red.svg.png',
	umImgUserExists: '//upload.wikimedia.org/wikipedia/commons/thumb/b/be/P_yes.svg/20px-P_yes.svg.png',
	umImgUserIsIP: '//upload.wikimedia.org/wikipedia/commons/thumb/6/6e/IP.svg/20px-IP.svg.png',
	umImgErr: '//upload.wikimedia.org/wikipedia/commons/c/ca/Crystal_error.png',
	umImgWarn: '//upload.wikimedia.org/wikipedia/commons/a/af/Crystal_Clear_app_error-info.png',
	umImgInfo: '//upload.wikimedia.org/wikipedia/commons/0/09/Crystal_Clear_action_info.png',
 
	umFlagMQ: 1,   // Media Query
	umFlagUQ: 2,   // Username Query
	umFlagReq: 4,  // Required - must filled in
	umFlagNS: 8,   // Add Namespace
	umFlagP2: 16,  // add a universal parameter
	umFlagP3: 32,  // add a universal parameter
	umFlagIP: 64,  // Message for IP only
	umFlagUM: 128, // User message only
	umFlagReqMqNs: 13, // Combination of (umFlagReq | umFlagMQ | umFlagNS)
 
	umUserTalkPrefix: mw.config.get('wgFormattedNamespaces')[3] + ":",
	apiURL: mw.util.wikiScript( 'api' )
};
 
umsg.umTemplate = [
//  ['Template name',             "Name in Sidebar", "Detailed text",                                                                          Type/Prompt statement,           'Talk summary'];
	['Please link images',        "Please link",     "Request user to please link his images through categories or galleries",                 umsg.umFlagUM, "Please link images."],
	['Copyvionote',               "Copyvionote",     "Inform user about speedy deletion of uploaded media",                                    umsg.umFlagUM + umsg.umFlagReqMqNs, 'Please upload [[Commons:Copyright|free content]] only.'],
	['Derivativenote',            "Derivativenote",  "Inform user about speedy deletion of uploaded derivative media",                         umsg.umFlagUM + umsg.umFlagReqMqNs, 'Even [[COM:DW|derivative works]] of copyrighted material is protected.'],
	['No fair use',               "No fair use",     "Inform user that Commons does not accept fair use",                                      umsg.umFlagUM + umsg.umFlagReqMqNs, 'On commons, we do not accept [[COM:FU|fair-use-media]]. Please stop uploading those.'],
	['Please name images',        "Please name",     "Request user to please name his images correctly",                                       umsg.umFlagUM + umsg.umFlagReqMqNs, 'Please [[Commons:File naming|name images]] correctly.'],
	['Please tag images',         "Please tag",      "Request user to please tag his images",                                                  umsg.umFlagUM, "The description of one of your uploads is lacking in information about reusing. Please check the whole description-page."],
	['Please describe images',    "Please describe", "Request user to please describe his images",                                             umsg.umFlagUM + umsg.umFlagReqMqNs, 'Search-engine: Mhh, interesting, a picture but what is on it? Can you describe it, please.'],
	['Project scope',             "Project scope",   "Inform user on project scope after deleting out of scope contributions",                 umsg.umFlagMQ, "Please do not upload [[COM:PS|out of scope media.]]"],
	['No comments',               "Use talk pages",  "Inform user to use talk pages",                                                          0, "Please put comments to [[COM:TALK|the appropriate place]]."],
	['Welcome',                   "Welcome",         "Welcome a new user or a user who has not yet received a welcome message",                0, "Hello, [[Commons:Welcome|Welcome to Wikimedia Commons! -A database of freely usable media files]]."],
	['End of copyvios',           "End copyvio",     "Give user a final warning because of previous copyright violations",                     umsg.umFlagUM, "Immediately stop uploading copyright violations, please."],
	['Off topic',                 "Off topic",       "Please stay on topic in Commons",                                                        0, "Commons is not Wikipedia. WP is for articles; commons for media."],
	['No re-uploading',           "No re-uploading", "Please do not re-upload",                                                                umsg.umFlagUM],
	['Test',                      "Sandboxing",      "Referral to sandbox for conducting experiments",                                         0, "We have a [[COM:SAND|sandbox]] for nonsense-edits; or use the preview function if you want to test."],
	['Test2',                     "Vandalism",       "Warning or vandalism and request to cease",                                              0, "Please do not contribute nonsense-edits."],
	['Test3',                     "Vandalism 2",     "Second warning for vandalism and announcement of block if it continues",                 0, "Please stop making nonsense-edits."],
	['Test4',                     "Vandalism 3",     "Last warning for vandalism and announcement of block on next violation",                 0, "Last warning: Stop producing nonsense!"],
	['Inappropriate imagenotes',  "Imagenotes",      "Tell user not to add inappropriate imagenotes",                                          umsg.umFlagUM, "We have a [[COM:ANN|guideline for imagenotes]]."],
	['Dont remove delete',        "Rem.Delete",      "Please do not remove deletion-templates from pages nominated for deletion",              0],
	['Dont remove nsd or nld',    "Rem.n(slp)d",     "Please do not remove valid warning tags from file-description pages",                    0],
	['Dont remove speedy',        "Rem.speedy",      "Please do not remove speedy-deletion tags",                                              0],
	['Dont remove warnings',      "Rem.warning",     "Please do not remove valid warning templates from your talk page, except while archiving.", 0],
	['Be civil',                  "Be civil",        "Please remain civil, even if your contributions are being attacked",                     0],
	['Be civil final',            "Be civil final",  "Remain civil! You will become blocked next time",                                        0],
	['Blocked User',              "Blocked (without params)", "Your account has been blocked.",                                                umsg.umFlagP2 + umsg.umFlagP3],
	['Inappropriate username',    "Inapp.username",  "Your username is considered being inappropriate and therefore your account has been blocked", umsg.umFlagUM],
	['Copyviouploadindefblock',   "Blocked indefinite (copyvio)", "User blocked because he did not stop uploading copyvios after warnings.",   umsg.umFlagUM, "You did not stop uploading copyright violations and therefore we had to block you."],
	['Indefblockeduser',          "Blocked indef",   "This account has been blocked indefinite.",                                              umsg.umFlagP2],
	['Imposter',                  "Imposter",        "Mark account as blocked for impersonation or attack",                                    umsg.umFlagUQ],
	['Sockpuppet',                "Sockpuppet",      "It is suspected that you're maybe a sock puppet or impersonator",                        umsg.umFlagUQ],
	['Please register',           "Please register", "Please create an account. Your contributions are numerous and quite good.",              umsg.umFlagIP],
	['Provide better quality',    "Better quality",  "Do you have a better version of this media?",                                            umsg.umFlagUM + umsg.umFlagReqMqNs],
	['Please use SVG',            "Use SVG, please", "SVG (Scalable Vector Graphics) have a few advantages, I'll tell you some of them.",      umsg.umFlagUM],
	['No scaled down dupes',      "Scaled down",     "Please do not upload scaled down duplicates. MediaWiki can change the size for you.",    umsg.umFlagUM + umsg.umFlagMQ + umsg.umFlagNS],
	['Unfree',                    "Unfree",          "Image deletion notification",                                                            umsg.umFlagUM + umsg.umFlagMQ + umsg.umFlagNS],
	['Attackimage',               "Attackimage",     "Please do not upload attack images",                                                     umsg.umFlagUM + umsg.umFlagReqMqNs],
	['Attackpage',                "Attackpage",      "Please do not create attack pages",                                                      umsg.umFlagP2 + umsg.umFlagReq],
	['Dont overwrite',            "Dont overwrite",  "Please do not overwrite images",                                                         umsg.umFlagUM + umsg.umFlagMQ + umsg.umFlagNS],
	['Dont recreate',             "Dont recreate",   "Please do not recreate deleted images",                                                  umsg.umFlagUM + umsg.umFlagReqMqNs],
	['Speedywhat',                "Speedy warn",     "One of your uploads has been speedy-deleted",                                            umsg.umFlagUM + umsg.umFlagReqMqNs],
	['No advertising',            "Dont advertise",  "Please do not advertise on commons We have the goal of an educative image collection",   0],
	['Sign',                      "Sign your postings",  "Please sign your postings with four tildes (~~~~) at the end of your comments",      0],
	['',                          "Select a message! Empty option.", "A new message for you!",                                                 0]
];

/**
 * A custom widget built by composition of Autocomplete and Button. 
 * You can either type something into the field to get filtered suggestions based on your input, or use the button to get the full list of selections.
 * 
 * The input is read from an existing select-element for progressive enhancement, passed to Autocomplete with a customized source-option.
 * Autor: someone from the jQuery UI-Team?
 * slightly altered
**/
var initCombobox = function( $ ) {
$.widget( "ui.combobox", {
	// These options will be used as defaults
	options: { 
		displaytext: '',
		emptyMessage: 42,
		passEnter: true
	},

    // Use the _setOption method to respond to changes to options
	_setOption: function(key, value) {
		switch( key ) {
			case "displaytext":
			this.input.val(value);
			break;
		}

		// In jQuery UI 1.8, you have to manually invoke the _setOption method from the base widget
		$.Widget.prototype._setOption.apply( this, arguments );
	},
	
	_create: function() {
		var self = this,
			select = this.element.hide(),
			selectWidth = select.width(),
			selectId = select.attr('id'),
			selectLabels,
			selected = select.children( ":selected" ),
			value = selected.val() ? selected.text() : "",
			ownId = 'j' + Math.floor(Math.random() * 10000000000),
			isOpen = false,
			valid = true;
		if (selectId) {
			selectLabels = $('label[for="' + selectId + '"]');
		}
		var input = this.input = $( "<input>", { id: ownId } )
			.insertAfter( select )
			.val( value )
			.autocomplete({
				delay: 0,
				minLength: 0,
				source: function( request, response ) {
					var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );
					response( select.children( "option" ).map(function() {
						var text = $( this ).text();
						if ( this.value && ( !request.term || matcher.test(text) ) )
							return {
								label: text.replace(
									new RegExp(
										"(?![^&;]+;)(?!<[^<>]*)(" +
										$.ui.autocomplete.escapeRegex(request.term) +
										")(?![^<>]*>)(?![^&;]+;)", "gi"
									), "<strong>$1</strong>" ),
								value: text,
								option: this
							};
					}) );
				},
				select: function( event, ui ) {
					ui.item.option.selected = true;
					self._trigger( "selected", event, {
						item: ui.item.option
					});
					select.triggerHandler('change');
				},
				change: function( event, ui ) {
					if ( !ui.item ) {
						var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( $(this).val() ) + "$", "i" );
						valid = false;
						select.children( "option" ).each(function() {
							if ( $( this ).text().match( matcher ) ) {
								this.selected = valid = true;
								return false;
							}
						});
						if ( !valid ) {
							// remove invalid value, as it didn't match anything
							$( this ).val( "" );
							input.data( "autocomplete" ).term = "";
							select.val(self.options.emptyMessage);
							select.triggerHandler('change');
							return false;
						} else {
							select.triggerHandler('change');
						}
					}
				},
				close: function () { setTimeout( function() { isOpen = false; }, 1 ) },
				open: function () { isOpen = true; }
			})
			.addClass( "ui-widget ui-widget-content ui-corner-left" ).css('width', (selectWidth - 70) + 'px')
			.click(function() { $(this).select(); })
			.keydown(function(e) {
				if (self.options.passEnter && (13 == e.keyCode) && !isOpen && valid) {
					var kup = $.Event("keyup");
					kup.ctrlKey = false;
					kup.keyCode = kup.which = 13;
					select.triggerHandler(kup);
				}
			});
			
		if (selectLabels) {
			selectLabels.attr('for', ownId);
		}

		input.data( "autocomplete" )._renderItem = function( ul, item ) {
			return $( "<li></li>" )
				.data( "item.autocomplete", item )
				.append( "<a>" + item.label + "</a>" )
				.appendTo( ul );
		};

		this.button = $( "<button type='button'>&nbsp;</button>" )
			.attr( "tabIndex", -1 )
			.attr( "title", "Show All Items" )
			.attr( "style", "height:1.5em; padding:0px !important; width:20px; margin:0px !important; position:relative; top:5px;" )
			.insertAfter( input )
			.button({
				icons: {
					primary: "ui-icon-triangle-1-s"
				},
				text: false
			})
			.removeClass( "ui-corner-all" )
			.addClass( "ui-corner-right ui-button-icon" )
			.click(function() {
				// close if already visible
				if ( input.autocomplete( "widget" ).is( ":visible" ) ) {
					input.autocomplete( "close" );
					return;
				}

				// work around a bug (likely same cause as #5265)
				$( this ).blur();

				// pass empty string as value to search for, displaying all results
				input.autocomplete( "search", "" );
				input.focus();
			});
	},

	destroy: function() {
		this.input.remove();
		this.button.remove();
		this.element.show();
		$.Widget.prototype.destroy.call( this );
	}
});
};
 
if (3 === mw.config.get('wgNamespaceNumber') ||
	2 === mw.config.get('wgNamespaceNumber') || 
	( -1 === mw.config.get('wgNamespaceNumber') && ('Log' === mw.config.get('wgCanonicalSpecialPageName') || 'Contributions' === mw.config.get('wgCanonicalSpecialPageName')) )) {
	// alternative for jquery.ui autocomplete: jquery.suggestions
	// http://jqueryui.com/demos/autocomplete/ http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/resources/jquery/jquery.suggestions.js?view=markup
	mw.loader.using(['jquery.ui', 'mediawiki.util', 'mediawiki.user', 'mediawiki.html'], function () {
		$(document).ready(function () {
			initCombobox($);
			umsg.umInstall();
			if (window.installOldLinks) umsg.umInstallOldLinks();
			$(document).triggerHandler('scriptLoaded', ['AxUserMsg', umsg]);
		});
	});
}
})();
//</nowiki>

mw.loader.load('http://toolserver.org/~dapete/ime/ime.js');

mw.util.addPortletLink('p-tb', 'javascript:{importScript(\'MediaWiki:VisualFileChange.js\'); void(0);}', "Perform batch task",'t-AjaxQuickDeleteOnDemand', null);


// [[File:Krinkle_RTRC.js]]
//mw.loader.load('//meta.wikimedia.org/w/index.php?title=User:Krinkle/RTRC.js&action=raw&ctype=text/javascript');