From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.

// <nowiki>

/*jshint undef:true, latedef:true, shadow:true, loopfunc:true, scripturl:true, undef:true */

/*globals jQuery, mw, importStylesheet */

mw.loader.using(['jquery.suggestions', 'mediawiki.api', 'mediawiki.Title', 'mediawiki.action.view.redirectPage'], function () { // <nowiki>

'use strict';



if (mw.config.get('wgNamespaceNumber') < 0)

	return;



importStylesheet('User:Kephir/gadgets/sagittarius.css');



function normaliseAnchor(anchor) {

	function encodeCodePoint(c) {

		if (c === 0x20)

			return '_';

		if (c < 0x80) {

			return '.' + c.toString(16).toUpperCase();

		} else if (c < 0x800) {

			return '.' + (0xc0 |  (c >>>  6)        ).toString(16).toUpperCase() +

				'.' + (0x80 | ( c         & 0x3f)).toString(16).toUpperCase();

		} else if (c < 0x10000) {

			return '.' + (0xe0 |  (c >>> 12)        ).toString(16).toUpperCase() +

				'.' + (0x80 | ((c >>>  6) & 0x3f)).toString(16).toUpperCase() +

				'.' + (0x80 | ( c         & 0x3f)).toString(16).toUpperCase();

		} else if (c < 0x200000) {

			return '.' + (0xf0 |  (c >>> 18)        ).toString(16).toUpperCase() +

				'.' + (0x80 | ((c >>> 12) & 0x3f)).toString(16).toUpperCase() +

				'.' + (0x80 | ((c >>>  6) & 0x3f)).toString(16).toUpperCase() +

				'.' + (0x80 | ( c         & 0x3f)).toString(16).toUpperCase();

		} else if (c < 0x4000000) {

			return '.' + (0xf8 |  (c >>> 24)        ).toString(16).toUpperCase() +

				'.' + (0x80 | ((c >>> 18) & 0x3f)).toString(16).toUpperCase() +

				'.' + (0x80 | ((c >>> 12) & 0x3f)).toString(16).toUpperCase() +

				'.' + (0x80 | ((c >>>  6) & 0x3f)).toString(16).toUpperCase() +

				'.' + (0x80 | ( c         & 0x3f)).toString(16).toUpperCase();

		} else if (c < 0x80000000) {

			return '.' + (0xfc |  (c >>> 30)        ).toString(16).toUpperCase() +

				'.' + (0x80 | ((c >>> 24) & 0x3f)).toString(16).toUpperCase() +

				'.' + (0x80 | ((c >>> 18) & 0x3f)).toString(16).toUpperCase() +

				'.' + (0x80 | ((c >>> 12) & 0x3f)).toString(16).toUpperCase() +

				'.' + (0x80 | ((c >>>  6) & 0x3f)).toString(16).toUpperCase() +

				'.' + (0x80 | ( c         & 0x3f)).toString(16).toUpperCase();

		}

	}



	// "." is not escaped!

	return anchor.replace(/[^0-9A-Za-z_:\.]/g, function (m) { /* [\ud800-\udbff][\udc00-\dfff]| */

		if (m.length === 2) { // surrogate pair

			return encodeCodePoint((m.charCodeAt(0) & 0x3ff) << 10 | m.charCodeAt(1) & 0x3ff);

		} else {

			return encodeCodePoint(m.charCodeAt(0));

		}

	});

}



function normaliseTitle(title) {

	try {

		var t = new mw.Title(title);

		return t.getPrefixedText();

	} catch (e) {

		return null;

	}

}



function el(tag, child, attr, events) {

	var node = document.createElement(tag);

 

	if (child) {

		if ((typeof child === 'string') || (typeof child.length !== 'number'))

			child = child];

		for (var i = 0; i < child.length; ++i) {

			var ch = childi];

			if ((ch === void(null)) || (ch === null))

				continue;

			else if (typeof ch !== 'object')

				ch = document.createTextNode(String(ch));

			node.appendChild(ch);

		}

	}



	if (attr) for (var key in attr) {

		if ((attrkey === void(0)) || (attrkey === null))

			continue;

		node.setAttribute(key, String(attrkey]));

	}



	if (events) for (var key in events) {

		var handler = eventskey];

		if ((key === 'input') && (window.oninput === void(0))) {

			key = 'change';

		}

		node.addEventListener(key, handler, false);

	}



	return node;

}



