Jump to content

User:Wugapodes/Capricorn.js: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
m Fix requested on WP:IANB
no longer runs on diff views; earlier and less costly abort on non-redirects; allow collapsing of rcats; remove deprecated API call; refactor code
Line 6: Line 6:
/*globals jQuery, mw, importStylesheet */
/*globals jQuery, mw, importStylesheet */


////////////////////////////////////////////////////////////////////////////////
$.getJSON("https://en.wikipedia.org/w/index.php?title=User:Wugapodes/Capricorn/RedirectAliases.json&action=raw&ctype=application/json",function(aliasJSON) {
// Helper function definitions
var templateAliases = aliasJSON;
function encodeCodePoint(c) {
console.log(templateAliases);
if (c === 0x20)
$.getJSON("https://en.wikipedia.org/w/index.php?title=User:Wugapodes/Capricorn/RedirectTemplates.json&action=raw&ctype=application/json",function(templateJSON) {
return '_';
var redirectTemplates = templateJSON;
if (c < 0x80) {
console.log(redirectTemplates);
return '.' + c.toString(16).toUpperCase();

} else if (c < 0x800) {
mw.loader.using(['jquery.suggestions', 'mediawiki.api', 'mediawiki.Title', 'mediawiki.action.view.redirectPage'], function () { // <nowiki>
return '.' + (0xc0 | (c >>> 6) ).toString(16).toUpperCase() +
'use strict';
'.' + (0x80 | ( c & 0x3f)).toString(16).toUpperCase();

} else if (c < 0x10000) {
if (mw.config.get('wgNamespaceNumber') < 0)
return '.' + (0xe0 | (c >>> 12) ).toString(16).toUpperCase() +
return;
'.' + (0x80 | ((c >>> 6) & 0x3f)).toString(16).toUpperCase() +

'.' + (0x80 | ( c & 0x3f)).toString(16).toUpperCase();
importStylesheet('User:Wugapodes/Capricorn.css');
} 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();
}
}


function normaliseAnchor(anchor) {
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!
// "." is not escaped!
return anchor.replace(/[^0-9A-Za-z_:\.]/g, function (m) { /* [\ud800-\udbff][\udc00-\dfff]| */
return anchor.replace(/[^0-9A-Za-z_:\.]/g, function (m) { /* [\ud800-\udbff][\udc00-\dfff]| */
Line 158: Line 144:
};
};


////////////////////////////////////////////////////////////////////////////////
var wgNamespaceIds = mw.config.get('wgNamespaceIds');
// Callback functions


function mainCallback(aliasJSON, templateJSON) {
var api = new mw.Api();
var templateAliases = aliasJSON;
var contentText = document.getElementById('mw-content-text');
var redirectTemplates = templateJSON;
var firstHeading = document.getElementById('firstHeading');
//console.log(templateAliases);
var redirMsg = contentText.getElementsByClassName('redirectMsg')[0];
//console.log(redirectTemplates);
var uiWrapper = el('div');
// <nowiki>
var edittoken = null;
'use strict';


importStylesheet('User:Wugapodes/Capricorn.css');
function MarkupBlob(markup) {
if (!markup) {
this.target = '';
var wgNamespaceIds = mw.config.get('wgNamespaceIds');
this.rcatt = {};
this.tail = '';
var contentText = document.getElementById('mw-content-text');
} else
var firstHeading = document.getElementById('firstHeading');
this.parse(markup);
var redirMsg = contentText.getElementsByClassName('redirectMsg')[0];
}
var uiWrapper = el('div');

var edittoken = null;
MarkupBlob.prototype.parse = function (markup) {
var rdrx = /^#REDIRECT:?\s*\[\[\s*([^\|{}[\]]+?)\s*]]\s*/i;
var tprx = /^\s*{{([A-Za-z ]+)((?:\|(?:[^|{}]*|{{[^|}]*}})+)*)}}\s*/i;
function MarkupBlob(markup) {
var m;
if (!markup) {

this.target = '';
if (!(m = rdrx.exec(markup)))
this.rcatt = {};
throw new Error('Not a redirect');
this.tail = '';
markup = markup.substr(m[0].length);
} else
this.target = m[1];
this.parse(markup);

this.rcatt = {};
out: while ((m = tprx.exec(markup))) {
var alias = normaliseTitle(m[1]);
while (templateAliases[alias])
alias = templateAliases[alias]; // hopefully there are no loops.
if (alias === "This is a redirect") {
var params = m[2].split('|');
for (var j = 0; j < params.length; ++j) {
if (!params[j])
continue;
if (params[j].indexOf('=') !== -1)
break out;
alias = normaliseTitle("R " + params[j]);
while (templateAliases[alias])
alias = templateAliases[alias]; // hopefully there are still no loops.
if (alias in redirectTemplates)
this.rcatt[alias] = true;
else
break out;
}
} else if (alias === "Redirect category shell") {
var mm, rr = /{{(.*?)}}/g;
while (mm = rr.exec(m[2])) {
alias = normaliseTitle(mm[1]);
while (templateAliases[alias])
alias = templateAliases[alias];
if (alias in redirectTemplates)
this.rcatt[alias] = true;
}
} else if (alias in redirectTemplates) {
if (m[2]) // TODO
break;
this.rcatt[alias] = true;
} else {
break;
}
markup = markup.substr(m[0].length);
}
}

this.tail = markup;
};

MarkupBlob.prototype.toString = function () {
var markup = '#REDIRECT [[' + this.target + ']]\n';
var tail = '';
var wrapped = [];
for (var key in this.rcatt) {
if (this.rcatt[key])
if ((wrapped.length < 6) && /^R\s+/.test(key))
wrapped.push('{{' + key + '}}\n');
else
tail += '{{' + key + '}}\n';
}
if (wrapped.length)
markup += "\n{{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) {
rcatt[key] = this.checked;
}
}),
' ',
redirectTemplates[key].label
], {
"title": redirectTemplates[key].tooltip
});
}
var list = el('dl', null, { "class": "tag-list" });
var group = {};
for (var key in templateGroups) {
list.appendChild(el('dt', templateGroups[key]));
list.appendChild(el('dd', group[key] = el('ul')));
}
for (var key in redirectTemplates) {
var label = makeCheckBox(key);
group[redirectTemplates[key].group].appendChild(el('li', label));
}
return list;
}

//
// Interface creation
//
function buildEditingUI(mblob, saveCallback) {
var statusbar;
var needsCheck = true;
var doSave;
var uiLink, uiTarget;
mblob = mblob || new MarkupBlob();
MarkupBlob.prototype.parse = function (markup) {
// Change text of status bar
var rdrx = /^#REDIRECT:?\s*\[\[\s*([^\|{}[\]]+?)\s*]]\s*/i;
function setStatus(status) {
var tprx = /^\s*{{([A-Za-z ]+)((?:\|(?:[^|{}]*|{{[^|}]*}})+)*)}}\s*/i;
while (statusbar.firstChild) // Remove previous statuses
var m;
statusbar.removeChild(statusbar.firstChild);
if (status) { // If status is a string, add it
m = rdrx.exec(markup)
if (typeof status === 'string')
markup = markup.substr(m[0].length);
statusbar.appendChild(document.createTextNode(status));
this.target = m[1];
else { // Otherwise, loop through list and add statuses
for (var j = 0; j < status.length; ++j) {
this.rcatt = {};
if (typeof status[j] === 'string')
out: while ((m = tprx.exec(markup))) {
statusbar.appendChild(document.createTextNode(status[j]));
var alias = normaliseTitle(m[1]);
while (templateAliases[alias])
alias = templateAliases[alias]; // hopefully there are no loops.
if (alias === "This is a redirect") {
var params = m[2].split('|');
for (var j = 0; j < params.length; ++j) {
if (!params[j])
continue;
if (params[j].indexOf('=') !== -1)
break out;
alias = normaliseTitle("R " + params[j]);
while (templateAliases[alias])
alias = templateAliases[alias]; // hopefully there are still no loops.
if (alias in redirectTemplates)
this.rcatt[alias] = true;
else
else
break out;
statusbar.appendChild(status[j]);
}
}
} else if (alias === "Redirect category shell") {
var mm, rr = /{{(.*?)}}/g;
while (mm = rr.exec(m[2])) {
alias = normaliseTitle(mm[1]);
while (templateAliases[alias])
alias = templateAliases[alias];
if (alias in redirectTemplates)
this.rcatt[alias] = true;
}
} else if (alias in redirectTemplates) {
if (m[2]) // TODO
break;
this.rcatt[alias] = true;
} else {
break;
}
}
markup = markup.substr(m[0].length);
}
}
}
this.tail = markup;
// Check if the target has changed??
};
// Not actually sure what this does yet 21 Oct 2019
function inputChanged(ev) {
MarkupBlob.prototype.toString = function () {
/*jshint validthis:true */
var markup = '#REDIRECT [[' + this.target + ']]\n';
try {
mblob.target = this.value;
var tail = '';
var t = new mw.Title(this.value);
var wrapped = [];
for (var key in this.rcatt) {
var frag = t.getFragment() ? '#' + normaliseAnchor(t.getFragment()) : '';
if (this.rcatt[key])
if (uiLink) uiLink.href = mw.util.getUrl(t.getPrefixedDb(), { redirect: "no" }) + frag;
if ((wrapped.length < 6) && /^R\s+/.test(key))
setStatus();
wrapped.push('{{' + key + '}}\n');
} catch (e) {
else
setStatus('Invalid title.');
tail += '{{' + key + '}}\n';
if (uiLink) uiLink.href = 'javascript:void(0);';
}
}
if (wrapped.length)
needsCheck = true;
markup += "\n{{Redirect category shell|\n" + wrapped.join("") + "}}\n";
}
markup += tail + '\n';
markup += this.tail;
return markup;
};
function buildTagList(rcatt) {
var uiStatusLine;
function makeCheckBox(key) {
var patrolLine;
return el('label', [
var origTarget = mblob.target
var ui = el('form', [
el('input', null, {
type: "checkbox",
el('div', [
checked: (key in rcatt) ? "checked" : null,
el('ul', [
el('li', [
}, {
uiTarget = el('input', null, {
change: function (ev) {
'type': 'text',
rcatt[key] = this.checked;
}
'class': 'redirectText',
}),
'value': mblob.target
}, {
' ',
redirectTemplates[key].label
'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', [
patrolLine = el('span', [], {}),
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(["WP:TMR"], mw.util.getUrl("Wikipedia:Template messages/Redirect pages")),
' • ',
link(["About Capricorn"], mw.util.getUrl("User:Wugapodes/Capricorn"))
], {
], {
"title": redirectTemplates[key].tooltip
'style': 'float: right;'
})
});
])
}
], {
var list = el('dl', null, { "class": "tag-list" });
'action': 'javascript:void(0)',
var group = {};
'class': 'kephir-sagittarius-editor'
for (var key in templateGroups) {
}, {
list.appendChild(el('dt', templateGroups[key]));
'submit': function (ev) {
list.appendChild(el('dd', group[key] = el('ul')));
ev.preventDefault();
}
if (uiStatusLine.childNodes[1].childNodes[0]) {
for (var key in redirectTemplates) {
var patrolVal = uiStatusLine.childNodes[1].childNodes[0].childNodes[0].checked;
var label = makeCheckBox(key);
if (patrolVal) {
group[redirectTemplates[key].group].appendChild(el('li', label));
api.get({
}
"action": "query",
"format": "json",
var collapsibleContent = el('div', list, {
"prop": "revisions",
"meta": "tokens",
"class": "mw-collapsible-content",
"id": "capricorn-toggle-content"
"titles": mw.config.get('wgPageName'),
})
"rvprop": "ids",
return collapsibleContent;
"rvslots": "",
}
"rvlimit": "1",
"rvdir": "newer",
//
"type": "patrol"
// Interface creation
}, {
//
success: function (result) {
function buildEditingUI(mblob, saveCallback) {
//console.log(mw.config.get('wgPageName'))
var statusbar;
var patrolToken = result["query"]["tokens"]["patroltoken"];
var revIDpart = result["query"]["pages"];
var needsCheck = true;
var revID = null;
var doSave;
var uiLink, uiTarget;
for (var page in revIDpart) {
mblob = mblob || new MarkupBlob();
revID = revIDpart[page]["revisions"][0]["revid"];
}
// Change text of status bar
//console.log(revID)
function setStatus(status) {
api.post({
while (statusbar.firstChild) // Remove previous statuses
"action": 'patrol',
statusbar.removeChild(statusbar.firstChild);
"revid": revID,
if (status) { // If status is a string, add it
"token": patrolToken
if (typeof status === 'string')
}, {
statusbar.appendChild(document.createTextNode(status));
success: function (result) {
else { // Otherwise, loop through list and add statuses
if (result.error) {
for (var j = 0; j < status.length; ++j) {
console.log(result.error);
if (typeof status[j] === 'string')
setStatus([
statusbar.appendChild(document.createTextNode(status[j]));
'API error: "',
result.error.info,
else
statusbar.appendChild(status[j]);
'" [code: ', el('code', [result.error.code]), ']'
]);
}
console.log(result.error)
return;
}
}
});
}
});
}
}
}
}
ui.doCheck(saveCallback);
}
}
});
// Check if the target has changed??
ui.statusLine = uiStatusLine;
// Not actually sure what this does yet 21 Oct 2019
ui.patrolLine = patrolLine;
function inputChanged(ev) {
ui.origTarget = origTarget
/*jshint validthis:true */

try {
var sectCache = {};
mblob.target = this.value;
var $uiTarget = jQuery(uiTarget);
var t = new mw.Title(this.value);
$uiTarget.suggestions({
var frag = t.getFragment() ? '#' + normaliseAnchor(t.getFragment()) : '';
submitOnClick: false,
if (uiLink) uiLink.href = mw.util.getUrl(t.getPrefixedDb(), { redirect: "no" }) + frag;
delay: 500,
setStatus();
fetch: function (query) {
} catch (e) {
$uiTarget.suggestions('suggestions', []);
setStatus('Invalid title.');
if (query.indexOf('#') !== -1) {
if (uiLink) uiLink.href = 'javascript:void(0);';
var title = query.substr(0, query.indexOf('#'));
}
var sect = query.substr(query.indexOf('#') + 1);
needsCheck = true;

}
if (sectCache[title]) {
var normSect = normaliseAnchor(sect);
var uiStatusLine;
$uiTarget.suggestions('suggestions',
var patrolLine;
sectCache[title].filter(function (item) {
var origTarget = mblob.target
var norm = normaliseAnchor(item.anchor);
var ui = el('form', [
return norm.substr(0, normSect.length) === normSect;
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' }),
return;
el('input', null, {
"type": "button",
"class":"capricorn-toggle",
"id": "capricorn-toggle-button",
"value": "Hide rcat list"
}, {
'click': function() {
$( "#capricorn-toggle-content" ).toggle();
var buttonText = $("#capricorn-toggle-button")[0].value;
if (buttonText === "Hide rcat list") {
$("#capricorn-toggle-button")[0].value = "Show rcat list"
} else {
$("#capricorn-toggle-button")[0].value = "Hide rcat list"
};
}
})
], { 'class': 'redirectMsg' }),
buildTagList(mblob.rcatt),
uiStatusLine = el('p', [
patrolLine = el('span', [], {}),
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(["WP:TMR"], mw.util.getUrl("Wikipedia:Template messages/Redirect pages")),
' • ',
link(["About Capricorn"], mw.util.getUrl("User:Wugapodes/Capricorn"))
], {
'style': 'float: right;'
})
])
], {
'action': 'javascript:void(0)',
'class': 'kephir-sagittarius-editor'
}, {
'submit': function (ev) {
ev.preventDefault();
if (uiStatusLine.childNodes[1].childNodes[0]) {
var patrolVal = uiStatusLine.childNodes[1].childNodes[0].childNodes[0].checked;
if (patrolVal) {
api.get({
"action": "query",
"format": "json",
"prop": "revisions",
"meta": "tokens",
"titles": mw.config.get('wgPageName'),
"rvprop": "ids",
"rvslots": "",
"rvlimit": "1",
"rvdir": "newer",
"type": "patrol"
}, {
success: function (result) {
//console.log(mw.config.get('wgPageName'))
var patrolToken = result["query"]["tokens"]["patroltoken"];
var revIDpart = result["query"]["pages"];
var revID = null;
for (var page in revIDpart) {
revID = revIDpart[page]["revisions"][0]["revid"];
}
//console.log(revID)
api.post({
"action": 'patrol',
"revid": revID,
"token": patrolToken
}, {
success: function (result) {
if (result.error) {
console.log(result.error);
setStatus([
'API error: "',
result.error.info,
'" [code: ', el('code', [result.error.code]), ']'
]);
console.log(result.error)
return;
}
}
});
}
});
}
}
}
ui.doCheck(saveCallback);

}
});
ui.statusLine = uiStatusLine;
ui.patrolLine = patrolLine;
ui.origTarget = origTarget
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 (sectCache[title]) {
var normSect = normaliseAnchor(sect);
$uiTarget.suggestions('suggestions',
sectCache[title].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);
sectCache[title] = 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, '%')), // XXX: hack
disambig: disambig,
toString: function () {
return this.title;
}
};
});
$uiTarget.suggestions('suggestions',
sectCache[title].filter(function (item) {
var norm = normaliseAnchor(item.anchor);
return norm.substr(0, normSect.length) === normSect;
})
);
});
return;
}
api.get({
api.get({
action: 'parse',
action: 'query',
page: title,
generator: 'allpages',
prop: 'sections|properties',
gapprefix: query,
redirects: '1'
gaplimit: 16,
prop: 'info|pageprops',
}).then(function (result) {
}).then(function (result) {
var pglist = [];
if (result.parse.redirects && result.parse.redirects.length) {
for (var pgid in result.query.pages) {
// XXX
var page = result.query.pages[pgid];
return;
}
pglist.push({
title: page.title,
pageid: page.pageid,
var disambig = false; // XXX
disambig: page.pageprops && ('disambiguation' in page.pageprops),

redirect: 'redirect' in page,
var normSect = normaliseAnchor(sect);
sectCache[title] = 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, '%')), // XXX: hack
disambig: disambig,
toString: function () {
toString: function () {
return this.title;
return this.title;
}
}
};
});
});
}
$uiTarget.suggestions('suggestions', pglist);

$uiTarget.suggestions('suggestions',
sectCache[title].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.pages[pgid];
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 = this[0];
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)']));
},
},
result: {
select: function ($textbox) {
render: function (item, content) {
var item = this.data('text');
var elm = this[0];
elm.appendChild(el('span', [item.title], {
var textbox = $textbox[0];
style: item.redirect ? 'font-style: italic' : ''

}));
textbox.value = item.title;
if (item.redirect) {
if (item.disambig)
elm.appendChild(el('small', [' (disambiguation page)']));
api.get({
action: 'query',
if (item.redirect)
elm.appendChild(el('small', [' (redirect)']));
pageids: item.pageid,
},
redirects: '1'
}).then(function (result) {
select: function ($textbox) {
var redir = result.query.redirects.pop();
var item = this.data('text');
textbox.value = redir.to + (redir.tofragment ? '#' + redir.tofragment : '');
var textbox = $textbox[0];
});
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;
}
}

return true;
}
}
}
});
});
ui.doCheck = function (callback) {

var that = this;
ui.doCheck = function (callback) {
var that = this;
if (!/^\s*[^\|{}[\]]+\s*$/.test(mblob.target)) {

setStatus(['Error: the target page name is invalid.']);
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;
return;
}
}

if (needsCheck) {
setStatus(['Checking target validity...']);
needsCheck = false;
var oldTarget = mblob.target;
var normTarget;

api.get({
try {
normTarget = new mw.Title(oldTarget);
action: 'parse',
} catch (e) {
page: oldTarget = mblob.target,
setStatus(['"', oldTarget, '" is not a valid page name. Try again to proceed anyway.']);
prop: 'sections',
redirects: '1'
return;
}, {
}
success: function (result) {
setStatus(['Checking target validity...']);
var m;
needsCheck = false;
if (result.error) {
if (result.error.code === 'missingtitle') {
setStatus([
api.get({
'Error: The target page "',
action: 'parse',
page: oldTarget = mblob.target,
link([normTarget.getPrefixedText()], mw.util.getUrl(normTarget.getPrefixedText(), { "class": "new" })),
prop: 'sections',
'" does not exist. Try again to proceed anyway.'
]);
redirects: '1'
} else {
}, {
success: function (result) {
setStatus([
'API error: "',
var m;
result.error.info,
if (result.error) {
'" [code: ', el('code', [result.error.code]), ']'
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;
}
}
return;
if (result.parse.redirects && result.parse.redirects[0]) {
}
var newTarget = result.parse.redirects[0].to + (result.parse.redirects[0].tofragment ? "#" + result.parse.redirects[0].tofragment : "");

if (result.parse.redirects && result.parse.redirects[0]) {
var newTarget = result.parse.redirects[0].to + (result.parse.redirects[0].tofragment ? "#" + result.parse.redirects[0].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.redirects[0].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 (sectlist[j].anchor === sect)
isValidSect = true;
}

if (!isValidSect) {
setStatus([
setStatus([
'Error: The target page "',
'Error: The target page "',
link([normTarget.getPrefixedText()], mw.util.getUrl(normTarget.getPrefixedText(), { redirect: "no" })),
link([normTarget.getPrefixedText()], mw.util.getUrl(normTarget.getPrefixedText(), { redirect: "no" })),
'" does not have a a section called "',
'" is already a redirect to "',
link([newTarget], mw.util.getUrl(newTarget, { redirect: "no" })),
normTarget.fragment,
'". Try again to proceed anyway.'
'". Try again to proceed anyway, or ',
link(['retarget this redirect to point there directly'], function () {
uiTarget.value = mblob.target = newTarget +
((!result.parse.redirects[0].tofragment && normTarget.fragment) ? '#' + normTarget.fragment : '');
needsCheck = true;
}),
'.'
]);
]);

return;
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 (sectlist[j].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);
}
}
});

callback(setStatus);
return;
}
callback(setStatus);
};
return ui;
}
function setSummary(current,orig) {
var summary;
if (orig === current) {
summary= "Modifying [[WP:RCAT|redirect categories]] using [[User:Wugapodes/Capricorn|Capricorn ♑]]";
} else {
summary = 'Redirecting to [[' + current + ']] ([[User:Wugapodes/Capricorn|♑]])'
}
return summary
}
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...']);
var summary = setSummary(mblob.target,ui.origTarget)
api.post({
action: 'edit',
title: mw.config.get('wgPageName'),
createonly: 1,
summary: summary,
text: mblob.toString(),
token: mw.user.tokens.get('csrfToken')
}, {
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 Capricorn'
], {
"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: 'revisions',
rvprop: 'timestamp|content',
pageids: mw.config.get('wgArticleId'),
rvstartid: mw.config.get('wgRevisionId'),
rvlimit: 1,
rvdir: 'older'
}, {
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.pages[mw.config.get('wgArticleId')];
return;
var mblob;
}
var token = mw.user.tokens.get('csrfToken')

try {
callback(setStatus);
mblob = new MarkupBlob(page.revisions[0]['*']);
};
} catch(e) {

uiWrapper.appendChild(el('div', ['Error: unable to parse page. Edit the source manually.'], {
return ui;
"class": "kephir-sagittarius-error"
}
}));

return;
function setSummary(current,orig) {
}
var summary;
redirMsg.parentNode.removeChild(redirMsg);
if (orig === current) {
var ui = buildEditingUI(mblob, function (setStatus) {
summary= "Modifying [[WP:RCAT|redirect categories]] using [[User:Wugapodes/Capricorn|Capricorn ♑]]";
setStatus(['Saving...']);
} else {
var summary = setSummary(mblob.target,ui.origTarget)
summary = 'Redirecting to [[' + current + ']] ([[User:Wugapodes/Capricorn|♑]])'
api.post({
}
action: 'edit',
return summary
title: mw.config.get('wgPageName'),
}
basetimestamp: page.revisions[0].timestamp,

summary: summary,
if ((mw.config.get('wgAction') === 'view') && (mw.config.get('wgArticleId') === 0)) { // nonexistent page.
text: mblob.toString(),
uiWrapper.appendChild(el('div', [
token: mw.user.tokens.get('csrfToken')
link(['Create a redirect'], function () {
}, {
while (uiWrapper.hasChildNodes())
success: function (result) {
uiWrapper.removeChild(uiWrapper.firstChild);
if (result.error) {
var mblob = new MarkupBlob();
setStatus([
var ui = buildEditingUI(mblob, function (setStatus) {
'API error: "',
setStatus(['Saving...']);
result.error.info,
var summary = setSummary(mblob.target,ui.origTarget)
'" [code: ', el('code', [result.error.code]), ']'
api.post({
action: 'edit',
]);
return;
title: mw.config.get('wgPageName'),
createonly: 1,
}
setStatus(['Saved. Reloading page...']);
summary: summary,
if (/redirect=no/.test(location.href)) // XXX
text: mblob.toString(),
location.reload();
token: mw.user.tokens.get('csrfToken')
}, {
else
location.search = location.search ? location.search + '&redirect=no' : '?redirect=no';
success: function (result) {
if (result.error) {
}
setStatus([
});
});
'API error: "',
var userName = mw.user.getName();
result.error.info,
api.get({
'" [code: ', el('code', [result.error.code]), ']'
]);
"action": "query",
return;
"format": "json",
"list": "users",
"usprop": "groups",
"ususers": userName
}, { success: function(result) {
var groups = result["query"]["users"][0]["groups"]
if (groups.includes("patroller")) {
ui.patrolLine.insertBefore(el('label', [
el('input', [], {
'class': 'checkbox',
'type': 'checkbox',
'id': 'patrol',
'value': 'patrol'
}), 'Mark as patrolled?']),null);
}
}
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, {
});
ui.statusLine.insertBefore(el('input', null, {
type: 'submit',
type: 'submit',
value: 'Save'
value: 'Save'
}), ui.statusLine.firstChild);
}), ui.statusLine.firstChild);
uiWrapper.appendChild(ui);
uiWrapper.appendChild(ui);
}), ' from this page with Capricorn'
], {
"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())
} else if ((mw.config.get('wgPageContentModel') === 'wikitext') && ((mw.config.get('wgAction') === 'edit') || (mw.config.get('wgAction') === 'submit'))) {
uiWrapper.removeChild(uiWrapper.firstChild);
if (mw.util.getParamValue('section'))
var page = result.query.pages[mw.config.get('wgArticleId')];
var mblob;
return;
var token = page.edittoken;
var editform = document.getElementById('editform');
if (!editform || !editform.wpTextbox1 || editform.wpTextbox1.readOnly)
return;
var uiPivot = document.getElementsByClassName('wikiEditor-ui')[0];
var ui, mblob;
firstHeading.appendChild(document.createTextNode(' '));
firstHeading.appendChild(link(['♑'], function () {
if (ui && ui.parentNode)
ui.parentNode.removeChild(ui);
try {
try {
mblob = new MarkupBlob(page.revisions[0]['*']);
mblob = new MarkupBlob(editform.wpTextbox1.value);
} catch(e) {
} catch (e) {
uiWrapper.appendChild(el('div', ['Error: unable to parse page. Edit the source manually.'], {
alert("Error: unable to parse page. This page is probably not a redirect.");
"class": "kephir-sagittarius-error"
}));
return;
return;
}
}
redirMsg.parentNode.removeChild(redirMsg);
currentTarget = mblob.target
var ui = buildEditingUI(mblob, function (setStatus) {
setStatus(['Saving...']);
var summary = setSummary(mblob.target,ui.origTarget)
ui = buildEditingUI(mblob, function () {
editform.wpSummary.value = 'Redirecting to [[' + mblob.target + ']] ([[User:Wugapodes/Capricorn|♑]])';
api.post({
editform.wpTextbox1.value = mblob.toString();
action: 'edit',
mblob = null;
title: mw.config.get('wgPageName'),
ui.style.display = 'none';
basetimestamp: page.revisions[0].timestamp,
uiPivot.style.display = '';
summary: summary,
text: mblob.toString(),
token: mw.user.tokens.get('csrfToken')
}, {
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.style.display = 'none';
var userName = mw.user.getName();
ui.statusLine.insertBefore(el('input', null, {
api.get({
"action": "query",
type: "button",
"format": "json",
value: "Cancel",
}, {
"list": "users",
"usprop": "groups",
click: function () {
"ususers": userName
mblob = null;
ui.style.display = 'none';
}, { success: function(result) {
uiPivot.style.display = '';
var groups = result["query"]["users"][0]["groups"]
if (groups.includes("patroller")) {
ui.patrolLine.insertBefore(el('label', [
el('input', [], {
'class': 'checkbox',
'type': 'checkbox',
'id': 'patrol',
'value': 'patrol'
}), 'Mark as patrolled?']),null);
}
}
}
}), ui.statusLine.firstChild);
});
ui.statusLine.insertBefore(el('input', null, {
ui.statusLine.insertBefore(el('input', null, {
type: 'submit',
type: "submit",
value: 'Save'
value: "Check"
}), ui.statusLine.firstChild);
}), ui.statusLine.firstChild);
uiPivot.parentNode.insertBefore(ui, uiPivot);
uiWrapper.appendChild(ui);
uiPivot.style.display = 'none';
ui.style.display = '';
}, {
"class": "kephir-sagittarius-editlink",
"title": "Edit this redirect with Capricorn"
}));
var submitButton;
var inputs = editform.getElementsByTagName('input');
for (var i = 0; i < inputs.length; ++i) {
inputs[i].addEventListener('click', function (ev) {
submitButton = this;
}, false);
}
}
});
editform.addEventListener('submit', function (ev) {
} else if ((mw.config.get('wgPageContentModel') === 'wikitext') && ((mw.config.get('wgAction') === 'edit') || (mw.config.get('wgAction') === 'submit'))) {
if (submitButton !== editform.wpSave)
if (mw.util.getParamValue('section'))
return;
return;
if (mblob) {
var editform = document.getElementById('editform');
ev.preventDefault();
ev.stopImmediatePropagation();
ui.doCheck(function (setStatus) {
setStatus(['Proceeding with saving...']);
var summary = setSummary(currentTarget,ui.origTarget)
editform.wpTextbox1.value = mblob.toString();
editform.wpSummary.value = summary;
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) {
pagesList[i].href += '?redirect=no';
}
}
}


function abortConditions() {
if (!editform || !editform.wpTextbox1 || editform.wpTextbox1.readOnly)
if (window.location.href.includes('&diff=')) {
return;
throw new Error('Viewing page diff. Please revert before editing redirect.');
}
if (mw.config.get('wgNamespaceNumber') < 0) {
throw new Error('Page is in a virtual namespace. Capricorn aborts.');
}
}


function Capricorn() {
var uiPivot = document.getElementsByClassName('wikiEditor-ui')[0];
$.getJSON("https://en.wikipedia.org/w/index.php?title=User:Wugapodes/Capricorn/RedirectAliases.json&action=raw&ctype=application/json", function(aliasJSON) {
$.getJSON("https://en.wikipedia.org/w/index.php?title=User:Wugapodes/Capricorn/RedirectTemplates.json&action=raw&ctype=application/json",function(templateJSON) {
mw.loader.using(['jquery.suggestions', 'mediawiki.api', 'mediawiki.Title', 'mediawiki.action.view.redirectPage'], function () {
mainCallback(aliasJSON,templateJSON);
});
});
});
}


var ui, mblob;
var api = new mw.Api();
firstHeading.appendChild(document.createTextNode(' '));
firstHeading.appendChild(link(['♑'], function () {
if (ui && ui.parentNode)
ui.parentNode.removeChild(ui);


api.get({
action: "query",
format: "json",
prop: "info",
formatversion: 2,
titles: mw.config.get('wgPageName')
}, {
success: function (result) {
try {
try {
abortConditions();
mblob = new MarkupBlob(editform.wpTextbox1.value);
} catch (e) {
} catch(error) {
console.info(error)
alert("Error: unable to parse page. This page is probably not a redirect.");
return;
return;
}
}
if (result.query.pages[0].redirect) {
Capricorn();
currentTarget = mblob.target
} else {

console.debug('Page is not a redirect.')
ui = buildEditingUI(mblob, function () {
editform.wpSummary.value = 'Redirecting to [[' + mblob.target + ']] ([[User:Wugapodes/Capricorn|♑]])';
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 Capricorn"
}));

var submitButton;
var inputs = editform.getElementsByTagName('input');
for (var i = 0; i < inputs.length; ++i) {
inputs[i].addEventListener('click', function (ev) {
submitButton = this;
}, false);
}

editform.addEventListener('submit', function (ev) {
if (submitButton !== editform.wpSave)
return;
return;
if (mblob) {
ev.preventDefault();
ev.stopImmediatePropagation();
ui.doCheck(function (setStatus) {
setStatus(['Proceeding with saving...']);
var summary = setSummary(currentTarget,ui.origTarget)
editform.wpTextbox1.value = mblob.toString();
editform.wpSummary.value = summary;
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) {
pagesList[i].href += '?redirect=no';
}
}
}

});
});
});
});
/*</source>
/*</source>

Revision as of 23:23, 7 June 2021

// <nowiki>
// This is a modified version of [[User:Sam Sailor/Scripts/Sagittarius+.js]] ([[Special:PermaLink/899463476]])
// Docs: [[User:Wugapodes/Capricorn]]

/*jshint undef:true, latedef:true, shadow:true, loopfunc:true, scripturl:true, undef:true */
/*globals jQuery, mw, importStylesheet */

////////////////////////////////////////////////////////////////////////////////
// Helper function definitions
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();
	}
}

function normaliseAnchor(anchor) {
	// "." 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 = child[i];
			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 ((attr[key] === void(0)) || (attr[key] === null))
			continue;
		node.setAttribute(key, String(attr[key]));
	}

	if (events) for (var key in events) {
		var handler = events[key];
		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 = {
    "fromRelatedInfo": "Related information, From",
    "toRelatedInfo": "Related information, To",
    "fromPartOfSpeech": "Parts of speech, From",
    "fromEngVar": "English variant spelling, From",
    "fromOrthographicModification": "Orthographic difference, From",
    "toOrthographicModification": "Orthographic difference, To",
    "fromAlt": "Alternative names, From",
    "fromDisambiguation": "Ambiguity, From",
    //"toDisambiguation": "Ambiguity, To",
    //"fromSpecificity": "Specificity, From",
    "fromAnthroponym": "Anthroponym, From",
    "fromFiction": "Fiction, From",
    "fromWork": "Works of art and works generally, From",
    "toWork": "Works of art and works generally, To",
    "fromLocationOrInfrastructure": "Geographic location or infrastructure, From",
    "fromFormerName": "Former names, From",
    "toFormerName": "Former names, To",
    "fromSystematicName": "Systematic name, From",
    "toSystematicName": "Systematic name, To",
    "fromPostal": "From postal information",
    "fromOrganization": "From organization",
    "fromMath": "From mathematical topic",
    "fromComic": "Comics, From",
    "toComic": "Comics, To",
    "fromMiddleEarth": "Middle-earth topic, From",
    "toMiddleEarth": "Middle-earth topic, To",
    "fromMisc": "From miscellaneous information",
    "fromMeta": "Meta information, From",
    "toMeta": "Meta information, To",
    "fromProtected": "Protection level, From",
    "toNameSpace": "Namespaces, To",
    "fromPrintworthiness":"Printworthiness"
};

////////////////////////////////////////////////////////////////////////////////
// Callback functions

function mainCallback(aliasJSON, templateJSON) {
	var templateAliases = aliasJSON;
	var redirectTemplates = templateJSON;
	//console.log(templateAliases);
	//console.log(redirectTemplates);
	// <nowiki>
	'use strict';
	
	importStylesheet('User:Wugapodes/Capricorn.css');
	
	
	var wgNamespaceIds = mw.config.get('wgNamespaceIds');
	
	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;
	
		m = rdrx.exec(markup)
		markup = markup.substr(m[0].length);
		this.target = m[1];
	
		this.rcatt = {};
		out: while ((m = tprx.exec(markup))) {
			var alias = normaliseTitle(m[1]);
			while (templateAliases[alias])
				alias = templateAliases[alias]; // hopefully there are no loops.
			
			if (alias === "This is a redirect") {
				var params = m[2].split('|');
				for (var j = 0; j < params.length; ++j) {
					if (!params[j])
						continue;
					if (params[j].indexOf('=') !== -1)
						break out;
					alias = normaliseTitle("R " + params[j]);
					while (templateAliases[alias])
						alias = templateAliases[alias]; // hopefully there are still no loops.
					if (alias in redirectTemplates)
						this.rcatt[alias] = true;
					else
						break out;
				}
			} else if (alias === "Redirect category shell") {
				var mm, rr = /{{(.*?)}}/g;
				while (mm = rr.exec(m[2])) {
					alias = normaliseTitle(mm[1]);
					while (templateAliases[alias])
						alias = templateAliases[alias];
					if (alias in redirectTemplates)
						this.rcatt[alias] = true;
				}
			} else if (alias in redirectTemplates) {
				if (m[2]) // TODO
					break;
				this.rcatt[alias] = true;
			} else {
				break;	
			}
			markup = markup.substr(m[0].length);
		}
	
		this.tail = markup;
	};
	
	MarkupBlob.prototype.toString = function () {
		var markup = '#REDIRECT [[' + this.target + ']]\n';
		var tail = '';
		var wrapped = [];
		for (var key in this.rcatt) {
			if (this.rcatt[key])
				if ((wrapped.length < 6) && /^R\s+/.test(key))
					wrapped.push('{{' + key + '}}\n');
				else
					tail += '{{' + key + '}}\n';
			
		}
		if (wrapped.length)
			markup += "\n{{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) {
						rcatt[key] = this.checked;
					}
				}),
				' ',
				redirectTemplates[key].label
			], {
				"title": redirectTemplates[key].tooltip
			});
		}
		
		var list = el('dl', null, { "class": "tag-list" });
		var group = {};
		for (var key in templateGroups) {
			list.appendChild(el('dt', templateGroups[key]));
			list.appendChild(el('dd', group[key] = el('ul')));
		}
		for (var key in redirectTemplates) {
			var label = makeCheckBox(key);
			group[redirectTemplates[key].group].appendChild(el('li', label));
		}
		
		var collapsibleContent = el('div', list, { 
			"class": "mw-collapsible-content",
			"id": "capricorn-toggle-content"
		})
		return collapsibleContent;
	}
	
	//
	//  Interface creation
	//
	function buildEditingUI(mblob, saveCallback) {
		var statusbar;
		var needsCheck = true;
		var doSave;
		var uiLink, uiTarget;
		mblob = mblob || new MarkupBlob();
		
		// Change text of status bar
		function setStatus(status) {
			while (statusbar.firstChild) // Remove previous statuses
				statusbar.removeChild(statusbar.firstChild);
			if (status) { // If status is a string, add it
				if (typeof status === 'string')
					statusbar.appendChild(document.createTextNode(status));
				else {  // Otherwise, loop through list and add statuses
					for (var j = 0; j < status.length; ++j) {
						if (typeof status[j] === 'string')
							statusbar.appendChild(document.createTextNode(status[j]));
						else
							statusbar.appendChild(status[j]);
					}
				}
			}
		}
		
		// Check if the target has changed??
		// Not actually sure what this does yet 21 Oct 2019
		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()) : '';
				if (uiLink) uiLink.href = mw.util.getUrl(t.getPrefixedDb(), { redirect: "no" }) + frag;
				setStatus();
			} catch (e) {
				setStatus('Invalid title.');
				if (uiLink) uiLink.href = 'javascript:void(0);';
			}
			needsCheck = true;
		}
		
		var uiStatusLine;
		var patrolLine;
		var origTarget = mblob.target
		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' }),
				el('input', null, {
					"type": "button",
					"class":"capricorn-toggle",
					"id": "capricorn-toggle-button",
					"value": "Hide rcat list"
				}, {
					'click': function() {
						$( "#capricorn-toggle-content" ).toggle();
						var buttonText = $("#capricorn-toggle-button")[0].value;
						if (buttonText === "Hide rcat list") {
							$("#capricorn-toggle-button")[0].value = "Show rcat list"
						} else {
							$("#capricorn-toggle-button")[0].value = "Hide rcat list"
						};
					}
				})
			], { 'class': 'redirectMsg' }),
			buildTagList(mblob.rcatt),
			uiStatusLine = el('p', [
				patrolLine = el('span', [], {}),
				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(["WP:TMR"], mw.util.getUrl("Wikipedia:Template messages/Redirect pages")),
					' • ',
					link(["About Capricorn"], mw.util.getUrl("User:Wugapodes/Capricorn"))
				], {
					'style': 'float: right;'
				})
			])
		], {
			'action': 'javascript:void(0)',
			'class': 'kephir-sagittarius-editor'
		}, {
			'submit': function (ev) {
				ev.preventDefault();
				if (uiStatusLine.childNodes[1].childNodes[0]) {
					var patrolVal = uiStatusLine.childNodes[1].childNodes[0].childNodes[0].checked;
					if (patrolVal) {
						api.get({
							"action": "query",
							"format": "json",
							"prop": "revisions",
							"meta": "tokens",
							"titles": mw.config.get('wgPageName'),
							"rvprop": "ids",
							"rvslots": "",
							"rvlimit": "1",
							"rvdir": "newer",
							"type": "patrol"
						}, {
							success: function (result) {
								//console.log(mw.config.get('wgPageName'))
								var patrolToken = result["query"]["tokens"]["patroltoken"];
								var revIDpart = result["query"]["pages"];
								var revID = null;
								for (var page in revIDpart) {
									revID = revIDpart[page]["revisions"][0]["revid"];
								}
								//console.log(revID)
								api.post({
									"action": 'patrol',
									"revid": revID,
									"token": patrolToken
								}, {
									success: function (result) {
										if (result.error) {
											console.log(result.error);
											setStatus([
												'API error: "',
												result.error.info,
												'" [code: ', el('code', [result.error.code]), ']'
											]);
											console.log(result.error)
											return;
										}
									}
								});
							}
						});
					}
				}
				ui.doCheck(saveCallback);
			}
		});
		ui.statusLine = uiStatusLine;
		ui.patrolLine = patrolLine;
		ui.origTarget = origTarget
	
		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 (sectCache[title]) {
						var normSect = normaliseAnchor(sect);
						$uiTarget.suggestions('suggestions',
							sectCache[title].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);
						sectCache[title] = 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, '%')), // XXX: hack
								disambig: disambig,
								toString: function () {
									return this.title;
								}
							};
						});
	
						$uiTarget.suggestions('suggestions',
							sectCache[title].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.pages[pgid];
						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 = this[0];
					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 = $textbox[0];
	
					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.redirects[0]) {
							var newTarget = result.parse.redirects[0].to + (result.parse.redirects[0].tofragment ? "#" + result.parse.redirects[0].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.redirects[0].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 (sectlist[j].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;
	}
	
	function setSummary(current,orig) {
	    var summary;
	    if (orig === current) {
	        summary= "Modifying [[WP:RCAT|redirect categories]] using [[User:Wugapodes/Capricorn|Capricorn ♑]]";
	    } else {
	        summary = 'Redirecting to [[' + current + ']] ([[User:Wugapodes/Capricorn|♑]])'
	    }
	    return summary
	}
	
	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...']);
					var summary = setSummary(mblob.target,ui.origTarget)
					api.post({
						action: 'edit',
						title: mw.config.get('wgPageName'),
						createonly: 1,
						summary: summary,
						text: mblob.toString(),
						token: mw.user.tokens.get('csrfToken')
					}, {
						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 Capricorn'
		], {
			"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: 'revisions',
			rvprop: 'timestamp|content',
			pageids: mw.config.get('wgArticleId'),
			rvstartid: mw.config.get('wgRevisionId'),
			rvlimit: 1,
			rvdir: 'older'
		}, {
			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.pages[mw.config.get('wgArticleId')];
				var mblob;
				var token = mw.user.tokens.get('csrfToken')
				try {
					mblob = new MarkupBlob(page.revisions[0]['*']);
				} 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...']);
					var summary = setSummary(mblob.target,ui.origTarget)
					api.post({
						action: 'edit',
						title: mw.config.get('wgPageName'),
						basetimestamp: page.revisions[0].timestamp,
						summary: summary,
						text: mblob.toString(),
						token: mw.user.tokens.get('csrfToken')
					}, {
						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';
						}
					});				
				});
				var userName = mw.user.getName();
				api.get({
					"action": "query",
					"format": "json",
					"list": "users",
					"usprop": "groups",
					"ususers": userName
				}, { success: function(result) {
					var groups = result["query"]["users"][0]["groups"]
					if (groups.includes("patroller")) {
						ui.patrolLine.insertBefore(el('label', [
									el('input', [], {
									'class': 'checkbox',
									'type': 'checkbox',
									'id': 'patrol',
									'value': 'patrol'
									}), 'Mark as patrolled?']),null);
						}
					}
				});
				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 || !editform.wpTextbox1 || editform.wpTextbox1.readOnly)
			return;
	
		var uiPivot = document.getElementsByClassName('wikiEditor-ui')[0];
	
		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;
			}
			
			currentTarget = mblob.target
	
			ui = buildEditingUI(mblob, function () {
				editform.wpSummary.value = 'Redirecting to [[' + mblob.target + ']] ([[User:Wugapodes/Capricorn|♑]])';
				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 Capricorn"
		}));
	
		var submitButton;
		var inputs = editform.getElementsByTagName('input');
		for (var i = 0; i < inputs.length; ++i) {
			inputs[i].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...']);
					var summary = setSummary(currentTarget,ui.origTarget)
					editform.wpTextbox1.value = mblob.toString();
					editform.wpSummary.value = summary;
					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) {
			pagesList[i].href += '?redirect=no';
		}
	}
}

