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/fliegelflagel]] <nowiki>
/*global mediaWiki, OO*/
/*jshint evil: true*/
(function ($, mw) {
"use strict";

mw.hook('userjs.schnark-fliegelflagel.l10n').fire({
//jscs:disable maximumLineLength
	en: {
		'schnark-fliegelflagel-manage-config-label': 'Configuration',
		'schnark-fliegelflagel-manage-enable-label': 'Enabled:',
		'schnark-fliegelflagel-manage-default-enabled': 'Default (yes)',
		'schnark-fliegelflagel-manage-default-disabled': 'Default (no)',
		'schnark-fliegelflagel-manage-user-disabled': 'no',
		'schnark-fliegelflagel-manage-user-enabled': 'yes',
		'schnark-fliegelflagel-manage-active': 'enabled',
		'schnark-fliegelflagel-manage-inactive': 'disabled',
		'schnark-fliegelflagel-manage-unsaved': 'unsaved changes',
		'schnark-fliegelflagel-manage-configured': 'non-default configuration',
		'schnark-fliegelflagel-manage-status-beta': 'experimental script',
		'schnark-fliegelflagel-manage-status-deprecated': 'deprecated script',
		'schnark-fliegelflagel-manage-status-unknown': 'status of script unknown',
		'schnark-fliegelflagel-manage-more-link': 'more',
		'schnark-fliegelflagel-manage-more-link-title': 'documentation page (opens in a new window)',
		'schnark-fliegelflagel-manage-more-link-wrapper': '&#160;<small>$1</small>',
		'schnark-fliegelflagel-manage-filter-all': 'all',
		'schnark-fliegelflagel-manage-filter-unstable': 'experimental/deprecated',
		'schnark-fliegelflagel-manage-filter-extended': 'extended filters',
		'schnark-fliegelflagel-manage-filter-label-active-inactive': 'Disabled/Enabled',
		'schnark-fliegelflagel-manage-filter-label-configured-unconfigured': 'with/without configuration',
		'schnark-fliegelflagel-manage-filter-label-saved-unsaved': '(un)saved',
		'schnark-fliegelflagel-manage-filter-label-category': 'Category',
		'schnark-fliegelflagel-manage-filter-label-status': 'Status',
		'schnark-fliegelflagel-manage-filter-active': 'enabled',
		'schnark-fliegelflagel-manage-filter-inactive': 'disabled',
		'schnark-fliegelflagel-manage-filter-unconfigured': 'without configuration',
		'schnark-fliegelflagel-manage-filter-configured': 'with configuration',
		'schnark-fliegelflagel-manage-filter-unsaved': 'unsaved changes',
		'schnark-fliegelflagel-manage-filter-saved': 'no changes',
		'schnark-fliegelflagel-manage-filter-category-read': 'Read',
		'schnark-fliegelflagel-manage-filter-category-nav': 'Navigation',
		'schnark-fliegelflagel-manage-filter-category-edit': 'Edit',
		'schnark-fliegelflagel-manage-filter-category-history': 'History/Contributions/etc.',
		'schnark-fliegelflagel-manage-filter-category-other': 'Other',
		'schnark-fliegelflagel-manage-filter-status-beta': 'experimental',
		'schnark-fliegelflagel-manage-filter-status-stable': 'stable',
		'schnark-fliegelflagel-manage-filter-status-deprecated': 'deprecated',
		'schnark-fliegelflagel-manage-filter-status-unknown': 'unknown status',
		'schnark-fliegelflagel-manage-filter-no-result': 'No matching scripts found',
		'schnark-fliegelflagel-manage-config-profile': 'Enabling and disabling scripts only affects the current profile <code>$1</code>.',
		'schnark-fliegelflagel-manage-config-save': 'Save changes',
		'schnark-fliegelflagel-manage-config-cancel': 'Discard changes',
		'schnark-fliegelflagel-manage-config': 'On this page you can manage your scripts. Use the filters to find the scripts you want to look at, configure them according to your wishes and save your changes.',
		'schnark-fliegelflagel-manage-config-global-1': 'Note that you can’t change preferences that you set [[Special:FliegelflagelGlobal|globally]] here.',
		'schnark-fliegelflagel-manage-config-global-2': 'These preferences are global and will be used on all wikis where you have Fliegelflagel installed. Note that you can not set local exceptions.',
		'schnark-fliegelflagel-manage-save-failed': 'Saving your changes failed!',
		'schnark-fliegelflagel-manage-debug-button': 'Debug scripts',
		'schnark-fliegelflagel-manage-debug-title': 'Debug scripts',
		'schnark-fliegelflagel-manage-debug-save': 'Save',
		'schnark-fliegelflagel-manage-debug-cancel': 'Cancel',
		'schnark-fliegelflagel-manage-debug-name': 'Title',
		'schnark-fliegelflagel-manage-debug-script': 'Script',
		'schnark-fliegelflagel-manage-update-code': 'Please update your code!',
		'schnark-fliegelflagel-manage-update-code-detail': 'You should update the code to load Fliegelflagel, see $1 for the current version.',
		'schnark-fliegelflagel-manage-suggested-save': 'Use suggested preferences',
		'schnark-fliegelflagel-manage-suggested-hint': 'You don’t use the suggested preferences!',
		'schnark-fliegelflagel-manage-suggested-detail': 'Some of your general preferences differ from the preferences suggested for use with Fliegelflagel. You can use the button to change those preferences. Note that if this changes your skin, you might have to move the code you use to load Fliegelflagel. Also there might be issues with local vs. global preferences. The following preferences will be changed: $1'
	},
	de: {
		'schnark-fliegelflagel-manage-config-label': 'Konfiguration',
		'schnark-fliegelflagel-manage-enable-label': 'Aktivieren:',
		'schnark-fliegelflagel-manage-default-enabled': 'Standard (ja)',
		'schnark-fliegelflagel-manage-default-disabled': 'Standard (nein)',
		'schnark-fliegelflagel-manage-user-disabled': 'nein',
		'schnark-fliegelflagel-manage-user-enabled': 'ja',
		'schnark-fliegelflagel-manage-active': 'aktiviert',
		'schnark-fliegelflagel-manage-inactive': 'deaktiviert',
		'schnark-fliegelflagel-manage-unsaved': 'ungespeicherte Änderungen',
		'schnark-fliegelflagel-manage-configured': 'vom Standard abweichende Konfiguration',
		'schnark-fliegelflagel-manage-status-beta': 'experimentelles Skript',
		'schnark-fliegelflagel-manage-status-deprecated': 'Skript wird nicht mehr weiterentwickelt',
		'schnark-fliegelflagel-manage-status-unknown': 'Status des Skripts ist unbekannt',
		'schnark-fliegelflagel-manage-more-link': 'mehr',
		'schnark-fliegelflagel-manage-more-link-title': 'ausführliche Dokumentation (wird in neuem Fenster geöffnet)',
		'schnark-fliegelflagel-manage-filter-all': 'alle',
		'schnark-fliegelflagel-manage-filter-unstable': 'experimentell/ungepflegt',
		'schnark-fliegelflagel-manage-filter-extended': 'erweiterter Filter',
		'schnark-fliegelflagel-manage-filter-label-active-inactive': '(De)aktivierte',
		'schnark-fliegelflagel-manage-filter-label-configured-unconfigured': 'mit/ohne Konfiguration',
		'schnark-fliegelflagel-manage-filter-label-saved-unsaved': '(Un)gespeicherte',
		'schnark-fliegelflagel-manage-filter-label-category': 'Kategorie',
		'schnark-fliegelflagel-manage-filter-label-status': 'Status',
		'schnark-fliegelflagel-manage-filter-active': 'aktivierte',
		'schnark-fliegelflagel-manage-filter-inactive': 'deaktivierte',
		'schnark-fliegelflagel-manage-filter-unconfigured': 'ohne eigene Konfiguration',
		'schnark-fliegelflagel-manage-filter-configured': 'mit eigener Konfiguration',
		'schnark-fliegelflagel-manage-filter-unsaved': 'ungespeicherte Änderungen',
		'schnark-fliegelflagel-manage-filter-saved': 'ohne Änderungen',
		'schnark-fliegelflagel-manage-filter-category-read': 'Lesen',
		'schnark-fliegelflagel-manage-filter-category-nav': 'Navigation',
		'schnark-fliegelflagel-manage-filter-category-edit': 'Bearbeiten',
		'schnark-fliegelflagel-manage-filter-category-history': 'Versionen/Benutzerbeiträge/etc.',
		'schnark-fliegelflagel-manage-filter-category-other': 'Sonstiges',
		'schnark-fliegelflagel-manage-filter-status-beta': 'experimentell',
		'schnark-fliegelflagel-manage-filter-status-stable': 'stabil',
		'schnark-fliegelflagel-manage-filter-status-deprecated': 'ungepflegt',
		'schnark-fliegelflagel-manage-filter-status-unknown': 'unbekannter Status',
		'schnark-fliegelflagel-manage-filter-no-result': 'Keine passenden Skripte gefunden',
		'schnark-fliegelflagel-manage-config-profile': 'Aktivieren und Deaktivieren von Skripten betrifft nur das aktuelle Profil <code>$1</code>.',
		'schnark-fliegelflagel-manage-config-save': 'Änderungen speichern',
		'schnark-fliegelflagel-manage-config-cancel': 'Änderungen verwerfen',
		'schnark-fliegelflagel-manage-config': 'Auf dieser Seite kannst du alle Skripte verwalten. Wähle mit den Filtern aus, welche angezeigt werden sollen, konfiguriere sie nach deinen Wünschen und speichere deine Änderungen.',
		'schnark-fliegelflagel-manage-config-global-1': 'Beachte, dass du hier keine Einstellungen ändern kannst, die du [[Special:FliegelflagelGlobal|global]] festgelegt hast.',
		'schnark-fliegelflagel-manage-config-global-2': 'Diese Einstellungen sind global und gelten für alle Wikis, auf denen du Fliegelflagel installiert hast. Beachte, dass du keine lokalen Ausnahmen festlegen kannst.',
		'schnark-fliegelflagel-manage-save-failed': 'Das Speichern schlug fehlt!',
		'schnark-fliegelflagel-manage-debug-button': 'Skripte debuggen',
		'schnark-fliegelflagel-manage-debug-title': 'Debug',
		'schnark-fliegelflagel-manage-debug-save': 'Speichern',
		'schnark-fliegelflagel-manage-debug-cancel': 'Abbrechen',
		'schnark-fliegelflagel-manage-debug-name': 'Titel',
		'schnark-fliegelflagel-manage-debug-script': 'Skript',
		'schnark-fliegelflagel-manage-update-code': 'Bitte aktualisiere deinen Code!',
		'schnark-fliegelflagel-manage-update-code-detail': 'Du solltest den Code aktualisieren, mit dem du Fliegelflagel lädst, siehe $1 für die aktuelle Version.',
		'schnark-fliegelflagel-manage-suggested-save': 'Vorgeschlagene Einstellungen verwenden',
		'schnark-fliegelflagel-manage-suggested-hint': 'Du verwendest nicht die vorgeschlagenen Einstellungen!',
		'schnark-fliegelflagel-manage-suggested-detail': 'Einige deiner allgemeinen Einstellungen weichen von denen ab, die bei der Verwendung von Fliegelflagel vorgeschlagen sind. Du kannst diese Einstellungen mit einem Klick auf die Schaltfläche ändern. Beachte, dass für den Fall, dass dabei dein Skin geändert wird, du eventuell den Code zum Laden von Fliegelflagel verschieben musst. Auch kann es Probleme geben, wenn Einstellungen global sind. Die folgenden Einstellungen werden geändert: $1'
	},
	'de-formal': {
		'schnark-fliegelflagel-manage-config': 'Auf dieser Seite können Sie alle Skripte verwalten. Wählen Sie mit den Filtern aus, welche angezeigt werden sollen, konfigurieren Sie sie nach Ihren Wünschen und speichern Sie Ihre Änderungen.',
		'schnark-fliegelflagel-manage-config-global-1': 'Beachten Sie, dass Sie hier keine Einstellungen ändern können, die Sie [[Special:FliegelflagelGlobal|global]] festgelegt haben.',
		'schnark-fliegelflagel-manage-config-global-2': 'Diese Einstellungen sind global und gelten für alle Wikis, auf denen Sie Fliegelflagel installiert haben. Beachte Sie, dass Sie keine lokalen Ausnahmen festlegen können.',
		'schnark-fliegelflagel-manage-update-code': 'Bitte aktualisieren Sie Ihren Code!',
		'schnark-fliegelflagel-manage-update-code-detail': 'Sie sollten den Code aktualisieren, mit dem Sie Fliegelflagel laden, siehe $1 für die aktuelle Version.',
		'schnark-fliegelflagel-manage-suggested-hint': 'Sie verwenden nicht die vorgeschlagenen Einstellungen!',
		'schnark-fliegelflagel-manage-suggested-detail': 'Einige Ihrer allgemeinen Einstellungen weichen von denen ab, die bei der Verwendung von Fliegelflagel vorgeschlagen sind. Sie können diese Einstellungen mit einem Klick auf die Schaltfläche ändern. Beachten Sie, dass für den Fall, dass dabei Ihr Skin geändert wird, Sie eventuell den Code zum Laden von Fliegelflagel verschieben müssen. Auch kann es Probleme geben, wenn Einstellungen global sind. Die folgenden Einstellungen werden geändert: $1'
	}
//jscs:enable maximumLineLength
});

var css = '.active-indicator {' +
	'color: #14866d;' + //green
'}' +
'.inactive-indicator {' +
	'color: #d33;' + //red
'}' +
'.unsaved-indicator {' +
	'color: #d33;' + //red
	'font-weight: bold;' +
'}' +
'.configured-indicator {' +
	'color: #36c;' + //blue
	'font-weight: bold;' +
'}' +
'.status-indicator {' +
	'color: #d33;' + //red
	'font-weight: bold;' +
'}' +
'.sticky-buttons {' +
	'background-color: #fff;' +
	'position: -webkit-sticky;' +
	'position: sticky;' +
	'bottom: 0;' +
	'margin-top: -1px;' +
	'border-top: 1px solid #c8ccd1;' +
	'padding: 1em 0;' +
'}' +
'.oo-ui-horizontalLayout.align-top > .oo-ui-layout {' +
	'vertical-align: top;' +
'}', globalPreferences = 0,
globalPreferencesMap = new mw.Map();

mw.hook('userjs.schnark-fliegelflagel.configextend').fire(function (Script, Collection) {
//virtual indent

function getGlobalUserOption (id, def) {
	return String(globalPreferencesMap.get('userjs-' + id, def));
}

function addDebugScript (title, script) {
	var data = {};
	if (title) {
		try {
			data = JSON.parse(sessionStorage['fliegelflagel-debug']) || {};
		} catch (e) {
		}
		if (script) {
			data[title] = script;
		} else {
			delete data[title];
		}
	}
	try {
		sessionStorage['fliegelflagel-debug'] = JSON.stringify(data);
	} catch (e) {
	}
}

function VariantIndexLayout (config) {
	config = $.extend({}, config, {menuPosition: 'top'});
	VariantIndexLayout.parent.call(this, config);

	this.tabData = {};

	//using a nested StackLayout shouldn't be necessary,
	//but it allows to easily re-use styles from IndexLayout
	this.innerContentPanel = new OO.ui.PanelLayout({
		expanded: this.expanded
	});
	this.stackLayout = new OO.ui.StackLayout({
		expanded: this.expanded,
		items: [this.innerContentPanel]
	});
	this.setContentPanel(this.stackLayout);

	this.tabSelectWidget = new OO.ui.TabSelectWidget({});
	this.tabPanel = new OO.ui.PanelLayout({
		expanded: this.expanded
	});
	this.setMenuPanel(this.tabPanel);

	this.toggleMenu(true);

	this.tabSelectWidget.connect(this, {
		select: 'onTabSelectWidgetSelect'
	});

	this.$element.addClass('oo-ui-indexLayout');
	this.stackLayout.$element.addClass('oo-ui-indexLayout-stackLayout');
	this.tabPanel.$element
		.addClass('oo-ui-indexLayout-tabPanel')
		.append(this.tabSelectWidget.$element);

	this.selectFirstSelectableTabPanel();
}

OO.inheritClass(VariantIndexLayout, OO.ui.MenuLayout);

VariantIndexLayout.prototype.onTabSelectWidgetSelect = function (item) {
	if (item) {
		this.emit('tabSelect', this.tabData[item.getData()]);
	}
};

VariantIndexLayout.prototype.addTab = function (name, data) {
	if (this.tabData[name]) {
		this.tabSelectWidget.removeItems([this.tabSelectWidget.findItemFromData(name)]);
	}

	this.tabSelectWidget.addItems(new OO.ui.TabOptionWidget({data: name, label: name}));
	this.tabData[name] = data;

	this.selectFirstSelectableTabPanel();
};

VariantIndexLayout.prototype.setContent = function ($el) {
	this.innerContentPanel.$element.empty().append($el);
};

VariantIndexLayout.prototype.selectFirstSelectableTabPanel = function () {
	if (!this.tabSelectWidget.findSelectedItem()) {
		this.tabSelectWidget.selectItem(this.tabSelectWidget.findFirstSelectableItem());
	}
	return this;
};

function ScriptSearchWidget (config) {
	ScriptSearchWidget.parent.call(this, config);
	OO.ui.mixin.LookupElement.call(this, config);
	this.limit = config.limit || 10;
	this.collection = config.collection;
}

OO.inheritClass(ScriptSearchWidget, OO.ui.TextInputWidget);
OO.mixinClass(ScriptSearchWidget, OO.ui.mixin.LookupElement);

ScriptSearchWidget.prototype.getLookupRequest = function () {
	var scripts = this.collection.searchScripts(this.value);
	if (scripts.length > this.limit) {
		scripts.length = this.limit;
	}
	return $.Deferred().resolve(scripts).promise({abort: $.noop});
};

ScriptSearchWidget.prototype.getLookupCacheDataFromResponse = function (r) {
	return r;
};

ScriptSearchWidget.prototype.getLookupMenuOptionsFromData = function (data) {
	var i, items = [];
	for (i = 0; i < data.length; i++) {
		items.push(new OO.ui.MenuOptionWidget({label: data[i], data: data[i]}));
	}
	return items;
};

function DebugDialog (config) {
	DebugDialog.parent.apply(this, arguments);
	this.collection = config.collection;
}
OO.inheritClass(DebugDialog, OO.ui.ProcessDialog);

DebugDialog.static.name = 'debug-dialog';
DebugDialog.static.title = mw.msg('schnark-fliegelflagel-manage-debug-title');
DebugDialog.static.actions = [
	{action: 'save', label: mw.msg('schnark-fliegelflagel-manage-debug-save'), flags: ['primary', 'progressive']},
	{label: mw.msg('schnark-fliegelflagel-manage-debug-cancel'), flags: ['safe', 'close']}
];

DebugDialog.prototype.initialize = function () {
	var layout;
	DebugDialog.parent.prototype.initialize.apply(this, arguments);
	this.content = new OO.ui.PanelLayout({padded: true, expanded: false});
	this.titleInput = new ScriptSearchWidget({
		$overlay: this.$overlay,
		collection: this.collection
	});
	this.scriptInput = new OO.ui.MultilineTextInputWidget();
	layout = new OO.ui.FieldsetLayout();
	layout.addItems([
		new OO.ui.FieldLayout(this.titleInput, {
			label: mw.msg('schnark-fliegelflagel-manage-debug-name'),
			align: 'top'
		}),
		new OO.ui.FieldLayout(this.scriptInput, {
			label: mw.msg('schnark-fliegelflagel-manage-debug-script'),
			align: 'top'
		})
	]);
	this.content.$element.append(layout.$element);
	this.$body.append(this.content.$element);
};

DebugDialog.prototype.getReadyProcess = function () {
	return DebugDialog.parent.prototype.getReadyProcess.apply(this, arguments)
		.next(function () {
			this.titleInput.focus();
		}, this);
};

DebugDialog.prototype.getActionProcess = function (action) {
	if (action === 'save') {
		addDebugScript(this.titleInput.getValue(), this.scriptInput.getValue());
	}
	return DebugDialog.parent.prototype.getActionProcess.call(this);
};

//Script extend
Script.prototype.l10n = function () {
	var i, j;
	if (this.data.titleMsg) {
		this.data.title = mw.msg(this.data.titleMsg);
	}
	if (this.data.descriptionMsg) {
		this.data.description = mw.msg(this.data.descriptionMsg);
	}
	if (this.data.config) {
		for (i = 0; i < this.data.config.length; i++) {
			if (this.data.config[i].descMsg) {
				this.data.config[i].desc = mw.msg(this.data.config[i].descMsg);
			}
			if (this.data.config[i].values) {
				for (j = 0; j < this.data.config[i].values.length; j++) {
					if (this.data.config[i].values[j].descMsg) {
						this.data.config[i].values[j].desc = mw.msg(this.data.config[i].values[j].descMsg);
					}
				}
			}
		}
	}
};

Script.prototype.checkManageable = function () {
	if ([undefined, 'stable', 'beta', 'deprecated', 'unknown'].indexOf(this.data.status) === -1) {
		mw.log.warn('Fliegelflagel: Unknown status "' + this.data.status + '" for "' + this.id + '"!');
		return false;
	}
	if ([undefined, 'read', 'nav', 'edit', 'history', 'other'].indexOf(this.data.category) === -1) {
		mw.log.warn('Fliegelflagel: Unknown category "' + this.data.category + '" for "' + this.id + '"!');
		return false;
	}
	return this.data.title && this.data.description;
};

Script.prototype.getEnabledData = function (profile) {
	return [
		this.checkDefaultEnabled(profile),
		this.getUserEnabled(profile, ''),
		globalPreferences === 1 && getGlobalUserOption(this.getEnabledKey(profile), '') !== ''
	];
};

Script.prototype.markAsUnsaved = function () {
	if (!this.status.saved) {
		return;
	}
	this.status.saved = false;
	$('#unsaved-indicator-container-' + this.id).append(this.getUnsavedIndicator());
};

Script.prototype.buildSwitcherWidget = function (profile) {
	var data = this.getEnabledData(profile), widget;
	widget = new OO.ui.ButtonSelectWidget({items: [
		new OO.ui.ButtonOptionWidget({
			data: '',
			label: mw.msg(data[0] ? 'schnark-fliegelflagel-manage-default-enabled' :
				'schnark-fliegelflagel-manage-default-disabled')
		}),
		new OO.ui.ButtonOptionWidget({
			data: '0',
			label: mw.msg('schnark-fliegelflagel-manage-user-disabled')
		}),
		new OO.ui.ButtonOptionWidget({
			data: '1',
			label: mw.msg('schnark-fliegelflagel-manage-user-enabled')
		})
	]});
	widget.selectItemByData(data[1]);
	if (data[2]) {
		widget.setDisabled(true);
	} else {
		widget.once('select', function () {
			this.markAsUnsaved();
		}.bind(this));
	}
	this.widgets.push({
		widget: widget,
		type: 'switch',
		name: this.getEnabledKey(profile),
		field: new OO.ui.FieldLayout(widget, {
			label: mw.msg('schnark-fliegelflagel-manage-enable-label')
		})
	});
};

Script.prototype.buildConfigWidget = function (data) {
	var widget,
		value = Script.getUserOption(data.id, ''),
		disabled = globalPreferences === 1 && getGlobalUserOption(data.id, '') !== '';
	switch (data.type) {
	case 'checkbox':
		widget = new OO.ui.CheckboxInputWidget({
			value: '1',
			selected: value === '1'
		});
		if (disabled) {
			widget.setDisabled(true);
		} else {
			widget.once('change', function () {
				this.markAsUnsaved();
			}.bind(this));
		}
		break;
	case 'dropdown':
		widget = new OO.ui.DropdownWidget({
			$overlay: true,
			menu: {
				items: data.values.map(function (item) {
					return new OO.ui.MenuOptionWidget({
						data: item.val,
						label: item.desc
					});
				})
			}
		});
		widget.getMenu().selectItemByData(value);
		if (disabled) {
			widget.setDisabled(true);
		} else {
			widget.getMenu().once('select', function () {
				this.markAsUnsaved();
			}.bind(this));
		}
		break;
	default:
		widget = new OO.ui.TextInputWidget({
			value: value
		});
		if (disabled) {
			widget.setDisabled(true);
		} else {
			widget.once('change', function () {
				this.markAsUnsaved();
			}.bind(this));
		}
	}
	this.widgets.push({
		widget: widget,
		type: data.type,
		name: data.id,
		field: new OO.ui.FieldLayout(widget, {
			label: data.desc,
			align: data.type === 'checkbox' ? 'inline' : 'left'
		})
	});
};

Script.prototype.buildWidgets = function (profile) {
	var i;
	this.widgets = [];
	if (this.data.defaultEnabled !== true) {
		this.buildSwitcherWidget(profile);
	}
	if (this.data.config) {
		for (i = 0; i < this.data.config.length; i++) {
			this.buildConfigWidget(this.data.config[i]);
		}
	}
};

Script.prototype.toggleFilter = function (filter) {
	var visible = true, i;
	for (i = 0; i < filter.length; i++) {
		if (this.status[filter[i].key] === filter[i].val) {
			visible = false;
			break;
		}
	}
	this.panel.toggle(visible);
	return visible;
};

Script.prototype.getUnsavedIndicator = function () {
	return mw.html.element('abbr',
		{'class': 'unsaved-indicator', title: mw.msg('schnark-fliegelflagel-manage-unsaved')}, '!') + ' ';
};

Script.prototype.getActiveIndicator = function (active) {
	return mw.html.element('span', {'class': active ? 'active-indicator' : 'inactive-indicator'},
		mw.msg(active ? 'schnark-fliegelflagel-manage-active' : 'schnark-fliegelflagel-manage-inactive'));
};

Script.prototype.getConfiguredIndicator = function (configured) {
	return configured ? mw.html.element('abbr',
		{'class': 'configured-indicator', title: mw.msg('schnark-fliegelflagel-manage-configured')}, '*') + ' ' : '';
};

Script.prototype.getStatusIndicator = function (status) {
	var icons = {beta: 'β', deprecated: '†', unknown: '?'};
	return status === 'stable' ? '' : mw.html.element('abbr', {
		'class': 'status-indicator',
		title: mw.msg('schnark-fliegelflagel-manage-status-' + status)
	}, icons[status]);
};

Script.prototype.getDescription = function () {
	var moreLink = '', headline;
	if (this.data.docpage) {
		moreLink = mw.msg('schnark-fliegelflagel-manage-more-link-wrapper', mw.html.element('a', {
			target: '_blank',
			rel: 'noopener',
			href: this.data.docpage,
			title: mw.msg('schnark-fliegelflagel-manage-more-link-title')
		}, mw.msg('schnark-fliegelflagel-manage-more-link')));
	}
	headline = mw.html.escape(this.data.title) +
		mw.html.element('small', {}, new mw.html.Raw(
			' (' +
			mw.html.element('code', {}, this.id) +
			', ' +
			this.getActiveIndicator(this.status.enabled) +
			') ' +
			mw.html.element('span', {id: 'unsaved-indicator-container-' + this.id}, '') +
			this.getConfiguredIndicator(this.status.configured) +
			this.getStatusIndicator(this.status.status)
		));
	return mw.html.element('h2', {}, new mw.html.Raw(headline)) +
		mw.html.element('p', {}, new mw.html.Raw(this.data.description + moreLink));
};

Script.prototype.buildPanel = function (profile) {
	var fieldset;

	this.status = {
		configured: this.isConfigured(profile),
		status: this.data.status || 'stable',
		enabled: this.checkEnabled(),
		category: this.data.category || 'other',
		saved: true
	};

	fieldset = new OO.ui.FieldsetLayout({label: mw.msg('schnark-fliegelflagel-manage-config-label')});
	this.buildWidgets(profile);
	fieldset.addItems(this.widgets.map(function (widget) {
		return widget.field;
	}));

	this.panel = new OO.ui.PanelLayout({
		expanded: false,
		content: [new OO.ui.HtmlSnippet(this.getDescription()), fieldset]
	});
	return this.panel;
};

Script.prototype.isConfigured = function (profile) {
	var i;
	if (this.getUserEnabled(profile, '') !== '') {
		return true;
	}
	if (!this.data.config) {
		return false;
	}
	for (i = 0; i < this.data.config.length; i++) {
		if (Script.getUserOption(this.data.config[i].id, '') !== '') {
			return true;
		}
	}
	return false;
};

Script.prototype.getDataToSave = function () {
	var data = {}, i, val;

	function add (key, val) {
		if (Script.getUserOption(key, '') !== val) {
			data['userjs-' + key] = val || null;
		}
	}

	if (this.status.saved) {
		return data;
	}
	for (i = 0; i < this.widgets.length; i++) {
		switch (this.widgets[i].type) {
		case 'switch':
			val = this.widgets[i].widget.findSelectedItem().getData();
			break;
		case 'checkbox':
			val = this.widgets[i].widget.isSelected() ? '1' : '';
			break;
		case 'dropdown':
			val = this.widgets[i].widget.getMenu().findSelectedItem().getData();
			break;
		default:
			val = this.widgets[i].widget.getValue();
		}
		add(this.widgets[i].name, val);
	}
	return data;
};

//Collection extend
Collection.prototype.getScripts = function () {
	var scripts;
	if (!this.cachedScripts) {
		scripts = [];
		this.forAll(function (script) {
			var s, i;
			s = script.getScripts();
			for (i = 0; i < s.length; i++) {
				if (scripts.indexOf(s[i]) === -1) {
					scripts.push(s[i]);
				}
			}
		}, true);
		this.cachedScripts = scripts;
	}
	return this.cachedScripts;
};

Collection.prototype.searchScripts = function (search) {
	var searchLower = search.toLowerCase();
	return this.getScripts().filter(function (title) {
		return title.toLowerCase().indexOf(searchLower) !== -1 && title !== search;
	});
};

Collection.prototype.buildFilter = function (key, val, label) {
	var widget = new OO.ui.CheckboxInputWidget({selected: true});
	widget.connect(this, {change: 'onFilterChange'});
	this.filterWidgets.push({
		widget: widget,
		key: key,
		val: val,
		field: new OO.ui.FieldLayout(widget, {
			label: label,
			align: 'inline'
		})
	});
};

Collection.prototype.buildFilters = function (list) {
	var i;
	this.filterWidgets = [];
	for (i = 0; i < list.length; i++) {
		this.buildFilter(list[i].key, list[i].val, list[i].label);
	}
};

Collection.prototype.getFilterPanel = function () {
	var activeFilter, configuredFilter, savedFilter, categoryFilter, statusFilter;

	function getFieldsFor (key, widgets) {
		var i, ret = [];
		for (i = 0; i < widgets.length; i++) {
			if (widgets[i].key === key) {
				ret.push(widgets[i].field);
			}
		}
		return ret;
	}

	this.buildFilters([
		{key: 'enabled', val: true, label: mw.msg('schnark-fliegelflagel-manage-filter-active')},
		{key: 'enabled', val: false, label: mw.msg('schnark-fliegelflagel-manage-filter-inactive')},
		{key: 'configured', val: false, label: mw.msg('schnark-fliegelflagel-manage-filter-unconfigured')},
		{key: 'configured', val: true, label: mw.msg('schnark-fliegelflagel-manage-filter-configured')},
		{key: 'saved', val: false, label: mw.msg('schnark-fliegelflagel-manage-filter-unsaved')},
		{key: 'saved', val: true, label: mw.msg('schnark-fliegelflagel-manage-filter-saved')},
		{key: 'category', val: 'read', label: mw.msg('schnark-fliegelflagel-manage-filter-category-read')},
		{key: 'category', val: 'nav', label: mw.msg('schnark-fliegelflagel-manage-filter-category-nav')},
		{key: 'category', val: 'edit', label: mw.msg('schnark-fliegelflagel-manage-filter-category-edit')},
		{key: 'category', val: 'history', label: mw.msg('schnark-fliegelflagel-manage-filter-category-history')},
		{key: 'category', val: 'other', label: mw.msg('schnark-fliegelflagel-manage-filter-category-other')},
		{key: 'status', val: 'beta', label: mw.msg('schnark-fliegelflagel-manage-filter-status-beta')},
		{key: 'status', val: 'stable', label: mw.msg('schnark-fliegelflagel-manage-filter-status-stable')},
		{key: 'status', val: 'deprecated', label: mw.msg('schnark-fliegelflagel-manage-filter-status-deprecated')},
		{key: 'status', val: 'unknown', label: mw.msg('schnark-fliegelflagel-manage-filter-status-unknown')}
	]);
	activeFilter = new OO.ui.FieldsetLayout({
		label: mw.msg('schnark-fliegelflagel-manage-filter-label-active-inactive')
	});
	activeFilter.addItems(getFieldsFor('enabled', this.filterWidgets));
	configuredFilter = new OO.ui.FieldsetLayout({
		label: mw.msg('schnark-fliegelflagel-manage-filter-label-configured-unconfigured')
	});
	configuredFilter.addItems(getFieldsFor('configured', this.filterWidgets));
	savedFilter = new OO.ui.FieldsetLayout({
		label: mw.msg('schnark-fliegelflagel-manage-filter-label-saved-unsaved')
	});
	savedFilter.addItems(getFieldsFor('saved', this.filterWidgets));
	categoryFilter = new OO.ui.FieldsetLayout({
		label: mw.msg('schnark-fliegelflagel-manage-filter-label-category')
	});
	categoryFilter.addItems(getFieldsFor('category', this.filterWidgets));
	statusFilter = new OO.ui.FieldsetLayout({
		label: mw.msg('schnark-fliegelflagel-manage-filter-label-status')
	});
	statusFilter.addItems(getFieldsFor('status', this.filterWidgets));

	return new OO.ui.PanelLayout({
		expanded: false,
		framed: true,
		padded: true,
		content: [new OO.ui.HorizontalLayout({
			items: [activeFilter, configuredFilter, savedFilter, categoryFilter, statusFilter],
			classes: ['align-top']
		})]
	});
};

Collection.prototype.getButtonBar = function () {
	//TODO disable buttons, enable on first change
	var saveButton, cancelButton, debugButton, $div = $('<div>');
	saveButton = new OO.ui.ButtonWidget({
		label: mw.msg('schnark-fliegelflagel-manage-config-save'),
		flags: ['primary', 'progressive'],
		accessKey: 's'
	});
	cancelButton = new OO.ui.ButtonWidget({
		label: mw.msg('schnark-fliegelflagel-manage-config-cancel'),
		flags: 'destructive'
	});
	debugButton = new OO.ui.ButtonWidget({
		label: mw.msg('schnark-fliegelflagel-manage-debug-button')
	});
	saveButton.connect(this, {click: 'onSaveClick'});
	cancelButton.connect(this, {click: 'onCancelClick'});
	debugButton.connect(this, {click: 'onDebugClick'});
	$div.append(saveButton.$element, cancelButton.$element);
	if (globalPreferences !== 2 && Script.getUserOption('schnark-fliegelflagel-manage-debug', '0') === '1') {
		$div.append(debugButton.$element);
	}
	return $div;
};

Collection.prototype.getInterface = function (profile, warnVersionUpdate) {
	var index, $div, suggestedOptions, suggestedButton, $buttons, url, canSticky;

	index = new VariantIndexLayout({
		expanded: false
	});
	index.connect(this, {tabSelect: 'onTabSelect'});
	$div = $('<div>');
	this.filterPanel = this.getFilterPanel();
	this.filterPanel.toggle(false);
	this.noScriptsPanel = new OO.ui.PanelLayout({
		expanded: false,
		content: [mw.msg('schnark-fliegelflagel-manage-filter-no-result')]
	});
	this.noScriptsPanel.toggle(false);

	$div.append(this.filterPanel.$element);
	$div.append(this.noScriptsPanel.$element);
	this.forAll(function (script) {
		if (script.checkExists(globalPreferences === 2) && script.checkManageable()) {
			$div.append(script.buildPanel(profile).$element);
			script.manage = true;
		}
	}, true);
	index.setContent($div);

	index.addTab(mw.msg('schnark-fliegelflagel-manage-filter-all'), []);
	index.addTab(mw.msg('schnark-fliegelflagel-manage-filter-active'), [{key: 'enabled', val: false}]);
	index.addTab(mw.msg('schnark-fliegelflagel-manage-filter-inactive'), [{key: 'enabled', val: true}]);
	index.addTab(mw.msg('schnark-fliegelflagel-manage-filter-configured'), [{key: 'configured', val: false}]);
	index.addTab(mw.msg('schnark-fliegelflagel-manage-filter-unsaved'), [{key: 'saved', val: true}]);
	index.addTab(mw.msg('schnark-fliegelflagel-manage-filter-category-read'), [
		{key: 'category', val: 'nav'}, {key: 'category', val: 'edit'},
		{key: 'category', val: 'history'}, {key: 'category', val: 'other'}
	]);
	index.addTab(mw.msg('schnark-fliegelflagel-manage-filter-category-nav'), [
		{key: 'category', val: 'read'}, {key: 'category', val: 'edit'},
		{key: 'category', val: 'history'}, {key: 'category', val: 'other'}
	]);
	index.addTab(mw.msg('schnark-fliegelflagel-manage-filter-category-edit'), [
		{key: 'category', val: 'read'}, {key: 'category', val: 'nav'},
		{key: 'category', val: 'history'}, {key: 'category', val: 'other'}
	]);
	index.addTab(mw.msg('schnark-fliegelflagel-manage-filter-category-history'), [
		{key: 'category', val: 'read'}, {key: 'category', val: 'nav'},
		{key: 'category', val: 'edit'}, {key: 'category', val: 'other'}
	]);
	index.addTab(mw.msg('schnark-fliegelflagel-manage-filter-category-other'), [
		{key: 'category', val: 'read'}, {key: 'category', val: 'nav'},
		{key: 'category', val: 'edit'}, {key: 'category', val: 'history'}
	]);
	index.addTab(mw.msg('schnark-fliegelflagel-manage-filter-unstable'), [{key: 'status', val: 'stable'}]);
	index.addTab(mw.msg('schnark-fliegelflagel-manage-filter-extended'), 'filter');

	$div = $('<div>');
	$div.append(mw.html.element('p', {}, new mw.html.Raw(mw.msg('schnark-fliegelflagel-manage-config'))));
	if (profile) {
		$div.append(mw.html.element('p', {},
			new mw.html.Raw(mw.msg('schnark-fliegelflagel-manage-config-profile', profile))));
	}
	if (globalPreferences > 0) {
		$div.append(mw.html.element('p', {}, new mw.html.Raw(
			mw.msg('schnark-fliegelflagel-manage-config-global-' + globalPreferences)
		)));
	}

	if (warnVersionUpdate) {
		url = 'https://de.wikipedia.org/wiki/Benutzer:Schnark/js/fliegelflagel';
		$div.append((new OO.ui.FieldsetLayout({
			items: [
				new OO.ui.FieldLayout(
					new OO.ui.MessageWidget({
						type: 'warning',
						label: mw.msg('schnark-fliegelflagel-manage-update-code'),
						inline: true
					})
				),
				new OO.ui.FieldLayout(
					new OO.ui.Widget({content: [new OO.ui.HtmlSnippet(
						mw.msg('schnark-fliegelflagel-manage-update-code-detail',
							'<a target="_blank" rel="noopener" href="' + url + '">' + url + '</a>')
					)]})
				)
			]
		})).$element);
	}

	suggestedOptions = this.getSuggestedOptions();
	if (suggestedOptions) {
		suggestedButton = new OO.ui.ButtonWidget({
			label: mw.msg('schnark-fliegelflagel-manage-suggested-save')
		});
		suggestedButton.connect(this, {click: 'onSuggestedClick'});

		$div.append((new OO.ui.FieldsetLayout({
			items: [
				new OO.ui.FieldLayout(
					new OO.ui.MessageWidget({
						type: 'warning',
						label: mw.msg('schnark-fliegelflagel-manage-suggested-hint'),
						inline: true
					})
				),
				new OO.ui.FieldLayout(
					suggestedButton, {
						help: mw.msg('schnark-fliegelflagel-manage-suggested-detail', JSON.stringify(suggestedOptions)),
						helpInline: true,
						align: 'top'
					}
				)
			]
		})).$element);
	}

	$buttons = this.getButtonBar();
	$buttons.addClass('sticky-buttons');
	$buttons.hide().appendTo('body');
	canSticky = $buttons.css('position') === 'sticky' || $buttons.css('position') === '-webkit-sticky';
	$buttons.detach().show();

	if (!canSticky) {
		$buttons.removeClass('sticky-buttons');
		$div.append('<hr>', $buttons, '<hr>');
	}
	$div.append((new OO.ui.PanelLayout({
		framed: true,
		expanded: false,
		content: [index]
	})).$element);
	if (canSticky) {
		$div.append($buttons);
	}

	return $div;
};

Collection.prototype.getRemovedData = function () {
	var list = this.removableOptions, data = {}, i, key;
	for (i = 0; i < list.length; i++) {
		key = list[i];
		if (Script.getUserOption(key, '') !== '') {
			data['userjs-' + key] = null;
		}
	}
	return data;
};

Collection.prototype.getDataToSave = function () {
	var data = this.getRemovedData();
	this.forAll(function (script) {
		if (script.manage) {
			$.extend(data, script.getDataToSave());
		}
	});
	return data;
};

Collection.prototype.getSuggestedOptions = function () {
	var options = {}, hasChanges = false,
		//FIXME nach -config
		suggestedOptions = {
			'visualeditor-enable': true,
			'visualeditor-betatempdisable': false,
			'visualeditor-newwikitext': true,
			'visualeditor-visualdiffpage': true,
			skin: 'vector',
			language: [
				'en', 'en-ca', 'en-gb', 'simple',
				'de', 'de-at', 'de-ch', 'de-formal'
			]
		};

	function isAsSuggested (is, suggested) {
		if (Array.isArray(suggested)) {
			return suggested.indexOf(is) > -1;
		}
		if (suggested === true) {
			return !!is;
		}
		if (suggested === false) {
			return !is;
		}
		if (typeof suggested !== 'string') {
			return suggested === is;
		}
		return suggested === String(is);
	}
	function getSuggested (suggested) {
		if (Array.isArray(suggested)) {
			return suggested[0];
		}
		if (suggested === true) {
			return 1;
		}
		if (suggested === false) {
			return 0;
		}
		return suggested;

	}
	$.each(suggestedOptions, function (name, suggested) {
		if (!isAsSuggested(mw.user.options.get(name, null), suggested)) {
			hasChanges = true;
			options[name] = getSuggested(suggested);
		}
	});
	return hasChanges && options;
};

Collection.prototype.saveOptions = function (options) {
	if ($.isEmptyObject(options)) {
		return;
	}
	mw.loader.using('mediawiki.api').then(function () {
		var api = new mw.Api();
		if (globalPreferences === 2) {
			//same as saveOptions, but with action: globalpreferences
			api.saveGlobalOptions = eval('(' +
				api.saveOptions.toString()
				.replace(/(['"])options\1/g, '"globalpreferences"') +
			')');
			return api.saveGlobalOptions(options);
		}
		return api.saveOptions(options);
		//no need to update mw.user.options, we will reload the page anyway
	}).then(function () {
		location.reload(false);
	}, function () {
		OO.ui.alert(mw.msg('schnark-fliegelflagel-manage-save-failed'));
	});
};

Collection.prototype.initDebugWindow = function () {
	if (this.openDebugWindow) {
		return;
	}
	var dialog = new DebugDialog({collection: this}), windowManager = new OO.ui.WindowManager();
	$('body').append(windowManager.$element);
	windowManager.addWindows([dialog]);
	this.openDebugWindow = function () {
		windowManager.openWindow(dialog);
	};
};

Collection.prototype.applyFilter = function (filter) {
	var visible = false;
	this.forAll(function (script) {
		if (script.manage) {
			if (script.toggleFilter(filter)) {
				visible = true;
			}
		}
	});
	this.noScriptsPanel.toggle(!visible);
};

Collection.prototype.onTabSelect = function (filter) {
	this.filterPanel.toggle(filter === 'filter');
	if (filter === 'filter') {
		this.onFilterChange.call(this);
		return;
	}
	this.applyFilter(filter);
};

Collection.prototype.onFilterChange = function () {
	var i, widget, filters = [];
	for (i = 0; i < this.filterWidgets.length; i++) {
		widget = this.filterWidgets[i];
		if (!widget.widget.isSelected()) {
			filters.push({key: widget.key, val: widget.val});
		}
	}
	this.applyFilter(filters);
};

Collection.prototype.onSaveClick = function () {
	this.saveOptions(this.getDataToSave());
};

Collection.prototype.onCancelClick = function () {
	location.reload(false);
};

Collection.prototype.onDebugClick = function () {
	this.initDebugWindow();
	this.openDebugWindow();
};

Collection.prototype.onSuggestedClick = function () {
	this.saveOptions(this.getSuggestedOptions() || {});
};

Collection.prototype.initConfigure = function (profile, globalPrefs, warnVersionUpdate) {
	var $content = $('#bodyContent, #mw_contentholder');

	if (globalPrefs) {
		globalPreferencesMap.set(globalPrefs);
		if (mw.config.get('wgTitle') === 'FliegelflagelGlobal') {
			globalPreferences = 2;
			Script.getUserOption = getGlobalUserOption;
		} else if (mw.config.get('wgTitle') === 'Fliegelflagel') {
			globalPreferences = 1;
		} else {
			return;
		}
	} else if (mw.config.get('wgTitle') !== 'Fliegelflagel') {
		return;
	}

	mw.util.addCSS(css);

	this.forAll(function (script) {
		script.l10n();
	});

	document.title = mw.msg('schnark-fliegelflagel');
	$('#firstHeading').text(mw.msg('schnark-fliegelflagel'));

	$content.empty().append(
		this.getInterface(profile, warnVersionUpdate)
	);
};

//virtual outdent
});

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