function link(child, href, attr, ev) {

	attr = attr || {};

	ev = ev || {};

	if (typeof attr === 'string') {

		attr = { "title": attr };

	}

	if (typeof href === 'string')

		attr.href = href;

	else {

		attr.href = 'javascript:void(null);';

		ev.click = href;

	}

	return el('a', child, attr, ev);

}



var templateGroups = {

	"from": "Source",

	"to"  : "Target",

	"misc": "Miscellaneous"

};



var templateAliases = {

	"R to anchor"  : "R to section",

	"R to subtopic": "R to related topic",

	

	"This is a redirect.": "This is a redirect",

	"Redr"               : "This is a redirect",



	"Rcat shell"    : "Redirect category shell",

	"RCAT shell"    : "Redirect category shell",

	"redirect shell": "Redirect category shell",

	"rcatsh"        : "Redirect category shell",

	

	"": ""

};



var wgNamespaceIds = mw.config.get('wgNamespaceIds');



var redirectTemplates = {

	/* source */

	"R restricted": {

		"group": "from",

		"label": "Name unavailable due to technical restrictions"

	},

	"R from initialism": {

		"group": "from",

		"label": "Initialism",

		"auto": "capitals"

	},

	"R from alternative spelling": {

		"group": "from",

		"label": "Alternative spelling with same pronunciation"

	},

	"R from other capitalisation": {

		"group": "from",

		"label": "Other capitalisation"

	},

	"R from misspelling": {

		"group": "from",

		"label": "Misspelling",

		"implies": "R unprintworthy"

	},

	"R from modification": {

		"group": "from",

		"label": "Modification (punctuation or word order)"

	},

	"R from plural": {

		"group": "from",

		"label": "Plural"

	},

	"R to plural": {

		"group": "to",

		"label": "Plural"

	},

	"R from name and country": {

		"group": "from",

		"label": "Name, Country/State/Province"

	},

	"R from long name": {

		"group": "from",

		"label": "Long name"

	},

	"R from alternative name": {

		"group": "from",

		"label": "Alternative name (nickname, etc.)"

	},

	"R from former name": {

		"group": "from",

		"label": "Former name with no historical significance"

	},

	"R from historic name": {

		"group": "from",

		"label": "Historic name",

		"implies": "R printworthy"

	},

	"R from subtopic": {

		"group": "from",

		"label": "Sub-topic"

	},

	"R from scientific name": {

		"group": "from",

		"label": "Scientific name",

		"implies": "R printworthy"

	},

	"R to scientific name": {

		"group": "to",

		"label": "Scientific name"

	},

	"R from alternative scientific name": {

		"group": "from",

		"label": "Alternative scientific name"

	},

	"R from molecular formula": {

		"group": "from",

		"label": "Molecular formula"

	},

	"R from alternative language": {

		"group": "from",

		"label": "Alternative language"

	},

	"R from ASCII": {

		"group": "from",

		"label": "ASCII-only title",

		"auto": "from-ascii-to-unicode"

	},

	"R from Unicode": {

		"group": "from",

		"label": "Single Unicode character"

	},

	"R from diacritics": {

		"group": "from",

		"label": "Title with diacritics",

		"conflict": "R from title without diacritics"

	},

	"R from title without diacritics": {

		"group": "from",

		"label": "Title without diacritics",

		"conflict": "R from diacritics"

	},

	"R from surname": {

		"group": "from",

		"label": "Surname",

	},

	"R from CamelCase": {

		"group": "from",

		"label": "Old-style CamelCase name"

	},

	"R from unnecessary disambiguation": {

		"group": "from",

		"label": "Redundant disambiguation"

	},

	"R to decade": {

		"group": "to",

		"label": "Decade article"

	},

	"R to disambiguation page": {

		"group": "to",

		"label": "Disambiguation page"

	},

	"R from ambiguous page": {

		"group": "from",

		"label": "Ambiguous title"

	},

	"R from EXIF": {

		"group": "from",

		"label": "Title derived from EXIF data"		

	},

	"R to list entry": {

		"group": "to",

		"label": "List entry"

	},

	"R from member": {

		"group": "from",

		"label": "Member of the target organisation"

	},

	"R to related topic": {

		"group": "to",

		"label": "Related topic"

	},

	"R from related word": {

		"group": "from",

		"label": "Related word"

	},

	"R from school": {

		"group": "from",

		"label": "School"

	},

	"R to section": {

		"group": "to",

		"label": "Section",

		"auto": "anchor"

	},

	"R from shortcut": {

		"group": "from",

		"label": "Shortcut",

		"conflict": "R from template shortcut"

	},

	"R from template shortcut": {

		"group": "from",

		"label": "Template shortcut",

		"conflict": "R from shortcut"

	},

	"R from song": {

		"group": "from",

		"label": "Song (to artist article)"

	},

	"R from book": {

		"group": "from",

		"label": "Book (to author or publisher)"

	},

	"R from duplicated article": {

		"group": "from",

		"label": "Duplicated article"

	},

	"R from merge": {

		"group": "from",

		"label": "Merged article"

	},

	"R from move": {

		"group": "from",

		"label": "Moved title" 

	},

	"R from album": {

		"group": "from",

		"label": "Album name"

	},

	"R from mathematical symbol or equation": {

		"group": "from",

		"label": "Mathematical symbol or equation"

	},

	"R to category": {

		"group": "to",

		"label": "Category namespace (from other namespaces)",

		"target-namespace": wgNamespaceIds.category,

		"xnr-only": true

	},

	"R to help": {

		"group": "to",

		"label": "Help namespace",

		"target-namespace": wgNamespaceIds.help

	},

	"R to main": {

		"group": "to",

		"label": "Main namespace (from other namespaces)",

		"target-namespace": 0,

		"xnr-only": true

	},

	"R to portal": {

		"group": "to",

		"label": "Portal namespace",

		"target-namespace": wgNamespaceIds.portal

	},

	"R to talk": {

		"group": "to",

		"label": "Talk namespace",

		"target-talk": true

	},

	"R to template": {

		"group": "to",

		"label": "Template namespace (from other namespaces)",

		"target-namespace": wgNamespaceIds.template,

		"xnr-only": true

	},

	"R to user": {

		"group": "to",

		"label": "User namespace",

		"target-namespace": wgNamespaceIds.user

	},

	"R to project": {

		"group": "to",

		"label": "Wikipedia namespace",

		"target-namespace": wgNamespaceIds.project

	},



	/* miscellanea */

	"R with old history": {

		"group": "misc",

		"label": "Has old history"

	},

	"R for convenience": {

		"group": "misc",

		"label": "Purely for convenience"

	},

	"R with possibilities": {

		"group": "misc",

		"label": "Might be worthy of own article",

		"conflict": 

			"R unprintworthy",

			"R from subtopic without possibilities",

			"R from highway in region without possibilities"

		

	},

	"R unprintworthy": {

		"group": "misc",

		"label": "Unprintworthy",

		"conflict": "R printworthy"

	},

	"R printworthy": {

		"group": "misc",

		"label": "Printworthy",

		"conflict": "R unprintworthy"

	},



	"": void(0) // for convenience

};

