Jump to content

میڈیا وکی:AxUserMsg.js

وکیپیڈیا توں

یاددہانی: محفوظ کرݨ دے بعد انہاں تبدیلیاں کوں ݙیکھݨ کیتے تہاکوں آپݨے براؤزر دا کیشی (cache) صاف کرݨاں ہوسی۔

  • فائرفاکس/ سفاری: ڄݙݨ Reload تے کلک کرو تاں Shift دبا تے رکھو، یا Ctrl-F5 یا Ctrl-R دباؤ (Mac تے R-⌘)
  • گوگل کروم: Ctrl-Shift-R دباؤ (Mac تے Shift-R-⌘)
  • انٹرنیٹ ایکسپلورر: ڄݙݨ Refresh تے کلک کرو تاں Ctrl یا Ctrl-F5 دباؤ
  • اوپیرا: Ctrl-F5 دباؤ۔
/**
 ** AxUserMsg.js - Ajax/API based adding comments and templates to User-Talk-Pages
 ** written in 2011 by [[User:Rillke]]; little parts are from [[MediaWiki:AjaxQuickDelete.js]] by DieBuche and from the old [[MediaWiki:Gadget-UserMessages.js]]
 ** bootstrap-code at [[MediaWiki:Gadget-UserMessages.js]] (which severed as idea for writing this script), help at [[Help:Gadget-UserMessages]]
 **
**/
//<nowiki>

// Invoke automated jsHint-validation on save: A feature on Wikimedia Commons.
// Interested? See [[:commons:MediaWiki:JSValidator.js]].

/*global $:false, mw:false, jsMsgAppend:false */
/*jshint curly:false, bitwise:false, laxbreak:true*/

