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/linkUnlinked]] <nowiki>
/*global mediaWiki*/

(function ($, mw, libs) {
"use strict";
var style = 'color: inherit !important; text-decoration: inherit !important; font: inherit !important;',
	cssClass = 'schnark-linkUnlinked-link',
	/* "Applications are free to use any of these noncharacter code points internally but should
	''never'' attempt to exchange them." (Unicode Standard, 16.7 Noncharacters) */
	uniqueChar = '\uFDD0',
	uniqueRe = new RegExp (uniqueChar + '(\\d+)' + uniqueChar, 'g'),
	protoRe = new RegExp ('^(?:' + mw.config.get('wgUrlProtocols') + ')'),
	linkRe;

function getProtos (tags, rel) {
	var protos = mw.config.get('wgUrlProtocols').split('|'), relPos, i;
	function insertUnique (c) {
		return c + tags;
	}
	if (!rel) {
		relPos = protos.indexOf('\\/\\/');
		if (relPos !== -1) {
			protos.splice(relPos, 1);
		}
	}
	for (i = 0; i < protos.length; i++) {
		protos[i] = protos[i].replace(/[^\\]|\\./g, insertUnique); //]/// <- dumme Syntaxhervorhebung
	}
	return protos.join('|');
}

function makeLinkRe () {
	var pZs = '\u0020\u00A0\u1680\u180E\u2000-\u200A\u202F\u205F\u3000',
		tags = '(?:' + uniqueChar + '\\d+' + uniqueChar + ')*',
		wikilinkChar = '[^\\[\\]\\|\\n\\{\\}]',
		extlinkChar = '[^\\[\\]"\u0000-\u0020\u007F' + pZs + ']',
		wikilink = '\\[' + tags + '\\[(' + wikilinkChar + '+)(?:\\|[^\\[\\]\\n]*)?(?:\\]' + tags + '\\]|\\|)',
		extlink = '\\[' + tags + '((?:' + getProtos(tags, true) + ')' + extlinkChar + '+)' +
			'(?:[' + pZs + ']*[^\\]\u0000-\u0008\u000a-\u001F]*)?\\]',
		extlink2 = '\\b((?:' + getProtos(tags) + ')' + extlinkChar + '+)',
		isbn = '\\bI' + tags + 'S' + tags + 'B' + tags + 'N' + tags + '\\s+' + tags +
			'((?:9' + tags + '7' + tags + '[89]' + tags + '[ -]?' + tags + ')?' +
			'(?:[0-9]' + tags + '[ -]?' + tags + '){9}[0-9Xx]' + tags + ')\\b';
	return new RegExp(wikilink + '|' + extlink + '|' + extlink2 + '|' + isbn, 'gi');
}

function rel2abs (rel, base) {
	if (rel.charAt(0) === '/') {
		return base + rel.replace(/\/(#|$)/, '$1');
	}
	if (rel.indexOf('../') !== 0) {
		return rel.replace(/^:/, '');
	}
	while (rel.indexOf('../') === 0 && base.indexOf('/') !== -1) {
		rel = rel.slice(3);
		base = base.replace(/\/[^\/]*$/, '');
	}
	return base + '/' + rel.replace(/\/(#|$)/, '$1');
}

function wikilinkToUrl (link) {
	try {
		link = decodeURIComponent(link);
	} catch (e) {}
	link = rel2abs(link, mw.config.get('wgPageName'));
	if (/[<>]|\.\.\//.test(link)) {
		return false;
	}
	if (link.indexOf('&') > -1) { //fix HTML entities
		//NOTE this is safe because link can't contain <>
		link = $('<span>').html(link).text();
	}
	if (link.charAt(0) === '#') {
		return '#' + mw.util.escapeIdForLink(link.slice(1));
	}
	return mw.util.getUrl(link);
}

function normalize (link) {
	link = link.replace(uniqueRe, '');
	link = link.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&amp;/g, '&');
	link = link.replace(/<!--.*?-->/g, '');
	link = link.replace(/<!--.*$/, '');
	return link;
}

function strip (html) {
	var tags = [],
		stripped = html.replace(/<\/?\w+(?:\s+\w+(?:="[^<>"\n]*")?)*(?:\s*\/)?>|<!--.*?-->/g, function (tag) {
			tags.push(tag);
			return uniqueChar + String(tags.length - 1) + uniqueChar;
		});
	return [stripped, tags];
}

function unstrip (stripped, tags) {
	return stripped.replace(uniqueRe, function (all, n) {
		return tags[Number(n)];
	});
}

function change (html, changeF) {
	if (/<a\b/.test(html)) {
		return false;
	}
	if (html.indexOf(uniqueChar) > -1) {
		return false;
	}
	var stripped, tags, ret = strip(html);
	stripped = ret[0];
	tags = ret[1];
	if (/[<>]/.test(stripped)) { //" kann vorkommen!
		return false;
	}
	stripped = changeF(stripped);
	html = unstrip(stripped, tags);
	if (html.indexOf(uniqueChar) > -1) {
		return false;
	}
	return html;
}

function replaceLinks (text) {
	return text.replace(linkRe, function (all, wikilink, extlink, extlink2, isbn) {
		var link, open, close, pre = '', post = '', result;
		if (wikilink) {
			link = normalize(wikilink);
			if (protoRe.test(link)) { //externer Link in doppelten eckigen Klammern
				pre = '[';
				all = all.slice(1);
				result = linkRe.exec(all);
				if (result && result[2]) {
					post = all.slice(result[0].length);
					all = result[0];
					link = normalize(result[2]);
					link = link.replace(/[<>].*$/, '');
				} else {
					link = false;
				}
			} else {
				link = wikilinkToUrl(link);
			}
		} else if (extlink) {
			link = normalize(extlink);
			link = link.replace(/[<>].*$/, '');
		} else if (extlink2) {
			link = normalize(extlink2);
			link = link.replace(/[<>].*$/, '');
			if (link.indexOf('(') === -1) {
				link = link.replace(/[,;.:!?)]+$/, '');
				post = (/[,.:!?)]*$/).exec(all)[0]; //FIXME müsste auch Ersetzungen von normalize beachten und ; entfernen
			} else {
				link = link.replace(/[,;.:!?]+$/, '');
				post = (/[,.:!?]*$/).exec(all)[0]; //FIXME müsste auch Ersetzungen von normalize beachten und ; entfernen
			}
			if (post.length) {
				all = all.slice(0, -post.length);
			}
		} else {
			link = normalize(isbn).replace(/[ \-]/g, '').replace(/x/, 'X');
			link = wikilinkToUrl('Special:Booksources/' + link);
		}
		if (!link) {
			return pre + all + post;
		}
		open = '<a href="' + mw.html.escape(link) + '" class="' + cssClass + '">';
		close = '</a>';
		return (pre + open + all.replace(uniqueRe, function (unique) {
			return close + unique + open;
		}) + close + post).replace(/<a [^>]+>(\s*)<\/a>/g, '$1');
	});
}

function changeHtml (html) {
	html = html.replace(/<a name="([^"]*)"><\/a>/g, '<span id="$1"></span>'); //Den Mist von [[phab:T182292]] korrigieren
	return change(html, replaceLinks);
}

function changeElements ($el) {
	$el.each(function () {
		var oldHtml = this.innerHTML, newHtml = changeHtml(oldHtml);
		if (newHtml && oldHtml !== newHtml) {
			this.innerHTML = newHtml;
		}
	});
}

function linkDiff ($diff) {
	changeElements($diff.find('td.diff-context, td.diff-addedline, td.diff-deletedline'));
}

function linkSyntax ($content) {
	changeElements($content.find('.mw-highlight pre, pre.source-javascript, pre.source-css'));
}

function linkSchnarkDiff ($diff) {
	changeElements($diff.find('ins, del, span'));
}

function init () {
	linkRe = makeLinkRe();
	mw.util.addCSS('.' + cssClass + '{' + style + '}');
	mw.hook('wikipage.diff').add(linkDiff);
	if (/\.(?:js|css)$/.test(mw.config.get('wgTitle')) && [2, 8].indexOf(mw.config.get('wgNamespaceNumber')) > -1) {
		mw.hook('wikipage.content').add(linkSyntax);
	}
	mw.hook('userjs.schnark-diff').add(linkSchnarkDiff);
	if (libs.qunit) {
		libs.qunit.rel2abs = rel2abs;
		libs.qunit.wikilinkToUrl = wikilinkToUrl;
		libs.qunit.strip = strip;
		libs.qunit.unstrip = unstrip;
		libs.qunit.changeHtml = changeHtml;
		libs.qunit.changeElements = changeElements;
	}
}

mw.loader.using('mediawiki.util').then(init);

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