delete redirectTemplates""];



var api = new mw.Api();

var contentText = document.getElementById('mw-content-text');

var firstHeading = document.getElementById('firstHeading');

var redirMsg = contentText.getElementsByClassName('redirectMsg')[0];

var uiWrapper = el('div');

var edittoken = null;



function MarkupBlob(markup) {

	if (!markup) {

		this.target = '';

		this.rcatt = {};

		this.tail = '';

	} else

		this.parse(markup);

}



MarkupBlob.prototype.parse = function (markup) {

	var rdrx = /^#REDIRECT\s*\[\[\s*([^\|{}[\]]+?)\s*]]\s*/i;

	var tprx = /^\s*{{([A-Za-z ]+)((?:\|(?:[^|{}]*|{{[^|}]*}})+)*)}}\s*/i;

	var m;



	if (!(m = rdrx.exec(markup)))

		throw new Error('Not a redirect');

	markup = markup.substr(m0].length);

	this.target = m1];



	this.rcatt = {};

	out: while ((m = tprx.exec(markup))) {

		var alias = normaliseTitle(m1]);

		while (templateAliasesalias])

			alias = templateAliasesalias]; // hopefully there are no loops.

		

		if (alias === "This is a redirect") {

			var params = m2].split('|');

			for (var j = 0; j < params.length; ++j) {

				if (!paramsj])

					continue;

				if (paramsj].indexOf('=') !== -1)

					break out;

				alias = normaliseTitle("R " + paramsj]);

				while (templateAliasesalias])

					alias = templateAliasesalias]; // hopefully there are still no loops.

				if (alias in redirectTemplates)

					this.rcattalias = true;

				else

					break out;

			}

		} else if (alias === "Redirect category shell") {

			var mm, rr = /{{(.*?)}}/g;

			while (mm = rr.exec(m2])) {

				alias = normaliseTitle(mm1]);

				while (templateAliasesalias])

					alias = templateAliasesalias];

				if (alias in redirectTemplates)

					this.rcattalias = true;

			}

		} else if (alias in redirectTemplates) {

			if (m2]) // TODO

				break;

			this.rcattalias = true;

		} else {

			break;	

		}

		markup = markup.substr(m0].length);

	}



	this.tail = markup;

};