function abortConditions() {
	if (window.location.href.includes('&diff=')) {
		throw new Error('Viewing page diff. Please revert before editing redirect.');
	}
	
	if (mw.config.get('wgNamespaceNumber') < 0) {
		throw new Error('Page is in a virtual namespace. Capricorn aborts.');
	}
}

function Capricorn() {
	$.getJSON("https://en.wikipedia.org/w/index.php?title=User:Wugapodes/Capricorn/RedirectAliases.json&action=raw&ctype=application/json", function(aliasJSON) {
		$.getJSON("https://en.wikipedia.org/w/index.php?title=User:Wugapodes/Capricorn/RedirectTemplates.json&action=raw&ctype=application/json",function(templateJSON) {
			mw.loader.using(['jquery.suggestions', 'mediawiki.api', 'mediawiki.Title', 'mediawiki.action.view.redirectPage'], function () {
				mainCallback(aliasJSON,templateJSON);
			});
		});
	});
}

var api = new mw.Api();

api.get({
	action: "query",
	format: "json",
	prop: "info",
	formatversion: 2,
	titles: mw.config.get('wgPageName')
}, {
	success: function (result) {
		try {
			abortConditions();
		} catch(error) {
			console.info(error)
			return;
		}
		if (result.query.pages[0].redirect) {
			Capricorn();
		} else {
			console.debug('Page is not a redirect.')
			return;
		}
	}
});
/*</source>
 
[[Category:Wikipedia scripts]]
*/
// </nowiki>