Benutzer:Schnark/js/small-world.js

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen

Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.

  • Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
  • Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
  • Internet Explorer/Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
  • Opera: Strg+F5
//Dokumentation unter [[Benutzer:Schnark/js/small-world]] <nowiki>
/*global mediaWiki*/
(function ($, mw) {
"use strict";
var namespace, linkCache, l10n = {
	en: {
		'pagetitle': '$1 - {{SITENAME}}',
		'small-world-title': 'Shortest connection',
		'small-world-legend': 'Shortest connection',
		'small-world-from': 'From:',
		'small-world-to': 'To:',
		'small-world-random': '⚄',
		'small-world-random-title': 'Random article',
		'small-world-namespace': 'Namespace:',
		'small-world-namespace-all': 'all',
		'small-world-namespace-main': 'articles',
		'small-world-namespace-other': 'other:',
		'small-world-namespace-other-title': 'Enter the numbers of the namespaces separeted by pipes',
		'small-world-submit': 'Find connection',
		'small-world-swap': '⇄',
		'small-world-swap-title': 'Swap the two articles',
		'small-world-empty': 'Please enter an article for both "From" and "To"!',
		'small-world-invalid-title': 'Article titles can\'t contain a pipe (<code>|</code>)!',
		'small-world-progress': 'Searching … (length: $2, articles: $1)',
		'small-world-no-path': 'No connection could be found.',
		'small-world-path': 'The shortest connection has length $1: $2'
	},
	de: {
		'pagetitle': '$1 – {{SITENAME}}',
		'small-world-title': 'Kürzeste Verbindung',
		'small-world-legend': 'Kürzeste Verbindung',
		'small-world-from': 'Startartikel:',
		'small-world-to': 'Zielartikel:',
		'small-world-random-title': 'Zufälliger Artikel',
		'small-world-namespace': 'Namensraum:',
		'small-world-namespace-all': 'alle',
		'small-world-namespace-main': 'Artikel',
		'small-world-namespace-other': 'Sonstige:',
		'small-world-namespace-other-title': 'Nummern der Namensräume Pipe-separiert angeben',
		'small-world-submit': 'Verbindung suchen',
		'small-world-swap-title': 'Start- und Zielartikel vertauschen',
		'small-world-empty': 'Bitte sowohl den Start- als auch den Zielartikel angeben!',
		'small-world-invalid-title': 'Artikelnamen können keinen senkrechten Strich (<code>|</code>) enthalten!',
		'small-world-progress': 'Verbindung wird gesucht … (Länge: $2, Artikel: $1)',
		'small-world-no-path': 'Es wurde keine Verbindung gefunden.',
		'small-world-path': 'Die kürzeste Verbindung hat die Länge $1: $2'
	}
};

function initL10N (l10n, keep) {
	var i, chain = mw.language.getFallbackLanguageChain();
	keep = $.grep(mw.messages.get(keep), function (val) {
		return val !== null;
	});
	for (i = chain.length - 1; i >= 0; i--) {
		if (chain[i] in l10n) {
			mw.messages.set(l10n[chain[i]]);
		}
	}
	mw.messages.set(keep);
}

function setNamespace (ns) {
	if (namespace !== ns) {
		linkCache = [{}, {}];
	}
	namespace = ns;
}

function getRandom () {
	var param = {action: 'query', list: 'random', format: 'json', formatversion: 2};
	if (namespace) {
		param.rnnamespace = namespace;
	}
	return $.getJSON(mw.util.wikiScript('api'), param).then(function (json) {
		if (json && json.query && json.query.random && json.query.random[0]) {
			return json.query.random[0].title;
		} else {
			return '';
		}
	}, function () {
		return '';
	});
}

function getLinksAPI (title, back) {
	var deferred = $.Deferred(), list = [];

	function nextCall (continueParam) {
		getLinksAPIWithContinue(title, back, continueParam).then(function (json) {
			var result, i;
			if (!json) {
				deferred.resolve(list);
				return;
			}
			if (back) {
				result = json.query && json.query.backlinks;
			} else {
				result = json.query && json.query.pages &&
					json.query.pages[0] && json.query.pages[0].links;
			}
			if (result) {
				for (i = 0; i < result.length; i++) {
					list.push(result[i].title);
				}
			}
			if (json['continue']) {
				nextCall(json['continue']);
			} else {
				deferred.resolve(list);
			}
		}, function () {
			deferred.resolve(list);
		});
	}

	nextCall({'continue': ''});
	return deferred.promise();
}

function getLinksAPIWithContinue (title, back, continueParam) {
	var param = continueParam;
	param.action = 'query';
	param.format = 'json';
	param.formatversion = 2;
	if (back) {
		param.list = 'backlinks';
		param.bltitle = title;
		if (namespace) {
			param.blnamespace = namespace;
		}
		param.bllimit = 'max';
		//param.blredirect = 1; findet nur Links auf Weiterleitungen, keine Weiterleitungen auf Links
	} else {
		param.prop = 'links';
		param.titles = title;
		if (namespace) {
			param.plnamespace = namespace;
		}
		//param.redirects = 1;
		param.pllimit = 'max';
	}
	return $.getJSON(mw.util.wikiScript('api'), param);
}

function getLinks (title, back) {
	if (!(title in linkCache[back ? 1 : 0])) {
		linkCache[back ? 1 : 0][title] = getLinksAPI(title, back);
	}
	return linkCache[back ? 1 : 0][title];
}

function findPath (from, to) {
	var pred = {}, succ = {}, fromList = [from], toList = [to],
		nodes = 0, depth = 2,
		back = false, nextDirChange = 1,
		deferred = $.Deferred();

	function getPath (middle) {
		var path = [];
		path.push(middle);
		while (middle !== to) {
			middle = succ[middle];
			path.push(middle);
		}
		middle = path[0];
		while (middle !== from) {
			middle = pred[middle];
			path.unshift(middle);
		}
		return path;
	}

	function findRecursive () {
		var list = back ? toList : fromList,
			hash = back ? succ : pred,
			otherHash = back ? pred : succ,
			el;
		if (list.length === 0) {
			deferred.reject();
			return;
		}
		el = list.shift();
		getLinks(el, back).then(function (next) {
			var i;
			nodes++;
			deferred.notify({nodes: nodes, depth: depth});
			for (i = 0; i < next.length; i++) {
				if (!(next[i] in hash)) {
					list.push(next[i]);
					hash[next[i]] = el;
					if (next[i] in otherHash) {
						deferred.resolve(getPath(next[i]));
						return;
					}
				}
			}
			nextDirChange--;
			if (nextDirChange === 0) {
				back = !back;
				depth++;
				nextDirChange = back ? toList.length : fromList.length;
			}
			findRecursive();
		}, deferred.reject);
	}

	if (from === to) {
		deferred.resolve([from]);
	} else {
		findRecursive();
	}
	return deferred.promise();
}

function createInterface () {
	document.title = mw.msg('pagetitle', mw.msg('small-world-title'));
	$('#firstHeading').text(mw.msg('small-world-title'));
	createForm($('#mw-content-text'));
	$('#smallWorldFromRandom').on('click', randomFrom);
	$('#smallWorldToRandom').on('click', randomTo);
	searchSuggestions('#smallWorldFrom');
	searchSuggestions('#smallWorldTo');
	$('#smallWorldNamespaceAll, #smallWorldNamespaceMain, #smallWorldNamespaceOther').on('click', function () {
		changeNS($('#smallWorldNamespace :checked').val());
	});
	$('#smallWorldSwap').on('click', swapFields);
	$('#smallWorldSubmit').on('click', run);
}

function makeRadioButton (value, id, labelMsg, checked) {
	return mw.html.element('input', {type: 'radio', name: 'smallWorldNamespace', value: value, checked: checked, id: id}) +
		mw.html.element('label', {'for': id}, mw.msg(labelMsg)) + ' ';
}

function createForm ($container) {
	$container.html(
		mw.html.element('fieldset', {}, new mw.html.Raw(
			mw.html.element('legend', {}, mw.msg('small-world-legend')) +
			mw.html.element('table', {}, new mw.html.Raw(
				generateInput('smallWorldFrom', 'small-world-from', 'from') +
				generateInput('smallWorldTo', 'small-world-to', 'to') +
				'<tr><td class="mw-label">' + mw.msg('small-world-namespace') + '</td>' +
				'<td id="smallWorldNamespace" class="mw-input">' +
					makeRadioButton('', 'smallWorldNamespaceAll', 'small-world-namespace-all', false) +
					makeRadioButton('0', 'smallWorldNamespaceMain', 'small-world-namespace-main', true) +
					makeRadioButton('?', 'smallWorldNamespaceOther', 'small-world-namespace-other', false) +
						mw.html.element('input', {type: 'text', size: 5, id: 'smallWorldNamespaceOtherInput',
							title: mw.msg('small-world-namespace-other-title')}) +
				'</td></tr>' +
				'<tr><td></td><td>' +
					mw.html.element('input', {type: 'button', id: 'smallWorldSubmit',
						value: mw.msg('small-world-submit')}) + ' ' +
					mw.html.element('input', {type: 'button', id: 'smallWorldSwap',
						value: mw.msg('small-world-swap'), title: mw.msg('small-world-swap-title')}) +
				'</td></tr>'
			))
		)) + mw.html.element('p', {id: 'smallWorldResult'}, '')
	);
}

function generateInput (id, labelMsg, param) {
	return '<tr><td class="mw-label">' +
		mw.html.element('label', {'for': id}, mw.msg(labelMsg)) +
		'</td><td class="mw-input">' +
		mw.html.element('input', {type: 'text', id: id, size: 50, value: mw.util.getParamValue(param) || ''}) +
		mw.html.element('input', {type: 'button', id: id + 'Random',
			value: mw.msg('small-world-random'), title: mw.msg('small-world-random-title')}) +
		'</td></tr>';
}

function searchSuggestions (id) {
	var $el = $(id), param = {action: 'opensearch', suggest: ''};
	$el.suggestions({
		fetch: function (query, response, maxRows) {
			if (query.length !== 0) {
				param.search = query;
				param.limit = maxRows;
				$el.data('request', $.getJSON(mw.util.wikiScript('api'), param).then(function (json) {
					if (json && json[1]) {
						response(json[1]);
					}
				}));
			}
		},
		cancel: function () {
			if ($el.data('request') && $el.data('request').abort) {
				$el.data('request').abort();
			}
		}
	});
}

function changeNS (val) {
	if (val === '?') {
		val = $('#smallWorldNamespaceOtherInput').val();
	}
	setNamespace(val);
}

function swapFields () {
	var from = $('#smallWorldFrom').val(), to = $('#smallWorldTo').val();
	$('#smallWorldFrom').val(to);
	$('#smallWorldTo').val(from);
}

function randomFromOrTo (id) {
	getRandom().then(function (title) {
		if (title) {
			$(id).val(title);
		}
	});
}

function randomFrom () {
	randomFromOrTo('#smallWorldFrom');
}

function randomTo () {
	randomFromOrTo('#smallWorldTo');
}

function run () {
	var $result = $('#smallWorldResult'), from = $('#smallWorldFrom').val(), to = $('#smallWorldTo').val();
	if (!from || !to) {
		$result.html(mw.msg('small-world-empty'));
	} else if (from.indexOf('|') > -1 || to.indexOf('|') > -1) {
		$result.html(mw.msg('small-world-invalid-title'));
	} else {
		$('#smallWorldSubmit, #smallWorldNamespace input').prop('disabled', true);
		$result.empty().injectSpinner('small-world');
		findPath(from, to).progress(showProgress).always(function () {
			$('#smallWorldSubmit, #smallWorldNamespace input').prop('disabled', false);
			$.removeSpinner('small-world');
		}).then(function (path) {
			$result.html(showPath(path));
			mw.hook('wikipage.content').fire($result);
		}, function () {
			$result.html(mw.msg('small-world-no-path'));
		});
	}
}

function showProgress (data) {
	$('#smallWorldResult').html(mw.msg('small-world-progress', data.nodes, data.depth));
}

function showPath (path) {
	var i, links = [], arrow = $('body').is('.rtl') ? '←' : '→';
	for (i = 0; i < path.length; i++) {
		links.push(mw.html.element('a', {href: mw.util.getUrl(path[i])}, path[i]));
	}
	return mw.msg('small-world-path', path.length, links.join('&#160;' + arrow + ' '));
}

function init () {
	initL10N(l10n, ['pagetitle']);
	setNamespace('0');
	if (mw.config.get('wgCanonicalSpecialPageName') === 'Blankpage' && mw.util.getParamValue('action') === 'small-world') {
		$.when(
			mw.loader.using(['mediawiki.jqueryMsg', 'jquery.spinner', 'jquery.suggestions']),
			$.ready
		).then(createInterface);
	}
}

mw.loader.using(['mediawiki.util', 'mediawiki.language']).then(init);

})(jQuery, mediaWiki);
//</nowiki>