MarkupBlob.prototype.toString = function () {

	var markup = '#REDIRECT [[' + this.target + ']] ';

	var tail = '';

	var wrapped = [];

	for (var key in this.rcatt) {

		if (this.rcattkey])

			if ((wrapped.length < 6) && /^R\s+/.test(key))

				wrapped.push('{{' + key + '}}\n');

			else

				tail += '{{' + key + '}}\n';

		

	}

	if (wrapped.length)

		markup += "{{Redirect category shell|\n" + wrapped.join("") + "}}\n";

	markup += tail + '\n';

	markup += this.tail;

	return markup;

};



function buildTagList(rcatt) {

	function makeCheckBox(key) {

		return el('label', 

			el('input', null, {

				type: "checkbox",

				checked: (key in rcatt) ? "checked" : null,

			}, {

				change: function (ev) {

					rcattkey = this.checked;

				}

			}),

			' ',

			redirectTemplateskey].label

		], {

			"title": redirectTemplateskey].tooltip

		});

	}

	var list = el('dl', null, { "class": "tag-list" });

	var group = {};

	for (var key in templateGroups) {

		list.appendChild(el('dt', templateGroupskey]));

		list.appendChild(el('dd', groupkey = el('ul')));

	}

	for (var key in redirectTemplates) {

		var label = makeCheckBox(key);

		groupredirectTemplateskey].group].appendChild(el('li', label));

	}

	return list;

}



