User:Example/sagittarius.js

From Sarkarverse
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
mw.loader.using(['mediawiki.api', 'mediawiki.Title'], function () {

if (wgNamespaceNumber < 0)
	return;
if (wgAction !== 'view')
	return;

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

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) {
		node.addEventListener(key, events[key], 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"
};

var redirectTemplates = {
	"R from initialism": {
		"group": "from",
		"label": "Initialism",
		"auto": "capitals"
	},
       "R from alternative Roman Sanskrit transliteration": {
		"group": "from",
		"label": "Roman Sanskrit transliteration"
	},
	"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 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 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": "Unicode title",
		"auto": "from-unicode-to-ascii"
	},
	"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 with possibilities": {
		"group": "misc",
		"label": "Might be worthy of own article",
		"conflict": "R without possibilities"
	},
	"R without possibilities": {
		"group": "misc",
		"label": "Not worthy of own article",
		"conflict": "R with possibilities"
	},
	"R unprintworthy": {
		"group": "misc",
		"label": "Unprintworthy",
		"conflict": "R printworthy"
	},
	"R printworthy": {
		"group": "misc",
		"label": "Printworthy",
		"conflict": "R unprintworthy"
	},	
	"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 duplicated article": {
		"group": "from",
		"label": "Duplicated article"
	},
	"R from merge": {
		"group": "from",
		"label": "Merged article"
	},
	"R from move": {
		"group": "from",
		"label": "Moved title" 
	},
	"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"]
	}
};

var api = new mw.Api();
var contentText = document.getElementById('mw-content-text');
var firstHeading = document.getElementById('firstHeading');
var redirMsg = contentText.getElementsByClassName('redirectMsg')[0];
var ui = el('div');
var edittoken = null;

function MarkupBlob(markup) {
	var rdrx = /^#REDIRECT\s*\[\[\s*([^\|{}[\]]+?)\s*]]\s*/i;
	var tprx = /^\s*{{(R [A-Za-z ]+)}}\s*/i;
	var m;

	if (!markup) {
		this.target = '';
		this.rcatt = {};
		this.tail = '';
		return this;
	}

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

	this.rcatt = {}
	while (m = tprx.exec(markup)) {
		var alias = m[1];
		markup = markup.substr(m[0].length);
		while (templateAliases[alias])
			alias = templateAliases[alias]; // hopefully there are no loops.
		this.rcatt[alias] = true;
	}

	this.tail = markup;
}

MarkupBlob.prototype.toString = function () {
	var markup = '#REDIRECT [[' + this.target + ']]\n\n';
	for (var key in this.rcatt) {
		if (this.rcatt[key])
			markup += '{{' + key + '}}\n';
	}
	markup += '\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.value;
				}
			}),
			' ',
			redirectTemplates[key].label
		])
	}
	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;
}

function buildEditingUI(mblob, stamp) {
	var statusbar;
	var needsCheck = true;
	var doSave;
	mblob = mblob || new MarkupBlob();

	return el('form', [
		el('div', [
			el('img', null, {
				'src': '//bits.wikimedia.org/static-1.22wmf18/skins/common/images/redirectltr.png', // XXX: make this site- and directionality-agnostic
				'alt': '#REDIRECT'
			}),
			el('input', null, {
				'type': 'text',
				'class': 'redirectText',
				'value': mblob.target
			}, {
				'change': function (ev) {
					mblob.target = this.value;
					statusbar.data = '';
					needsCheck = true;
				}
			})
		], {
			'class': 'redirectMsg'
		}),
		buildTagList(mblob.rcatt),
		el('p', [
			el('input', null, { 'type': 'submit', 'value': 'Save' }),
			el('span', [statusbar = document.createTextNode('')], {
				'class': 'status-line'
			})
		])
	], {
		'action': 'javascript:void(0)',
		'class': 'kephir-sagittarius-editor'
	}, {
		'submit': doSave = function (ev) {
			var that = this;
			ev.preventDefault();

			if (!/^\s*[^\|{}[\]]+\s*$/.test(mblob.target)) {
				statusbar.data = 'Error: the target page name is invalid.'
				return;
			}

			if (needsCheck) {
				statusbar.data = 'Checking target validity...';
				needsCheck = false;

				api.get({
					action: 'query',
					titles: mblob.target,
					redirects: '1'
				}, {
					success: function (result) {
						if (result.query.redirects) {
							statusbar.data = 'Error: The target page is already a redirect. Click "Save" again to proceed anyway.';
							return;
						}

						if (result.query.pages["-1"]) {
							statusbar.data = 'Error: The target page does not exist. Click "Save" again to proceed anyway.';
							return;
						}

						doSave(ev);
					}
				})
				
				return;
			}

			statusbar.data = 'Saving...';
			api.post({
				action: 'edit',
				title: wgPageName,
				basetimestamp: stamp,
				token: mw.user.tokens.get('editToken'),
				summary: 'Redirecting to [[' + mblob.target + ']] (using [[User:Kephir/gadgets/sagittarius|Sagittarius]])',
				text: mblob.toString()
			}, {
				success: function (result) {
					statusbar.data = 'Saved. Reloading page...'
					if (/redirect=no/.test(location.href)) // XXX
						location.reload();
					else
						location.search = location.search ? location.search + '&redirect=no' : '?redirect=no';
				}
			});
		}
	});
}

function buildCreationUI() {
	var mblob = new MarkupBlob();
	// ...
	return el('div', [
		buildTagList(mblob.rcatt)
	], {
		'class': 'kephir-sagittarius-editor'
	});
}

if (wgArticleId === 0) { // nonexistent page.
	ui.appendChild(el('div', [
		link(['Create a redirect'], function () {
			while (ui.hasChildNodes())
				ui.removeChild(ui.firstChild);
			ui.appendChild(buildEditingUI(null, null));
		}), ' from this page with Sagittarius'
	], {
		"class": "kephir-sagittarius-invite"
	}));
	contentText.parentNode.insertBefore(ui, contentText);
} else if (wgIsRedirect && redirMsg) {
	// start editor immediately
	ui.appendChild(el('div', ['Loading page source…'], {
		"class": "kephir-sagittarius-loading"
	}));
	contentText.appendChild(ui);
	api.get({
		action: 'query',
		prop: 'info|revisions',
		rvprop: 'timestamp|content',
		pageids: wgArticleId,
		rvstartid: wgCurRevisionId,
		rvlimit: 1,
		rvdir: 'older',
		intoken: 'edit',
	}, {
		success: function (result) {
			while (ui.hasChildNodes())
				ui.removeChild(ui.firstChild);
			var page = result.query.pages[wgArticleId];
			var mblob;
			token = page.edittoken;
			try {
				mblob = new MarkupBlob(page.revisions[0]['*']);
			} catch(e) {
				ui.appendChild(el('div', ['Error: unable to parse page. Edit the source manually.'], {
					"class": "kephir-sagittarius-error"
				}));
				return;
			}
			redirMsg.parentNode.removeChild(redirMsg);
			ui.appendChild(buildEditingUI(mblob, page.revisions[0].timestamp));
		}
	})
} else if (wgUserName === 'Kephir') { // testing for now
	firstHeading.appendChild(document.createTextNode(' '));
	firstHeading.appendChild(link(['←'], function () {
		ui.appendChild(buildCreationUI());
	}, {
		"class": "kephir-sagittarius-createlink",
		"title": "Create redirects to this page"
	}));
}

});