
		/* form provides function setCFF(id, key, value) and setCFFOrder(form, Array fids) */
		function changeFeature(id, key, value) {
			/* key: mode, position, order, move */
			var el = $('#form-'+id);
			if (key == 'move') {
				// move is a bit special
				var features = el.parent().children('.fop-form-feature:not(.fop-form-feature-tabheader)');
				var newIndex;
				if (value == 'up') {
					newIndex = (features.length + features.index(el) - 1) % features.length;
					if (newIndex == features.length - 1) {
						el.insertAfter(features[newIndex]);
					} else {
						el.insertBefore(features[newIndex]);
					}
				} else if (value == 'down') {
					newIndex = (features.index(el) + 1) % features.length;
					if (newIndex == 0) {
						el.insertBefore(features[newIndex]);
					} else {
						el.insertAfter(features[newIndex]);
					}
				}
				features = el.parent().children('.fop-form-feature:not(.fop-form-feature-tabheader)');
				fids = Array.from(features).map(f => f.id.substring(5));	// feature-ids ohne 'form-' extrahieren
				// AJAX update order of all features
				setCFFOrder(fids);
			} else if (key == 'reset') {
				// reset
				setCFF(id, key, value);
			} else {
				// otherwise: add/remove class
				el.removeClass(function (i, className) {
					// className contains whitespace separated classes
					return className.split(/\s+/).filter(x => x.startsWith('fop-form-feature-'+key+'-')).join(' ');
				});
				el.addClass('fop-form-feature-'+key+'-'+value);
				if (key == 'mode' && value == 'tab') {
					el.removeClass('fop-form-feature-collapsed'); // start tabs uncollapsed; refreshTabHeaders will collapse them if there are multiple
				}
				// AJAX call save to settings
				setCFF(id, key, value);
			}
			/* revisit tab headers */
			refreshTabHeaders(el.parent());
		}
		function refreshTabHeaders(el) {
			var features = el.children('.fop-form-feature');
			var tabheader = null, tabheaderPosition = -1, tabheaderOpen = null;
			for (var i = 0; i < features.length; i++) {
				// state machine for feature sanitization
				var f = $(features[i]);
				if (f.hasClass('fop-form-feature-tabheader')) {
					if (tabheader && !tabheaderPosition) {
						// empty tabheader
						tabheader.remove();
						features.splice(i-1, 1);
						i--;
						tabheader = f; // set f as new tabheader
						tabheaderPosition = 0;
						tabheaderOpen = null;
					} else if (tabheader) {
						// double tabheader: remove the second one
						f.remove();
						features.splice(i, 1);
						i--;
					} else {
						// remember f as current tabheader
						tabheader = f;
						tabheaderPosition = 0;
						tabheaderOpen = null;
					}
				} else if (f.hasClass('fop-form-feature-mode-tab')) {
					if (!tabheader) {
						// insert tabheader before
						tabheader = $('<div></div>');
						tabheader.addClass('fop-form-feature fop-form-feature-tabheader mdl-tabs is-upgraded'); // hack is-upgraded is used in some CSS rules
						tabheader.append($('<div></div>').addClass('mdl-tabs__tab-bar'));
						tabheader.insertBefore(f);
						// 3 buttons for navigation (prevButton & nextButton2 links von Tabs, nextButton rechts von Tabs)
						var prevButton = $('<button type="button" class="mdl-button fop-button-half mdl-js-button mdl-button--raised fop-form-wizard-button-left"><i class="material-icons">keyboard_arrow_left</i></button>').click(function () { var th = $(this).closest('.fop-form-feature-tabheader').find('.mdl-tabs__tab-bar'); $.merge(th.find('.mdl-tabs__tab.is-active').prevAll('.mdl-tabs__tab').first(), th.children().last()).first().click(); });
						tabheader.prepend(prevButton);
						var nextButton = $('<button type="button" class="mdl-button fop-button-half mdl-js-button mdl-button--raised fop-form-wizard-button-right"><i class="material-icons">keyboard_arrow_right</i></button>').click(function () { var th = $(this).closest('.fop-form-feature-tabheader').find('.mdl-tabs__tab-bar'); $.merge(th.find('.mdl-tabs__tab.is-active').nextAll('.mdl-tabs__tab').first(), th.children().first()).first().click(); });
						tabheader.append(nextButton);
						tabheaderPosition = 0;
						tabheaderOpen = null;
						// also insert into features[]
						features.splice(i, 0, tabheader);
						i++;
					}
					// add next-button to the end of CFF
					f.find('>.fop-form-wizard-button-right').remove();
					if (f.next().is('.fop-form-feature-mode-tab')) {
						var nextButton = $('<button type="button" class="mdl-button mdl-js-button mdl-button--raised fop-form-wizard-button-right">' + window.i18n_weiter + '<i class="material-icons">keyboard_arrow_right</i></button>').click(function () { var th = $(this).closest('.fop-form-feature').prevAll('.fop-form-feature-tabheader:first').find('.mdl-tabs__tab-bar'); $.merge(th.find('.mdl-tabs__tab.is-active').nextAll('.mdl-tabs__tab').first(), th.children().first()).first().click(); });
						f.prepend(nextButton);
					}

					// ensure nth child in tabheder children list is me
					var tmp_title = f.find('.fop-form-feature-title').clone();
					tmp_title.find('i').remove();
					tmp_title = tmp_title.text().trim();
					var tabheaders = tabheader.find('.mdl-tabs__tab');
					while (tabheaders[tabheaderPosition] && tabheaders[tabheaderPosition].innerText != tmp_title) {
						$(tabheaders.splice(tabheaderPosition, 1)[0]).remove();
					}
					if (!tabheaders[tabheaderPosition]) {
						// add me to the list
						var newtabheader = $('<div></div>');
						newtabheader.text(tmp_title);
						newtabheader.attr('title', tmp_title);
						newtabheader.addClass('mdl-tabs__tab');
						f.data(); 	// Workaround für Bug in jQuery 3.6.1
						newtabheader.data('feature', f.data('feature'));
						newtabheader.click(function () {
							var self = $(this);
							var other = self.parent().children('.is-active');

							self.addClass('is-active');
							other.removeClass('is-active');

							setCFF(self.data('feature'), 'collapsed', 0);
							setCFF(other.data('feature'), 'collapsed', 1);

							el.children('.fop-form-feature').each(function (i, x) {
								x = $(x);
								if (x.data('feature') == self.data('feature')) {
									x.removeClass('fop-form-feature-collapsed');
									var focusel = x.find('input[type!="hidden"],textarea,select[tabindex!="-1"]').filter(':visible').first();
									if (focusel.length > 0) {
										focusel.focus();
										focusel.select();
									} else {
										document.activeElement.blur();
									}
									x.find('.fop-lazy-reload').click(); // reload lazy elements
								}
								if (x.data('feature') == other.data('feature')) {
									x.addClass('fop-form-feature-collapsed');
								}
							});
						});
						tabheader.find('.mdl-tabs__tab-bar').append(newtabheader);
						tabheaders[tabheaderPosition] = newtabheader[0];
					}
					if (!f.hasClass('fop-form-feature-collapsed')) {
						if (tabheaderOpen) {
							// collapse this feature since we already have an open one (but not persist it, otherwise it will retrigger refreshTabHeaders)
							f.addClass('fop-form-feature-collapsed');
							$(tabheaders[tabheaderPosition]).removeClass('is-active');
						} else {
							// this is the first non-collapsed
							tabheaderOpen = f;
							$(tabheaders[tabheaderPosition]).addClass('is-active');
						}
					} else {
						$(tabheaders[tabheaderPosition]).removeClass('is-active');
					}
					tabheaderPosition++;

				} else {
					// neither tabheader nor tab: forget our old tabheader
					if (tabheader && !tabheaderPosition) {
						// remove unwanted elements
						f.find('>.fop-form-wizard-button-right').remove();

						// empty tabheader
						tabheader.remove();
						features.splice(i-1, 1);
						i--;
					}
					tabheader = null;
				}
			}
		}
	