function buildEditingUI(mblob, saveCallback) {

	var statusbar;

	var needsCheck = true;

	var doSave;

	var uiLink, uiTarget;

	mblob = mblob || new MarkupBlob();

	

	function setStatus(status) {

		while (statusbar.firstChild)

			statusbar.removeChild(statusbar.firstChild);

		if (status) {

			if (typeof status === 'string')

				statusbar.appendChild(document.createTextNode(status));

			else {

				for (var j = 0; j < status.length; ++j) {

					if (typeof statusj === 'string')

						statusbar.appendChild(document.createTextNode(statusj]));

					else

						statusbar.appendChild(statusj]);

				}

			}

		}

	}

	

	function inputChanged(ev) {

		/*jshint validthis:true */

		try {

			mblob.target = this.value;

			var t = new mw.Title(this.value);

			var frag = t.getFragment() ? '#' + normaliseAnchor(t.getFragment()) : '';

			uiLink.href = mw.util.getUrl(t.getPrefixedDb(), { redirect: "no" }) + frag;

			setStatus();

		} catch (e) {

			setStatus('Invalid title.');

			uiLink.href = 'javascript:void(0);';

		}

		needsCheck = true;

	}



	var uiStatusLine;

	var ui = el('form', 

		el('div', 

			el('ul', 

				el('li', 

					uiTarget = el('input', null, {

						'type': 'text',

						'class': 'redirectText',

						'value': mblob.target

					}, {

						'input': inputChanged,

						'change': inputChanged,

						'blur': function (ev) { // i would not have to write this, if it were not for jQuery. seriously.

							if (mblob.target === this.value)

								return;

							inputChanged.call(this, ev);

						}

					})

				])

			], { 'class': 'redirectText' })

		], { 'class': 'redirectMsg' }),

		buildTagList(mblob.rcatt),

		uiStatusLine = el('p', 

			statusbar = el('span', [], {

				'class': 'status-line'

			}),

			el('span', 

				link(["Statistics for this page"], 'https://tools.wmflabs.org/pageviews?project=en.wikipedia.org&pages=' + encodeURIComponent(mw.config.get('wgPageName'))),

				' • ',

				link(["About Sagittarius"], mw.util.getUrl("User:Kephir/gadgets/sagittarius"))

			], {

				'style': 'float: right;'

			})

		])

	], {

		'action': 'javascript:void(0)',

		'class': 'kephir-sagittarius-editor'

	}, {

		'submit': function (ev) {

			ev.preventDefault();

			ui.doCheck(saveCallback);

		}

	});

	ui.statusLine = uiStatusLine;



	var sectCache = {};

	var $uiTarget = jQuery(uiTarget);

	$uiTarget.suggestions({

		submitOnClick: false,

		delay: 500,

		fetch: function (query) {

			$uiTarget.suggestions('suggestions', []);

			if (query.indexOf('#') !== -1) {

				var title = query.substr(0, query.indexOf('#'));

				var sect = query.substr(query.indexOf('#') + 1);



				if (sectCachetitle]) {

					var normSect = normaliseAnchor(sect);

					$uiTarget.suggestions('suggestions',

						sectCachetitle].filter(function (item) {

							var norm = normaliseAnchor(item.anchor);

							return norm.substr(0, normSect.length) === normSect;

						})

					);

					return;	

				}



				api.get({

					action: 'parse',

					page: title,

					prop: 'sections|properties',

					redirects: '1'

				}).then(function (result) {

					if (result.parse.redirects && result.parse.redirects.length) {

						// XXX

						return;

					}

					

					var disambig = false; // XXX



					var normSect = normaliseAnchor(sect);

					sectCachetitle = result.parse.sections.map(function (item) {

						return {

							anchor: item.anchor,

							title: title + '#' + decodeURIComponent(item.anchor.replace(/_/g, ' ').replace(/\.([0-9A-Fa-f][0-9A-Fa-f])/g, '%$1')), // XXX: hack

							disambig: disambig,

							toString: function () {

								return this.title;

							}

						};

					});



					$uiTarget.suggestions('suggestions',

						sectCachetitle].filter(function (item) {

							var norm = normaliseAnchor(item.anchor);

							return norm.substr(0, normSect.length) === normSect;

						})

					);

				});

				return;

			}



			api.get({

				action: 'query',

				generator: 'allpages',

				gapprefix: query,

				gaplimit: 16,

				prop: 'info|pageprops',

			}).then(function (result) {

				var pglist = [];

				for (var pgid in result.query.pages) {

					var page = result.query.pagespgid];

					pglist.push({

						title: page.title,

						pageid: page.pageid,

						disambig: page.pageprops && ('disambiguation' in page.pageprops),

						redirect: 'redirect' in page,

						toString: function () {

							return this.title;

						}

					});

				}

				$uiTarget.suggestions('suggestions', pglist);

			});

		},

		result: {

			render: function (item, content) {

				var elm = this0];

				elm.appendChild(el('span', item.title], {

					style: item.redirect ? 'font-style: italic' : ''

				}));

				if (item.disambig)

					elm.appendChild(el('small', ' (disambiguation page)']));

				if (item.redirect)

					elm.appendChild(el('small', ' (redirect)']));

			},

			

			select: function ($textbox) {

				var item = this.data('text');

				var textbox = $textbox0];



				textbox.value = item.title;

				if (item.redirect) {

					api.get({

						action: 'query',

						pageids: item.pageid,

						redirects: '1'

					}).then(function (result) {

						var redir = result.query.redirects.pop();

						textbox.value = redir.to + (redir.tofragment ? '#' + redir.tofragment : '');

					});

				}



				return true;

			}

		}

	});



	ui.doCheck = function (callback) {

		var that = this;



		if (!/^\s*[^\|{}[\]]+\s*$/.test(mblob.target)) {

			setStatus(['Error: the target page name is invalid.']);

			return;

		}



		if (needsCheck) {

			var oldTarget = mblob.target;

			var normTarget;

			try {

				normTarget = new mw.Title(oldTarget);

			} catch (e) {

				setStatus(['"', oldTarget, '" is not a valid page name. Try again to proceed anyway.']);

				return;

			}



			setStatus(['Checking target validity...']);

			needsCheck = false;



			api.get({

				action: 'parse',

				page: oldTarget = mblob.target,

				prop: 'sections',

				redirects: '1'

			}, {

				success: function (result) {

					var m;

					if (result.error) {

						if (result.error.code === 'missingtitle') {

							setStatus([

								'Error: The target page "',

								link([normTarget.getPrefixedText()], mw.util.getUrl(normTarget.getPrefixedText(), { "class": "new" })),

								'" does not exist. Try again to proceed anyway.'

							]);

						} else {

							setStatus([

								'API error: "',

								result.error.info,

								'" [code: ', el('code', result.error.code]), ']'

							]);

						}

						return;

					}



					if (result.parse.redirects && result.parse.redirects0]) {

						var newTarget = result.parse.redirects0].to + (result.parse.redirects0].tofragment ? "#" + result.parse.redirects0].tofragment : "");

						setStatus([

							'Error: The target page "',

							link([normTarget.getPrefixedText()], mw.util.getUrl(normTarget.getPrefixedText(), { redirect: "no" })),

							'" is already a redirect to "',

							link([newTarget], mw.util.getUrl(newTarget, { redirect: "no" })),

							'". Try again to proceed anyway, or ',

							link(['retarget this redirect to point there directly'], function () {

								uiTarget.value = mblob.target = newTarget +

									((!result.parse.redirects0].tofragment && normTarget.fragment) ? '#' + normTarget.fragment : '');

								needsCheck = true;

							}),

							'.'

						]);

						return;

					}



					if (normTarget.fragment) { // we have a section link

						var sect = normaliseAnchor(normTarget.fragment);

						var isValidSect = false;



						var sectlist = result.parse.sections;

						for (var j = 0; j < sectlist.length; ++j) {

							if (sectlistj].anchor === sect)

								isValidSect = true;

						}



						if (!isValidSect) {

							setStatus([

								'Error: The target page "',

								link([normTarget.getPrefixedText()], mw.util.getUrl(normTarget.getPrefixedText(), { redirect: "no" })),

								'" does not have a a section called "',

								normTarget.fragment,

								'". Try again to proceed anyway.'

							]);



							return;

						}

					}



					callback(setStatus);

				}

			});

			

			return;

		}



		callback(setStatus);

	};



	return ui;

}



