Greasy Fork is available in English.

GM_config (eight's version)

A library to help you set up configure in greasemonkey script.

Ce script ne devrait pas être installé directement. C'est une librairie créée pour d'autres scripts. Elle doit être inclus avec la commande // @require https://update.greasyfork.org/scripts/7212/156587/GM_config%20%28eight%27s%20version%29.js

/* exported GM_config */

/*
Copyright 2009+, GM_config Contributors (https://github.com/sizzlemctwizzle/GM_config)

GM_config Contributors:
    Mike Medley <medleymind@gmail.com>
    Joe Simmons
    Izzy Soft
    Marti Martz

GM_config is distributed under the terms of the GNU Lesser General Public License.

    GM_config is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// ==UserScript==
// @name        GM_config (eight's version)
// @description	A library to help you set up configure in greasemonkey script.
// @namespace   eight04.blogspot.com
// @version     2.2.0
// @grant       GM_setValue
// @grant		GM_getValue
// @license		LGPL version 3 or any later version; http://www.gnu.org/copyleft/lgpl.html
// @copyright   2015+, eight <https://github.com/eight04/GM_config>
// @homepageURL https://github.com/eight04/GM_config
// @supportURL  https://github.com/eight04/GM_config/issues
// @attribution sizzlemctwizzle (https://github.com/sizzlemctwizzle/GM_config)
// @attribution Joe Simmons (https://greasyfork.org/en/scripts/1884-gm-config)
// ==/UserScript==

var GM_config = function(){

	"use strict";

	var config = {
		title: null,
		settings: null,
		local: false
	}, dialog, css, GM_config;

	function addChild(e, children) {
		if (!children) {
			return;
		}

		if (!Array.isArray(children)) {
			children = [children];
		}

		var i;
		for (i = 0; i < children.length; i++) {
			if (typeof children[i] == "string") {
				children[i] = document.createTextNode(children[i]);
			}
			e.appendChild(children[i]);
		}
	}

	function element(tag, attr, children) {
		var e, key, key2;

		e = document.createElement(tag);

		if (attr) {
			for (key in attr) {
				if (typeof attr[key] == "boolean") {
					if (attr[key]) {
						e.setAttribute(key, "");
					} else {
						e.removeAttribute(key);
					}

				} else if (key == "event") {
					for (key2 in attr[key]) {
						e["on" + key2] = attr[key][key2];
					}

				} else {
					e.setAttribute(key, attr[key]);
				}
			}
		}

		addChild(e, children);

		return e;
	}

	function frag(children) {
		var fragment = document.createDocumentFragment();
		addChild(fragment, children);
		return fragment;
	}

	function getValue(key) {
		if (config.local) {
			key = location.hostname + "/" + key;
		}
		var value = GM_getValue(key);
		if (GM_getValue(key + "/type") == "object") {
			value = JSON.parse(value);
		}
		return value;
	}

	function setValue(key, value) {
		if (config.local) {
			key = location.hostname + "/" + key;
		}
		if (typeof value == "object") {
			GM_setValue(key + "/type", "object");
			value = JSON.stringify(value);
		}
		GM_setValue(key, value);
	}

	function read() {
		var key, s;
		config.local = GM_getValue(location.hostname, false);
		for (key in config.settings) {
			s = config.settings[key];
			s.value = getValue(key, s.type);
			if (s.value == null) {
				s.value = s.default;
			}
		}
	}

	function save() {
		var key, s;
		GM_setValue(location.hostname, config.local);
		for (key in config.settings) {
			s = config.settings[key];
			if (s.value == null) {
				setValue(key, s.default);
			} else {
				setValue(key, s.value);
			}
		}
	}

	function destroyDialog() {
		dialog.element.classList.remove("config-dialog-ani");
		setTimeout(function() {
			document.body.classList.remove("config-dialog-open");
			document.body.style.paddingRight = "";
			dialog.element.parentNode.removeChild(dialog.element);
			dialog = null;
		}, 220);
	}

	function createDialog(title) {
		var iframe = element("iframe", {"class": "config-dialog-content"});
		var modal = element("div", {"class": "config-dialog", "tabindex": "-1"}, [
			element("style", null, "body.config-dialog-open { padding-right: " + (window.innerWidth - document.documentElement.offsetWidth) + "px; }"),
			iframe
		]);

		var head = element("div", {"class": "config-dialog-head"}, title);
		var body = element("div", {"class": "config-dialog-body"});
		var footer = element("div", {"class": "config-dialog-footer form-inline"});

		var style = element("style", null, getConfigCssString());

		document.body.classList.add("config-dialog-open");
		document.body.appendChild(modal);

		function manipulateIframe() {
			var doc = iframe.contentDocument;
			doc.head.appendChild(style);
			doc.body.appendChild(head);
			doc.body.appendChild(body);
			doc.body.appendChild(footer);
		}

		iframe.contentWindow.onload = manipulateIframe;

		manipulateIframe();

		function render() {
			var body = iframe.contentDocument.body,
				w = body.offsetWidth,
				h = body.scrollHeight;

			iframe.style.width = w + "px";
			iframe.style.height = h + "px";
			modal.focus();

			modal.classList.add("config-dialog-ani");
		}

		return {
			element: modal,
			head: head,
			body: body,
			footer: footer,
			render: render
		};
	}

	function close(saveFlag) {
		var key, s;

		if (!dialog) {
			return;
		}
		destroyDialog();

		for (key in config.settings) {
			s = config.settings[key];
			if (saveFlag) {
				switch (s.type) {
					case "number":
						s.value = +s.element.value;
						break;

					case "checkbox":
						s.value = s.element.checked;
						break;

					case "radio":
						s.value = s.element.querySelector("input:checked").value;
						break;

					case "select":
						if (!s.multiple) {
							s.value = s.element.value;
						} else {
							s.value = Array.prototype.map.call(
								s.element.selectedOptions,
								function(ele){
									return ele.value;
								}
							);
						}
						break;

					default:
						s.value = s.element.value;
				}
				// Create inputs
			}
			// Create inputs
			s.element = null;
		}

		if (saveFlag) {
			save();
			if (GM_config.onload) {
				GM_config.onload();
			}
		}

		if (GM_config.onclose) {
			GM_config.onclose(saveFlag);
		}
	}

	function getConfigCssString() {
		return "*,*:before,*:after{box-sizing:border-box}a{transition:all .2s linear}a:link,a:visited{color:#707070}a:hover{color:#0a53f8}a:active{color:#0a53f8}.link{color:#707070;text-decoration:underline;cursor:pointer}body{font-size:16px;font-family:\"Helvetica Neue\",Helvetica,Arial,\"微軟正黑體\",sans-serif;color:#3d3d3d;padding:0;margin:0;line-height:1}h1,h2,h3,h4,h5,h6{margin-top:30px;margin-bottom:15px;font-weight:bold}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-size:70%;color:#8a8a8a}h1 .head-reset,h2 .head-reset,h3 .head-reset,h4 .head-reset,h5 .head-reset,h6 .head-reset{font-size:16px;font-weight:normal}h1{font-size:260%}h2{font-size:215%;border-bottom:1px solid #808080;padding-bottom:.25em}h3{font-size:170%}h4{font-size:125%}h5{font-size:100%}h6{font-size:85%}p{margin-top:.9375em;margin-bottom:.9375em;line-height:1.55}input,textarea,select,button{font-size:inherit;font-family:inherit;line-height:inherit;margin:0;padding:0;vertical-align:middle;height:2em;color:inherit;background-color:transparent;border:none}input:focus,textarea:focus,select:focus,button:focus{outline:none}::-moz-placeholder,:-moz-placeholder{opacity:1}:invalid{outline:none;box-shadow:none}textarea{height:auto;line-height:1.55;padding-top:.225em;padding-bottom:.225em}select{cursor:pointer;line-height:1.55}select[multiple]{height:auto}button{cursor:pointer;text-align:center;background-image:none}button::-moz-focus-inner{border:0;padding:0}img{vertical-align:text-bottom;max-width:100%;max-height:100%}code{font-family:Menlo,Monaco,Consolas,\"Courier New\",\"細明體\",monospace;background-color:#f0f0f0;font-size:90%;padding:.25em}pre{margin-top:1em;margin-bottom:1em;font-family:Menlo,Monaco,Consolas,\"Courier New\",\"細明體\",monospace}small{font-size:90%}hr{border:none;border-top:1px solid #808080;margin:15px 0}::selection{background-color:rgba(255,169,46,0.4)}::-moz-selection{background-color:rgba(255,169,46,0.4)}fieldset,legend{border:0 none;margin:0;padding:0}input[type=\"checkbox\"],input[type=\"radio\"]{padding:0}.row-gap>fieldset>legend{position:relative;top:15px}input.ng-invalid,textarea.ng-invalid,select.ng-invalid,input.ng-invalid:hover,textarea.ng-invalid:hover,select.ng-invalid:hover,input.ng-invalid:focus,textarea.ng-invalid:focus,select.ng-invalid:focus{border-color:#cb1b1b}input[type=\"checkbox\"].ng-invalid,input[type=\"radio\"].ng-invalid,input[type=\"checkbox\"].ng-invalid:hover,input[type=\"radio\"].ng-invalid:hover,input[type=\"checkbox\"].ng-invalid:focus,input[type=\"radio\"].ng-invalid:focus{box-shadow:0 0 0 1px #cb1b1b}::-webkit-input-placeholder{color:#c9c9c9}::-moz-placeholder{color:#c9c9c9}:-ms-input-placeholder{color:#c9c9c9}:-moz-placeholder{color:#c9c9c9}.form-group{margin-top:1em;margin-bottom:1em}.row>.form-group{margin:0}label,legend{vertical-align:bottom}label+.form-control,legend+.form-control,label+.input-group,legend+.input-group,label+.radio,legend+.radio,label+.checkbox,legend+.checkbox,label .form-control,legend .form-control,label .input-group,legend .input-group,label .radio,legend .radio,label .checkbox,legend .checkbox{margin-top:.3em}.form-control{border:1px solid #808080;border-radius:.1875em;display:block;width:100%;line-height:1;display:inline-block;padding-left:.5em;padding-right:.5em;color:#707070;transition:.2s all linear}.form-control:hover{border-color:#ccc}.form-control:focus{border-color:#0a53f8;color:#242424}.form-control[disabled]{background-color:#f0f0f0;border-color:#ccc;cursor:not-allowed}.form-control[disabled]:hover,.form-control[disabled]:active{border-color:#ccc}.radio,.checkbox{position:relative}.radio input[type=\"radio\"],.checkbox input[type=\"radio\"],.radio input[type=\"checkbox\"],.checkbox input[type=\"checkbox\"]{color:inherit;position:absolute;width:auto;height:auto;top:0;bottom:0;left:0;margin:auto 0}.radio label,.checkbox label,label.radio,label.checkbox{display:table;padding-left:1.5em;cursor:pointer;line-height:1.55;transition:.2s all linear}.radio label:hover,.checkbox label:hover,label.radio:hover,label.checkbox:hover{color:#707070}.radio+.radio,.radio+.checkbox,.checkbox+.radio,.checkbox+.checkbox{margin-top:0}.form-inline input,.form-inline select,.form-inline button,.form-inline .radio,.form-inline .checkbox,.form-inline fieldset,.form-inline .form-group{display:inline-block;vertical-align:middle;width:auto;margin:0}.btn-default{border:1px solid #808080;border-radius:.1875em;transition-property:border-color,box-shadow;transition-duration:.2s;transition-timing-function:linear;padding-left:.5em;padding-right:.5em;min-width:4.25em}.btn-default:hover{border-color:#ccc}.btn-default:focus{color:#3d3d3d;border-color:#0a53f8}.btn-default:active{border-color:#0a53f8;box-shadow:inset .12em .12em .5em #dedede}.btn-default[disabled]{background-color:#f0f0f0;border-color:#ccc;cursor:not-allowed}.btn-default[disabled]:hover,.btn-default[disabled]:active{border-color:#ccc}.btn-sm{border:1px solid #808080;border-radius:.1875em;transition-property:border-color,box-shadow;transition-duration:.2s;transition-timing-function:linear;width:2em;line-height:.8}.btn-sm:hover{border-color:#ccc}.btn-sm:focus{color:#3d3d3d;border-color:#0a53f8}.btn-sm:active{border-color:#0a53f8;box-shadow:inset .12em .12em .5em #dedede}.btn-sm[disabled]{background-color:#f0f0f0;border-color:#ccc;cursor:not-allowed}.btn-sm[disabled]:hover,.btn-sm[disabled]:active{border-color:#ccc}.btn-close{width:1em;height:1em;vertical-align:baseline;color:#707070}.btn-close:hover{color:inherit}.btn-circle{display:inline-block;width:1.25em;height:1.25em;text-align:center;line-height:1.25;font-size:80%;vertical-align:baseline;border-radius:50%;border:1px solid #707070;margin:-1px 0;color:#707070}.btn-circle:hover{color:inherit;border-color:inherit}.btn-block{display:block;width:100%}.btn-group{display:block;display:inline-block;border:1px solid #808080;border-radius:.1875em}.btn-group>*{display:table-cell;vertical-align:middle;white-space:nowrap}.btn-group>*{border-width:0 1px;border-radius:0;margin-right:-1px}.btn-group>*:first-child{margin-left:-1px}body{display:inline-block;padding:30px;overflow:hidden}.config-dialog-head{font-weight:bold;font-size:120%}.config-dialog-head .btn-sm{font-size:50%;font-weight:normal;width:auto;padding:0 2px;float:right}.config-dialog-head .btn-sm+*{margin-right:5px}.config-dialog-footer.form-inline{white-space:nowrap}.config-dialog-footer.form-inline>*+*{margin-left:15px}.config-dialog-footer.form-inline label.radio{padding:4px 0 1px 18px}";
	}

	function getCssString() {
		return ".config-dialog-open{overflow:hidden}.config-dialog{position:fixed;top:0;left:0;right:0;bottom:0;vertical-align:middle;text-align:center;background:rgba(0,0,0,0.5);overflow:auto;z-index:99999;opacity:0;transition:opacity .2s linear;white-space:nowrap}.config-dialog:before{content:\"\";display:inline-block;height:100%;vertical-align:middle}.config-dialog-ani{opacity:1}.config-dialog-content{text-align:left;display:inline-block;width:90%;vertical-align:middle;background:white;margin:30px 0;box-shadow:0 0 30px black;border-width:0;transition:transform .2s linear;transform:translateY(-20px)}.config-dialog-ani .config-dialog-content{transform:none}";
	}

	function setupDialogValue (reset, imports) {
		var key, setting, value;

		for (key in config.settings) {
			setting = config.settings[key];

			if (reset) {
				value = setting.default;
			} else {
				if (imports && imports[key] != undefined) {
					value = imports[key];
				} else {
					value = setting.value;
				}
			}

			switch (setting.type) {
				case "number":
					setting.element.value = value.toString();
					break;

				case "checkbox":
					setting.element.checked = value;
					break;

				case "radio":
					setting.element.querySelector("[value=" + value + "]").checked = true;
					break;

				case "select":
					if (!setting.multiple) {
						setting.element.querySelector("[value=" + value + "]").selected = true;
					} else {
						while (setting.element.selectedOptions.length) {
							setting.element.selectedOptions[0].selected = false;
						}
						value.forEach(function(value){
							setting.element.querySelector("[value=" + value + "]").selected = true;
						});
					}
					break;

				default:
					setting.element.value = value;
					break;
			}
		}
	}

	function createInputs(dialog) {
		var key, s, group;

		for (key in config.settings) {
			s = config.settings[key];

			if (s.type == "textarea") {
				s.element = element("textarea", {"id": key});
				s.element.classList.add("form-control");
				group = [
					element("label", {"for": key}, s.label),
					s.element
				];
			} else if (s.type == "radio") {
				s.element = element("fieldset", null, [element("legend", null, s.label)].concat(Object.keys(s.options).map(function(optKey){
					return element("label", {class: "radio"}, [
						element("input", {type: "radio", name: key, value: optKey}),
						s.options[optKey]
					]);
				})));
				group = [
					s.element
				];
			} else if (s.type == "select") {
				s.element = element(
					"select",
					{class: "form-control", multiple: !!s.multiple},
					Object.keys(s.options).map(function(optKey){
						return element(
							"option",
							{value: optKey},
							s.options[optKey]
						);
					})
				);
				group = element("label", null, [
					s.label,
					s.element
				]);
			} else {
				s.element = element("input", {"id": key, "type": s.type});

				switch (s.type) {
					case "number":
						s.element.classList.add("form-control");
						group = [
							element("label", {"for": key}, s.label),
							s.element
						];
						break;
					case "checkbox":
						group = element("div", {"class": "checkbox"}, [
							s.element,
							element("label", {"for": key}, s.label)
						]);
						break;
					default:
						s.element.classList.add("form-control");
						group = [
							element("label", {"for": key}, s.label),
							s.element
						];
				}
			}

			dialog.body.appendChild(
				element("div", {"class": "form-group"}, group)
			);
		}
	}

	function createFooter(dialog) {
		var local = config.local;

		dialog.footer.appendChild(frag([
			element("button", {"class": "btn-default", event: {
				click: function () {
					config.local = local;
					close(true);
				}
			}}, "Save"),

			element("button", {"class": "btn-default", event: {
				click: function() {
					close();
				}
			}}, "Cancel"),

			element("button", {class: "btn-default", event: {
				click: function() {
					setupDialogValue(true);
				}
			}}, "Default"),

			element("label", {class: "radio"}, [
				element("input", {type: "radio", name: "working-scope", checked: !local, event: {
					change: function () {
						local = !this.checked;
					}
				}}),
				"Global setting"
			]),

			element("label", {class: "radio"}, [
				element("input", {type: "radio", name: "working-scope", checked: local, event: {
					change: function () {
						local = this.checked;
					}
				}}),
				"On " + location.hostname
			])
		]));
	}

	function exportSetting() {
		var exports = JSON.stringify(getConfigObj());
		prompt("Copy:", exports);
	}

	function importSetting() {
		var imports = prompt("Paste your setting:"), setting;
		if (!imports) {
			return;
		}
		try {
			setting = JSON.parse(imports);
		} catch (err) {
			alert("Invalid JSON!");
			return;
		}
		setupDialogValue(false, setting);
	}

	function createHead(dialog) {
		dialog.head.appendChild(frag([
			element("button", {class: "btn-sm", event: {
				click: exportSetting
			}}, "Export"),
			element("button", {class: "btn-sm", event: {
				click: importSetting
			}}, "Import")
		]));
	}

	function open() {
		if (!css) {
			css = element("style", {"id": "config-css"}, getCssString());
			document.head.appendChild(css);
		}

		if (!dialog) {
			dialog = createDialog(config.title);

			// Create head
			createHead(dialog);

			// Create inputs
			createInputs(dialog);

			// Setup values
			setupDialogValue();

			// Create footer
			createFooter(dialog);

			// Render
			dialog.render();
		}
	}

	function getConfigObj(key) {
		var con;

		if (typeof key == "string") {
			return config.settings[key].value;
		} else {
			if (typeof key == "object") {
				con = key;
			} else {
				con = {};
			}
			for (key in config.settings) {
				con[key] = config.settings[key].value;
			}
			return con;
		}
	}
	
	function setup(options, loadCallback) {
		GM_config.init(GM_info.script.name, options);
		GM_config.onload = loadCallback;
		GM_registerMenuCommand(GM_info.script.name + " - Configure", GM_config.open);
		loadCallback();
	}

	GM_config = {
		init: function(title, settings) {
			config.title = title;
			config.settings = settings;
			read();
			return GM_config.get();
		},
		open: open,
		close: close,
		get: getConfigObj,
		setup: setup
	};

	return GM_config;
}();