(function() {
'use strict';

if (window.AxUserMsg) return;

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.on('input', function() { 
		$userInputField.keyup();
	});
	$userInputField.on('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 (!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 (!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 (!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 (!$('#t-AjaxUserMessage').length) {
			var _this = this;
			var $LODLinkNode = $('#t-AjaxUserMessageLOD');
			var $Href = $LODLinkNode.length ? $LODLinkNode.eq(0) : $(mw.util.addPortletLink('p-tb', '#', "Notify this user", 't-AjaxUserMessage', 'Click here to launch user messages'));
			$Href.click(function(e) {
				e.preventDefault();
				_this.fireImmediately = false;
				_this.umNotifyUser();
			});
		}
		window.AxUserMsgPreSelect = (window.AxUserMsgPreSelect || this.umTemplate.length - 1); // default last msg
	},

	umInstallOldLinks: function () {
		// written for Herby, who needs this for working on Commons
		var _this = this;
		$.each(this.umTemplate, function(id, ti) {
			// Create portlet link
			var portletLink = mw.util.addPortletLink( 'p-tb', '#' + id, ti[1], 't-gadgetUserMessage' + id, ti[2]);
			// Bind click handler
			$(portletLink).click( function( e ) {
				e.preventDefault();
				window.AxUserMsgPreSelect = $(this).find('a').attr('href').split('#')[1];
				if (window.AxUserMsgFireAsYouClick) {
					var umType = _this.umTemplate[window.AxUserMsgPreSelect][3];
					if (!(umType & _this.umFlagUQ) && !(umType & _this.umFlagReq))
						_this.fireImmediately = true;
					else {
						_this.fireImmediately = false;
					}
				}
				_this.umNotifyUser();
			});
		});
	},

	umNotifyUser: function () {
		var css = 'a.new {color: #BA0000 !important;}';
		mw.util.addCSS(css);
		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();
 
		this.umUser = mw.libs.commons.guessUser();
		this.umDlg();
	},
 
	umFillSelect: function (caller, o) {
		var userstate = caller.userNameExists;
		$.each(o.umTemplate, function(id, currentTag) {
			if ( !$('#umOpt' + id, o.$tagSelect).length ) { // 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 + '">' + mw.html.escape(currentTag[1] + ' - ' + currentTag[2]) + '</option>' );
					return;
				}
			} else { // check wether to remove
				if ( (-1 === userstate && (currentTag[3] & o.umFlagUM)) || (true === userstate && (currentTag[3] & o.umFlagIP)) ) {
					$('#umOpt' + id, o.$tagSelect).remove();
					return;
				}
			}
		});
		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>').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(), 800)) / 2)],
			title: '<a href="//ur.wikipedia.org/wiki/وپ:تذکیر" target="_blank">ویکی آلہ تذکیر</a>',
			height: Math.min($(window).height(), 800),
			width: Math.min($(window).width(), 850),
			buttons: dlgButtons,
			close: function () {
				$(this).dialog("destroy");
				$(this).remove();
				_this.umDlgPresent = false;
			},
			open: function() {
				var $dlg = $(this),
					$buttons = $dlg.parent().find('.ui-dialog-buttonpane button'),
					$submitButton = $buttons.eq(0).specialButton('proceed'),
					$cancelButton = $buttons.eq(1).specialButton('cancel');
					
				$dlg.parents('.ui-dialog').css({position:'fixed', top:Math.round(($(window).height() - Math.min($(window).height(), 800)) / 2) + 'px'});
			}
		});
		this.umDlgPresent = true;
 
		if (this.dlg) {
			var $AjaxUmContainer = $('#AjaxUmContainer');
			
			$AjaxUmContainer.append('<label for="umUser">' + mw.html.escape(this.i18n.umFillInUser) + '</label><br><input type="text" id="umUser" style="width: 95%;" value="' + mw.html.escape(this.umUser) + '"/>' + 
				this.umInitImgUserExists.replace('%ID%', 'umUserExists') + '<br><br>');
 
			this.$tagSelect = $('<select>', {
				size : '1',
				id   : 'umTagToInsert',
				style: 'width: 99%;'
			} );
 
			$AjaxUmContainer.append([
				'<label for="umTagToInsert">' + mw.html.escape(this.i18n.umSelectTag) + '</label><br>',
				this.$tagSelect, '<br><br>',
				'<span id="umMediaWrapper"><label for="umMedia">' + mw.html.escape(this.i18n.umFillInMedia) + '</label><br><input type="text" id="umMedia" style="width: 95%;" value="فائل:"/><br><br></span>',
				'<span id="umP2Wrapper"><label for="umP2">' + mw.html.escape(this.i18n.umFillInAdditional) + '</label><br><input type="text" id="umP2" style="width: 95%;"/><br><br></span>',
				'<span id="umP3Wrapper"><label for="umP3">' + mw.html.escape(this.i18n.umFillInAdditional) + '</label><br><input type="text" id="umP3" style="width: 95%;"/><br><br></span>',
				'<span id="umRelatedUserWrapper"><label for="umRelatedUser">' + mw.html.escape(this.i18n.umFillInRelUser) + '</label><br><input type="text" id="umRelatedUser" style="width: 95%;" value="صارف:"/>' + this.umInitImgUserExists.replace('%ID%', 'umRelatedUserExists') + '<br><br></span>',
				'<span id="umSummaryWrapper"><label for="umSummary">' + mw.html.escape(this.i18n.umFillInSummary) + '</label><br><input type="text" id="umSummary" style="width: 95%;" value="خلاصہ"/><br><br></span>',
				'<label for="umAddText">' + mw.html.escape(this.i18n.umAddText) + '</label><br><textarea id="umAddText" style="width: 95%; height: 5em;">' + (mw.html.escape(window.AxUserMsgCustomText || '')) + '</textarea><br><br>'
			]);
 
			this.talkTag = '';
			var	$umMedia       = $('#umMedia'),
				$umP2          = $('#umP2'),
				$umP3          = $('#umP3'),
				$umUser        = $('#umUser'),
				$umRelatedUser = $('#umRelatedUser'),
				$umSummary     = $('#umSummary'),
				$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) { 
				if (13 === event.which) submitButton.click();
			});
 
			// guessing the related file thanks User:Platonides
			var guessFile = function() {
				var f = mw.util.getParamValue('title', document.referrer);
				if (f && /File:/.test(f)) return f;
				f = mw.util.getParamValue('page', document.referrer);
				if (f && /File:/.test(f)) return f;
				f = mw.util.getParamValue('target', document.referrer);
				if (f && /File:/.test(f)) return f;
				var m = document.referrer.match(/File:(.+)/);
				try {
					if (m) { if (/&.+=/.test(m[1])) return('File:' + decodeURI(m[1]).match(/^(.+)&/)[1]); else return('File:' + m[1]); }
				} catch (ex) {}
			};
			var umFile = guessFile();
			if (umFile) $umMedia.val(decodeURIComponent(umFile).replace(/_/g, ' '));
 
			$umUser.keyup( function(event) {
				$(this).val( $(this).val().replace(/<|>|\^/g, '') );
				if (event) if (event.which) if (13 === event.which) 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.on('input', function() { 
				$(this).val( $(this).val().replace(/<|>|\^/g, '') );
				_this.umValidateInput(_this); 
			});
			$umMedia.keyup( function(event) { 
				$(this).val( $(this).val().replace(/<|>|\^/g, '') );
				if (13 === event.which) submitButton.click();
			});
			$umMedia.autocomplete({
				minLength: 1,
				source: function ( request, callback ) { _this.umSeekFiles( request, callback ); },
				close: function( e, ui ) { $umMedia.triggerHandler('input'); }
			});
			$umRelatedUser.keyup( function(event) { 
				$(this).val( $(this).val().replace(/<|>|\^/g, '') );
				if (event) if (event.which) if (13 === event.which) submitButton.click();
				_this.umValidateInput(_this);
			});
			$umRelatedUser.autocomplete({
				minLength: 1,
				source: function ( request, callback ) { _this.umSeekUsers( request, callback ); },
				close: function( e, ui ) { $umRelatedUser.triggerHandler('selected'); }
			});
			$umP2.change( function() { 
				_this.umValidateInput(_this);
			});			
			$umP2.on('input', function() { 
				_this.umValidateInput(_this); 
			});
			$umP2.keyup( function(event) { 
				if (13 === event.which) submitButton.click();
			});
			$umP3.change( function() { 
				_this.umValidateInput(_this);
			});			
			$umP3.on('input', function() { 
				_this.umValidateInput(_this); 
			});
			$umP3.keyup( function(event) { 
				if (13 === event.which) submitButton.click();
			});
			$umAddText.change( function() { 
				_this.umValidateInput(_this);
			});
			$umAddText.on('input', function() { 
				_this.umValidateInput(_this);
			});
			$umSummary.keyup( function(event) { 
				if (13 === event.which) submitButton.click();
			});
 
			submitButton.focus();
 
			$AjaxUmContainer.append(this.umInstPrevContainer.clone().text('فوری نمائش کا خانہ خالی ہے۔'));
			this.$tagSelect.change( function (/* e*/) {
				var umType = _this.umTemplate[$(this).val()][3];
 
				$umSummary.val(_this.umTemplate[$('#umTagToInsert').val()][4] ? _this.umTemplate[$('#umTagToInsert').val()][4] : (_this.umTemplate[$('#umTagToInsert').val()][2] + '۔'));
				_this.umValidateInput(_this);
				_this.$tagSelect.combobox({'displaytext': _this.$tagSelect.val() ? _this.$tagSelect.children( ":selected" ).text() : ""});

				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();
				}
			});
			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 = [];
		$.each(result, function(id, usi) {
			searchTerms.push( { 'id': usi.userid, 'value': usi.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 = [];
		$.each(result, function(id, fii) {
			searchTerms.push( { 'id': fii.timestamp, 'value': 'File:' + fii.name } );
		});
		if ('function' === typeof pCallback) pCallback(searchTerms);
	},
 
	umUserExistsCB: function (result) {
		this.umCurrentUserQuery.evalAPICall(result);
	},
 
	umShowInfo: function(info, o) {
		$('#umInstantPreviewContainer').empty().html('<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);
			// If the user wants the old behaviour back, we fire immediately
			if (this.fireImmediately) this.umNotifyUserExecute();
		} 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 (!_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,
				title: _this.umUserTalkPrefix + $('#umUser').val(),
				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').empty().append($containerText).resizable({ alsoResize: '#AjaxUmContainer' });
		}
	},
 
	umObtainEditToken: function () {
		if (mw.user && mw.user.tokens) this.editToken = mw.user.tokens.get( 'csrfToken' );
		this.editToken = (this.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
			if (pages.hasOwnProperty(id)) {
				this.editToken = pages[id].edittoken;
			}
		}
	},
 
	umNotifyUserExecute: function () {
		if (this.umExecuting) return;
		this.pageName = this.umUserTalkPrefix + $('#umUser').val();
		this.talkSummary = $('#umSummary').val();
		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').empty().html('<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, ':');
		var newLoc = mw.config.get('wgServer') + mw.config.get('wgArticlePath').replace("$1", encTitle);
		if (window.location.pathname === mw.config.get('wgArticlePath').replace("$1", encTitle)) {
			window.location.hash = '#footer';
			window.location.reload();
		} else {
			window.location.href = newLoc + '#footer';
		}
		
		
		this.umExecuting = false;
	},
 
	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').empty().html('<p align="center"><img src="' + this.umImgErr + '" width="64" height="64"/></p><br>' +
			"During the execution of AxUserMsg, the following error occured:<br>" + mw.html.escape(err));
		} else {
			jsMsgAppend("During the execution of AxUserMsg, the following error occured: " + err);
		}
 
	},
 
	i18n: {
		umFillInUser: "صارف کا نام درج کریں:",
		umSelectTag: "صارف کو جس امر کی یاد دہانی کرانا مقصود ہو اس کا سانچہ یہاں سے منتخب کریں:",
		umFillInMedia: "براہ کرم اس فائل کو درج کریں جس سے متعلق آپ اطلاع دے رہے ہیں:",
		umFillInAdditional: "اس سانچہ میں اضافی پیرامیٹر ہیں جنہیں آپ یہاں درج کر سکتے ہیں۔",
		umFillInRelUser: "اس سے پیغام سے متعلق صارف کون ہے؟",
		umFillInSummary: "خلاصہ ترمیم",
		umAddText: "اگر مذکورہ پیغام کے ساتھ مزید کوئی پیغام دینا ہو تو اسے یہاں درج کریں:",
		submitButtonLabel: "پیغام درج کریں",
		cancelButtonLabel: "منسوخ کریں"
	},
 
	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 = [
/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 * Append new messages at the bottom. Otherwise pre-selection for users will break.
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
//  ['Template name',             "Name in Sidebar", "Detailed text",                                                                          Type/Prompt statement,           'Talk summary'];
	['Welcome',                   "خوش آمدید",         "اگر صارف کو ویکیپیڈیا پر خوش آمدید نہیں کہا گیا ہے تو اسے خوش آمدید کہیں",                0, "خیر مقدمی پیغام"],
	['Test',                      "تختہ مشق",      "تجربات کے لیے تختہ مشق پر بھیجیں",                                         umsg.umFlagP2 + umsg.umFlagP3, "تجربات کے لیے تختہ مشق کی رہنمائی"],
	['Test2',                     "تخریب کاری",       "تخریب کاری سے گریز کی تاکید",                                              umsg.umFlagP2 + umsg.umFlagP3, "غیر تعمیری ترامیم نہ کریں۔"],
	['Test3',                     "تخریب کاری 2",     "مسلسل تخریب کاری پر انتباہ اور جاری رکھنے پر پابندی عائد کی اطلاع",                 umsg.umFlagP2 + umsg.umFlagP3, "براہ کرم تخریب کاری سے گریز کریں۔"],
	['Test4',                     "تخریب کاری 3",     "تخریب کاری پر آخری انتباہ اور پابندی عائد کرنے کی اطلاع",                 0, "آخری انتباہ: تخریبی ترامیم سے احتراز کریں!"],
	['Please register',           "کھاتا بنائیں", "کھاتا بنانے کی درخواست",               umsg.umFlagIP],
	['No advertising',            "تشہیر نہ کریں",  "تشہیر سے گریز کریں",   0],
	['Sign',                      "دستخط کریں",  "تبصروں کے آخر میں چار دفعہ ~ کی علامت (~~~~) درج کر کے اپنے دستخط لازماً شامل کریں",      0],
	['Blocked',                   "پابندی",         "آپ کے کھاتے پر پابندی عائد کر دی گئی ہے",                                                          umsg.umFlagP2 + umsg.umFlagP3],
	['Anonblock',                 "گمنام ترمیم پر پابندی", "آپ کے آئی پی پتے سے ترمیم کرنے پر پابندی عائد کر دی گئی ہے، براہ کرم کھاتا بنائیں۔", umsg.umFlagIP],
	['You\'ve got mail',          "ڈاک ارسال",        "برقی ڈاک ارسال کرنے کی اطلاع",                             umsg.umFlagUM],
	['',                          "پیغام منتخب کریں! خالی آپشن", "آپ کی خدمت میں نیا پیغام!",                                                 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,
		shutOff: window.AxUserMsgUseSelect
	},

    // Use the _setOption method to respond to changes to options
	_setOption: function(key, value) {
		if (this.options.shutOff) return;
		switch( key ) {
			case 'displaytext':
			this.input.val(value);
			break;
			case 'passEnter':
			this.options.passEnter = 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() {
		if (this.options.shutOff) return;
		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 portMessure = this.portMessure = $('<div>', { id: ownId + 'vp' }).css({ position: 'fixed', top: '0', height: '0' });
		$('body').append(portMessure);
		var input = this.input = $( "<input>", { id: ownId } )
			.insertAfter( select )
			.val( value )
			.autocomplete({
				delay: 0,
				minLength: 0,
				source: function( request, response ) {
					var i = 0,
						matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );

					response( select.children( "option" ).map(function() {
						if ( (i > (window.AxUserMsgMaxSelect || 20)) && request.term ) return;
						var text = $( this ).text();
						if ( this.value && ( !request.term || matcher.test(text) ) ) {
							i++;
							return {
								label: text.replace(
									new RegExp(
										"(?![^&;]+;)(?!<[^<>]*)(" +
										$.ui.autocomplete.escapeRegex(request.term) +
										")(?![^<>]*>)(?![^&;]+;)", "gi"
									), "<b>$1</b>" ),
								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');
						}
					}
				},
				create: function (e, ui) { 
					var _t = $(this),
						t_top = _t.offset().top - portMessure.offset().top;
					$('.ui-autocomplete.ui-menu').css({
						'position': 'fixed',
						'overflow': 'auto',
						'max-height': Math.round($(window).height() - t_top - _t.height() - 20 ) + 'px'
					});
				},
				close: function (e, ui) { setTimeout( function() { isOpen = false; }, 1 ); },
				open: function (e, ui) { 
					isOpen = true;
					var _t = $(this),
						t_top = _t.offset().top - portMessure.offset().top;
					$('.ui-autocomplete.ui-menu')
						.css({
							'position': 'fixed',
							'max-height': Math.round($(window).height() - t_top - _t.height() - 20 ) + 'px'
						});
				}
			})
			.addClass( "ui-widget ui-widget-content ui-corner-right" ).css('width', (selectWidth - 70) + 'px')
			.click(function() { $(this).select(); })
			.keydown(function(e) {
				if (self.options.passEnter && (13 === e.which) && !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>" )
				.data( "item.autocomplete", item )
				.append( "<a>" + item.label + "</a>" )
				.appendTo( ul );
		};

		this.button = $( "<button>", {
			"tabIndex": -1,
			"type": "button",
			"text": "&nbsp;",
			"title": "Show All Items",
			"style": "height:1.5em;padding:0!important;width:20px;margin:0!important;position:relative;top:5px;"
		} )
			.insertAfter( input )
			.button({
				icons: {
					primary: "ui-icon-triangle-1-s"
				},
				text: false
			})
			.removeClass( "ui-corner-all" )
			.addClass( "ui-corner-left 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() {
		if (this.options.shutOff) return;
		this.input.remove();
		this.portMessure.remove();
		this.button.remove();
		this.element.show();
		$.Widget.prototype.destroy.call( this );
	}
});
};

if ($.inArray(mw.config.get('wgNamespaceNumber'), [-1, 2, 3]) !== -1) {
	// 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
	$.when(mw.loader.using([
		'jquery.ui', 
		'mediawiki.util', 
		'mediawiki.user', 
		'ext.gadget.libUtil',
		'ext.gadget.libJQuery'
		]), $.ready)
		.then(function () {
			initCombobox($);
			umsg.umInstall();
			if (window.installOldLinks) umsg.umInstallOldLinks();
			$(document).triggerHandler('scriptLoaded', ['AxUserMsg', umsg]);
	});
}
})();
//</nowiki>