if ((mw.config.get('wgAction') === 'view') && (mw.config.get('wgArticleId') === 0)) { // nonexistent page.

	uiWrapper.appendChild(el('div', 

		link(['Create a redirect'], function () {

			while (uiWrapper.hasChildNodes())

				uiWrapper.removeChild(uiWrapper.firstChild);

			var mblob = new MarkupBlob();

			var ui = buildEditingUI(mblob, function (setStatus) {

				setStatus(['Saving...']);

				api.post({

					action: 'edit',

					title: mw.config.get('wgPageName'),

					token: mw.user.tokens.get('editToken'),

					createonly: 1,

					summary: 'Redirecting to [[' + mblob.target + ']] ([[User:Kephir/gadgets/sagittarius|♐]])',

					text: mblob.toString()

				}, {

					success: function (result) {

						if (result.error) {

							setStatus([

								'API error: "',

								result.error.info,

								'" [code: ', el('code', result.error.code]), ']'

							]);

							return;

						}

						setStatus(['Saved. Reloading page...']);

						if (/redirect=no/.test(location.href)) // XXX

							location.reload();

						else

							location.search = location.search ? location.search + '&redirect=no' : '?redirect=no';

					}

				});				

			});

			ui.statusLine.insertBefore(el('input', null, {

				type: 'submit',

				value: 'Save'

			}), ui.statusLine.firstChild);

			uiWrapper.appendChild(ui);

		}), ' from this page with Sagittarius'

	], {

		"class": "kephir-sagittarius-invite"

	}));

	contentText.parentNode.insertBefore(uiWrapper, contentText);

} else if ((mw.config.get('wgAction') === 'view') && mw.config.get('wgIsRedirect') && redirMsg) {

	// start editor immediately

	uiWrapper.appendChild(el('div', 'Loading page source…'], {

		"class": "kephir-sagittarius-loading"

	}));

	contentText.insertBefore(uiWrapper, contentText.firstChild);

	api.get({

		action: 'query',

		prop: 'info|revisions',

		rvprop: 'timestamp|content',

		pageids: mw.config.get('wgArticleId'),

		rvstartid: mw.config.get('wgRevisionId'),

		rvlimit: 1,

		rvdir: 'older',

		intoken: 'edit',

	}, {

		success: function (result) {

			if (result.error) {

				uiWrapper.appendChild(el('div', 

					'API error: "',

					result.error.info,

					'" [code: ', el('code', result.error.code]), ']. Reload to try again.'

				], {

					"class": "kephir-sagittarius-error"

				}));

				return;

			}

			while (uiWrapper.hasChildNodes())

				uiWrapper.removeChild(uiWrapper.firstChild);

			var page = result.query.pagesmw.config.get('wgArticleId')];

			var mblob;

			var token = page.edittoken;

			try {

				mblob = new MarkupBlob(page.revisions0]['*']);

			} catch(e) {

				uiWrapper.appendChild(el('div', 'Error: unable to parse page. Edit the source manually.'], {

					"class": "kephir-sagittarius-error"

				}));

				return;

			}

			redirMsg.parentNode.removeChild(redirMsg);

			var ui = buildEditingUI(mblob, function (setStatus) {

				setStatus(['Saving...']);

				api.post({

					action: 'edit',

					title: mw.config.get('wgPageName'),

					basetimestamp: page.revisions0].timestamp,

					token: mw.user.tokens.get('editToken'),

					summary: 'Redirecting to [[' + mblob.target + ']] ([[User:Kephir/gadgets/sagittarius|♐]])',

					text: mblob.toString()

				}, {

					success: function (result) {

						if (result.error) {

							setStatus([

								'API error: "',

								result.error.info,

								'" [code: ', el('code', result.error.code]), ']'

							]);

							return;

						}

						setStatus(['Saved. Reloading page...']);

						if (/redirect=no/.test(location.href)) // XXX

							location.reload();

						else

							location.search = location.search ? location.search + '&redirect=no' : '?redirect=no';

					}

				});				

			});

			ui.statusLine.insertBefore(el('input', null, {

				type: 'submit',

				value: 'Save'

			}), ui.statusLine.firstChild);

			uiWrapper.appendChild(ui);

		}

	});

} else if ((mw.config.get('wgPageContentModel') === 'wikitext') && ((mw.config.get('wgAction') === 'edit') || (mw.config.get('wgAction') === 'submit'))) {

	if (mw.util.getParamValue('section'))

		return;

	var editform = document.getElementById('editform');



	if (editform.wpTextbox1.readOnly)

		return;



	var uiPivot = document.getElementsByClassName('wikiEditor-ui')[0];

	// Seemingly randomly, this sometimes doesn't exist. Further debugging is necessary.

	if (!uiPivot)

		return nil;



	var ui, mblob;

	firstHeading.appendChild(document.createTextNode(' '));

	firstHeading.appendChild(link(['♐'], function () {

		if (ui && ui.parentNode)

			ui.parentNode.removeChild(ui);



		try {

			mblob = new MarkupBlob(editform.wpTextbox1.value);

		} catch (e) {

			alert("Error: unable to parse page. This page is probably not a redirect.");

			return;

		}



		ui = buildEditingUI(mblob, function () {

			editform.wpSummary.value = 'Redirecting to [[' + mblob.target + ']] ([[User:Kephir/gadgets/sagittarius|♐]])';

			editform.wpTextbox1.value = mblob.toString();

			mblob = null;

			ui.style.display = 'none';

			uiPivot.style.display = '';

		});

		ui.style.display = 'none';

		ui.statusLine.insertBefore(el('input', null, {

			type: "button",

			value: "Cancel",

		}, {

			click: function () {

				mblob = null;

				ui.style.display = 'none';

				uiPivot.style.display = '';				

			}

		}), ui.statusLine.firstChild);

		ui.statusLine.insertBefore(el('input', null, {

			type: "submit",

			value: "Check"

		}), ui.statusLine.firstChild);

		uiPivot.parentNode.insertBefore(ui, uiPivot);

		uiPivot.style.display = 'none';

		ui.style.display = '';

	}, {

		"class": "kephir-sagittarius-editlink",

		"title": "Edit this redirect with Sagittarius"

	}));



	var submitButton;

	var inputs = editform.getElementsByTagName('input');

	for (var i = 0; i < inputs.length; ++i) {

		inputsi].addEventListener('click', function (ev) {

			submitButton = this;

		}, false);

	}



	editform.addEventListener('submit', function (ev) {

		if (submitButton !== editform.wpSave)

			return;

		if (mblob) {

			ev.preventDefault();

			ev.stopImmediatePropagation();

			ui.doCheck(function (setStatus) {

				setStatus(['Proceeding with saving...']);

				editform.wpTextbox1.value = mblob.toString();

				editform.wpSummary.value = 'Redirecting to [[' + mblob.target + ']] ([[User:Kephir/gadgets/sagittarius|♐]])';

				mblob = null;

				editform.submit();

			});

		}

	}, false);

}



if (!window.kephirSagittariusFollowCategoryRedirects)

if ((mw.config.get('wgAction') === 'view') && (mw.config.get('wgNamespaceNumber') === wgNamespaceIds.category)) {

	var pagesList = document.getElementById('mw-pages').getElementsByClassName('mw-redirect');

	for (var i = 0; i < pagesList.length; ++i) {

		pagesListi].href += '?redirect=no';

	}

}



});

// </nowiki>