Benutzer:Schnark/js/templateEditor.js

aus Wikipedia, der freien Enzyklopädie

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/templateEditor]] <nowiki>

/*global mediaWiki, ve, OO*/

(function ($, mw, libs) {
"use strict";
if (libs.templateEditor) { //gegen doppelte Einbindung schützen
	return;
}

var hasOwn = Object.prototype.hasOwnProperty, templateEditor = {
	version: 2.17,

	status: 'register',
	started: false,

	$footer: null,
	footitems: [],
	$tbody: null,

	editData: {
		title: '',
		text: '',
		comments: [],
		minor: true,
		watch: false,
		starttime: '',
		edittime: ''
	},

	targets: [],

	plugins: {},
	runningPlugins: [],
	ve: false
}, l10n = {
/*jscs:disable maximumLineLength*/
	en: {
		'comma-separator': ', ',
		'schnark-template-editor-autocorr': '(automatically corrected)',
		'schnark-template-editor-autofill': '(automatically added)',
		'schnark-template-editor-quote': '“$1”',
		'schnark-template-editor-suggestion-title': 'adopt suggestion',
		'schnark-template-editor-exclam': '<strong style="color:#d33;">!</strong>',
		'schnark-template-editor-suggestions': '{{PLURAL:$2|Suggestion|Suggestions}}: $1',
		'schnark-template-editor-default-headline': 'Edit',
		'schnark-template-editor-edit-button-text': 'Proceed',
		'schnark-template-editor-default-list-head': '-Suggestions-'
	},
	de: {
		'schnark-template-editor-autocorr': '(automatisch korrigiert)',
		'schnark-template-editor-autofill': '(automatisch hinzugefügt)',
		'schnark-template-editor-quote': '„$1“',
		'schnark-template-editor-suggestion-title': 'Vorschlag übernehmen',
		'schnark-template-editor-suggestions': '{{PLURAL:$2|Vorschlag|Vorschläge}}: $1',
		'schnark-template-editor-default-headline': 'Bearbeiten',
		'schnark-template-editor-edit-button-text': 'Übernehmen',
		'schnark-template-editor-default-list-head': '-Vorschläge-'
	},
	'de-ch': {
		'schnark-template-editor-quote': '«$1»'
	}
};
/*jscs:enable maximumLineLength*/

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 callHook (name, data) {
	templateEditor.status = name;
	for (var i = 0; i < templateEditor.runningPlugins.length; i++) {
		templateEditor.plugins[templateEditor.runningPlugins[i]].hooks[name](data);
	}
}

function register (id, short, version, hooks) {
	if (templateEditor.status !== 'register') {
		return false;
	}
	if (templateEditor.plugins[id]) {
		return false;
	}
	if (libs.templateEditor.qunit) {
		if (id.indexOf('QUnit') !== 0) {
			return false;
		}
	} else {
		if (id.indexOf('QUnit') === 0) {
			return false;
		}
	}
	templateEditor.plugins[id] = {
		short: short,
		version: version,
		hooks: {},
		elements: {}
	};
	//jscs:disable
	templateEditor.plugins[id].hooks.start = hooks.onStart || function () {return false;};
	//jscs:enable
	templateEditor.plugins[id].hooks.ready = hooks.onReady || function () {};
	templateEditor.plugins[id].hooks.finish = hooks.onFinish || function () {};
	return getFunctions(id);
}

function getFunctions (id) {
	if (!(id in templateEditor.plugins)) {
		return false;
	}
	return {
		getFunctions: getFunctions,
		getVersion: getVersion,
		start: function (data) {
			return begin(id, data);
		},
		addInput: function (id2, data) {
			if (id2 in templateEditor.plugins[id].elements) {
				return null;
			}
			return addInput(id, id2, data);
		},
		addRow: addRow,
		addFootitem: addFootitem,
		setVal: function (id2, val) {
			if (!(id2 in templateEditor.plugins[id].elements)) {
				return null;
			}
			return setVal(id, id2, val);
		},
		getVal: function (id2) {
			if (id2 !== undefined && !(id2 in templateEditor.plugins[id].elements)) {
				return null;
			}
			return getVal(id, id2);
		},
		get$: function (id2, el) {
			if (!(id2 in templateEditor.plugins[id].elements)) {
				return null;
			}
			return get$(id, id2, el);
		},
		setSuggestions: function (id2, suggestions) {
			if (!(id2 in templateEditor.plugins[id].elements)) {
				return null;
			}
			setSuggestions(id, id2, suggestions);
		},
		setList: function (id2, list, title, origval) {
			if (!(id2 in templateEditor.plugins[id].elements)) {
				return null;
			}
			setList(id, id2, list, title, origval);
		},
		setWarning: function (id2, warning) {
			if (!(id2 in templateEditor.plugins[id].elements)) {
				return null;
			}
			setWarning(id, id2, warning);
		},
		getWarning: function (id2) {
			if (!(id2 in templateEditor.plugins[id].elements)) {
				return null;
			}
			return getWarning(id, id2);
		},
		getTitle: getTitle,
		getText: getText,
		setText: setText,
		addComment: addComment,
		setNotMinor: setNotMinor
	};
}

function getVersion (flag) {
	function formatVersion (version) {
		if (typeof version === 'number' && version === Math.floor(version)) {
			return String(version) + '.0';
		}
		return String(version);
	}

	var versions = [], i, p;
	if (flag === undefined) {
		return templateEditor.version;
	}
	if (flag === false) {
		versions.push(formatVersion(templateEditor.version));
		for (p in templateEditor.plugins) {
			if (hasOwn.call(templateEditor.plugins, p) && templateEditor.plugins[p].version) {
				versions.push(templateEditor.plugins[p].short + '-' + formatVersion(templateEditor.plugins[p].version));
			}
		}
		return versions.join('/');
	}
	if (flag === true) {
		versions.push(formatVersion(templateEditor.version));
		for (i = 0; i < templateEditor.runningPlugins.length; i++) {
			versions.push(templateEditor.plugins[templateEditor.runningPlugins[i]].short + '-' +
				formatVersion(templateEditor.plugins[templateEditor.runningPlugins[i]].version));
		}
		return versions.join('/');
	}
	if (!templateEditor.plugins[flag]) {
		return null;
	}
	return templateEditor.plugins[flag].version;
}

function begin (id, data) {
	if (templateEditor.started) {
		return false;
	}
	templateEditor.started = true;
	callAPI(function () {
		initL10N(l10n, ['comma-separator']);
		templateEditor.status = 'start';
		var order, orders = {}, plugin, headline, redirect;
		for (plugin in templateEditor.plugins) {
			if (hasOwn.call(templateEditor.plugins, plugin)) {
				if (plugin === id) {
					order = data.order || 0;
				} else {
					order = templateEditor.plugins[plugin].hooks.start({id: id, data: data});
				}
				if (order || plugin === id) {
					templateEditor.runningPlugins.push(plugin);
					orders[plugin] = order;
				}
			}
		}
		templateEditor.runningPlugins.sort(function (a, b) {
			return orders[a] - orders[b];
		});

		headline = data.headline;
		redirect = data.redirect;
		if (headline === undefined) {
			headline = mw.msg('schnark-template-editor-default-headline');
		}
		if (redirect === undefined) {
			redirect = true;
		}
		start(headline, data.$, redirect);
	});
	return true;
}

function getDataEdit () {
	if (window.ve && ve.init && ve.init.target && ve.init.target.active) {
		templateEditor.ve = ve.init.target;
		return getDataVE();
	}
	return $.getJSON(mw.util.wikiScript('api'), {
		action: 'query',
		titles: mw.config.get('wgPageName'),
		prop: 'info|revisions',
		rvprop: 'content|timestamp',
		rvslots: 'main',
		inprop: 'watched',
		curtimestamp: true,
		format: 'json',
		formatversion: 2
	});
}

function getDataVE () {
	var target = templateEditor.ve;
	return target.serialize(target.getSurface().getDom()).then(function (data) {
		return {
			query: {
				pages: [{
					revisions: [{
						timestamp: target.baseTimeStamp,
						slots: {
							main: {
								content: data.content.replace(/\r?\n/g, '\n')
							}
						}
					}],
					watched: !!$('#ca-unwatch').length
				}]
			},
			curtimestamp: target.startTimeStamp
		};
	});
}

function prepareData (json) {
	var page = json.query.pages[0];
	if (page.watched) {
		templateEditor.editData.watch = true;
	}
	templateEditor.editData.starttime = json.curtimestamp;
	templateEditor.editData.edittime = page.revisions[0].timestamp;
	if (!libs.templateEditor.qunit) {
		templateEditor.editData.starttime = templateEditor.editData.starttime.replace(/\D/g, '');
		templateEditor.editData.edittime = templateEditor.editData.edittime.replace(/\D/g, '');
	}
	setText(page.revisions[0].slots.main.content);
}

function callAPI (callback) {
	if (libs.templateEditor.qunit) {
		//synchron, alle Module wurden im QUnit-Test schon geladen
		prepareData({
			query: {
				pages: [{
					revisions: [{
						timestamp: 'edittime',
						slots: {
							main: {
								content: 'text'
							}
						}
					}]
				}]
			},
			curtimestamp: 'starttime'
		});
		callback();
		return;
	}
	mw.loader.load(['mediawiki.language', 'mediawiki.jqueryMsg']);
	mw.loader.using('mediawiki.util').then(function () {
		getDataEdit().then(function (json) {
			if (!json || !json.query || !json.query.pages) {
				return;
			}
			prepareData(json);
			mw.loader.using(['mediawiki.language', 'mediawiki.jqueryMsg']).then(callback);
		});
	});
}

function start (headline, $el, redirect) {
	initInterface(headline, $el);
	if (!libs.templateEditor.qunit && !templateEditor.ve && redirect && mw.config.get('wgIsRedirect')) {
		$.get(
			mw.util.getUrl(
				$('.redirectText').first().find('a').last().attr('title'), {
					action: 'render'
				}
			)
		).then(function (html) {
			var $div = $('<div>').html(html);
			mw.hook('wikipage.content').fire($div);
			$('.redirectMsg').first().after($div);
		});
	}
}

function setWikitextVE (wikitext, summary) {
	var target = ve.init.target, surface = target.getSurface(), surfaceModel;
	target.initialSummary = summary;
	if (surface.getMode() !== 'source') {
		target.reloadSurface('visual',
			mw.libs.ve.targetLoader.requestParsoidData(target.pageName, {
				oldId: target.revid,
				targetName: 'schnark-templateEditor',
				modified: true,
				wikitext: wikitext
			})
		);
	} else {
		surfaceModel = surface.getModel();
		surfaceModel.setLinearSelection(
			new ve.Range(0, surfaceModel.getDocument().data.getLength())
		);
		surfaceModel.getFragment().insertContent(wikitext).collapseToStart().select();
	}
}

function doEdit () {
	callHook('finish');
	var $form, form,
		text = getText(),
		comment = templateEditor.editData.comments.join(', '),
		minor = templateEditor.editData.minor,
		watch = templateEditor.editData.watch,
		starttime = templateEditor.editData.starttime,
		edittime = templateEditor.editData.edittime;

	if (templateEditor.ve) {
		templateEditor.oowin.close();
		setWikitextVE(text, comment);
		templateEditor.started = false;
		templateEditor.status = 'register';
		templateEditor.$footer = null;
		templateEditor.footitems = [];
		templateEditor.$tbody = null;
		templateEditor.targets = [];
		templateEditor.runningPlugins = [];
		$.each(templateEditor.plugins, function (id, data) {
			data.elements = {};
		});
		templateEditor.oowin.simpleWindowContent.remove();
		return;
	}

	form =
		mw.html.element('textarea', {name: 'wpTextbox1'}, '\n' + text) +
		mw.html.element('input', {type: 'hidden', name: 'wpSummary', value: comment}) +
		mw.html.element('input', {type: 'checkbox', name: 'wpMinoredit', value: 1, checked: minor}) +
		mw.html.element('input', {type: 'checkbox', name: 'wpWatchthis', value: 1, checked: watch}) +
		mw.html.element('input', {type: 'hidden', name: 'wpEdittime', value: edittime}) +
		mw.html.element('input', {type: 'hidden', name: 'wpStarttime', value: starttime}) +
		mw.html.element('input', {type: 'hidden', name: 'wpDiff', value: 'wpDiff'}) +
		mw.html.element('input', {type: 'hidden', name: 'wpUltimateParam', value: 1});

	$form = $(mw.html.element('form', {
		style: 'display: none;', method: 'post', enctype: 'multipart/form-data',
		action: mw.config.get('wgScript') + '?' + $.param({title: mw.config.get('wgPageName'), action: 'submit'})
	}, new mw.html.Raw(form)));
	$('body').append($form);
	if (!libs.templateEditor.qunit) {
		$form.trigger('submit');
	}
}

function initInterface (headline, $el) {
	if (!$el) {
		$el = $('<div>');
		if (templateEditor.ve) {
			$('body').append($el.addClass('oo-ui-element-hidden')); //add to DOM, but hide
			mw.loader.using(['oojs', 'oojs-ui']).then(function () {
				showAsDialog($el.removeClass('oo-ui-element-hidden'));
			});
		} else {
			$('#siteSub').after($el);
		}
	}
	var editButton = mw.html.element('input', {
			type: 'button', 'class': 'templateEditorButton',
			value: mw.msg('schnark-template-editor-edit-button-text')
		}),
		html = '<table class="templateEditor-table">' +
			'<thead><tr>' + mw.html.element('th', {colspan: 2}, headline) +
			'<td>' + editButton + '</td></tr></thead>' +
			'<tbody></tbody>' +
			'<tfoot><tr><td class="templateEditor-foot" colspan="2"></td>' +
			'<td>' + editButton + '</td></tr></tfoot>' +
			'</table>';
	$el.html(html);
	templateEditor.$footer = $el.find('tfoot').find('td.templateEditor-foot');
	templateEditor.$tbody = $el.find('tbody');

	$el.find('.templateEditorButton').on('click', doEdit);
	$el.on('click', '.templateEditor-suggestion', function (e) {
		var $this = $(this),
			target = templateEditor.targets[$this.attr('data-template-editor-target')],
			text = $this.find('.templateEditor-suggestion-inner').text();
		setVal(target[0], target[1], text).trigger('change');
		e.preventDefault();
	});
	callHook('ready');
}

function showAsDialog ($el) {
	function SimpleWindow ($el) {
		SimpleWindow.parent.call(this, {size: 'larger'});
		$el.css('padding', '2em'); //FIXME properly call resize
		setTimeout(function () {
			$el.css('padding', '');
		}, 1000);
		this.simpleWindowContent = $el;
	}

	OO.inheritClass(SimpleWindow, OO.ui.Dialog);

	SimpleWindow.static.name = 'schnark-templateEditor';
	SimpleWindow.prototype.initialize = function () {
		SimpleWindow.parent.prototype.initialize.call(this);
		this.$body.append(this.simpleWindowContent);
	};

	var windowManager = new OO.ui.WindowManager(), win = new SimpleWindow($el);
	$('body').append(windowManager.$element);
	windowManager.addWindows([win]);
	windowManager.openWindow(win);
	templateEditor.oowin = win;
}

function addInput (id1, id2, options) {
	var opt = $.extend({
			type: 'text', autofill: false,
			autocorr: function (val) {
				return val;
			}
		}, options),
		html, id = ('templateEditor-input-' + id1 + '-' + id2).replace(/[^a-zA-Z0-9\-]/g, '-'),
		$el, $input, $warning, $additional, $list;
	if (opt.type === 'text') {
		html = '<tr style="vertical-align:top;">' + mw.html.element('td', {}, new mw.html.Raw(opt.text)) +
			mw.html.element('td', {}, new mw.html.Raw(
				mw.html.element('input', {id: id, type: 'text', value: '', size: '70'})
			)) +
			mw.html.element('td', {}, new mw.html.Raw(
				mw.html.element('span', {style: 'margin-right:1em;', 'class': 'templateEditor-comment'}, '') +
				mw.html.element('span', {'class': 'templateEditor-additional'}, '') +
				mw.html.element('div', {'class': 'templateEditor-list'}, '')
			)) + '</tr>';
	} else {
		html = '<tr style="vertical-align:top;">' + mw.html.element('td', {}, '') +
			mw.html.element('td', {}, new mw.html.Raw(
				mw.html.element('input', {id: id, type: 'checkbox'}) +
				mw.html.element('label', {'for': id}, new mw.html.Raw(opt.text))
			)) +
			mw.html.element('td', {}, new mw.html.Raw(
				mw.html.element('span', {style: 'margin-right:1em;', 'class': 'templateEditor-comment'}, '') +
				mw.html.element('span', {'class': 'templateEditor-additional'}, '') +
				mw.html.element('div', {'class': 'templateEditor-list'}, '')
			)) + '</tr>';
	}
	$el = $(html);
	$input = $el.find('#' + id);
	$warning = $el.find('.templateEditor-comment');
	$additional = $el.find('.templateEditor-additional');
	$list = $el.find('.templateEditor-list');
	templateEditor.plugins[id1].elements[id2] = {
		opt: opt,
		$: {input: $input, warning: $warning, additional: $additional, list: $list},
		warning: '',
		origval: (opt.type === 'text') ? '' : false,
		suggestion: null
	};
	templateEditor.$tbody.append($el);
	if (opt.val) {
		templateEditor.plugins[id1].elements[id2].origval = opt.val;
		setVal(id1, id2, opt.val);
	}
	$input.on('change', function () {
		if (getWarning(id1, id2) === makeWarning('autofill')) {
			setWarning(id1, id2, getVal(id1, id2) === '' ? makeWarning('empty') : '');
		}
		setSuggestions(id1, id2, templateEditor.plugins[id1].elements[id2].suggestion);
	});
	return $additional;
}

function addRow ($row) {
	templateEditor.$tbody.append($row);
	return $row;
}

function addFootitem (html) {
	templateEditor.footitems.push(html);
	templateEditor.$footer.html(templateEditor.footitems.join(' • '));
}

function setVal (id1, id2, val) {
	var opt = templateEditor.plugins[id1].elements[id2].opt,
		$input = templateEditor.plugins[id1].elements[id2].$.input,
		newVal = opt.autocorr(val);
	if (newVal !== val && getWarning(id1, id2) === '') {
		setWarning(id1, id2, makeWarning('autocorr'));
	}
	if (opt.type === 'text') {
		$input.val(newVal);
	} else {
		$input.prop('checked', newVal);
	}
	return $input;
}

function getVal (id1, id2) {
	var vals = {}, id, opt, $input, val;
	if (id2 === undefined) {
		for (id in templateEditor.plugins[id1].elements) {
			if (hasOwn.call(templateEditor.plugins[id1].elements, id)) {
				vals[id] = getVal(id1, id);
			}
		}
		return vals;
	}

	opt = templateEditor.plugins[id1].elements[id2].opt;
	$input = templateEditor.plugins[id1].elements[id2].$.input;
	if (opt.type === 'text') {
		val = $input.val() || '';
	} else {
		val = $input.prop('checked');
	}
	return opt.autocorr(val);
}

function get$ (id1, id2, item) {
	return templateEditor.plugins[id1].elements[id2].$[item || 'input'];
}

function setSuggestions (id1, id2, suggestions) {
	var oldWarn, opt, target, suggestionList = [], i;
	templateEditor.plugins[id1].elements[id2].suggestion = suggestions;
	if (suggestions === null) {
		suggestions = [];
	}
	if (!Array.isArray(suggestions)) {
		suggestions = [suggestions];
	}

	oldWarn = getWarning(id1, id2);
	if (oldWarn === makeWarning('autofill')) {
		setVal(id1, id2, ''); //reset
	}

	if (suggestions.length === 0) {
		if (oldWarn !== makeWarning('autocorr')) {
			setWarning(id1, id2, '');
		}
		return;
	}
	opt = templateEditor.plugins[id1].elements[id2].opt;
	if (
		suggestions.length === 1 && opt.autofill && suggestions[0] !== '' &&
		getVal(id1, id2) === '' && oldWarn !== makeWarning('empty')
	) {
		setVal(id1, id2, suggestions[0]);
		setWarning(id1, id2, makeWarning('autofill'));
		return;
	}
	if (suggestions.length === 1 && getVal(id1, id2) === suggestions[0]) {
		if (get$(id1, id2, 'warning').find('.templateEditor-suggestion').length > 0) {
			setWarning(id1, id2, '');
		}
		return;
	}
	target = templateEditor.targets.length;
	templateEditor.targets[target] = [id1, id2];
	for (i = 0; i < suggestions.length; i++) {
		suggestionList.push(
			mw.html.element('a', {
				'class': 'templateEditor-suggestion internal',
				title: mw.msg('schnark-template-editor-suggestion-title'),
				href: '#', 'data-template-editor-target': target
			}, new mw.html.Raw(
				mw.msg('schnark-template-editor-quote',
					mw.html.element('span', {
						'class': 'templateEditor-suggestion-inner'
					}, suggestions[i])
				)
			))
		);
	}
	setWarning(id1, id2, '&nbsp;' + mw.msg('schnark-template-editor-exclam') + '&nbsp;' +
		mw.msg('schnark-template-editor-suggestions',
			suggestionList.join(mw.msg('comma-separator')),
			suggestionList.length
		)
	);
}

function setList (id1, id2, list, title, origval) {
	var $list = templateEditor.plugins[id1].elements[id2].$.list, html, i;
	if (list.length === 0) {
		$list.html('');
		return;
	}
	if (origval === undefined) {
		origval = templateEditor.plugins[id1].elements[id2].origval;
	}
	if (title === undefined) {
		title = mw.msg('schnark-template-editor-default-list-head');
	}
	html = '<select size="1">';
	html += mw.html.element('option', {value: origval}, title);
	for (i = 0; i < list.length; i++) {
		html += mw.html.element('option', {value: list[i][1]}, list[i][0]);
	}
	html += '</select>';
	$list.html(html)
		.find('select').on('change', function () {
			setVal(id1, id2, this.value).trigger('change');
		});
}

function makeWarning (type) {
	var warnings = {
		autocorr: '<span>&nbsp;' + mw.msg('schnark-template-editor-autocorr') + '</span>',
		autofill: '<span>&nbsp;' + mw.msg('schnark-template-editor-autofill') + '</span>',
		empty: '<span style="display:none;">geleert</span>' //für interne Zwecke
	};
	return warnings[type];
}

function setWarning (id1, id2, warning) {
	templateEditor.plugins[id1].elements[id2].warning = warning;
	templateEditor.plugins[id1].elements[id2].$.warning.html(warning);
}

function getWarning (id1, id2) {
	return templateEditor.plugins[id1].elements[id2].warning;
}

function getTitle () {
	return templateEditor.editData.title;
}
function setTitle (title) {
	templateEditor.editData.title = title;
}

function getText () {
	return templateEditor.editData.text;
}
function setText (text) {
	templateEditor.editData.text = text;
}

function addComment (comment) {
	templateEditor.editData.comments.push(comment);
}

function setNotMinor () {
	templateEditor.editData.minor = false;
}

setTitle(mw.config.get('wgTitle'));

libs.templateEditor = register;
mw.hook('userjs.load-script.templateEditor').fire(register);

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