Benutzer:Schnark/js/edithelper.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
  • Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
//Dokumentation unter [[Benutzer:Schnark/js/edithelper]] <nowiki>
/*global mediaWiki*/
(function ($, mw) {
"use strict";

var l10n = {
	en: {
		'comma-separator': ', ',
		'schnark-edithelper-no-suggestions': '(no suggestions)',
		'schnark-edithelper-no-additional-suggestions': '(no additional suggestions)',
		'schnark-edithelper-additional-suggestions': '({{PLURAL:$1|one additional suggestion|$1 additional suggestions}})',
		'schnark-edithelper-suggestions': '$1 $2',
		'schnark-edithelper-compose-active': 'Compose activated',
		'schnark-edithelper-compose-example': '$1 → $2'
	},
	de: {
		'schnark-edithelper-no-suggestions': '(keine Vorschläge)',
		'schnark-edithelper-no-additional-suggestions': '(keine weiteren Vorschläge)',
		'schnark-edithelper-additional-suggestions': '({{PLURAL:$1|ein weiterer Vorschlag|$1 weitere Vorschläge}})',
		'schnark-edithelper-compose-active': 'Compose aktiviert'
	}
}, config = {
//Vorschläge
	suggest: {
		enabled: true, //eingeschaltet?
		keyAccept: 13, //Taste zum Übernehmen (Enter)
		keyNext: 9, //Taste für nächsten Vorschlag (Tab)
		keyCancel: 27, //Taste zum Abbrechen (Esc)
		delim: '-\n \t,;.:!"§$%&/()=?+*#\'\\{[]}|<>', //Worttrenner
		minlen: 1, //Länge, die eingetippt werden muss, bevor Vorschläge kommen
		minwordlen: 4, //Länge, die ein Wort mindestens haben muss
		timeout: 100 //Dauer, bis ein Vorschlag kommt
	},
	compose: {
		enabled: true, //eingeschaltet?
		key: 17, //Compose-Taste (Strg)
		timeout: 300, //Zeit zwischen doppeltem Drücken
		suggCount: 3, //Anzahl der Vorschläge
		suggPause: 3000, //Zeit bis zum Wechsel der Vorschläge
		map: {
			'>"': '„', '<"': '“', '<<': '«', '>>': '»', '>\'': '‚', '<\'': '‘',
			'.<': '‹', '.>': '›', //Anführungszeichen
			'..': '…', '\'\'': '’', '--': '–', '-|': '†', '|-': '†', '  ': '&nbsp;',
			'\r\r': '<br />\n', '!!': '¡', '??': '¿', //Satzzeichen u. ä.
			'12': '½', '14': '¼', '34': '¾', '.-': '·', 'xx': '×', ':-': '÷', '-:': '÷',
			'+-': '±', '->': '→', '<-': '←', '<=': '⇐', '=>': '⇒', //mathematische Sonderzeichen
			'<c': '<code>', '>c': '</code>', '<g': '<gallery>', '>g': '</gallery>', '<i': '<includeonly>',
			'>i': '</includeonly>', '<m': '<math>', '>m': '</math>', '<n': '<nowiki>', '>n': '<' + '/nowiki>',
			'<o': '<onlyinclude>', '>o': '</onlyinclude>', '<r': '<ref>', '<R': '<ref name="">', '>r': '</ref>',
			'<s': ['<syntaxhighlight lang="">', '<syntaxhighlight>'], '>s': '</syntaxhighlight>',
			'<!': '<!--', '>!': '-->', //Tags
			').': ']]', '.)': ']]', '*)': '}}', ')*': '}}', '))': '}}', //Vorlagen etc.
			'´a': 'á', '´c': 'ć', '´e': 'é', '´g': 'ǵ', '´i': 'í', '´k': 'ḱ', '´l': 'ĺ', '´m': 'ḿ', '´n': 'ń',
			'´o': 'ó', '´p': 'ṕ', '´r': 'ŕ', '´s': 'ś', '´u': 'ú', '´w': 'ẃ', '´y': 'ý', '´z': 'ź', '´A': 'Á',
			'´C': 'Ć', '´E': 'É', '´G': 'Ǵ', '´I': 'Í', '´K': 'Ḱ', '´L': 'Ĺ', '´M': 'Ḿ', '´N': 'Ń', '´O': 'Ó',
			'´P': 'Ṕ', '´R': 'Ŕ', '´S': 'Ś', '´U': 'Ú', '´W': 'Ẃ', '´Y': 'Ý', '´Z': 'Ź', //´ + Buchstabe: Akut
			'`a': 'à', '`e': 'è', '`i': 'ì', '`n': 'ǹ', '`o': 'ò', '`u': 'ù', '`w': 'ẁ', '`y': 'ỳ', '`A': 'À',
			'`E': 'È', '`I': 'Ì', '`N': 'Ǹ', '`O': 'Ò', '`U': 'Ù', '`W': 'Ẁ', '`Y': 'Ỳ', //` + Buchstabe: Gravis
			'^a': 'â', '^c': 'ĉ', '^e': 'ê', '^g': 'ĝ', '^h': 'ĥ', '^i': 'î', '^j': 'ĵ', '^o': 'ô', '^s': 'ŝ',
			'^u': 'û', '^w': 'ŵ', '^y': 'ŷ', '^z': 'ẑ', '^A': 'Â', '^C': 'Ĉ', '^E': 'Ê', '^G': 'Ĝ', '^H': 'Ĥ',
			'^I': 'Î', '^J': 'Ĵ', '^O': 'Ô', '^S': 'Ŝ', '^U': 'Û', '^W': 'Ŵ', '^Y': 'Ŷ', '^Z': 'Ẑ', //^ + Buchstabe:
			//Zirkumflex
			'°a': 'å', '°u': 'ů', '°w': 'ẘ', '°y': 'ẙ', '°A': 'Å', '°U': 'Ů', //° + Buchstabe: Ring
			'"a': 'ä', '"e': 'ë', '"h': 'ḧ', '"i': 'ï', '"o': 'ö', '"t': 'ẗ', '"u': 'ü', '"w': 'ẅ', '"x': 'ẍ',
			'"y': 'ÿ', '"A': 'Ä', '"E': 'Ë', '"H': 'Ḧ', '"I': 'Ï', '"O': 'Ö', '"U': 'Ü', '"W': 'Ẅ', '"X': 'Ẍ',
			'"Y': 'Ÿ', //" + Buchstabe: Trema
			'~a': 'ã', '~e': 'ẽ', '~i': 'ĩ', '~n': 'ñ', '~o': 'õ', '~u': 'ũ', '~v': 'ṽ', '~y': 'ỹ', '~A': 'Ã',
			'~E': 'Ẽ', '~I': 'Ĩ', '~N': 'Ñ', '~O': 'Õ', '~U': 'Ũ', '~V': 'Ṽ', '~Y': 'Ỹ', //~ + Buchstabe: Tilde
			'va': 'ǎ', 'vc': 'č', 'vd': 'ď', 've': 'ě', 'vg': 'ǧ', 'vh': 'ȟ', 'vi': 'ǐ', 'vj': 'ǰ', 'vk': 'ǩ',
			'vl': 'ľ', 'vn': 'ň', 'vo': 'ǒ', 'vr': 'ř', 'vs': 'š', 'vt': 'ť', 'vu': 'ǔ', 'vz': 'ž', 'vA': 'Ǎ',
			'vC': 'Č', 'vD': 'Ď', 'vE': 'Ě', 'vG': 'Ǧ', 'vH': 'Ȟ', 'vI': 'Ǐ', 'vK': 'Ǩ', 'vL': 'Ľ', 'vN': 'Ň',
			'vO': 'Ǒ', 'vR': 'Ř', 'vS': 'Š', 'vT': 'Ť', 'vU': 'Ǔ', 'vZ': 'Ž', //v + Buchstabe: Hatschek
			'ua': 'ă', 'ue': 'ĕ', 'ug': 'ğ', 'ui': 'ĭ', 'uo': 'ŏ', 'uu': 'ŭ', 'uA': 'Ă', 'uE': 'Ĕ', 'uG': 'Ğ',
			'uI': 'Ĭ', 'uO': 'Ŏ', 'uU': 'Ŭ', //u + Buchstabe: Breve
			'_a': 'ā', '_e': 'ē', '_g': 'ḡ', '_i': 'ī', '_o': 'ō', '_u': 'ū', '_y': 'ȳ', '_A': 'Ā', '_E': 'Ē',
			'_G': 'Ḡ', '_I': 'Ī', '_O': 'Ō', '_U': 'Ū', '_Y': 'Ȳ', //_ + Buchstabe: Makron
			',c': 'ç', ',d': 'ḑ', ',e': 'ȩ', ',g': 'ģ', ',h': 'ḩ', ',k': 'ķ', ',l': 'ļ', ',n': 'ņ', ',r': 'ŗ',
			',s': 'ş', ',t': 'ţ', ',C': 'Ç', ',D': 'Ḑ', ',E': 'Ȩ', ',G': 'Ģ', ',H': 'Ḩ', ',K': 'Ķ', ',L': 'Ļ',
			',N': 'Ņ', ',R': 'Ŗ', ',S': 'Ş', ',T': 'Ṫ', //, + Buchstabe: Cedille
			';a': 'ą', ';e': 'ę', ';i': 'į', ';o': 'ǫ', ';u': 'ų', ';A': 'Ą', ';E': 'Ę', ';I': 'Į', ';O': 'Ǫ',
			';U': 'Ų', //; + Buchstabe: Ogonek
			'.a': 'ȧ', '.b': 'ḃ', '.c': 'ċ', '.d': 'ḋ', '.e': 'ė', '.f': 'ḟ', '.g': 'ġ', '.h': 'ḣ', '.i': 'ı',
			'.l': 'ŀ', '.m': 'ṁ', '.n': 'ṅ', '.o': 'ȯ', '.p': 'ṗ', '.r': 'ṙ', '.s': 'ṡ', '.t': 'ṫ', '.w': 'ẇ',
			'.x': 'ẋ', '.y': 'ẏ', '.z': 'ż', '.A': 'Ȧ', '.B': 'Ḃ', '.C': 'Ċ', '.D': 'Ḋ', '.E': 'Ė', '.F': 'Ḟ',
			'.G': 'Ġ', '.H': 'Ḣ', '.I': 'İ', '.L': 'Ŀ', '.M': 'Ṁ', '.N': 'Ṅ', '.O': 'Ȯ', '.P': 'Ṗ', '.R': 'Ṙ',
			'.S': 'Ṡ', '.T': 'Ṫ', '.W': 'Ẇ', '.X': 'Ẋ', '.Y': 'Ẏ', '.Z': 'Ż', //. + Buchstabe: Punkt
			//(oben, bei L Mitte, bei i ohne)
			'/a': 'ⱥ', '/A': 'Ⱥ', '-b': 'ƀ', '-B': 'Ƀ', '/c': 'ȼ', '/C': 'Ȼ', '-d': 'đ', '-D': 'Đ', '/e': 'ɇ',
			'/E': 'Ɇ', '-g': 'ǥ', '-G': 'Ǥ', '-h': 'ħ', '-H': 'Ħ', '-i': 'ɨ', '-I': 'Ɨ', '-j': 'ɉ', '-J': 'Ɉ',
			'-l': 'ł', '/l': 'ł', '-L': 'Ł', '/L': 'Ł', '-P': 'Ᵽ', '-r': 'ɍ', '-R': 'Ɍ', '-t': 'ŧ', '-T': 'Ŧ',
			'/o': 'ø', '/O': 'Ø', '-y': 'ɏ', '-Y': 'Ɏ', '-z': 'ƶ', '-Z': 'Ƶ', //- oder / + Buchstabe: Strich
			'ae': 'æ', 'AE': 'Æ', 'dh': 'ð', 'DH': 'Ð', 'ij': 'ij', 'IJ': 'IJ', 'ng': 'ŋ', 'NG': 'Ŋ', 'ss': 'ß',
			'th': 'þ', 'TH': 'Þ', 'oe': 'œ', 'OE': 'Œ' //sonstige Buchstaben (Ligaturen)
		} //Compose-Map
	}
},

suggestData = {
	disabled: false, //kurzzeitig ausgeschaltet?
	pre: '', //vorderer Wortteil
	suggestions: [], //Vorschläge
	number: 0, //Nummer des aktuellen Vorschlags
	words: [], //Wörterliste
	timeoutid: null
},

composeData = {
	keypressed: false, //einmal Strg gedrückt?
	suggTimeout: null,
	firstchar: '' //erstes Zeichen
},

$textbox, hasOwn = Object.prototype.hasOwnProperty;

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 additionalComposeMap () {
	/*jshint onecase: true*/
	var additional = {};
	switch (mw.config.get('wgDBname')) {
	case 'dewiki':
		additional = {
			'=E': ['== Einzelnachweise ==\n<references />\n\n', 'Einzelnachweise'], '=L': ['== Literatur ==\n', 'Literatur'],
			'=W': ['== Weblinks ==\n', 'Weblinks'], //Überschriften
			'(a': '* {{ADB|', '(b': '[[Benutzer:', '(c': '{{Commons|', '(d': '* {{DNB-Portal|', '(k': '[[Kategorie:',
			'(l': '{{lang|', '(n': '{{Normdaten|TYP=p|GND=', '(q': '{{Belege fehlen|', '(s': '{{SORTIERUNG:',
			'(u': '{{ers:unsigniert|', '(ü': '{{Überarbeiten}}', '(w': '[[Wikipedia:', '(z': '{{Zitat|' //Vorlagen/Links
		};
	}
	return additional;
}

function initSuggest () {
	var delimEsc = mw.RegExp.escape(config.suggest.delim);
	suggestData.reSplit = new RegExp ('[' + delimEsc + ']');
	suggestData.reLastWord = new RegExp ('[^' + delimEsc + ']*$');
	suggestData.reLastCompleteWord = new RegExp ('([^' + delimEsc + ']*)(?:.|\n)$');

	if (config.suggest.enabled) {
		getWordsForSuggest();
	}
}

function acceptSuggest () { //Vorschlag übernehmen
	insert(suggestData.suggestions[suggestData.number].slice(suggestData.pre.length));
	removeSuggest();
}
function nextSuggest () { //nächsten Vorschlag
	suggestData.number++;
	if (suggestData.number >= suggestData.suggestions.length) {
		suggestData.number = 0;
	}
	showSuggest();
}
function removeSuggest () { //entfernt alle Vorschläge
	suggestData.pre = '';
	suggestData.suggestions = [];
	suggestData.number = -1;
	showSuggest();
}
function addWordAsSuggest (word) { //neues Wort in Liste aufnehmen
	if (word.length >= config.suggest.minwordlen && $.inArray(word, suggestData.words) === -1) {
		suggestData.words.push(word);
	}
}
function getWordsForSuggest () { //Wortliste initialisieren
	var text = getTextContent() + '\n' + mw.config.get('wgTitle'),
		liste = text.split(suggestData.reSplit),
		i;
	for (i = 0; i < liste.length; i++) {
		addWordAsSuggest(liste[i]);
	}
}

function showSuggest () { //Vorschläge zeigen
	var text, html = '';
	switch (suggestData.suggestions.length) {
	case 0:
		if (suggestData.number > -1) {
			text = mw.msg('schnark-edithelper-no-suggestions');
		}
		break;
	case 1:
		text = mw.msg('schnark-edithelper-no-additional-suggestions');
		break;
	default:
		text = mw.msg('schnark-edithelper-additional-suggestions', suggestData.suggestions.length - 1);
	}
	if (text) {
		html = mw.html.element('span', {'class': 'edithelper-suggest-number'}, text);
	}
	if (suggestData.suggestions.length > 0) {
		html = mw.msg('schnark-edithelper-suggestions',
			mw.html.element('span', {'class': 'edithelper-suggest-pre'}, suggestData.pre) +
			mw.html.element('span', {'class': 'edithelper-suggest-suggestion'},
				suggestData.suggestions[suggestData.number].slice(suggestData.pre.length)),
			html);
	}
	show(html, false);
}
function getSuggestions (pre, post) { //findet Vorschläge
	var match, word, i;
	if (suggestData.disabled) {
		removeSuggest();
		return;
	}
	if (post === '') {
		post = ' ';
	}
	if (config.suggest.delim.indexOf(post.slice(0, 1)) === -1) { //mitten im Wort
		removeSuggest();
	} else {
		match = pre.match(suggestData.reLastWord);
		word = match ? match[0] : '';
		if (word.length < config.suggest.minlen) {
			removeSuggest();
			if (word.length > 0) {
				return;
			}
		} else {
			suggestData.pre = word;
			suggestData.suggestions = [];
			for (i = 0; i < suggestData.words.length; i++) {
				if (suggestData.words[i].indexOf(word) === 0 && suggestData.words[i] !== word) {
					suggestData.suggestions.push(suggestData.words[i]);
				}
			}
			if (suggestData.number >= suggestData.suggestions.length || suggestData.number === -1) {
				suggestData.number = 0;
			}
			showSuggest();
			return;
		}
	}
	if (config.suggest.delim.indexOf(pre.slice(-1)) !== -1) { //neues Wort in Liste aufnehmen
		match = pre.match(suggestData.reLastCompleteWord);
		if (match) {
			addWordAsSuggest(match[1]);
		}
	}
}
function updateSuggestions () { //aktualisiert Vorschläge
	var pos = getCursorPosition(), text;
	if (pos[0] === pos[1]) {
		text = getTextContent();
		getSuggestions(text.slice(0, pos[0]), text.slice(pos[0]));
	} else {
		removeSuggest();
	}
}
function onkeydownSuggest (ev) {
	if (config.suggest.enabled && suggestData.suggestions.length > 0) {
		switch (ev.keyCode) {
		case config.suggest.keyAccept:
			acceptSuggest();
			return false;
		case config.suggest.keyNext:
			nextSuggest();
			return false;
		case config.suggest.keyCancel:
			suggestData.disabled = true;
			removeSuggest();
			return false;
		}
	}
	suggestData.disabled = false;
}
function onkeyupSuggest () {
	if (config.suggest.enabled) {
		if (suggestData.timeoutid) {
			clearTimeout(suggestData.timeoutid);
		}
		suggestData.timeoutid = setTimeout(updateSuggestions, config.suggest.timeout);
	}
}

//Simulation einer Compose-Taste: zweimal Strg

function showCompose (on, begin) { //Hilfe anzeigen (true) / entfernen (false)
	var html = '', examples = [], n = 0, map = config.compose.map, keys, hint;
	if (composeData.suggTimeout !== null) {
		clearTimeout(composeData.suggTimeout);
	}
	composeData.suggTimeout = null;
	if (begin === undefined) {
		begin = 0;
	}
	if (on) {
		html = mw.html.element('span', {'class': 'edithelper-compose-active'}, mw.msg('schnark-edithelper-compose-active'));
		for (keys in map) {
			if (hasOwn.call(map, keys)) {
				if (keys.indexOf(composeData.firstchar) === 0) {
					n++;
					if (n <= begin) {
						continue;
					}
					hint = map[keys];
					if (typeof hint !== 'string') {
						hint = hint[1];
					}
					examples.push(mw.html.escape(
						mw.msg('schnark-edithelper-compose-example',
							keys.replace(/[\n\r]+/g, '↲').replace(/ /g, '␣'), hint.replace(/\n/g, ''))
					));
				}
				if (n === config.compose.suggCount + begin) {
					break;
				}
			}
		}
		if (n < config.compose.suggCount + begin) {
			begin = -config.compose.suggCount;
		}
		html += mw.html.element('span', {'class': 'edithelper-compose-examples'},
			new mw.html.Raw(examples.join(mw.msg('comma-separator'))));
		composeData.suggTimeout = setTimeout(function () {
			showCompose(true, begin + config.compose.suggCount);
		}, config.compose.suggPause);
	}
	show(html, true);
}
function insertCompose (secondchar) { //Zeichen einfügen
	var chars = composeData.firstchar + secondchar, ins = chars;
	if (hasOwn.call(config.compose.map, chars)) {
		ins = config.compose.map[chars];
		if (typeof ins !== 'string') {
			ins = ins[0];
		}
	}
	insert(ins);
	showCompose(false);
	installHandler(true);
}
function onkeyupCompose (ev) { //Event-Handler
	if (config.compose.enabled && ev.keyCode === config.compose.key) {
		if (composeData.keypressed) {
			composeData.firstchar = '';
			showCompose(true);
			installHandler(false);
		} else {
			composeData.keypressed = true;
			setTimeout(function () {
				composeData.keypressed = false;
			}, config.compose.timeout);
		}
	}
}
function onkeypressCompose (ev) { //Event-Handler
	if (ev.which === 0) {
		showCompose(false);
		installHandler(true);
		return true;
	}
	if (composeData.firstchar === '') {
		composeData.firstchar = String.fromCharCode(ev.which);
		showCompose(true);
		return false;
	}
	insertCompose(String.fromCharCode(ev.which));
	return false;
}

function insert (text) { //fügt Zeichen ein
	$textbox.textSelection('encapsulateSelection', {peri: text, replace: true, selectPeri: false});
}
function getTextContent () { //holt den gesamten Inhalt des Eingabefeldes
	return $textbox.textSelection('getContents');
}
function getCursorPosition () {
	return $textbox.textSelection('getCaretPosition', {startAndEnd: true});
}
function installHandler (normal) { //normalen Key-Handler oder Compose-Key installieren
	if (normal) {
		$textbox.off('.edithelper')
			.on('keyup.edithelper', onkeyupCompose)
			.on('keyup.edithelper', onkeyupSuggest)
			.on('keydown.edithelper', onkeydownSuggest);
	} else {
		$textbox.off('.edithelper').on('keypress.edithelper', onkeypressCompose);
	}
	composeData.keypressed = false;
}
function show (html, links) { //zeigt HTML an
	$(links ? '#edithelper-compose' : '#edithelper-suggest').html(html);
}
function run () {
	$textbox = $('#wpTextbox1');
	var $we = $('#wikiEditor-ui-toolbar'),
		html = '<div id="edithelper-panel"><span id="edithelper-compose"></span><span id="edithelper-suggest"></span></div>',
		css = '#edithelper-panel {height:1.5em; line-height:1.5em; border:thin solid; position:relative;}' +
			'#edithelper-compose {position:absolute; left:0;}' +
			'#edithelper-suggest {position:absolute; right:0;}' +
			'.edithelper-suggest-number {font-style:italic; margin-left: 2em;}' +
			'.edithelper-suggest-pre {font-weight:bold;}' +
			'.edithelper-compose-examples {margin-left:1em; font-family:monospace;}';

	if ($we.length === 1) {
		$we.after(html);
	} else {
		$textbox.before(html);
	}
	mw.util.addCSS(css);

	initSuggest();
	installHandler(true);
}

function init () {
	var deps;
	if (mw.config.get('wgAction') === 'edit' || mw.config.get('wgAction') === 'submit') {
		deps = ['mediawiki.RegExp', 'mediawiki.util', 'mediawiki.language', 'mediawiki.jqueryMsg', 'jquery.textSelection'];
		if (mw.user.options.get('usebetatoolbar')) {
			deps.push('ext.wikiEditor'); //soll unterhalb der Editleiste landen, also erst danach installieren
		}
		mw.loader.using(deps, function () {
			initL10N(l10n, ['comma-separator']);
			$(run);
		});
	}
}

$.extend(config.compose.map, additionalComposeMap());
mw.hook('userjs.load-script.edithelper').fire(config);
mw.loader.using('user.options', init);

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