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/veCustomize]] <nowiki>
/*global mediaWiki, ve, OO*/

(function ($, mw) {
"use strict";

var l10n = {
	en: {
		'schnark-vecustomize-small-title': 'Small font',
		'schnark-vecustomize-small-help': '',
		'schnark-vecustomize-small-sample': 'small font',
		'schnark-vecustomize-br-title': 'Line break',
		'schnark-vecustomize-br-help': '',
		'schnark-vecustomize-poem-title': 'Poem',
		'schnark-vecustomize-poem-help': 'Poem',
		'schnark-vecustomize-poem-sample': 'Poem',
		'schnark-vecustomize-la-title': 'Löschantrag',
		'schnark-vecustomize-la-help': 'LA-Vorlage',
		'schnark-vecustomize-nbsp-help': 'Non-breaking space'
	},
	de: {
		'schnark-vecustomize-small-title': 'Kleine Schriftgröße',
		'schnark-vecustomize-small-help': '',
		'schnark-vecustomize-small-sample': 'kleine Schriftgröße',
		'schnark-vecustomize-br-title': 'Zeilenumbruch',
		'schnark-vecustomize-br-help': '',
		'schnark-vecustomize-poem-title': 'Gedicht',
		'schnark-vecustomize-poem-help': 'Gedicht',
		'schnark-vecustomize-poem-sample': 'Gedicht',
		'schnark-vecustomize-nbsp-help': 'Geschütztes Leerzeichen'
	}
}, toolCount = 0;

function initL10N (l10n) {
	var i, chain = mw.language.getFallbackLanguageChain();
	for (i = chain.length - 1; i >= 0; i--) {
		if (chain[i] in l10n) {
			mw.messages.set(l10n[chain[i]]);
		}
	}
}

function makeName () {
	return 'schnark-ve-tool-' + (toolCount++);
}

function makeTrigger (trigger) {
	function convertToTrigger (input) {
		return Array.isArray(input) ?
			input.map(function (str) {
				return new ve.ui.Trigger(str);
			}) :
			new ve.ui.Trigger(input);
	}

	if (typeof trigger === 'string') {
		trigger = {mac: trigger.replace('ctrl', 'cmd'), pc: trigger};
	}
	return {mac: convertToTrigger(trigger.mac), pc: convertToTrigger(trigger.pc)};
}

function makeTemplate (name, params, block, subst) {
	var formattedParams = {}, param;
	for (param in params) {
		if (Object.prototype.hasOwnProperty.call(params, param)) {
			formattedParams[param] = {wt: params[param]};
		}
	}
	return [{
		type: block ? 'mwTransclusionBlock' : 'mwTransclusionInline',
		attributes: {
			mw: {
				parts: [{
					template: {
						target: {
							href: mw.config.get('wgFormattedNamespaces')[10] + ':' + name,
							wt: (subst ? 'subst:' : '') + name
						},
						params: formattedParams
					}
				}]
			}
		}
	}, {
		type: block ? '/mwTransclusionBlock' : '/mwTransclusionInline'
	}];
}

function replaceLinebreaks (s) {
	var data = [];
	if (s.indexOf('\n') === -1) {
		return s;
	}
	s.split('').forEach(function (c) {
		if (c === '\n') {
			data.push({type: '/paragraph'}, {type: 'paragraph'});
		} else {
			data.push(c);
		}
	});
	return data;
}

function enableSignEverywhere (all) {
	var ns, module;
	if (!all) {
		all = [];
		for (ns in mw.config.get('wgFormattedNamespaces')) {
			if (!isNaN(ns) && ns >= 0) {
				all.push(Number(ns));
			}
		}
	}
	mw.config.set('wgExtraSignatureNamespaces', all);
	//HACK
	try {
		module = mw.loader.moduleRegistry['ext.visualEditor.mwsignature'];
		if (module && module.state === 'ready') {
			module.script($, $, mw);
		}
	} catch (e) {
		mw.log.warn(e);
	}
}

function makeCustomTool (config) {
	var name = makeName();
	config = $.extend({
		parent: config.insert ? ve.ui.MWTransclusionDialogTool : ve.ui.AnnotationTool,
		group: config.insert ? 'object' : 'textStyle',
		icon: 'template',
		title: '',
		insert: false,
		dialog: false,
		annotation: false,
		collapseToEnd: true,
		selections: config.insert ? ['linear'] : ['linear', 'table']
	}, config);

	if (config.icon.slice(0, 8) === 'https://') {
		mw.util.addCSS('.oo-ui-icon-' + name + '{background-image: url(' + config.icon + ');​}');
		config.icon = name;
	} else if (config.iconModule) {
		mw.loader.load(config.iconModule);
	}

	function InsertAndOpenCommand (name, type, options) {
		InsertAndOpenCommand.parent.call(this, name, null, null, options);
		this.windowType = type;
	}
	OO.inheritClass(InsertAndOpenCommand, ve.ui.Command);

	InsertAndOpenCommand.prototype.execute = function (surface, args) {
		args = args || this.args;
		surface.getModel().getFragment().collapseToEnd().insertContent(args[0], args[1]).select();
		surface.execute('window', 'open', this.windowType);
		return true;
	};

	function CustomTool () {
		CustomTool.parent.apply(this, arguments);
	}
	OO.inheritClass(CustomTool, config.parent);

	CustomTool.static.name = name;
	CustomTool.static.group = config.group;
	CustomTool.static.icon = config.icon;
	CustomTool.static.title = config.title;
	if (!config.insert) {
		CustomTool.static.annotation = {name: config.annotation};
	}
	CustomTool.static.commandName = name;
	if (config.icon) {
		ve.ui.toolFactory.register(CustomTool);
	}

	if (config.insert) {
		if (config.dialog) {
			ve.ui.commandRegistry.register(
				new InsertAndOpenCommand(name, config.dialog, {
					args: [config.insert, config.annotation],
					supportedSelections: config.selections
				})
			);
		} else {
			ve.ui.commandRegistry.register(
				new ve.ui.Command(name, 'content', 'insert', {
					args: [config.insert, config.annotation, config.collapseToEnd],
					supportedSelections: config.selections
				})
			);
		}
	} else {
		ve.ui.commandRegistry.register(
			new ve.ui.Command(name, 'annotation', 'toggle', {
				args: [config.annotation],
				supportedSelections: config.selections
			})
		);
	}
	if (config.wikitext && ve.ui.wikitextCommandRegistry) {
		if (Array.isArray(config.wikitext)) {
			ve.ui.wikitextCommandRegistry.register(
				new ve.ui.Command(name, 'mwWikitext', config.annotation ? 'toggleWrapSelection' : 'wrapSelection', {
					//NOTE: I prefer this order
					args: [
						replaceLinebreaks(config.wikitext[0]),
						replaceLinebreaks(config.wikitext[2]),
						replaceLinebreaks(config.wikitext[1])
					],
					supportedSelections: ['linear']
				})
			);
		} else {
			ve.ui.wikitextCommandRegistry.register(
				new ve.ui.Command(name, 'content', 'insert', {
					args: [config.wikitext, false, config.collapseToEnd],
					supportedSelections: ['linear']
				})
			);
		}
	}
	if (config.sequence) {
		ve.ui.sequenceRegistry.register(
			new ve.ui.Sequence(name, name, config.sequence, config.sequence.length)
		);
	}
	if (config.trigger) {
		ve.ui.triggerRegistry.register(
			name, makeTrigger(config.trigger)
		);
	}
	if (config.help) {
		ve.ui.commandHelpRegistry.register(config.insert ? 'insert' : 'textStyle', name, {
			sequences: config.sequence ? [name] : [],
			trigger: config.trigger ? name : undefined,
			label: config.help
		});
	}
}

function makeToolFromCode (code) {
	var data;
	switch (code) {
	case 'small': //no longer necessary
		data = {
			annotation: 'textStyle/small',
			wikitext: ['<small>', mw.msg('schnark-vecustomize-small-sample'), '</small>'],
			icon: 'smaller',
			iconModule: 'oojs-ui.styles.icons-editing-styling'
		};
		break;
	case 'br':
		data = {
			insert: [{type: 'break'}, {type: '/break'}],
			wikitext: '<br>',
			icon: 'newline',
			iconModule: 'oojs-ui.styles.icons-editing-advanced'
		};
		break;
	case 'poem':
		data = [{
			type: 'mwAlienBlockExtension',
			attributes: {
				mw: {
					name: 'poem',
					body: {
						extsrc: ''
					}
				}
			}
		}, {
			type: '/mwAlienBlockExtension'
		}];
		data = {
			insert: data,
			collapseToEnd: false,
			dialog: 'alienExtension',
			wikitext: ['\n<poem>\n', mw.msg('schnark-vecustomize-poem-sample'), '\n</poem>\n'],
			icon: 'textFlow',
			iconModule: 'oojs-ui.styles.icons-layout',
			sequence: '<poem'
		};
		break;
	case 'la': //actually more proof of concept than anything else
		data = {
			insert: makeTemplate('Löschantrag', {1: '\'\'Grund\'\' --~~~~'}, true, true),
			dialog: 'transclusion',
			wikitext: ['\n{{subst:Löschantrag|1=\'\'', 'Grund', '\'\' --~~~~}}\n'],
			sequence: '{LA}',
			icon: 'tag',
			iconModule: 'oojs-ui.styles.icons-content',
			trigger: 'ctrl+shift+l'
		};
		break;
	case 'nbsp':
		data = {
			insert: [
				{type: 'mwEntity', attributes: {character: '\u00a0'}},
				{type: '/mwEntity'}
			],
			wikitext: '&nbsp;',
			annotation: true,
			icon: '', //no visible tool
			trigger: {mac: 'alt+space', pc: ['ctrl+space', 'ctrl+shift+space']},
			sequence: '&nbsp;'
		};
		break;
	case 'sig':
		return;
	default:
		mw.log.warn('Code "' + code + '" not defined!');
		return;
	}
	data.title = mw.msg('schnark-vecustomize-' + code + '-title');
	data.help = mw.msg('schnark-vecustomize-' + code + '-help');
	makeCustomTool(data);
}

function init () {
	var codes = mw.user.options.get('userjs-schnark-vecustomize-tools', 'sig, br').split(/\W+/),
		deps = ['mediawiki.language', 'ext.visualEditor.core', 'ext.visualEditor.mwtransclusion'];

	if (codes.indexOf('sig') > -1) {
		enableSignEverywhere();
	}

	if (mw.libs.ve.isWikitextAvailable) {
		deps.push('ext.visualEditor.mwwikitext');
	}

	mw.loader.using(deps).then(function () {
		var i;
		initL10N(l10n);
		for (i = 0; i < codes.length; i++) {
			makeToolFromCode(codes[i]);
		}
		mw.hook('userjs.script-ready.veCustomize').fire();
	});
}

mw.loader.using('user.options').then(init);

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