GM_config_lz-string ConfigLzString

Refactor GM_config, this version uses lz-string to access data for a Library script

Version vom 04.10.2018. Aktuellste Version

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greatest.deepsurf.us/scripts/372760/633995/GM_config_lz-string%20ConfigLzString.js

// ==UserScript==
// @namespace     http://tampermonkey.net/
// @exclude       *

// ==UserLibrary==
// @name          GM_config_lz-string ConfigLzString 
// @description   Refactor GM_config, this version uses lz-string to access data for a Library script
// @author        avan
// @license       MIT
// @version       0.5

// ==/UserScript==

// ==/UserLibrary==
GM_configField.prototype.toNode = function() {
	var field = this.settings,
		value = this.value,
		options = field.options,
		type = field.type,
		className = field.class,
		style = field.style,
		id = this.id,
		configId = this.configId,
		labelPos = field.labelPos,
		create = this.create;
	function addLabel(pos, labelEl, parentNode, beforeEl) {
		if (!beforeEl) beforeEl = parentNode.firstChild;
		switch (pos) {
			case 'right':
			case 'below':
				if (pos == 'below')
					parentNode.appendChild(create('br', {}));
				parentNode.appendChild(labelEl);
				break;
			default:
				if (pos == 'above')
					parentNode.insertBefore(create('br', {}), beforeEl);
				parentNode.insertBefore(labelEl, beforeEl);
		}
	}
	var retNode = create('div', {
		className: 'config_var',
		id: configId + '_' + id + '_var',
		title: field.title || '',

	}),
		firstProp;
	// Retrieve the first prop
	for (var i in field) {
		firstProp = i;
		break;
	}
	var label = field.label && type != "button" ?
		create('label', {
			id: configId + '_' + id + '_field_label',
			for: configId + '_field_' + id,
			className: 'field_label'
		}, field.label) : null;
	var props = {className: className || '', style: style || ''};
	switch (type) {
		case 'textarea':
			props.innerHTML = value;
			props.id = configId + '_field_' + id;
			props.className = (props.className ? props.className + ' ' : '') + 'block';
			props.cols = (field.cols ? field.cols : 20);
			props.rows = (field.rows ? field.rows : 2);
			retNode.appendChild((this.node = create('textarea', props)));
			break;
		case 'radio':
			props.id = configId + '_field_' + id;
			var wrap = create('div', props);
			this.node = wrap;
			for (var i = 0, len = options.length; i < len; ++i) {
				var radLabel = create('label', {
					className: 'radio_label'
				}, options[i]);
				var rad = wrap.appendChild(create('input', {
					value: options[i],
					type: 'radio',
					name: id,
					checked: options[i] == value
				}));
				var radLabelPos = labelPos &&
					(labelPos == 'left' || labelPos == 'right') ?
					labelPos : firstProp == 'options' ? 'left' : 'right';
				addLabel(radLabelPos, radLabel, wrap, rad);
			}
			retNode.appendChild(wrap);
			break;
		case 'select':
			props.id = configId + '_field_' + id;
			var wrap = create('select', props);
			this.node = wrap;
			for (var i = 0, len = options.length; i < len; ++i) {
				var option = options[i];
				wrap.appendChild(create('option', {
					value: option,
					selected: option == value
				}, option));
			}
			retNode.appendChild(wrap);
			break;
		default: // fields using input elements
			props.id = configId + '_field_' + id;
			props.type = type;
			props.value = type == 'button' ? field.label : value;
			switch (type) {
				case 'checkbox':
					props.checked = value;
					break;
				case 'button':
					props.size = field.size ? field.size : 25;
					if (field.script) field.click = field.script;
					if (field.click) props.onclick = field.click;
					break;
				case 'hidden':
					break;
				case 'password':
					props.size = field.size ? field.size : 25;
					break;
				default:
					// type = text, int, or float
					props.type = 'text';
					props.size = field.size ? field.size : 25;
			}
			retNode.appendChild((this.node = create('input', props)));
	}
	if (label) {
		// If the label is passed first, insert it before the field
		// else insert it after
		if (!labelPos)
			labelPos = firstProp == "label" || type == "radio" ?
				"left" : "right";
		addLabel(labelPos, label, retNode);
	}
	return retNode;
};
var ConfigLzString = function () {
	GM_configStruct.apply(this, arguments);
	if (arguments.length > 0 && arguments[0].src) {
		this.srcs = arguments[0].src.replace(/ *[ ;,]+ */g, ' ').split(/[ ;,]/);
	}
}
ConfigLzString.prototype = GM_configStruct.prototype;
ConfigLzString.prototype.open = function() {
	// Die if the menu is already open on this page
	// You can have multiple instances but you can't open the same instance twice
	var match = document.getElementById(this.id);
	if (match && (match.tagName == "IFRAME" || match.childNodes.length > 0)) return;
	// Sometimes "this" gets overwritten so create an alias
	var config = this;
	// Function to build the mighty config window :)
	function buildConfigWin(body, head) {
		var create = config.create,
			fields = config.fields,
			configId = config.id,
			bodyWrapper = create('div', {
				id: configId + '_wrapper'
			});
		// Append the style which is our default style plus the user style
		head.appendChild(
			create('style', {
				type: 'text/css',
				textContent: config.css.basic + config.css.stylish
			}));
		// Add header and title
		bodyWrapper.appendChild(create('div', {
			id: configId + '_header',
			className: 'config_header block center'
		}, config.title));
		// Append elements
		var section = bodyWrapper,
			secNum = 0; // Section count
		// loop through fields
		for (var id in fields) {
			var field = fields[id],
				settings = field.settings;
			if (settings.section) { // the start of a new section
				section = bodyWrapper.appendChild(create('div', {
					className: 'section_header_holder',
					id: configId + '_section_' + secNum
				}));
				if (Object.prototype.toString.call(settings.section) !== '[object Array]')
					settings.section = [settings.section];
				if (settings.section[0])
					section.appendChild(create('div', {
						className: 'section_header center',
						id: configId + '_section_header_' + secNum
					}, settings.section[0]));
				if (settings.section[1])
					section.appendChild(create('p', {
						className: 'section_desc center',
						id: configId + '_section_desc_' + secNum
					}, settings.section[1]));
				++secNum;
			}
			// Create field elements and append to current section
			section.appendChild((field.wrapper = field.toNode()));
		}
		// Add save and close buttons
		bodyWrapper.appendChild(create('div', {
			id: configId + '_buttons_holder'
		},
									   create('button', {
			id: configId + '_saveBtn',
			textContent: 'Save',
			title: 'Save settings',
			className: 'saveclose_buttons',
			onclick: function() {
				config.save()
			}
		}),
									   create('button', {
			id: configId + '_closeBtn',
			textContent: 'Close',
			title: 'Close window',
			className: 'saveclose_buttons',
			onclick: function() {
				config.close()
			}
		}),
									   create('div', {
			className: 'reset_holder block'
		},
											  // Reset link
											  create('a', {
			id: configId + '_resetLink',
			textContent: 'Reset to defaults',
			href: '#',
			title: 'Reset fields to default values',
			className: 'reset',
			onclick: function(e) {
				e.preventDefault();
				config.reset()
			}
		})
											 )));
		body.appendChild(bodyWrapper); // Paint everything to window at once
		config.center(); // Show and center iframe
		window.addEventListener('resize', config.center, false); // Center frame on resize
		// Call the open() callback function
		config.onOpen(config.frame.contentDocument || config.frame.ownerDocument,
					  config.frame.contentWindow || window,
					  config.frame);
		// Close frame on window close
		window.addEventListener('beforeunload', function() {
			config.close();
		}, false);
		// Now that everything is loaded, make it visible
		config.frame.style.display = "block";
		config.isOpen = true;
	}
	// Change this in the onOpen callback using this.frame.setAttribute('style', '')
	var defaultStyle = 'bottom: auto; border: 1px solid #000; display: none; height: 75%;' +
		' left: 0; margin: 0; max-height: 95%; max-width: 95%; opacity: 0;' +
		' overflow: auto; padding: 0; position: fixed; right: auto; top: 0;' +
		' width: 75%; z-index: 9999;';
	// Either use the element passed to init() or create an iframe
	if (this.frame) {
		this.frame.id = this.id; // Allows for prefixing styles with the config id
		this.frame.setAttribute('style', defaultStyle);
		var head = this.frame.ownerDocument.getElementsByTagName('head')[0];
		if (this.srcs && this.srcs.length > 0 ) {
			this.srcs.forEach(function(src) {
				head.appendChild(this.create('script', {src: src}));
			})
		}
		buildConfigWin(this.frame, head);
	} else {
		// Create frame
		document.body.appendChild((this.frame = this.create('iframe', {
			id: this.id,
			style: defaultStyle
		})));
		// In WebKit src can't be set until it is added to the page
		this.frame.src = 'about:blank';
		// we wait for the iframe to load before we can modify it
		this.frame.addEventListener('load', function(e) {
			var frame = config.frame;
			var body = frame.contentDocument.getElementsByTagName('body')[0];
			body.id = config.id; // Allows for prefixing styles with the config id
			var head = frame.contentDocument.getElementsByTagName('head')[0];
			if (config.src) head.appendChild(config.create('script', {src: config.src}));
			if (config.srcs && config.srcs.length > 0 ) {
				config.srcs.forEach(function(src) {
					head.appendChild(config.create('script', {src: src}));
				})
			}
			buildConfigWin(body, head);
		}, false);
	}
};
ConfigLzString.prototype.read = function (store) {
	var rval, cKey, dValue;
	try {
		cKey = LZString.compressToUTF16(store || this.id);
		dValue = LZString.decompressFromUTF16(this.getValue(cKey, '{}'));
		rval = this.parser(dValue);
	} catch(e) {
		this.log("GM_config failed to read saved settings!");
		rval = {};
	}
	return rval;
};
ConfigLzString.prototype.write = function (store, obj) {
	if (!obj) {
		var values = {},
			forgotten = {},
			fields = this.fields;

		for (var id in fields) {
			var field = fields[id];
			var value = field.toValue();

			if (field.save) {
				if (value !== null) {
					values[id] = value;
					field.value = value;
				} else
					values[id] = field.value;
			} else
				forgotten[id] = value;
		}
	}
	try {
		var cKey = LZString.compressToUTF16(store || this.id),
			cValue = LZString.compressToUTF16(this.stringify(obj || values));
		this.setValue(cKey, cValue);
	} catch(e) {
		this.log("GM_config failed to save settings!");
	}

	return forgotten;
};